crash2.cpp

Posting crash2.cpp here because appearently home.kimo.com.tw will die soon.

This thing (from command prompt) takes a process id and a thread id, and it crashes on that thread. Actually, it pauses that thread, crashes on a new thread, and fakes the stacktrace 🙂 Tested on WinXP/MSVC7.1


/*
* based on "crash" by timeless, http://viper.haque.net/~timeless/crash/
* and the DLL injection article at http://www.codeproject.com/threads/winspy.asp
* plus MSDN:
* ms-help://MS.VSCC.2003/MS.MSDNQTR.2003APR.1033/debug/base/debugging_functions.htm
* ms-help://MS.VSCC.2003/MS.MSDNQTR.2003APR.1033/debug/base/structured_exception_handling_functions.htm
* ms-help://MS.VSCC.2003/MS.MSDNQTR.2003APR.1033/dllproc/base/process_and_thread_functions.htm
*
* crashes a process in a given thread.
* tested ONLY on WinXP SP2 / MSVC 7.1 (no extra PSDK)
*
* compile: (or just not have debugging at all, that works too)
* cl crash2.cpp /Fd /Zi /link /incremental:no
*
* usage: crash2 <process id> <thread id>
* process id and thread id can be obtained via SysInternals' Process Explorer.
*
* Mook, 2005. Public domain, as-is. Will definitely crash stuff :)
*/

#include <windows.h>
#include <cstdio>
#include <string.h>

struct t_InjectionData {
LPVOID pointer;
size_t length;
};

HANDLE inject(HANDLE hProcess, LPTHREAD_START_ROUTINE pRoutine, size_t nRoutineLength, t_InjectionData* pData, size_t nDataLength)
{
size_t totalSize = nRoutineLength + sizeof(LPVOID) * nDataLength;
for (int i = 0; i < nDataLength; ++i)
totalSize += pData[i].length + 0x10; // padding is just to be safe

LPVOID dest =
::VirtualAllocEx(
hProcess,
NULL,
totalSize,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE
);
if (!dest)
throw GetLastError();
SIZE_T count;
BOOL success =
::WriteProcessMemory(
hProcess,
dest,
pRoutine,
nRoutineLength,
&count);
if (!success)
throw GetLastError();
if (count < nRoutineLength) {
throw ERROR_WRITE_FAULT;
}

LPVOID nextPointer = (LPVOID)((size_t)dest + nRoutineLength);
LPVOID nextBlock = (LPVOID)((size_t)nextPointer + sizeof(LPVOID) * nDataLength);
for (int i = 0; i < nDataLength; ++i) {
success =
::WriteProcessMemory(
hProcess,
nextPointer,
&nextBlock,
sizeof(LPVOID),
&count
);
if (!success)
throw GetLastError();
if (count < sizeof(LPVOID)) {
throw ERROR_WRITE_FAULT;
}
success =
::WriteProcessMemory(
hProcess,
nextBlock,
pData[i].pointer,
pData[i].length,
&count);
if (!success)
throw GetLastError();
if (count < pData[i].length) {
throw ERROR_WRITE_FAULT;
}
nextPointer = (LPVOID)((size_t)nextPointer + sizeof(LPVOID));
nextBlock = (LPVOID)((size_t)(nextBlock) + pData[i].length);
}

HANDLE hThread =
::CreateRemoteThread(
hProcess,
NULL,
0,
(LPTHREAD_START_ROUTINE)dest,
(LPVOID)((size_t)dest + nRoutineLength),
0,
NULL
);

return hThread;
}

typedef FARPROC (WINAPI *TT_GetProcAddress) (HMODULE, LPCSTR);
typedef void (WINAPI *TT_DebugBreak) (void);
typedef LPTOP_LEVEL_EXCEPTION_FILTER (WINAPI *TT_SetUnhandledExceptionFilter)(LPTOP_LEVEL_EXCEPTION_FILTER);
typedef BOOL (WINAPI *TT_GetThreadContext)(HANDLE, LPCONTEXT);
typedef HANDLE (WINAPI *TT_OpenThread)(DWORD, BOOL, DWORD);
typedef DWORD (WINAPI *TT_SuspendThread)(HANDLE);

struct t_ThreadParams {
HMODULE hKernel32;
CHAR szDebugBreak[0x10];
CHAR szSetUnhandledExceptionFilter[0x20];
CHAR szGetThreadContext[0x20];
CHAR szOpenThread[0x10];
CHAR szSuspendThread[0x10];
LPTOP_LEVEL_EXCEPTION_FILTER pOldUnhandledExceptionHandler;
DWORD dwThreadId;

// we don't have enough stack in the injected functions, so we
// use the space here instead
TT_GetProcAddress pGetProcAddress;
TT_DebugBreak pDebugBreak;
TT_SetUnhandledExceptionFilter pSetUnhandledExceptionFilter;
TT_GetThreadContext pGetThreadContext;
TT_OpenThread pOpenThread;
TT_SuspendThread pSuspendThread;

HANDLE hThread;
};

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
LPVOID *pointers = (LPVOID*)lpParameter;

