让EXE文件不能直接启动的方法以防止直接调试的方法
让EXE文件不能直接启动的方法以防止直接调试的方法
write by 九天雁翎(JTianLing) -- www.jtianling.com
游戏中经常需要这样的技术,即让游戏主程序不能直接启动,通过这样的方式,可以在一定程序上达到防止调试的目的,虽然仅仅是最最简单的防止,但是仍然有一定的作用。
这里讲讲这样的技术。。。。。其实研究甚浅。。。。
基本思路有两种,其一就是直接破坏PE头,那么此文件根本无法加载,自然更没有办法加载了。但是我们自己必须的完全模拟系统加载PE文件的过程,代价有点大。所以,虽然此方案更好,但是我没有深入的研究。其二就是仅仅破坏一段有效的代码,只要你在程序必须执行的地方插入了一堆无效数据,程序自然一运行就崩溃。目的达到了。
这里从破坏《从最简单的Win32汇编程序,HelloWorld说起》文中介绍的一个最简单的程序开始。
原程序源代码:
.486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include windows.inc
include masm32.inc
include user32.inc
include kernel32.inc
includelib masm32.lib
includelib user32.lib
includelib kernel32.lib
.data
szCaption db "A MessageBox !",0
szText db "Hello,World !",0
.code
start:
invoke MessageBox,NULL,offset szText,/
offset szCaption,MB_OK
invoke ExitProcess,NULL
end start
反汇编的代码:
00401000 >/$ 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401002 |. 68 00304000 PUSH helloWor.00403000 ; |Title = "A MessageBox !"
00401007 |. 68 0F304000 PUSH helloWor.0040300F ; |Text = "Hello,World !"
0040100C |. 6A 00 PUSH 0 ; |hOwner = NULL
0040100E |. E8 07000000 CALL <JMP.&user32.MessageBoxA> ; /MessageBoxA
00401013 |. 6A 00 PUSH 0 ; /ExitCode = 0
00401015 /. E8 06000000 CALL <JMP.&kernel32.ExitProcess> ; /ExitProcess
0040101A $- FF25 08204000 JMP NEAR DWORD PTR [<&user32.Message>; user32.MessageBoxA
00401020 .- FF25 00204000 JMP NEAR DWORD PTR [<&kernel32.ExitP>; kernel32.ExitProcess
这里我将其前一个字节改为55,那么其前三句将会解析成如下形式:
00401000 >/$ 55 PUSH EBP ; |Title = ""
00401001 |. 0068 00 ADD BYTE PTR [EAX], CH ; |
00401004 |. 3040 00 XOR BYTE PTR [EAX], AL ; |
这是再运行这个程序必然是崩溃的。这里必须要我们自己的程序去修复它,然后再运行它才能运行成功,这里以一个字节的修改为例,其实实际中你愿意改多少,改多少段完全是由你自己决定的。
启动程序基本思路及过程如下,首先用CreateProcess启动刚才修改过的应用程序,但是将其挂起,然后用VirtualProctectEx函数将挂起的进程需要修改的代码段属性设为可写,然后再用WriteProcessMemroy函数将正确的结果写入,然后再通过ResumeThread将挂起的进程运行。这时,就可以通过你的启动程序去启动被破坏的程序了,而正常情况下,被破坏的程序只能是由你的启动程序来启动。
全部启动源代码如下:
1 #include <windows.h>
2 #include <tchar.h>
3
4
5 int main(int argc, char* argv[])
6 {
7 STARTUPINFO si;
8 PROCESS_INFORMATION pi;
9 LPTSTR szCmdline=_tcsdup(TEXT("HelloWorld2"));
10
11 ZeroMemory( &si, sizeof(si) );
12 si.cb = sizeof(si);
13 ZeroMemory( &pi, sizeof(pi) );
14
15 // Start the child process.
16 if( !CreateProcess( NULL, // No module name (use command line)
17 szCmdline, // Command line
18 NULL, // Process handle not inheritable
19 NULL, // Thread handle not inheritable
20 FALSE, // Set handle inheritance to FALSE
21 CREATE_SUSPENDED, // Suspended the process, the key!
22 NULL, // Use parent's environment block
23 NULL, // Use parent's starting directory
24 &si, // Pointer to STARTUPINFO structure
25 &pi ) // Pointer to PROCESS_INFORMATION structure
26 )
27 {
28 printf( "CreateProcess failed (%d)./n", GetLastError() );
29 return -1;
30 }
31
32 DWORD ldwOldPro = 0;
33 if(!VirtualProtectEx(pi.hProcess, (void*)0x401000, 1, PAGE_EXECUTE_READWRITE, &ldwOldPro))
34 {
35 printf( "VirtualProtectEx failed (%d)./n", GetLastError() );
36 TerminateProcess(pi.hProcess, -1);
37 // Close process and thread handles.
38 CloseHandle( pi.hProcess );
39 CloseHandle( pi.hThread );
40 return -1;
41 }
42
43 DWORD ldwWritten = 0;
44 BYTE lbt = 0x6A;
45 if(!WriteProcessMemory(pi.hProcess, (void*)0x401000, &lbt, 1, &ldwWritten))
46 {
47 printf( "WriteProcessMemory failed (%d)./n", GetLastError() );
48 TerminateProcess(pi.hProcess, -1);
49 // Close process and thread handles.
50 CloseHandle( pi.hProcess );
51 CloseHandle( pi.hThread );
52 return -1;
53 }
54
55 if(!VirtualProtectEx(pi.hProcess, (void*)0x401000, 1, ldwOldPro, &ldwOldPro))
56 {
57 printf( "VirtualProtectEx failed (%d)./n", GetLastError() );
58 TerminateProcess(pi.hProcess, -1);
59 // Close process and thread handles.
60 CloseHandle( pi.hProcess );
61 CloseHandle( pi.hThread );
62 return -1;
63 }
64
65 if(-1==ResumeThread(pi.hThread))
66 {
67 printf( "ResumeThread failed (%d)./n", GetLastError() );
68 TerminateProcess(pi.hProcess, -1);
69 // Close process and thread handles.
70 CloseHandle( pi.hProcess );
71 CloseHandle( pi.hThread );
72 return -1;
73 }
74
75
76
77 // Wait until child process exits.
78 WaitForSingleObject( pi.hProcess, INFINITE );
79
80 // Close process and thread handles.
81 CloseHandle( pi.hProcess );
82 CloseHandle( pi.hThread );
83
84
85 return 0;
86 }
再次说明,这里仅仅是示例,所以仅仅修改了一个字节,假如你改动字节比较多的话,直接通过OD或者SoftIce来调试你的应用程序就没有办法了,当然,假如你的启动程序没有任何防护,是可以先调试你的启动程序的。
但是方法有个完全的破坏方法,那就是Dump。
Dump方法:
因为此例实在是太过于简单,所以在程序运行后,也就是弹出对话框后,再调用LordPE载入进程Dump也完全可以,但是在实际中,可能因为程序在启动时修改该了一些全局的数据,此时Dump会有问题。
正确的Dump步骤应该是在程序恢复运行的一瞬间,也就是将要启动却还未启动的时候。以前在一个程序将要启动却还未启动时Dump有个小技巧,那就是将其前一个字节修改为0xCC,那么,启动的一瞬间就会出现调试中断,然后将OD等调试工具设为默认调试工具,就可以在此时中断进程,并进行Dump,但是在此例中比较特殊,因为第一个字节本来就是由程序写入的,所以你没有办法通过修改文件首字节的办法来完成工作:)而且就我注意到,目前所有可以Dump的程序(也许是我见得不多,http://www.pediy.com/bbshtml/bbs7/pediy7-659-5.htm
一文中介绍的Dump工具我都用过)
都是先遍历进程,然后再Dump的,但是挂起还没有运行的进程它们竟然都检查不出来-_-!这点我很奇怪,我也不知道他们都是用什么来遍历进程的,但是windows的任务管理器就可以遍历出来(Windows的任务管理器其实相当的强悍,以前我做了一个进程占用CPU,内存资源百分比的监视工具,才知道要做好那么多任务不是那么简单的)。
呵呵,可以进行的办法是动态修改首字节,或者自己写一个给出进程ID就可以进行Dump的工具。。。。或者直接修改我比较习惯的LordPE的遍历进程方式,让其可以遍历出挂起的进程。。。。
这里我做个程序用于Dump上述程序。
此程序用于将指定进程ID的进程首字节动态改为CC以出现调试中断,并且将原有的首字节读取出来,并输出,以方便中断出现后,在OD中将其改为原有值,然后Dump。经测试,此方式的确可以做到Dump上述动态修改的挂起进程的目的:)
此程序也起到了很方便的作用:)
源代码如下:
1 #include "windows.h"
2 int main(int argc, char* argv[])
3 {
4 printf("Give me a Process ID: ");
5
6 DWORD ldwPid = 0;
7 scanf("%d", &ldwPid);
8
9 HANDLE lhPro = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
10 FALSE, ldwPid);
11
12 if(lhPro == NULL)
13 {
14 printf( "Error Process ID(%u) or OpenProcess failed (%d)./n",ldwPid, GetLastError() );
15 return -1;
16 }
17
18 DWORD ldwOldPro = 0;
19 if(!VirtualProtectEx(lhPro, (void*)0x401000, 1, PAGE_EXECUTE_READWRITE, &ldwOldPro))
20 {
21 printf( "VirtualProtectEx failed (%d)./n", GetLastError() );
22 CloseHandle( lhPro );
23 return -1;
24 }
25
26 DWORD ldwReaded = 0;
27 BYTE lbtFirst = 0;
28 if(!ReadProcessMemory(lhPro, (void*)0x401000, &lbtFirst, 1, &ldwReaded))
29 {
30 printf( "ReadProcessMemory failed (%d)./n", GetLastError() );
31 CloseHandle( lhPro );
32 return -1;
33 }
34
35
36 DWORD ldwWritten = 0;
37 BYTE lbt = 0xcc;
38 if(!WriteProcessMemory(lhPro, (void*)0x401000, &lbt, 1, &ldwWritten))
39 {
40 printf( "WriteProcessMemory failed (%d)./n", GetLastError() );
41 CloseHandle( lhPro );
42 return -1;
43 }
44
45 printf( "First BYTE Changed to CC,and origin first BYTE is %X./n", lbtFirst);
46
47 if(!VirtualProtectEx(lhPro, (void*)0x401000, 1, ldwOldPro, &ldwOldPro))
48 {
49 printf( "VirtualProtectEx failed (%d)./n", GetLastError() );
50 CloseHandle( lhPro );
51 return -1;
52 }
53 // Close process handles.
54 CloseHandle( lhPro );
55
56
57
58 return 0;
59 }
因为此程序已经有一定的实用性,方便了目前不是太方便的动态修改头字节为CC以实现Dump动态修改并挂起的进程,为了方便不喜欢编译的兄弟,我将其编译后放到讨论新闻组及文件,名字为DynamicChangeFirstBYTE.exe
write by 九天雁翎(JTianLing) -- www.jtianling.com
分类:
未分类
标签:
Posted By 九天雁翎 at 九天雁翎的博客 on 2009年02月06日