劳动节脑洞大开!利用Debug API 获取 加壳客户端的MD5值

系统 : Windows xp

程序 : 某游戏客户端

程序下载地址 :不提供

要求 : 远程注入 & 获取MD5值

使用工具 : vc++6.0 & OD

 

案例说明:

该游戏客户端对自身进行散列计算,并将md5值打包加密发给服务端。由于客户端本体带有病毒和压缩壳,索性采用硬件断点,和Debug API获取该MD5的内存地址。

 

逆向该客户端:

客户端启动时,先初始化md5内存:

00419DE4    B9 08000000     mov     ecx, 8
00419DE9    33C0            xor eax, eax 00419DEB    8DBD B8610200   lea edi, dword ptr [ebp+261B8] 00419DF1    F3:AB           rep     stos dword ptr es:[edi]          ; (initial cpu selection)

然后开始计算散列值:

004EA3E2    BE B8610200     mov esi, 261B8 004EA3E7    33D2            xor edx, edx 004EA3E9    8D45 EE         lea     eax, dword ptr [ebp-12] 004EA3EC    8A543D B4       mov dl, byte ptr [ebp+edi-4C] 004EA3F0    52              push edx 004EA3F1    68 E06D5300     push    00536DE0                         ; %02x
004EA3F6    50              push eax 004EA3F7    E8 ABEE0100     call    005092A7
004EA3FC    8B15 943C5400   mov edx, dword ptr [543C94] 004EA402    66:8B4D EE      mov     cx, word ptr [ebp-12] 004EA406    83C4 0C         add     esp, 0C
004EA409    47              inc edi 004EA40A    66:890C16       mov word ptr [esi+edx], cx 004EA40E    83C6 02         add     esi, 2
004EA411    81FE D8610200   cmp esi, 261D8 004EA417  ^ 7C CE           jl      short 004EA3E7

跳出循环后,md5值就计算完毕了。我们利用DebugAPI模拟以上操作就可以。

但是要注意一点,传参 DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS调用CreateProcess函数会将exe占用。这时,程序打开自身的CreateFile函数将失败:

0012FC98   004EA381  /CALL to CreateFileA from xxx.004EA37F
0012FC9C   0012FCC4  |FileName = "xxx.exe" 0012FCA0 80000000 |Access = GENERIC_READ 0012FCA4 00000000 |ShareMode = 0 0012FCA8 00000000 |pSecurity = NULL 0012FCAC 00000003 |Mode = OPEN_EXISTING 0012FCB0 00000000 |Attributes = 0 0012FCB4 00000000 \hTemplateFile = NULL

解决办法很简单,将ShareMode改成FILE_SHARE_READ即可。

 

最后整理一下我们分析的结果:

1.对程序下硬件断点,保存md5的内存地址。

2.patch掉ShareMode,改为0x01(FILE_SHARE_READ)。

3.对计算完md5之后的指令下断,并读取md5.

 

部分实现代码:

//用于createprocess的两个参数
 STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(STARTUPINFO)); ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); si.cb = sizeof(STARTUPINFO); char ReadBuffer[128] = {0}; bool WhileDoFlag = true; //创建进程
    if ( !CreateProcess( FileName,NULL,NULL,NULL,FALSE, DEBUG_ONLY_THIS_PROCESS | DEBUG_PROCESS,NULL,NULL,&si,&pi ) ){ MessageBox( "打开程序失败!" ); DWORD dwRet = GetLastError(); CString strText(_T("")); strText.Format(_T("%d"), dwRet); AfxMessageBox("错误代码:"+strText); return ; } m_Pi = pi; DEBUG_EVENT DBEvent; CONTEXT Regs; DWORD dwSSCnt = 0; //存放md5地址
 DWORD READ_ADDRESS; Regs.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS ; //设置程序在Single Step模式下执行
    GetThreadContext( pi.hThread,&Regs ); Regs.EFlags |= 0x100; SetThreadContext( pi.hThread,&Regs ); ResumeThread( pi.hThread ); while (WhileDoFlag) { WaitForDebugEvent (&DBEvent, INFINITE); switch (DBEvent.dwDebugEventCode) { case EXCEPTION_DEBUG_EVENT: switch (DBEvent.u.Exception.ExceptionRecord.ExceptionCode) { case EXCEPTION_SINGLE_STEP : { ++dwSSCnt ; if (dwSSCnt == 1) { //当收到第一个EXCEPTION_SINGLE_STEP异常信号,表示中断在程序的第一条指令,即入口点 //把Dr0设置成程序的入口地址
 GetThreadContext(pi.hThread,&Regs); Regs.Dr0=Regs.Eax; Regs.Dr7=0x101; SetThreadContext(pi.hThread,&Regs); } else if (dwSSCnt == 2) { //第二次中断在起先设置的入口点,在BP_MOLLC处设置硬件断点
 GetThreadContext(pi.hThread, &Regs) ; Regs.Dr0 = BP_MOLLC; Regs.Dr7 = 0x101 ; SetThreadContext(pi.hThread, &Regs) ; } else if (dwSSCnt == 3) { //第三次中断,己到指定的地址,读取EDI寄存器的数据
                        GetThreadContext(pi.hThread, &Regs) ; Regs.Dr0 = BP_INITMD5; Regs.Dr7 = 0x101 ; READ_ADDRESS = Regs.Edi; BYTE NewMode = 0x01; int a = WriteProcessMemory( pi.hProcess,(LPVOID)SET_SHAREMODE,&NewMode,1,0 ); SetThreadContext(pi.hThread, &Regs) ; /* //挂起进程 SuspendThread( pi.hThread ); //通知进程异常已处理 ContinueDebugEvent(DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE) ; //卸载调试器 int a = _DebugSetProcessKillOnExit( FALSE ); int b = _DebugActiveProcessStop(DBEvent.dwProcessId); //最后运行线程 ResumeThread( pi.hThread ); //int b = GetLastError(); WhileDoFlag=FALSE; */ } else if (dwSSCnt == 4) { //第四次中断,MD5算出,读取数据.
                        GetThreadContext(pi.hThread, &Regs) ; Regs.Dr0 = Regs.Dr7 = 0 ; ReadProcessMemory( pi.hProcess,(LPVOID)READ_ADDRESS,ReadBuffer,32,NULL ); SetDlgItemText( IDC_EDITMD5,ReadBuffer ); SetThreadContext(pi.hThread, &Regs) ; } break; } } break ; case EXIT_PROCESS_DEBUG_EVENT : WhileDoFlag=FALSE; break ; } //pi.dwProcessId和pi.dwThreadId 要改成如下的代码
 ContinueDebugEvent(DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE) ; } //.end while

 

你可能感兴趣的:(劳动节脑洞大开!利用Debug API 获取 加壳客户端的MD5值)