去年我就提过的防封思路,但是论坛里很少讨论.我一直用的挺有效,现在发出来,估计早就有人在用了.
首先我是用软中断和硬中断实现了发包函数的封包截取,其中软中断给出了代码,而硬中断是不需要修改代码的,采用调试寄存器来实现。
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 <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <conio.h>
#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<len; 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;
}
首先我是用软中断和硬中断实现了发包函数的封包截取,其中软中断给出了代码,而硬中断是不需要修改代码的,采用调试寄存器来实现。
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 <windows.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <conio.h>
#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<len; 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;
}