打造能处理 OutputDebugString("%s%s%s") 的OD
最近好多壳利用 OutputDebugString 函数来对付 OD, 当然处理方法也比较简单, 更有大侠出了修改版 OD.
不过我这个人比较喜欢寻根究底, 想看看 OD 的毛病出在哪里.
1. 基础知识 from MSDN
下面是调试器和被调试进程之间的关系, 调试器用 CreateProcess 创建被调试进程.
被调试进程运行中会遇到 9 种需要向调试器汇报的事件, 调试器处理好后, 被调试进程才能继续.
平时我们最关心 Exception, 今天我们的主角是 DebugString.
EXCEPTION_DEBUG_EVENT equ 1
CREATE_THREAD_DEBUG_EVENT equ 2
CREATE_PROCESS_DEBUG_EVENT equ 3
EXIT_THREAD_DEBUG_EVENT equ 4
EXIT_PROCESS_DEBUG_EVENT equ 5
LOAD_DLL_DEBUG_EVENT equ 6
UNLOAD_DLL_DEBUG_EVENT equ 7
OUTPUT_DEBUG_STRING_EVENT equ 8
RIP_EVENT equ 9
while(WaitForDebugEvent(&DBEvent, INFINITE))
{
if (DBEvent.dwDebugEventCode==EXCEPTION_DEBUG_EVENT)
{
// 子进程遇到一个异常
}
else if (DBEvent.dwDebugEventCode==OUTPUT_DEBUG_STRING_EVENT)
{
// 子进程执行了 OutputDebugString 函数
}
.... // 共有 9 种情形
ContinueDebugEvent(DBEvent.dwProcessId, DBEvent.dwThreadId, dwContinueStatus);
}
typedef struct _DEBUG_EVENT { // de
DWORD dwDebugEventCode;
DWORD dwProcessId;
DWORD dwThreadId;
union {
EXCEPTION_DEBUG_INFO Exception;
CREATE_THREAD_DEBUG_INFO CreateThread;
CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
EXIT_THREAD_DEBUG_INFO ExitThread;
EXIT_PROCESS_DEBUG_INFO ExitProcess;
LOAD_DLL_DEBUG_INFO LoadDll;
UNLOAD_DLL_DEBUG_INFO UnloadDll;
OUTPUT_DEBUG_STRING_INFO DebugString;
RIP_INFO RipInfo;
} u;
} DEBUG_EVENT;
typedef struct _OUTPUT_DEBUG_STRING_INFO { // odsi
LPSTR lpDebugStringData;
WORD fUnicode;
WORD nDebugStringLength;
} OUTPUT_DEBUG_STRING_INFO;
int vsprintf( char *buffer, const char *format, va_list argptr ); 这个是错误的根源
2. 测试用的程序
//=========================================== TEST1.EXE ===============================
.386
.model flat, stdcall
option casemap:none
include c:\masm32\include\kernel32.inc
include c:\masm32\include\user32.inc
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\user32.lib
include c:\masm32\include\windows.inc
.const
szOutput db "%s",0
szMessage db "The program is fine", 0
szTitle db "Test",0
szFormat db "%s",0
.data?
szBuffer db 20 dup (?)
;------------ CODE ----------
.code
main:
invoke wsprintf, offset szBuffer, offset szFormat, offset szMessage
invoke OutputDebugString, offset szOutput
invoke MessageBox, 0, offset szBuffer, offset szTitle, MB_OK
invoke ExitProcess,0
end main
//===================================== End ==============================================
用 OD 调试 test1.exe, 执行完 OutputDebugString, OD 左下角出现了 "Debug string: "
//================================= Test2.EXE ============================================
其余不变, 只变一句
szOutput db "%s%s",0
用 OD 调试 test2.exe, 执行 OutputDebugString, OD 在 4a74cf 出现访问 [00000260]异常
//================================== Test3.EXE ===========================================
其余不变, 只变一句
szOutput db "%s%s%s",0
用 OD 调试 test2.exe, 执行 OutputDebugString, OD 在 4a74cf 出现访问 [00000001]异常
为什么 Test1 没问题, Test2, Test3 有问题? Test2 和 Test3 有区别吗? Let's go
3. 我们来看看 OD 是如何处理的
将 Ollydbg.exe 拷贝到 CMD.EXe, 运行 CMD.exe, 菜单 File - Open - Ollydbg.EXE ,
这样 CMD.exe 是调试器, Ollydbg.exe 就是被调试的程序, 别搞错了.
找 WaitForDebugEvent, 比较容易找到处理 DebugString 的地方, CMD 下断 431276.
F9 运行 Ollydbg.exe, 再用 Ollydbg.exe 打开 TEST3.EXE, F9 运行, CMD 断下, F7 跟进
00439616 > \6A 00 PUSH 0 ; /Timeout = 0. ms
00439618 . 68 14574D00 PUSH OLLYDBG.004D5714 ; |pDebugEvent = OLLYDBG.004D5714
0043961D . E8 E85B0700 CALL ; \WaitForDebugEvent
00439622 . 85C0 TEST EAX,EAX
00439624 . 75 44 JNZ SHORT OLLYDBG.0043966A
...
0043977D . 51 PUSH ECX ; /Arg1
0043977E . E8 4D54FFFF CALL OLLYDBG.0042EBD0 ; \OLLYDBG.0042EBD0, F7 跟进
0042EBD0 /$ 55 PUSH EBP
0042EBD1 |. 8BEC MOV EBP,ESP
0042EBD3 |. 81C4 04F0FFFF ADD ESP,-0FFC
0042EBD9 |. 50 PUSH EAX
0042EBDA |. 81C4 00F5FFFF ADD ESP,-0B00
0042EBE0 |. 53 PUSH EBX
0042EBE1 |. 56 PUSH ESI
0042EBE2 |. 57 PUSH EDI
0042EBE3 |. 8B35 1C574D00 MOV ESI,DWORD PTR DS:[4D571C] ; dwThreadID
0042EBE9 |. 56 PUSH ESI
0042EBEA |. E8 5DF8FFFF CALL OLLYDBG.0042E44C
0042EBEF |. 8BF8 MOV EDI,EAX
0042EBF1 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
0042EBF4 |. 59 POP ECX
0042EBF5 |. 8938 MOV DWORD PTR DS:[EAX],EDI
0042EBF7 |. 8B15 14574D00 MOV EDX,DWORD PTR DS:[4D5714] ; pDebugEvent
0042EBFD |. 83FA 09 CMP EDX,9 ; Switch (cases 1..9) 被调试进程遇到的9种情况
0042EC00 |. 0F87 EE270000 JA OLLYDBG.004313F4
0042EC06 |. FF2495 0DEC42>JMP DWORD PTR DS:[EDX*4+42EC0D]
0042EC0D |. F4134300 DD OLLYDBG.004313F4 ; Switch table used at 0042EC06
0042EC11 |. 35EC4200 DD OLLYDBG.0042EC35
0042EC15 |. FF0C4300 DD OLLYDBG.00430CFF
0042EC19 |. D70D4300 DD OLLYDBG.00430DD7
0042EC1D |. 3F0F4300 DD OLLYDBG.00430F3F
0042EC21 |. 37104300 DD OLLYDBG.00431037
0042EC25 |. 2D114300 DD OLLYDBG.0043112D
0042EC29 |. B7114300 DD OLLYDBG.004311B7
0042EC2D |. 76124300 DD OLLYDBG.00431276 ; OUTPUT_DEBUG_STRING_EVENT 在 431276 处理
0042EC31 |. C7134300 DD OLLYDBG.004313C7
00431276 |> \830D 74574D00>OR DWORD PTR DS:[4D5774],4 ; Case 8 of switch 0042EBFD
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课