• 劳动节脑洞大开!利用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
  • 相关阅读:
    AWS上的游戏服务:Lumberyard + Amazon GameLift + Twitch
    Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案
    iOS多线程与网络开发之小文件上传
    VMware-Fusion-7.0.0-2103067 Pro SN:序列号+ 百度云下载地址
    PCA的数学原理Matlab演示
    typedef,结构体,共用体,联合体
    XMPP系列(三)---获取好友列表、加入好友
    王立平--eclipse中改动android项目的版本
    linux入门
    机器学习(3)——多变量线性回归
  • 原文地址:https://www.cnblogs.com/ZRBYYXDM/p/5465849.html
Copyright © 2020-2023  润新知