/* Author : mithun shanbhag, mrshanbh@syr.edu */ /* note : jeffrey richter used something like this in his msj article back in 94, i think. But i can bet on the fact that someone thought of this before him. This technique has been used before along with windows subclassing to spy on password fields in other processes's text box. */ #include "intcpt.h" //----< Opens the target process & retrieves its handle >----// bool OpenTargetProcess ( const DWORD & dwTargetPid, HANDLE & hProc ) { /* pre-condition check */ ///////////////////////// assert ( dwTargetPid != 0 ) ; if ( dwTargetPid == 0 ) return false ; /* open the target process & get its handle */ ////////////////////////////////////////////// if ( ( hProc = OpenProcess ( ( PROCESS_QUERY_INFORMATION | /* */ PROCESS_CREATE_THREAD | /* */ PROCESS_VM_OPERATION | /* */ PROCESS_VM_WRITE /* */ ), FALSE, /* handle not inheritable */ dwTargetPid /* process id */ ) ) == NULL ) { return false ; } return true ; } //----< Allocates some memory in the target process >----// bool AllocateMemInTarget ( char * pszReplacementDll, DWORD & dwSizeToBeAllocated, LPVOID & lpvAllocBase, HANDLE & hProc ) { /* pre-condition check */ ///////////////////////// assert ( pszReplacementDll != 0 ) ; if ( pszReplacementDll == 0 ) return false ; assert ( dwSizeToBeAllocated == 0 ) ; if ( dwSizeToBeAllocated != 0 ) return false ; assert ( lpvAllocBase == 0 ) ; if ( lpvAllocBase != 0 ) return false ; assert ( hProc != NULL ) ; if ( hProc == NULL ) return false ; assert ( hProc != INVALID_HANDLE_VALUE ) ; if ( hProc == INVALID_HANDLE_VALUE ) return false ; /* do the allocation in the target process */ ///////////////////////////////////////////// dwSizeToBeAllocated = ( (DWORD) strlen ( pszReplacementDll ) + /* length of the string */ 1 /* account for null-termination */ ) * ( sizeof (char) ) ; if ( ( lpvAllocBase = VirtualAllocEx ( hProc, NULL, /* let system decide where to allocate */ dwSizeToBeAllocated, MEM_COMMIT, PAGE_READWRITE ) ) == NULL ) { return false ; } return true ; } //----< Writes name of replacement dll to memory of target process >----// bool WriteReplacementDllNameIntoTarget ( char * pszReplacementDll, const DWORD & dwSizeToBeAllocated, LPVOID & lpvAllocBase, HANDLE & hProc ) { /* pre-condition check */ ///////////////////////// assert ( pszReplacementDll != NULL ) ; if ( pszReplacementDll == NULL ) return false ; assert ( dwSizeToBeAllocated != 0 ) ; if ( dwSizeToBeAllocated == 0 ) return false ; assert ( lpvAllocBase != 0 ) ; if ( lpvAllocBase == 0 ) return false ; assert ( hProc != NULL ) ; if ( hProc == NULL ) return false ; assert ( hProc != INVALID_HANDLE_VALUE ) ; if ( hProc == INVALID_HANDLE_VALUE ) return false ; /* now start writing */ /////////////////////// if ( WriteProcessMemory ( hProc, lpvAllocBase, pszReplacementDll, dwSizeToBeAllocated, NULL ) == FALSE ) { return false ; } return true ; } //----< Writes name of replacement dll to memory of target process >----// // // notes : # 1 - assuming unicode env, hence LoadLibraryW(). This needs an upgrade. // # 2 - assuming that target app links with CRT libraries. // # 3 - assuming that system dlls in upper 2 GB of any process's addr space, // -> cannot not rebased manually. // -> are not reloacted by the loader. // Which simply means that we are assuming the address for LoadLibraryW() // in memory to be the same across all processes. bool TrackLoadLibAddress ( FARPROC & dwLoadLibAddr, HANDLE & hProc ) { /* get handle to the "kernel32.dll", currently loaded */ /* in the target process's address space. We are assuming */ /* that the target app has linked with the CRT libraries */ ////////////////////////////////////////////////////////////////// /* now get the address of LoadLibraryW() from kernel32.dll */ ///////////////////////////////////////////////////////////// if ( ( dwLoadLibAddr = (FARPROC) GetProcAddress ( GetModuleHandle("kernel32.dll"), "LoadLibraryW" ) ) == NULL ) { return false ; } return true ; } //----< spwans a remote thread inside target process >----// bool SpawnRemoteThreadInTarget ( FARPROC & dwLoadLibAddr, LPVOID & lpvAllocBase, HANDLE & hProc ) { /* internal state check */ ////////////////////////// assert ( hProc != NULL ) ; if ( hProc == NULL ) return false ; assert ( hProc != INVALID_HANDLE_VALUE ) ; if ( hProc == INVALID_HANDLE_VALUE ) return false ; assert ( dwLoadLibAddr != 0 ) ; if ( dwLoadLibAddr == 0 ) return false ; assert ( lpvAllocBase != 0 ) ; if ( lpvAllocBase == 0 ) return false ; /* now spawn a thread in the address space of the target process */ /////////////////////////////////////////////////////////////////// HANDLE hRemoteThr ; if ( ( hRemoteThr = CreateRemoteThread ( hProc, NULL, 0, (LPTHREAD_START_ROUTINE)dwLoadLibAddr, // <<---- does all the trick ! lpvAllocBase, 0, NULL ) ) == NULL ) { CloseHandle ( hRemoteThr ) ; /* close thread handle */ return false ; } WaitForSingleObject ( hRemoteThr, INFINITE ) ; CloseHandle ( hRemoteThr ) ; return true ; } //----< spwans a remote thread inside target process >----// bool FreeMemFromTarget ( LPVOID & lpvAllocBase, HANDLE & hProc ) { /* internal state check */ ////////////////////////// assert ( hProc != NULL ) ; if ( hProc == NULL ) return false ; assert ( hProc != INVALID_HANDLE_VALUE ) ; if ( hProc == INVALID_HANDLE_VALUE ) return false ; assert ( lpvAllocBase != 0 ) ; if ( lpvAllocBase == 0 ) return false ; /* free the allocated memory from the target process */ /////////////////////////////////////////////////////// if ( VirtualFreeEx ( hProc, lpvAllocBase, 0, MEM_RELEASE ) == FALSE ) { return false ; } return true ; } //----< spawns a remote thread inside target process >----// bool InjectReplacementCodeintoTarget ( const DWORD & dwTargetPid, char * pszReplacementDll ) { /* some pre-condition checks */ /////////////////////////////// assert ( dwTargetPid != NULL ) ; if ( dwTargetPid == NULL ) return false ; assert ( pszReplacementDll != NULL ) ; if ( pszReplacementDll == NULL ) return false ; ////////////////////////////////////////////////////// /* Six simple steps to attain Nirvana */ ////////////////////////////////////////////////////// /* 1: open the target process & get its handle */ /* 2: allocate memory in the target process */ /* 3: write the replacement dll name into target */ /* 4: now track the address of LoadLibrary() */ /* 5: spawn a remote thread in target process */ /* 6: free allocated memory from the target */ ////////////////////////////////////////////////////// HANDLE hProc ; LPVOID lpvAllocBase = 0 ; DWORD dwSizeToBeAllocated = 0 ; FARPROC dwLoadLibAddr ; bool bStatus ; if ((bStatus = OpenTargetProcess(dwTargetPid, hProc)) == true) if ((bStatus = AllocateMemInTarget(pszReplacementDll,dwSizeToBeAllocated,lpvAllocBase,hProc)) == true) if ((bStatus = WriteReplacementDllNameIntoTarget(pszReplacementDll,dwSizeToBeAllocated,lpvAllocBase,hProc)) == true) if ((bStatus = TrackLoadLibAddress(dwLoadLibAddr,hProc)) == true) if ((bStatus = SpawnRemoteThreadInTarget(dwLoadLibAddr,lpvAllocBase,hProc)) == true) if ((bStatus = FreeMemFromTarget(lpvAllocBase,hProc)) == true) ; /* clean up and leave */ //////////////////////// CloseHandle ( hProc ) ; return ( bStatus ) ; } /////////////////////////// //----< Entry Point >----// /////////////////////////// void main ( int argc, char * argv[] ) { DWORD dwTargetPid ; /* a little command-line check */ ///////////////////////////////// if ( argc != 5 ) { cout << "Syntax Error ! \n" << "Usage : intcpt \n" ; return ; } if ( (dwTargetPid = atoi((char *)argv[1]) ) == 0 ) { cout << "Error! Invalid Process id.\n\n" ; return ; } char * pszOriginalDll = argv[2] ; char * pszReplacementDll = argv[3] ; char * pszCallToBePatched = argv[4] ; //////////////////////////////////////////////////// /* honestly, i could not make it any more simpler */ //////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// /* Step # 1 : Inject your replacement code into target process */ ///////////////////////////////////////////////////////////////// bool bInjected = InjectReplacementCodeintoTarget ( dwTargetPid, /* id of process to be patched */ pszReplacementDll /* dll containing replacement code */ ) ; if ( ! bInjected ) { cout << "Error! Process # 0x" << hex << dwTargetPid << " could not be patched.\n\n" ; return ; } }