02.06
Welcome, this is my first post in this blog.
Today I will show you how to inject code into a remote process under Windows XP. I chose this as my first post, because it is indeed an interesting topic. As you may have already read at my About page, I do not take any responsibility for the way how information taken from here is used. If you want more detailed information about the API’s used here, please refer to the PSDK or MSDN. In this example, we will inject code into explorer.exe to show a simple message box. If you want, you can replace the thread with your own…
There are several steps which we will follow:
1. Write code to execute in explorer.exe, then add a limiter.
#include <windows.h> #include <tlhelp32.h> typedef int (WINAPI *MBOX)(HWND, LPCTSTR, LPCTSTR, UINT); //We will import the APIs, so we will need this. typedef struct { //The structure containing: MBOX mbx; //The holder of the function's address char x[512]; //The first parameter, the text char y[512]; //The second parameter, the caption } CI_DATA; #pragma check_stack(off) //Needed in debug mode thanks to Napalm for helping me fix the debug mode crash... DWORD WINAPI iCode(CI_DATA *ciData) { if(ciData) //If the pointer to the structure is not NULL ciData->mbx(0, ciData->x, ciData->y, MB_OK); //Don't be shy and display the message box return 0; } which we will later need to determine the size #pragma check_stack(on)//-""- |
You will later need the address and size of the function. For the size you will need a function that starts right after your to-be-injected code. Then you subtract the TBI (to-be-injected) function address from the limiters address. Our work gets a bit complicated with the addresses. In release mode the adresses have no change, so there’s no problem. However, in debug mode the addresses are not found where they normally would in release mode. A relative jump is taken when a function is called in debug mode. You need to add the relative address to the address following the jmp, and the result will be the function’s ‘true’ address.
So now we have a function which we can copy into explorer.exe’s memory space.
2. Next we will make a function to open a process by name, and give you debugging privileges. The function will return a handle to the process with full access.
HANDLE OpenNamedProcess(char *pName) { HANDLE hProcessSnap; HANDLE hProcess = NULL; PROCESSENTRY32 pEntry32; ZeroMemory(&pEntry32, sizeof(PROCESSENTRY32)); pEntry32.dwSize = sizeof(PROCESSENTRY32); hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if(hProcessSnap == INVALID_HANDLE_VALUE) return NULL; if(!Process32First(hProcessSnap, &pEntry32)) { CloseHandle(hProcessSnap); return NULL; } do { if(!lstrcmpi(pEntry32.szExeFile, pName)) { //If the process name matches HANDLE hToken; LUID luid; TOKEN_PRIVILEGES tp; ZeroMemory(&tp, sizeof(TOKEN_PRIVILEGES)); if(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) { if(LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) { tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL); //Try to get debug privileges } CloseHandle(hToken); } hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pEntry32.th32ProcessID); //No matter what open the process CloseHandle(hProcessSnap); //Close the handle to the snapshot return hProcess; //Return the process } } while(Process32Next(hProcessSnap, &pEntry32)); CloseHandle(hProcessSnap); return NULL; } |
Ok, so now we have a TBI function and OpenNamedProcess. Now it’s time to make the injecting function.
3. This is what we will do:
A. Allocate memory in remote process.
B. Copy function into the allocated space.
C. Copy function’s data into the allocated space.
D. Execute function
E. Clean the mess
Here is the function prototype:
BOOL InjectCode(char *pName, void *func, void *limiter, void *strct, size_t strctSize)
Here is the full function:
BOOL InjectCode(char *pName, void *func, void *limiter, void *strct, size_t strctSize) { HANDLE hProcess = 0; LPVOID lpAddr = 0; LPVOID prmAddr = 0; DWORD apiAddr = 0; HANDLE hRemoteThread = 0; DWORD dwSize; DWORD *cx; #ifndef _DEBUG //If there's no debug info cx = (DWORD)func; //the function addresses are not changed dwSize = (DWORD)((DWORD)limiter - (DWORD)cx); //subtract the function from the limiter to obtain the function's size #else //If there is... DWORD *jmpAddr = (DWORD)limiter + 1; //get the relative jump value dwSize = (DWORD)*jmpAddr + (DWORD)limiter + 5; //Add it to the next opcode sequence jmpAddr = (DWORD)func + 1; //Get the other relative jump value cx = (DWORD)*jmpAddr + (DWORD)func + 5; //find the address for the actual function dwSize -= (DWORD)cx; //subtract the function from the limiter to obtain the function's size #endif hProcess = OpenNamedProcess(pName); //Open the process if(!hProcess) { MessageBox(0, "Couldn't open process!", "Error!", MB_OK | MB_ICONERROR); return FALSE; } //Allocate memory for the function lpAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(!lpAddr) { CloseHandle(hProcess); MessageBox(0, "Couldn't allocate memory!", "Error!", MB_OK | MB_ICONERROR); return FALSE; } //Allocate memory for the functions data prmAddr = VirtualAllocEx(hProcess, NULL, strctSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(!prmAddr) { VirtualFreeEx(lpAddr, lpAddr, 0, MEM_RELEASE); CloseHandle(hProcess); MessageBox(0, "Couldn't allocate memory!", "Error!", MB_OK | MB_ICONERROR); return FALSE; } //Copy the function WriteProcessMemory(hProcess, lpAddr, cx, dwSize, NULL); //Copy the data WriteProcessMemory(hProcess, prmAddr, (LPVOID)strct, strctSize, NULL); //Start the function hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpAddr, prmAddr, 0, NULL); if(hRemoteThread) { //If it is successfully created, WaitForSingleObject(hRemoteThread, INFINITE); //Wait until it finishes, CloseHandle(hRemoteThread); //then close the handle } VirtualFreeEx(lpAddr, lpAddr, 0, MEM_RELEASE); //Free the memory VirtualFreeEx(prmAddr, prmAddr, 0, MEM_RELEASE); //-""- CloseHandle(hProcess);//Close the process' handle return TRUE; //Succeed. } |
Full example source:
#include <windows.h> #include <tlhelp32.h> typedef int (WINAPI *MBOX)(HWND, LPCTSTR, LPCTSTR, UINT); //We will import the APIs, so we will need this. typedef struct { //The structure containing: MBOX mbx; //The holder of the function's address char x[512]; //The first parameter, the text char y[512]; //The second parameter, the caption } CI_DATA; #pragma check_stack(off) //Needed in debug mode thanks to napalm for helping me fix the debug mode crash... DWORD WINAPI iCode(CI_DATA *ciData) { if(ciData) //If the pointer to the structure is not NULL ciData->mbx(0, ciData->x, ciData->y, MB_OK); //Don't be shy and display the message box return 0; } void __declspec(naked) __stdcall iCodeEnd(){ } //This is the limiter, which we will later need to determine the size #pragma check_stack(on)//-""- HANDLE OpenNamedProcess(char *pName) { HANDLE hProcessSnap; HANDLE hProcess = NULL; PROCESSENTRY32 pEntry32; ZeroMemory(&pEntry32, sizeof(PROCESSENTRY32)); pEntry32.dwSize = sizeof(PROCESSENTRY32); hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if(hProcessSnap == INVALID_HANDLE_VALUE) return NULL; if(!Process32First(hProcessSnap, &pEntry32)) { CloseHandle(hProcessSnap); return NULL; } do { if(!lstrcmpi(pEntry32.szExeFile, pName)) { //If the process name matches HANDLE hToken; LUID luid; TOKEN_PRIVILEGES tp; ZeroMemory(&tp, sizeof(TOKEN_PRIVILEGES)); if(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) { if(LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) { tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL); //Try to get debug privileges } CloseHandle(hToken); } hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pEntry32.th32ProcessID); //No matter what open the process CloseHandle(hProcessSnap); //Close the handle to the snapshot return hProcess; //Return the process } } while(Process32Next(hProcessSnap, &pEntry32)); CloseHandle(hProcessSnap); return NULL; } BOOL InjectCode(char *pName, void *func, void *limiter, void *strct, size_t strctSize) { HANDLE hProcess = 0; LPVOID lpAddr = 0; LPVOID prmAddr = 0; DWORD apiAddr = 0; HANDLE hRemoteThread = 0; DWORD dwSize; DWORD *cx; #ifndef _DEBUG //If there's no debug info cx = (DWORD)func; //the function addresses are not changed dwSize = (DWORD)((DWORD)limiter - (DWORD)cx); //subtract the function from the limiter to obtain the function's size #else //If there is... DWORD *jmpAddr = (DWORD)limiter + 1; //get the relative jump value dwSize = (DWORD)*jmpAddr + (DWORD)limiter + 5; //Add it to the next opcode sequence jmpAddr = (DWORD)func + 1; //Get the other relative jump value cx = (DWORD)*jmpAddr + (DWORD)func + 5; //find the address for the actual function dwSize -= (DWORD)cx; //subtract the function from the limiter to obtain the function's size #endif hProcess = OpenNamedProcess(pName); //Open the process if(!hProcess) { MessageBox(0, "Couldn't open process!", "Error!", MB_OK | MB_ICONERROR); return FALSE; } //Allocate memory for the function lpAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(!lpAddr) { CloseHandle(hProcess); MessageBox(0, "Couldn't allocate memory!", "Error!", MB_OK | MB_ICONERROR); return FALSE; } //Allocate memory for the functions data prmAddr = VirtualAllocEx(hProcess, NULL, strctSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(!prmAddr) { VirtualFreeEx(lpAddr, lpAddr, 0, MEM_RELEASE); CloseHandle(hProcess); MessageBox(0, "Couldn't allocate memory!", "Error!", MB_OK | MB_ICONERROR); return FALSE; } //Copy the function WriteProcessMemory(hProcess, lpAddr, cx, dwSize, NULL); //Copy the data WriteProcessMemory(hProcess, prmAddr, (LPVOID)strct, strctSize, NULL); //Start the function hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpAddr, prmAddr, 0, NULL); if(hRemoteThread) { //If it is successfully created, WaitForSingleObject(hRemoteThread, INFINITE); //Wait until it finishes, CloseHandle(hRemoteThread); //then close the handle } VirtualFreeEx(lpAddr, lpAddr, 0, MEM_RELEASE); //Free the memory VirtualFreeEx(prmAddr, prmAddr, 0, MEM_RELEASE); //-""- CloseHandle(hProcess);//Close the process' handle return TRUE; //Succeed. } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { CI_DATA ourData; ourData.mbx = (MBOX)GetProcAddress(GetModuleHandle("user32.dll"), "MessageBoxA"); wsprintf(ourData.x, "Hi"); wsprintf(ourData.y, "Hello"); InjectCode("notepad.exe", iCode, iCodeEnd, &ourData, sizeof(CI_DATA)); return 0; } |
Well hai thur!
nice blog :D
keep up …nice web