如何让指定的程序崩溃

admin 1862 2026-01-26 11:07:04

为了验证程序崩溃信息的收集和上报机制,需要让程序崩溃。为了达到这个目的,一般的做法是临时在程序中加入引发异常的代码。这种做法只在开发人员自己编译的版本中有效,在正式版本中无法进行验证。另外一种方法是加入一个隐藏开关,但这样会在正式版本中带上调试代码,也不理想。最好的方法是在不修改任何代码的前提下,也能够让程序崩溃。

这看起来似乎不可能,但实际上是可行的,而且做法简单得让人惊讶。Windows API有一个CreateRemoteThread函数,这个函数可以在指定的进程中创建一个线程,并且能够让这个线程执行任意代码——DLL注入就是通过这种方式实现的。显然,只要用这个函数创建一个会产生异常的线程就可以了。先来看一下这个函数的原型:

123456789HANDLE WINAPI CreateRemoteThread( _In_ HANDLE hProcess, _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ SIZE_T dwStackSize, _In_ LPTHREAD_START_ROUTINE lpStartAddress, _In_ LPVOID lpParameter, _In_ DWORD dwCreationFlags, _Out_ LPDWORD lpThreadId);

值得关注的是hProcess和lpStartAddress参数,其它参数的含义可以参考MSDN文档,这里不再赘述。先来看lpStartAddress参数,这个参数指定线程的入口点地址,这个地址必须是在目标进程的地址空间中。在本文的特殊需求下,我们并不需要传一个实际可执行的地址,只需要传入0即可。这样的话,该线程会尝试从0地址执行,这毫无疑问会引发访问违规异常,导致程序崩溃——这就达到了我们的目的。

再来看hProcess参数,这个参数表示目标进程的句柄,这个句柄必须具有以下访问权限:

PROCESS_CREATE_THREAD

PROCESS_QUERY_INFORMATION

PROCESS_VM_OPERATION

PROCESS_VM_WRITE

PROCESS_VM_READ

有了这些基础知识,我们就可以写一个简单的命令行程序来让指定的进程崩溃了。完整的代码如下:

1234567891011121314151617181920212223242526272829303132333435#include #include int main(int argc, char** argv) { if (argc < 2) { std::wcout << L"A PID is needed." << std::endl; return 1; } int pid = atoi(argv[1]); if (pid <= 0) { std::wcout << L"Invalid PID." << std::endl; return 2; } HANDLE process_handle = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, pid); if (process_handle == nullptr) { std::wcout << L"Open process failed. Error: " << GetLastError() << '.' << std::endl; return 3; } HANDLE thread_handle = CreateRemoteThread(process_handle, nullptr, 0, 0, nullptr, 0, nullptr); if (thread_handle == nullptr) { std::wcout << L"Create remote thread failed. Error: " << GetLastError() << '.' << std::endl; return 4; } std::wcout << L"Crashed. :)" << std::endl; return 0;}

假设这个命令行程序的名称是crasher.exe,目标进程的PID是10032,那么只要像下面这样调用就能让这个进程崩溃:

crasher.exe 10032

如果你的目标进程是一个普通程序,那么本文到这里就结束了。但如果你的目标进程是一个服务程序,情况就有所不同了。CreateRemoteThread有一个限制,它不能跨会话来创建线程。例如,服务程序都在0号会话下运行,而制造崩溃的程序是在当前用户登录的会话下运行(必定不是0号会话),所以CreateRemoteThread的调用会失败。为了解决这个问题,需要让制造崩溃的程序转移到0号会话下运行。可以借助Sysinternals出品的PsExec工具来实现这个目的,只要执行下面的命令行即可:

PsExec.exe -accepteula -s crasher.exe 10032

-accepteula参数表示接受Sysinternals的最终用户许可协议。Sysinternals的工具在首次使用的时候都会弹出最终用户许可协议对话框,使用这个参数可以防止对话框弹出。-s表示在系统会话,也就是0号会话中执行后面的命令。最终效果就是在0号会话中执行了crasher.exe 10032这个命令。

上一篇
下一篇
相关文章