如何让指定的程序崩溃
为了验证程序崩溃信息的收集和上报机制,需要让程序崩溃。为了达到这个目的,一般的做法是临时在程序中加入引发异常的代码。这种做法只在开发人员自己编译的版本中有效,在正式版本中无法进行验证。另外一种方法是加入一个隐藏开关,但这样会在正式版本中带上调试代码,也不理想。最好的方法是在不修改任何代码的前提下,也能够让程序崩溃。
这看起来似乎不可能,但实际上是可行的,而且做法简单得让人惊讶。Windows API有一个CreateRemoteThread
函数,这个函数可以在指定的进程中创建一个线程,并且能够让这个线程执行任意代码——DLL注入就是通过这种方式实现的。显然,只要用这个函数创建一个会产生异常的线程就可以了。先来看一下这个函数的原型:
1 | HANDLE WINAPI CreateRemoteThread( |
值得关注的是hProcess
和lpStartAddress
参数,其它参数的含义可以参考MSDN文档,这里不再赘述。先来看lpStartAddress
参数,这个参数指定线程的入口点地址,这个地址必须是在目标进程的地址空间中。在本文的特殊需求下,我们并不需要传一个实际可执行的地址,只需要传入0
即可。这样的话,该线程会尝试从0地址执行,这毫无疑问会引发访问违规异常,导致程序崩溃——这就达到了我们的目的。
再来看hProcess
参数,这个参数表示目标进程的句柄,这个句柄必须具有以下访问权限:
- PROCESS_CREATE_THREAD
- PROCESS_QUERY_INFORMATION
- PROCESS_VM_OPERATION
- PROCESS_VM_WRITE
- PROCESS_VM_READ
有了这些基础知识,我们就可以写一个简单的命令行程序来让指定的进程崩溃了。完整的代码如下:
1 |
|
假设这个命令行程序的名称是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
这个命令。