t_ThreadParams * params = (t_ThreadParams*) pointers[0];

params->pGetProcAddress = (TT_GetProcAddress)params->pGetProcAddress;
params->pDebugBreak = (TT_DebugBreak)params->pGetProcAddress(params->hKernel32, params->szDebugBreak);
params->pSetUnhandledExceptionFilter =
(TT_SetUnhandledExceptionFilter)params->pGetProcAddress(params->hKernel32, params->szSetUnhandledExceptionFilter);

params->pGetThreadContext = (TT_GetThreadContext)params->pGetProcAddress(params->hKernel32, params->szGetThreadContext);
params->pOpenThread = (TT_OpenThread)params->pGetProcAddress(params->hKernel32, params->szOpenThread);
params->pSuspendThread = (TT_SuspendThread)params->pGetProcAddress(params->hKernel32, params->szSuspendThread);

HANDLE hThread = params->pOpenThread(THREAD_ALL_ACCESS, FALSE, params->dwThreadId);
params->pSuspendThread(hThread);

params->pOldUnhandledExceptionHandler =
params->pSetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)pointers[1]);
params->pDebugBreak();
return 0;
}

LONG myUnhandledExceptionFilter( struct _EXCEPTION_POINTERS* ExceptionInfo )
{
t_ThreadParams* params;
_asm {
call dummy
dummy:
pop ecx
sub ecx, 9
mov params, ecx
}
params--;

params->pGetProcAddress = (TT_GetProcAddress)params->pGetProcAddress;
params->pGetThreadContext = (TT_GetThreadContext)params->pGetProcAddress(params->hKernel32, params->szGetThreadContext);
params->pOpenThread = (TT_OpenThread)params->pGetProcAddress(params->hKernel32, params->szOpenThread);
params->hThread = params->pOpenThread(THREAD_ALL_ACCESS, FALSE, params->dwThreadId);
params->pGetThreadContext(params->hThread, ExceptionInfo->ContextRecord);

// call the existing handler (talkback)
return params->pOldUnhandledExceptionHandler(ExceptionInfo);
}

int main(int argc, char**argv)
{
if (argc < 3)
return 1;

DWORD dwProcessId = atoi(argv[1]);
printf("process id: %li\n", dwProcessId);

DWORD dwThreadId = atoi(argv[2]);
printf("thread id: %li\n", dwThreadId);

DWORD dwDesiredAccess =
PROCESS_CREATE_THREAD ||
PROCESS_QUERY_INFORMATION ||
PROCESS_VM_OPERATION ||
PROCESS_VM_WRITE ||
PROCESS_VM_READ;
dwDesiredAccess = PROCESS_ALL_ACCESS;
BOOL bInheritHandle = 0;
HANDLE hProcess = OpenProcess(
dwDesiredAccess,
bInheritHandle,
dwProcessId
);

t_ThreadParams params;
params.hKernel32 = ::GetModuleHandle(TEXT("Kernel32"));
params.pGetProcAddress = (TT_GetProcAddress)::GetProcAddress( params.hKernel32, "GetProcAddress" );
strcpy(params.szDebugBreak, "DebugBreak");
strcpy(params.szSetUnhandledExceptionFilter, "SetUnhandledExceptionFilter");
strcpy(params.szGetThreadContext, "GetThreadContext");
strcpy(params.szOpenThread, "OpenThread");
strcpy(params.szSuspendThread, "SuspendThread");
params.dwThreadId = dwThreadId;

t_InjectionData data[2];
data[0].pointer = ¶ms;
data[0].length = sizeof(params);
data[1].pointer = myUnhandledExceptionFilter;
data[1].length = ((size_t)main) - ((size_t)myUnhandledExceptionFilter);

printf("filter size: %08x\n", ((size_t)main) - ((size_t)myUnhandledExceptionFilter));
printf("proc size: %08x\n", ((size_t)myUnhandledExceptionFilter) - ((size_t)ThreadProc));
printf("main: %08x\nfilter:%08x\n,proc:%08x\n", main, myUnhandledExceptionFilter, ThreadProc);

HANDLE newThread =
inject(
hProcess,
ThreadProc,
((size_t)myUnhandledExceptionFilter) - ((size_t)ThreadProc),
data,
sizeof(data) / sizeof(data[0])
);

WaitForSingleObject(newThread, INFINITE);

CloseHandle(hProcess);
return 0;
}

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: