通过调试寄存器不修改代码实现bt功能

去年我就提过的防封思路,但是论坛里很少讨论.我一直用的挺有效,现在发出来,估计早就有人在用了.

首先我是用软中断和硬中断实现了发包函数的封包截取,其中软中断给出了代码,而硬中断是不需要修改代码的,采用调试寄存器来实现。
http://www.ghoffice.com/bbs/read.php?tid-44149.html


下面以封包截取的代码来说明实现bt功能的原理。其中好些地方我用的也不熟练,复杂了,高手就不用看了。主要是会影响些速度,尤其是我的本本,开时间长了就死机.


首先分析要中断的位置

    .text:00588F78 mov    ecx, [esp+30h+arg_0]
    .text:00588F7C push    esi            ; size_t
    .text:00588F7D push    ecx            ; void *
    .text:00588F7E push    eax            ; void *
    .text:00588F7F call    ds:memmove

上面是发包函数里的部分代码,其中00588F7D位置ecx存储封包数据地址,00588F7C位置esi存储封包大小。
如果在此处将程序中断,并将所需数据读取出来,目的就达到了。

对于变态功能,以空中漫步为例:

    .text:00461792 mov    eax, [ecx+5E0h]
    .text:00461798 fild    [esp+10h+var_8]
    .text:0046179C cmp    eax, ebx
    .text:0046179E fmul    ds:flt_85EC2C
    .text:004617A4 fstp    [esp+10h+arg_0]
    .text:004617A8 jz      short loc_461828

修改004617A8处的跳转就可以空中漫步,如果在0046179C处中断,然后修改eax或者ebx的值,也可以实现相应功能但不需要修改指令(注意跳转过后要记得恢复寄存器的值,隐藏建筑和跳跃飞天也可以这样实现)。


下面是封包截取的实现过程,修改一下即可实现bt功能。

1.CreateProcess或者FindWindow或者完美进程ID(dwW2iProcessId)

2.调试进程DebugActiveProcess(dwW2iProcessId)

3.进入调试循环体等待调试事件产生WaitForDebugEvent(&DebugEv, 10)

4.接收到CREATE_PROCESS_DEBUG_EVENT事件时,对全部线程设立断点。
(我可能复杂了,用CREATE_THRAD_DEBUG_EVENT事件可能简单些,刚会用就不错了呵呵,开始我只在主线程里设置了断点,死活断不下来)

下面是设置断点的方法,使用GetThreadContext和SetThreadContext函数
对于bt功能,修改寄存器的值,也通过这两个函数来实现。

    HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadInfo.th32ThreadID);
    SuspendThread(hThread);

    CONTEXT Regs = {0};
    Regs.ContextFlags = CONTEXT_DEBUG_REGISTERS; //CONTEXT_DEBUG
    ::GetThreadContext(hThread, &Regs);

    Regs.Dr0 = W2I_SENDCALL_ADDR1; //中断地址
    Regs.Dr7 = BREAK_DR7_FLAG; //0x401启用dr0断点

    ::SetThreadContext(hThread, &Regs);

    ResumeThread(hThread);
    CloseHandle(hThread);

5.当在所需地址处中断时,来进行我们真正的功能操作,读取数据或者设置数据,同时设置数据改回处的断点。

    ReadProcessMemory(hW2iProcess, (void*)Regs.Ecx, buf, len, &len);

6.当在数据改回处中断时,将寄存器值恢复,并设置修改处断点(其实可以一起设置好,但是要占用两个dr寄存器。dr寄存器一共4个,所以节省下只用一个,这样最多也只可以实现4个中断功能,不知道说清楚没)

7.当完美进程结束时,响应EXIT_PROCESS_DEBUG_EVENT事件,也退出。

注意每次中断时要用ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, dwContinueStatus)继续。

这就是整个过程,其实修改代码也不一定不防封。个人理解游戏协议分为两大类,封包是其实现,一类是正常的游戏所需数据,一类是防止外挂的检测协议。(始终要记得,网络游戏,服务器所需的一切数据必是通过网络来传输,必是通过封包来运载。)如果是检测协议,就回复一个正常结果的检测包,必能防封,所以说脱机挂是最难封的,因为这建立在全部协议破解的基础上。想当初传神和赤月挂,因为技术上无法封杀,只好派人去抓程序员了。

不修改代码也不一定防封,反调试的技术也很多,具体我还不清楚呵呵。



下面是截包程序的代码:


#include
#include
#include
#include

#define W2I_WINDOW_TITLE TEXT("Element Client")
#define W2I_WINDOW_CLASS TEXT("ElementClient Window")

//#define W2I_WINDOW_TITLE NULL
//#define W2I_WINDOW_CLASS TEXT("Notepad")

#define W2I_SENDCALL      0x00588EF0

#define W2I_SENDCALL_ADDR1      (W2I_SENDCALL+0x8E)
#define W2I_SENDCALL_ADDR2      (W2I_SENDCALL+0x8F)

#define BREAK_DR7_FLAG          0x401

int main(int argc, char* argv[])
{
    HANDLE hW2iProcess;
    DWORD dwW2iProcessId;
    HWND hW2iWnd;
    DWORD Count = 0;


    //查找窗口,并且获取窗口进程线程ID
    hW2iWnd = ::FindWindow(W2I_WINDOW_CLASS, W2I_WINDOW_TITLE);

    if( hW2iWnd>0 && ::GetWindowThreadProcessId(hW2iWnd, &dwW2iProcessId)
        && dwW2iProcessId && ( hW2iProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwW2iProcessId) )
        && DebugActiveProcess(dwW2iProcessId) )
    {
        DEBUG_EVENT DebugEv;
        DWORD dwContinueStatus;

        while(TRUE)
        {
            if(WaitForDebugEvent(&DebugEv, 10))
            {
                dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;

                switch(DebugEv.dwDebugEventCode)
                {
                case EXCEPTION_DEBUG_EVENT:
                    {
                        if((DWORD)DebugEv.u.Exception.ExceptionRecord.ExceptionAddress==W2I_SENDCALL_ADDR1)
                        {
                            HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, DebugEv.dwThreadId);
                            SuspendThread(hThread);

                            CONTEXT Regs = {0};
                            Regs.ContextFlags = CONTEXT_DEBUG_REGISTERS|CONTEXT_INTEGER;
                            ::GetThreadContext(hThread, &Regs);

                            Regs.Dr0 = W2I_SENDCALL_ADDR2;
                            Regs.Dr7 = BREAK_DR7_FLAG;

                            printf("%d/n", Count++);
                            //Regs.Esi - len
                            //Regs.Ecx - buf
                            BYTE buf[1024];
                            SIZE_T len = (Regs.Esi<512 ? Regs.Esi : 512);
                            if( ReadProcessMemory(hW2iProcess, (void*)Regs.Ecx, buf, len, &len) )
                            {
                                FILE * fp = fopen("Cap.txt", "a+");
                                for(SIZE_T i=0; i                                {
                                    printf("%02X ", buf );
                                    if(fp)
                                        fprintf(fp, "%02X ", buf);
                                }
                                printf("/n/n");
                                if(fp)
                                {
                                    fprintf(fp, "/n/n");
                                    fclose(fp);
                                }
                            }

                            ::SetThreadContext(hThread, &Regs);

                            ResumeThread(hThread);
                            CloseHandle(hThread);
                        }

                        if((DWORD)DebugEv.u.Exception.ExceptionRecord.ExceptionAddress==W2I_SENDCALL_ADDR2)
                        {
                            HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, DebugEv.dwThreadId);
                            SuspendThread(hThread);

                            CONTEXT Regs = {0};
                            Regs.ContextFlags = CONTEXT_DEBUG_REGISTERS; //CONTEXT_DEBUG
                            ::GetThreadContext(hThread, &Regs);

                            Regs.Dr0 = W2I_SENDCALL_ADDR1;
                            Regs.Dr7 = BREAK_DR7_FLAG;

                            ::SetThreadContext(hThread, &Regs);

                            ResumeThread(hThread);
                            CloseHandle(hThread);
                        }

                        dwContinueStatus = DBG_CONTINUE;
                        break;
                    }

                case CREATE_PROCESS_DEBUG_EVENT:
                    {
                        HANDLE hSnapshot = NULL;
                        THREADENTRY32 ThreadInfo = { sizeof(THREADENTRY32) };

                        hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL);
                        if(hSnapshot>0)
                        {
                            if(Thread32First(hSnapshot, &ThreadInfo))
                            {
                                do {
                                    if(ThreadInfo.th32OwnerProcessID==dwW2iProcessId)
                                    {
                                        //printf("%08x %08x/n", ThreadInfo.th32ThreadID, ThreadInfo.th32OwnerProcessID);

                                        HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, ThreadInfo.th32ThreadID);
                                        SuspendThread(hThread);

                                        CONTEXT Regs = {0};
                                        Regs.ContextFlags = CONTEXT_DEBUG_REGISTERS; //CONTEXT_DEBUG
                                        ::GetThreadContext(hThread, &Regs);

                                        Regs.Dr0 = W2I_SENDCALL_ADDR1;
                                        Regs.Dr7 = BREAK_DR7_FLAG;

                                        ::SetThreadContext(hThread, &Regs);

                                        ResumeThread(hThread);
                                        CloseHandle(hThread);
                                    }
                                } while(Thread32Next(hSnapshot, &ThreadInfo));
                            }
                            CloseHandle(hSnapshot);
                        }


                        printf("附加到完美进程成功!/n截获文件将保存到Cap.txt/n/n");

                        break;
                    }

                case EXIT_PROCESS_DEBUG_EVENT:
                    {
                        return 0;
                        break;
                    }
                }

                ContinueDebugEvent(DebugEv.dwProcessId, DebugEv.dwThreadId, dwContinueStatus);
            }
        }
    }
    else
    {
        printf("附加到完美进程失败!/n/n");
    }

    printf("/n按任意键退出!/n");
    getch();

    return 0;
}
 

你可能感兴趣的:(游戏内挂,VC)