本来想写xxx游戏多开补丁的,后来想想算了,这个游戏有违道德,so 选择一个微软自己的东西搞搞理论就好了。

     经常用任务管理器的人都知道taskmgr默认是只能开一个的,除非电脑非常卡的时候可以开很多个,就像xxx游戏在运行一个客户端以后,迅速的再双击,又可以再运行一个差不多原理(这个跟多线程安全差不多)。

      程序只能运行一个实例的方法有很多,理论就不讲了,直接开场taskmgr的方法。

      首先,这个taskmgr是XP SP3系统上面的。其他自己研究。

      用OD加载直接代码就是Call   xxxxx  然后 jmp  xxxx,如果逆向多的话,这个就很清楚了,至少是VS05以上的C/C++编译器(好像这个多开跟编译器没多大关系吧...)

      继续跟下去,大家都很清楚:对于VC编译器入口还是不管是_tmain还是_tWinMain都是在调用了GetCommandLine以后有很多个push  push xxx之后的;exit、ExitProcess之前的那个函数就是了。所以我假设你已经找到WinMain函数地址 0x0100538E。

       WinMain代码及简陋过程分析如下:


1. 0100538E  /$  8BFF          MOV EDI,EDI  
2. 01005390  |.  55            PUSH EBP  
3. 01005391  |.  8BEC          MOV EBP,ESP  
4. 01005393  |.  81EC 10040000 SUB ESP,410  
5. 01005399  |.  A1 94540101   MOV EAX,DWORD PTR DS:[1015494]  
6. 0100539E  |.  53            PUSH EBX  
7. 0100539F  |.  56            PUSH ESI  
8. 010053A0  |.  8B75 08       MOV ESI,DWORD PTR SS:[EBP+8]  
9. 010053A3  |.  57            PUSH EDI  
10. 010053A4  |.  33FF          XOR EDI,EDI  
11. 010053A6  |.  47            INC EDI  
12. 010053A7  |.  33DB          XOR EBX,EBX  
13. 010053A9  |.  68 4C1B0001   PUSH taskmgr.01001B4C                    ; /MsgName = "TaskbarCreated"  
14. 010053AE  |.  8945 FC       MOV DWORD PTR SS:[EBP-4],EAX             ; |  
15. 010053B1  |.  89B5 28FCFFFF MOV DWORD PTR SS:[EBP-3D8],ESI           ; |  
16. 010053B7  |.  8935 285E0101 MOV DWORD PTR DS:[1015E28],ESI           ; |  
17. 010053BD  |.  89BD 2CFCFFFF MOV DWORD PTR SS:[EBP-3D4],EDI           ; |  
18. 010053C3  |.  899D 20FCFFFF MOV DWORD PTR SS:[EBP-3E0],EBX           ; |定义一个新的窗口消息,保证整个系统中是唯一的  
19. 010053C9  |.  FF15 A0120001 CALL DWORD PTR DS:[<&USER32.RegisterWind>; \RegisterWindowMessageW  
20. 010053CF  |.  68 C0140001   PUSH taskmgr.010014C0                    ; /MutexName = "NTShell Taskman Startup Mutex"  
21. 010053D4  |.  57            PUSH EDI                                 ; |InitialOwner => TRUE  
22. 010053D5  |.  53            PUSH EBX                                 ; |pSecurity => NULL  
23. 010053D6  |.  A3 105E0101   MOV DWORD PTR DS:[1015E10],EAX           ; |创建一个互斥体,如果存在则GetLastError为 ERROR_ALREADY_EXISTS (0x0B7)  
24. 010053DB  |.  FF15 2C110001 CALL DWORD PTR DS:[<&KERNEL32.CreateMute>; \CreateMutexW  
25. 010053E1  |.  3BC3          CMP EAX,EBX  
26. 010053E3  |.  A3 145E0101   MOV DWORD PTR DS:[1015E14],EAX  
27. 010053E8  |.  BF 10270000   MOV EDI,2710  
28. 010053ED  |.  74 1A         JE SHORT taskmgr.01005409  
29. 010053EF  |.  FF15 84110001 CALL DWORD PTR DS:[<&KERNEL32.GetLastErr>; [GetLastError  
30. 010053F5  |.  3D B7000000   CMP EAX,0B7  
31. 010053FA  |.  75 0D         JNZ SHORT taskmgr.01005409               ;  如果不存在互斥体,跳转  
32. 010053FC  |.  57            PUSH EDI                                 ; /Timeout => 10000. ms  
33. 010053FD  |.  FF35 145E0101 PUSH DWORD PTR DS:[1015E14]              ; |有互斥体存在,等待10000ms,使其他taskmgr可以显示窗口,事实证明这个10000ms还是太短了  
34. 01005403  |.  FF15 40110001 CALL DWORD PTR DS:[<&KERNEL32.WaitForSin>; \WaitForSingleObject  
35. 01005409  |>  68 985E0101   PUSH taskmgr.01015E98                    ; /Arg3 = 01015E98  
36. 0100540E  |.  68 945E0101   PUSH taskmgr.01015E94                    ; |Arg2 = 01015E94  
37. 01005413  |.  68 905E0101   PUSH taskmgr.01015E90                    ; |Arg1 = 01015E90  
38. 01005418  |.  E8 ACE9FFFF   CALL taskmgr.01003DC9                    ; \taskmgr.01003DC9  
39. 0100541D  |.  391D 905E0101 CMP DWORD PTR DS:[1015E90],EBX  
40. 01005423  |.  74 12         JE SHORT taskmgr.01005437  
41. 01005425  |.  68 9C5E0101   PUSH taskmgr.01015E9C  
42. 0100542A  |.  FF15 30110001 CALL DWORD PTR DS:[<&KERNEL32.GetCurrent>; [GetCurrentProcessId  
43. 01005430  |.  50            PUSH EAX  
44. 01005431  |.  FF15 34110001 CALL DWORD PTR DS:[<&KERNEL32.ProcessIdT>;  kernel32.ProcessIdToSessionId  
45. 01005437  |>  68 04010000   PUSH 104                                 ; /Count = 104 (260.)  
46. 0100543C  |.  8D85 30FCFFFF LEA EAX,DWORD PTR SS:[EBP-3D0]           ; |  
47. 01005442  |.  50            PUSH EAX                                 ; |Buffer  
48. 01005443  |.  68 13270000   PUSH 2713                                ; |RsrcID = STRING "Windows 任务管理器"  
49. 01005448  |.  56            PUSH ESI                                 ; |hInst  
50. 01005449  |.  8B35 BC130001 MOV ESI,DWORD PTR DS:[<&USER32.LoadStrin>; |USER32.LoadStringW  
51. 0100544F  |.  FFD6          CALL ESI                                 ; \LoadStringW  
52. 01005451  |.  85C0          TEST EAX,EAX  
53. 01005453  |.  74 6A         JE SHORT taskmgr.010054BF  
54. 01005455  |.  8D85 30FCFFFF LEA EAX,DWORD PTR SS:[EBP-3D0]  
55. 0100545B  |.  50            PUSH EAX                                 ; /Title  
56. 0100545C  |.  68 02800000   PUSH 8002                                ; |Class = 8002  
57. 01005461  |.  FF15 9C120001 CALL DWORD PTR DS:[<&USER32.FindWindowW>>; \FindWindowW  
58. 01005467  |.  3BC3          CMP EAX,EBX                              ;  查找窗口类为0x8002的窗口,用spy++可以知道#32770 (Dialog)正是taskmgr的窗口类  
59. 01005469  |.  8985 14FCFFFF MOV DWORD PTR SS:[EBP-3EC],EAX           ;  所以这里是检测获取另一个窗口的窗口HWND  
60. 0100546F  |.  74 4E         JE SHORT taskmgr.010054BF                ;  关键点:如果找到窗体则激活那个窗体,将退出本进程(只要这里一直jmp就可以实现多开了)  
61. 01005471  |.  8D8D 1CFCFFFF LEA ECX,DWORD PTR SS:[EBP-3E4]  
62. 01005477  |.  51            PUSH ECX                                 ; /pProcessID  
63. 01005478  |.  50            PUSH EAX                                 ; |hWnd  
64. 01005479  |.  899D 1CFCFFFF MOV DWORD PTR SS:[EBP-3E4],EBX           ; |  
65. 0100547F  |.  FF15 98120001 CALL DWORD PTR DS:[<&USER32.GetWindowThr>; \GetWindowThreadProcessId  
66. 01005485  |.  FFB5 1CFCFFFF PUSH DWORD PTR SS:[EBP-3E4]  
67. 0100548B  |.  FF15 94120001 CALL DWORD PTR DS:[<&USER32.AllowSetFore>;  USER32.AllowSetForegroundWindow  
68. 01005491  |.  8D85 18FCFFFF LEA EAX,DWORD PTR SS:[EBP-3E8]  
69. 01005497  |.  50            PUSH EAX                                 ; /pResult  
70. 01005498  |.  57            PUSH EDI                                 ; |Timeout  
71. 01005499  |.  6A 02         PUSH 2                                   ; |Flags = SMTO_NORMAL|SMTO_ABORTIFHUNG  
72. 0100549B  |.  53            PUSH EBX                                 ; |lParam  
73. 0100549C  |.  53            PUSH EBX                                 ; |wParam  
74. 0100549D  |.  BF 0B040000   MOV EDI,40B                              ; |  
75. 010054A2  |.  57            PUSH EDI                                 ; |Message => WM_USER+11.  
76. 010054A3  |.  FFB5 14FCFFFF PUSH DWORD PTR SS:[EBP-3EC]              ; |hWnd  
77. 010054A9  |.  FF15 90120001 CALL DWORD PTR DS:[<&USER32.SendMessageT>; \SendMessageTimeoutW  
78. 010054AF  |.  85C0          TEST EAX,EAX  
79. 010054B1  |.  74 0C         JE SHORT taskmgr.010054BF  
80. 010054B3  |.  39BD 18FCFFFF CMP DWORD PTR SS:[EBP-3E8],EDI  
81. 010054B9  |.  0F84 FB020000 JE taskmgr.010057BA                      ;  已存在taskmgr则跳去释放互斥体,然后退出  
82. 010054BF  |>  8D85 24FCFFFF LEA EAX,DWORD PTR SS:[EBP-3DC]           ;  跳到这里是说明可以生成一个taskmgr界面了

在OD里面将

JE SHORT 010054BF

改成

JMP SHORT 010054BF

会发现只是 地址0x0100546F的0x74变成了0xEB,所以多开补丁如下:


1. #include <windows.h>  
2.   
3. BYTE buf[] = "\x89\x85\x14\xFC\xFF\xFF\x74\x4E";  
4.   
5. void MyFunc()  
6. {  
7.     STARTUPINFO sInfo;   
8.     PROCESS_INFORMATION pInfo;    
9. BYTE RemoteMemory[8];  
10. DWORD wBytes;  
11.   
12. sizeof(sInfo) );   
13. sizeof(sInfo);   
14.     sInfo.dwFlags = STARTF_USESHOWWINDOW;   
15.     sInfo.wShowWindow = SW_SHOWNORMAL;   
16. sizeof(pInfo) );   
17.   
18. if( CreateProcess( NULL,      
19. "taskmgr.exe"),    
20.         NULL,    
21.         NULL,    
22.         FALSE,    
23. // 线程启动后在入口暂停  
24.         NULL,    
25.         NULL,    
26.         &sInfo,    
27.         &pInfo )               
28.         )    
29.     {  
30. // 读取 0x01005469开始的8个数据,包括要修改的0x0100546F处1Byte  
31. if (ReadProcessMemory(pInfo.hProcess, (LPVOID)0x01005469, RemoteMemory, 8, &wBytes))  
32.         {  
33. // 多读取的数据为了判断是不是我们要修改的数据,如果不是则不修改,  
34. // 这样就不需要判断是什么系统了  
35. if (memcmp(RemoteMemory, buf, 8) == 0)  
36.             {  
37. '\xEB'; // RemoteMemory[6] = '\x74'; je 修改成 0xEB jmp   
38. LPVOID)0x01005469, RemoteMemory, 8, &wBytes); //写入修改后的数据  
39. // 程序继续运行  
40.             }  
41. else  
42.             {  
43. // 留着这个进程没作用,直接关了吧  
44.                 TerminateProcess(pInfo.hProcess, 0);  
45.             }  
46.         }  
47.         CloseHandle( pInfo.hProcess );   
48.         CloseHandle( pInfo.hThread );   
49.     }   
50. }


       可是像上面的代码可能在某些程序里面有问题,原因是程序这样的修改是永久性的,不管程序运行多久,地址0x0100546F的数据永远都变成0xEB,如果程序自校验的就会被发现的,那么再跳转过后,就需要把这个代码改回去的,不过修改回去的方法可没有先前改的简单了,需要用到调试运行设置断点来修改,不细说了,代码如下


 
   
 
   
 
 
1. #include <windows.h>  
2.   
3. BYTE buf[] = "\x89\x85\x14\xFC\xFF\xFF\x74\x4E";  
4. BYTE BreakPoint[] = "\x8D\x85\x24\xFC\xFF\xFF\x50\x68";  
5.   
6. typedef void (WINAPI * PDebugSetProcessKillOnExit)(BOOL);  
7.   
8. void MyFunc()  
9. {  
10.     STARTUPINFO sInfo;   
11.     PROCESS_INFORMATION pInfo;    
12.     DEBUG_EVENT debug;  
13.     CONTEXT context;  
14.   
15. BYTE RemoteMemory[16];  
16. DWORD wBytes;  
17. BOOL flags = TRUE;  
18.   
19. sizeof(sInfo) );   
20. sizeof(sInfo);   
21.     sInfo.dwFlags = STARTF_USESHOWWINDOW;   
22.     sInfo.wShowWindow = SW_SHOWNORMAL;   
23. sizeof(pInfo) );   
24.   
25. if( CreateProcess( NULL,      
26. "taskmgr.exe"),    
27.         NULL,    
28.         NULL,    
29.         FALSE,    
30.         DEBUG_ONLY_THIS_PROCESS,   
31.         NULL,    
32.         NULL,    
33.         &sInfo,    
34.         &pInfo )               
35.         )    
36.     {    
37. // 在调试器退出的时候,被调试程序不会退出  
38. HMODULE hmodule = LoadLibrary(TEXT("kernel32.dll"));  
39.         PDebugSetProcessKillOnExit DebugSetProcessKillOnExit =   
40. "DebugSetProcessKillOnExit");  
41. if ( DebugSetProcessKillOnExit != NULL )  
42.         {  
43.             DebugSetProcessKillOnExit(FALSE);  
44.         }  
45.           
46. sizeof(context));  
47.         context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;  
48. // 等待运行到断点处  
49. while(flags)  
50.         {  
51.             WaitForDebugEvent( &debug , INFINITE );  
52. switch(debug.dwDebugEventCode)  
53.             {  
54. case CREATE_PROCESS_DEBUG_EVENT: // 启动调试事件  
55. if (ReadProcessMemory(pInfo.hProcess, (LPVOID)0x01005469, RemoteMemory, 8, &wBytes))  
56.                     {  
57. if (memcmp(RemoteMemory, buf, 8) == 0)  
58.                         {  
59. // 跳过检测窗口,下断点  
60. '\xEB';  
61. LPVOID)0x01005469, RemoteMemory, 8, &wBytes);  
62. LPVOID)0x010054BF, "\xCC", 1, &wBytes);  
63.                         }  
64.                     }  
65. else  
66.                     {  
67.                         flags = FALSE;  
68.                         TerminateProcess(pInfo.hProcess, 0);  
69.                     }  
70. break;  
71. case EXCEPTION_DEBUG_EVENT: // 异常发生,捕获int 3断点就可以了  
72. if ( debug.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT )  
73.                     {  
74. if ((DWORD)debug.u.Exception.ExceptionRecord.ExceptionAddress == 0x010054BF )  
75.                         {  
76. // 将数据改回去  
77. LPVOID)0x01005469, buf, 8, &wBytes);  
78. LPVOID)0x010054BF, BreakPoint, 4, &wBytes);  
79.                               
80. // 将EIP往回退1byte  
81. if (GetThreadContext(pInfo.hThread, &context))  
82.                             {  
83.                                 context.Eip -= 1;  
84.                                 SetThreadContext(pInfo.hThread, &context);  
85.                             }  
86. // 退出循环  
87.                         }                         
88.                     }  
89. break;  
90.             }  
91.               
92. if (!ContinueDebugEvent(debug.dwProcessId, debug.dwThreadId, DBG_CONTINUE ))  
93.             {  
94. // continue error  
95.                 TerminateProcess(pInfo.hProcess, 0);  
96.                 ExitProcess(0);  
97.             }  
98.         }  
99.   
100.         FreeLibrary(hmodule);  
101.         CloseHandle( pInfo.hProcess );   
102.         CloseHandle( pInfo.hThread );   
103.     }   
104. }

    程序还有可能是用调试的方法加载的自身,还有可能HOOK掉了几个关键的API,还有的可能是驱动级的保护……这个技术实在太多了,想修改那些代码实现多开也是可以的,有矛必有盾嘛。

      太累了,还是搞个无图无真相吧。