|
// Sample code that shows how to call CreateRemoteThread with
// multiple parameters.
//
// Coded by. Defsanguje [July 26 2008]
#include <stdio.h>
#include <windows.h>
using namespace std;
////////////////////////////////////////////////////////////////////////////////
// "Extended" version of CreateRemoteThread. Supports multiple parameters and //
// handles parameter passing. //
////////////////////////////////////////////////////////////////////////////////
// Parameters: //
// //
// - __in HANDLE hProcess //
// A handle to the process in which the thread is to be created //
// - __in LPSECURITY_ATTRIBUTES lpThreadAttributes //
// A pointer to a SECURITY_ATTRIBUTES structure that specifies a security //
// descriptor for the new thread. If lpThreadAttributes is NULL, the thread //
// gets a default security descriptor. //
// - __in SIZE_T dwStackSize //
// The initial size of the stack, in bytes. The system rounds this value to //
// the nearest page. If this parameter is 0 (zero), the new thread uses the //
// default size for the executable. //
// - __in LPTHREAD_START_ROUTINE lpStartAddress //
// A pointer to the application-defined function of type //
// LPTHREAD_START_ROUTINE to be executed by the thread and represents the //
// starting address of the thread in the remote process. The function must //
// exist in the remote process. //
// - __in DWORD dwCreationFlags //
// The flags that control the creation of the thread. //
// (0, CREATE_SUSPENDED, STACK_SIZE_PARAM_IS_A_RESERVATION) //
// - __out LPDWORD lpThreadId //
// A pointer to a variable that receives the thread identifier. Can be NULL //
// - __in LPSTR szFormat //
// Similar to format in printf/scanf. Specifies types of arguments. //
// There's three different specifiers for szFormat: //
// %x/%u/%d - DWORD, 4 bytes, value passed directly to the thread //
// %[num]d - Write num bytes from argument to remote process, pass pointer //
// - to the data for the thread (ex. %10d) //
// %s - Write ASCII string to remote process, pass pointer to the //
// string for the thread //
////////////////////////////////////////////////////////////////////////////////
// Remarks: //
// //
// Threads that exits using TerminateThread(), ExitThread() or similar doesn't//
// free memory that's allocated for "callgate" //
////////////////////////////////////////////////////////////////////////////////
HANDLE
CreateRemoteThreadEx(HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress,
DWORD dwCreationFlags, LPDWORD lpThreadId,
LPSTR szFormat, ...)
{
char *lpCallGate,
*lpData,
*lpCode,
*lpArg;
va_list vlParameters;
BYTE i, j;
DWORD dwDataSize,
dwCallSize,
dwWritten,
dwAmount = 0; // amount of parameters
char CallGateShellcode[] = "\xE8\x1D\x00\x00\x00" // CALL $+0x1D
/*/*/
/*|*/"\x50" // PUSH EAX
/*|*/"\x68\x58\x58\xC3\x90" // PUSH 90C35858 (code for POP EAX\nPOP EAX\nRETN)"
/*|*/"\x68\x00\x40\x00\x00" // PUSH MEM_RELEASE
/*|*/"\x6A\x01" // PUSH 1
/*|*/"\x68\x00\x00\x00\x00" // PUSH 00000000 (-> PUSH lpCallGate)
/*|*/"\x54" // PUSH ESP
/*|*/"\x83\x04\x24\x0C" // ADD DWORD [ESP], 0x0C
/*|*/"\x68\x00\x00\x00\x00" // PUSH 00000000 (-> PUSH VirtualFree)
/*|*/"\xC3" // RETN
/*\*/
"\x68\x00\x00\x00\x00" // PUSH 00000000 (-> PUSH lpStartAddress)
"\xC3" // RETN
;
// Calculate the size of our callgate. Depends on
// amount of parameters.
if(szFormat)
{
// Count %'s
for(i = 0; szFormat != '\0'; i++)
if(szFormat == '%')
dwAmount++;
// Calculate size of data (%s, %[num]d)
i = 0;
dwDataSize = 0;
va_start(vlParameters, dwAmount);
while(szFormat != '\0')
{
if(szFormat != '%')
return NULL;
i++;
switch(szFormat)
{
case 'd':
case 'u':
case 'x':
va_arg(vlParameters, DWORD);
break;
case 's':
dwDataSize += lstrlen(va_arg(vlParameters, char*)) + 1;
break;
default: // number?
// Conversion from string to integer
for(j = 0; szFormat >= '0' && szFormat <= '9'; i++)
j = j * 10 + szFormat - '0';
if(!j || szFormat != 'd') return 0; // Converting failed
va_arg(vlParameters, char*);
dwDataSize += j;
break;
}
i++;
}
va_end(vlParameters);
} // if(szFormat)
dwCallSize = dwAmount * (4+1) // Size of PUSH instructions
+ dwDataSize // %s, %d
+ sizeof(CallGateShellcode);
// Allocate memory for callgate constructing (local process)
char* lpShellcodeBuffer = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwCallSize);
if(!lpShellcodeBuffer)
return NULL;
// Allocate memory from remote process
lpCallGate = (char*)VirtualAllocEx(hProcess, NULL, dwCallSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if(!lpCallGate)
{
HeapFree(GetProcessHeap(), 0, lpShellcodeBuffer);
return NULL;
}
// Construct it. Copy data/strings to beginning of the buffer,
// code to the end.
va_start(vlParameters, dwAmount);
lpData = lpShellcodeBuffer;
lpCode = lpShellcodeBuffer + dwDataSize + dwAmount * (1+4);
i = 0;
if(szFormat)
{
while(szFormat != '\0')
{
i++; // %
switch(szFormat)
{
case 'd':
case 'u':
case 'x':
lpCode -= 5; // due to calling conventions
(*lpCode) = 0x68; // PUSH
*(DWORD*)(lpCode+1) = (DWORD)va_arg(vlParameters, DWORD);
break;
case 's':
lpArg = va_arg(vlParameters, char*);
j = lstrlen(lpArg) + 1;
break;
default:
// Conversion from string to integer
lpArg = va_arg(vlParameters, char*);
for(j = 0; szFormat >= '0' && szFormat <= '9'; i++)
j = j * 10 + szFormat - '0';
break;
}
if(szFormat == 's' || szFormat == 'd')
{
lpCode -= 5;
(*lpCode) = 0x68;
*(DWORD*)(lpCode+1) = (DWORD)(lpCallGate + (lpData-lpShellcodeBuffer));
while(j)
{
(*lpData) = *lpArg;
lpData++, lpArg++;
j--;
}
}
i++; // xsd
}
va_end(vlParameters);
} // if(szFormat)
// Copy the shellcode
// (it's responsible to push arguments to stack, virtualfree itself, call the thread)
lpCode = lpShellcodeBuffer + dwDataSize + dwAmount * (1+4);
*(DWORD*)(CallGateShellcode + 19) = (DWORD)lpCallGate;
*(DWORD*)(CallGateShellcode + 35) = (DWORD)lpStartAddress;
*(DWORD*)(CallGateShellcode + 29) = (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"), "VirtualFree");
for(i = 0; i < sizeof(CallGateShellcode); i++, lpCode++)
(*lpCode) = CallGateShellcode;
// Write the shellcode to remote process and call it
WriteProcessMemory(hProcess, lpCallGate, lpShellcodeBuffer, dwCallSize, &dwWritten);
HeapFree(GetProcessHeap(), 0, lpShellcodeBuffer);
if(!dwWritten)
return NULL;
return CreateRemoteThread(hProcess, lpThreadAttributes, dwStackSize,
(LPTHREAD_START_ROUTINE)(lpCallGate + dwDataSize), 0,
dwCreationFlags, lpThreadId);
}
int main(int argc, char *argv[])
{
char szWnd[256];
HANDLE hProcess = NULL, hThr = NULL;
HWND hWnd = NULL;
DWORD dwPID = 0, dwExitCode;
printf("Boring CreateRemoteThreadEx() demo\n");
do
{
printf("Give window (not process) name\n");
// whitespaces also
scanf("%255[^\t\n]", szWnd);
while(getchar() != (int)'\n');
GetWindowThreadProcessId(FindWindow(NULL, szWnd), &dwPID);
if(dwPID)
{
hProcess =
OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION
| PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
FALSE,
dwPID
);
} else printf ("FindWindow(); failed\n");
} while(!hProcess);
hThr = CreateRemoteThreadEx(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("user32.dll"), "MessageBoxA"),
0, NULL,
"%x%s%8d%x",
NULL, "Hello world", "Caption", MB_ICONEXCLAMATION | MB_YESNOCANCEL);
/*hThr = CreateRemoteThreadEx(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetTickCount"),
0, NULL,
NULL); // "");*/
CloseHandle(hProcess);
if(hThr)
{
printf("Created remote thread succesfully!\n"
"Waiting for thread exit code...\n");
WaitForSingleObject(hThr, INFINITE);
GetExitCodeThread(hThr, &dwExitCode);
CloseHandle(hThr);
printf("Thread exited with code: %d\n", dwExitCode);
} else printf("Remote thread creation failed!");
system("PAUSE");
return EXIT_SUCCESS;
} |
|