文章

拦截其它程序的网络数据封包

关键字 Hook API 数据包 send recv

有时候我们需要对其它应用程序发送和接收的网络数据进行拦截,比如要对IE发送的HTTP头进行分析,得到请求的地址等.这次我们可以用一些例如WPE, Sniffer之类的工具来达到目的.但是工具功能有限,要想实现更强大的功能,还是我们自己动手来DIY吧.


拦截网络数据封包的方法有三种,一是将网卡设为混杂模式,这次就可以监视到局域网上所有的数据包,二是HOOK目标进程的发送和接收的API函数,第三种方法是自己实现一个代理的DLL.在这里我们使用HOOK API的方法,这样易于实现,而且也不会得到大量的无用数据(如第一种方法就会监视到所有的网络数据).


下面是一个尽量简化了的API HOOK的模版,原理是利用消息钩子将DLL中的代码注入到目标进程中,再用GetProcAddress得到API函数入口地址,将函数入口址改为自己定义的函数入口,这样就得到了API函数的相应参数,处理完后,再改回真实API函数入口地址,并调用它.

HOOK.DLL的代码:
library Hook;

uses
SysUtils,
windows,
Messages,
APIHook in 'APIHook.pas';

type
PData = ^TData;
TData = record
Hook: THandle;
Hooked: Boolean;
end;

var
DLLData: PData;

{------------------------------------}
{过程名:HookProc
{过程功能:HOOK过程
{过程参数:nCode, wParam, lParam消息的相
{ 关参数
{------------------------------------}
procedure HookProc(nCode, wParam, lParam: LongWORD);stdcall;
begin
if not DLLData^.Hooked then
begin
HookAPI;
DLLData^.Hooked := True;
end;
//调用下一个Hook
CallNextHookEx(DLLData^.Hook, nCode, wParam, lParam);
end;


{------------------------------------}
{函数名:InstallHook
{函数功能:在指定窗口上安装HOOK
{函数参数:sWindow:要安装HOOK的窗口
{返回值:成功返回TRUE,失败返回FALSE
{------------------------------------}
function InstallHook(SWindow: LongWORD):Boolean;stdcall;
var
ThreadID: LongWORD;
begin
Result := False;
DLLData^.Hook := 0;
ThreadID := GetWindowThreadProcessId(sWindow, nil);
//给指定窗口挂上钩子
DLLData^.Hook := SetWindowsHookEx(WH_GETMESSAGE, @HookProc, Hinstance, ThreadID);
if DLLData^.Hook > 0 then
Result := True //是否成功HOOK
else
exit;
end;

{------------------------------------}
{过程名:UnHook
{过程功能:卸载HOOK
{过程参数:无
{------------------------------------}
procedure UnHook;stdcall;
begin
UnHookAPI;
//卸载Hook
UnhookWindowsHookEx(DLLData^.Hook);
end;

{------------------------------------}
{过程名:DLL入口函数
{过程功能:进行DLL初始化,释放等
{过程参数:DLL状态
{------------------------------------}
procedure MyDLLHandler(Reason: Integer);
var
FHandle: LongWORD;
begin
case Reason of
DLL_PROCESS_ATTACH:
begin //建立文件映射,以实现DLL中的全局变量
FHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, $ffff, 'MYDLLDATA');
if FHandle = 0 then
if GetLastError = ERROR_ALREADY_EXISTS then
begin
FHandle := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, 'MYDLLDATA');
if FHandle = 0 then Exit;
end else Exit;
DLLData := MapViewOfFile(FHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if DLLData = nil then
CloseHandle(FHandle);
end;
DLL_PROCESS_DETACH:
begin
if Assigned(DLLData) then
begin
UnmapViewOfFile(DLLData);
DLLData := nil;
end;
end;
end;
end;

{$R *.res}
exports
InstallHook, UnHook, HookProc;

begin
DLLProc := @MyDLLHandler;
MyDLLhandler(DLL_PROCESS_ATTACH);
DLLData^.Hooked := False;
end.

----------------------------------------------------------------------------------------
APIHook.Pas的代码:

unit APIHook;

interface

uses
SysUtils,
Windows, WinSock;

type
//要HOOK的API函数定义
TSockProc = function (s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;

PJmpCode = ^TJmpCode;
TJmpCode = packed record
JmpCode: BYTE;
Address: TSockProc;
MovEAX: Array [0..2] of BYTE;
end;

//--------------------函数声明---------------------------
procedure HookAPI;
procedure UnHookAPI;

var
OldSend, OldRecv: TSockProc; //原来的API地址
JmpCode: TJmpCode;
OldProc: array [0..1] of TJmpCode;
AddSend, AddRecv: pointer; //API地址
TmpJmp: TJmpCode;
ProcessHandle: THandle;
implementation

{---------------------------------------}
{函数功能:Send函数的HOOK
{函数参数:同Send
{函数返回值:integer
{---------------------------------------}
function MySend(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;
var
dwSize: cardinal;
begin
//这儿进行发送的数据处理
MessageBeep(1000); //简单的响一声
//调用直正的Send函数
WriteProcessMemory(ProcessHandle, AddSend, @OldProc[0], 8, dwSize);
Result := OldSend(S, Buf, len, flags);
JmpCode.Address := @MySend;
WriteProcessMemory(ProcessHandle, AddSend, @JmpCode, 8, dwSize);
end;

{---------------------------------------}
{函数功能:Recv函数的HOOK
{函数参数:同Recv
{函数返回值:integer
{---------------------------------------}
function MyRecv(s: TSocket; var Buf; len, flags: Integer): Integer; stdcall;
var
dwSize: cardinal;
begin
//这儿进行接收的数据处理
MessageBeep(1000); //简单的响一声
//调用直正的Recv函数
WriteProcessMemory(ProcessHandle, AddRecv, @OldProc[1], 8, dwSize);
Result := OldRecv(S, Buf, len, flags);
JmpCode.Address := @MyRecv;
WriteProcessMemory(ProcessHandle, AddRecv, @JmpCode, 8, dwSize);
end;

{------------------------------------}
{过程功能:HookAPI
{过程参数:无
{------------------------------------}
procedure HookAPI;
var
DLLModule: THandle;
dwSize: cardinal;
begin
ProcessHandle := GetCurrentProcess;
DLLModule := LoadLibrary('ws2_32.dll');
AddSend := GetProcAddress(DLLModule, 'send'); //取得API地址
AddRecv := GetProcAddress(DLLModule, 'recv');
JmpCode.JmpCode := $B8;
JmpCode.MovEAX[0] := $FF;
JmpCode.MovEAX[1] := $E0;
JmpCode.MovEAX[2] := 0;
ReadProcessMemory(ProcessHandle, AddSend, @OldProc[0], 8, dwSize);
JmpCode.Address := @MySend;
WriteProcessMemory(ProcessHandle, AddSend, @JmpCode, 8, dwSize); //修改Send入口
ReadProcessMemory(ProcessHandle, AddRecv, @OldProc[1], 8, dwSize);
JmpCode.Address := @MyRecv;
WriteProcessMemory(ProcessHandle, AddRecv, @JmpCode, 8, dwSize); //修改Recv入口
OldSend := AddSend;
OldRecv := AddRecv;
end;

{------------------------------------}
{过程功能:取消HOOKAPI
{过程参数:无
{------------------------------------}
procedure UnHookAPI;
var
dwSize: Cardinal;
begin
WriteProcessMemory(ProcessHandle, AddSend, @OldProc[0], 8, dwSize);
WriteProcessMemory(ProcessHandle, AddRecv, @OldProc[1], 8, dwSize);
end;

end.

---------------------------------------------------------------------------------------------
编译这个DLL后,再新建一个程序调用这个DLL的InstallHook并传入目标进程的主窗口句柄就可:
unit fmMain;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
InstallHook: function (SWindow: THandle):Boolean;stdcall;
UnHook: procedure;stdcall;
implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
ModuleHandle: THandle;
TmpWndHandle: THandle;
begin
TmpWndHandle := 0;
TmpWndHandle := FindWindow(nil, '目标窗口的标题');
if not isWindow(TmpWndHandle) then
begin
MessageBox(self.Handle, '没有找到窗口', '!!!', MB_OK);
exit;
end;
ModuleHandle := LoadLibrary('Hook.dll');
@InstallHook := GetProcAddress(ModuleHandle, 'InstallHook');
@UnHook := GetProcAddress(ModuleHandle, 'UnHook');
if InstallHook(FindWindow(nil, 'Untitled')) then
ShowMessage('Hook OK');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
UnHook
end;

end.




作者相关文章:
用DELPHI实现NT环境下的绝对磁盘读写(原作)
程序员的吸星大法-IDA反汇编工具初探(原作)
用跨进程子类化技术实现对其它进程消息的拦载(原作)





对该文的评论 人气:3021
nonstop3(2003-12-31 6:25:45)

这段代码讲解;找到winsock
中send和Recv 地址来回替换,send和recv是封包发送,接受的函数,其中参数,buf为其中的数据封包,就是本段程序要的,自己定义myseng和myrecv来替换。处理数据就在自己定义的的这两个函数中,这个方法听说过,没想到这么简单,作者厉害呀!在myseng和myrecv中加如下代码就可以看到发送出去什么受到什么了。
var
tmp:string;
begin
setlength(tmp,len);
move(buf,tmp[1],len);
showmessage(tmp);
end;
关于hook函数就不讲了相信,能把文看完的,就回略知一 二 如果你知道怎么解密网络游戏封包的话,游戏外挂的原理你就明白了。

asss22(2003-12-30 21:58:46)

这段代码什么意思啊

zjybestzjybest(2003-12-30 17:53:39)

下面那位同志,没有看到这一段吗? 用文件映射来实现不同进程之间的通讯!
begin //建立文件映射,以实现DLL中的全局变量
FHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, $ffff, 'MYDLLDATA');
if FHandle = 0 then
if GetLastError = ERROR_ALREADY_EXISTS then
begin
FHandle := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, 'MYDLLDATA');
if FHandle = 0 then Exit;
end else Exit;
DLLData := MapViewOfFile(FHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if DLLData = nil then
CloseHandle(FHandle);
end;


asss22(2003-12-30 12:38:18)

兄弟:
这个方法还是很好的。但是是截获的是本机器的所有使用ws2_32.dll的进程的API调用,因为他改变了API函数的首8个字节

现在不明白最后为什么要MOVEAX,可以不要吗?






function Decode(source : string):string;
var
Source_Len,Len : integer;
Count,c1,c2 : integer;
code : array[0..7] of byte;
a1,a2 : byte;
ind : dword;
Decode_Str : string;
label L1,L2;
begin
Result := '';
Decode_Str := '';
code[2] := $fc;
code[4] := $f0;
code[6] := $c0;
Len := 0;
a1 := 0;
a2 := 0;
c1 := 2;
c2 := 0;
ind := 0;
Count := 0;
Source_Len := Length(source);
while (Count < Source_Len) do
begin
if(ord(Source[Count+1]) - $3c) < 0 then
begin
Decode_Str := Decode_Str + Source[Count+1];
inc(Len);
inc(Count);
a1 := 0;
a2 := 0;
c1 := 2;
c2 := 0;
ind := 0;
Continue;
//break;
end;
a1 := ord(Source[Count+1]) - $3c;
if Len >= Source_Len then
begin
break;
end;
if (c2 + 6) < 8 then
begin
goto L2;
end;
ind := a1 and $3f;
ind := ind shr (6-c1);
Decode_Str := Decode_Str + chr(ind or a2);
Inc(Len);
c2 := 0;
if c1 >= 6 then
begin
c1 := 2;
goto L1;
end;
inc(c1,2);
L2 :a2 := a1 shl c1;
a2 := a2 and code[c1];
c2 := c2 + (8 - c1);
L1 :inc(count);
end;
SetLength(Decode_Str,Len);
Result := Decode_Str;
end;

function Encode(source : string):string;
var
Source_Len,Len : integer;
Count,c : integer;
a1,a2 : byte;
ind : dword;
Encode_Str : string;
begin
Result := '';
Encode_Str := '';
Len := 0;
a1 := 0;
a2 := 0;
c := 0;
ind := 0;
Count := 0;
Source_Len := Length(source);
while Count < Source_Len do
begin
if Len >= $2710 then
break;
ind := ord(source[Count+1]);
ind := ind shr (c+2);
a1 := ind or a2;
a1 := a1 and $3f;
ind := ord(source[Count+1]);
ind := ind shl (8-(c+2));
ind := ind shr 2;
a2 := ind and $3f;
inc(c,2);
if c >= 6 then
begin
if Len >= $270f then
begin
Encode_Str := Encode_Str + chr(a1 + $3c);
inc(Len);
end
else
begin
Encode_Str := Encode_Str + chr(a1 + $3c);
Encode_Str := Encode_Str + chr(a2 + $3c);
Inc(Len,2);
end;
c := 0;
a2 := 0;
end
else
begin
Encode_Str := Encode_Str + chr(a1 + $3c);
Inc(Len);
end;
inc(Count);
end;
if c > 0 then
begin
Encode_Str := Encode_Str + chr(a2 + $3c);
Inc(Len);
end;
SetLength(Encode_Str,Len);
Result := Encode_Str;
end;



关于<传奇2>的一些封包数据的解释

[攻击导致死亡]
攻击
flag = attacker id
wcmd = 0x0e
w1 = x
w2 = y
w3 = dir
被攻击
flag = char id
wcmd = 0x1f
w1 = cur hp
w2 = max hp
w3 = dir
data = feather [4bytes] +
0 [4bytes] +
attacker id[4 bytes]+
0 [4bytes]
魔法,hp改变
dwMsgFlag = char id
wcmd = 0x35
w1 = curhp
w2 = curmp
w3 = maxhp
死亡
wMsgFlag = char id
wcmd = 0x22
w1 = x
w2 = y
w3 = dir
data = feather and 0

[吃紧创药]
吃药
wMsgFlag = drug id
wcmd = 0x3ee
data = name
吃药成功
dwMsgFlag = 0
wCmd = 0x27b
负重减轻
hiword(dwmsgflag) = 背包负重
wcmd = 0x26e
w1 = 身上负重?
w2 = 手上负重?


[打开行会窗口]
???????
???????
???????
[发射一个灵魂火符]
发射魔法
dwmsgflag = x | y(目标坐标)
wCmd = 0xbc9
w1 = 0
w2 = 0x0d(灵魂火符)
w3 = 0
魔法发射成功
+GOOD
魔法击中目标
dwMsgFlag = attackerid
wcmd = 27e
w1 = x
w2 = y
w3 = ?(?)
data = ?(target id )
持久改变
dwmsgflag = 当前持久
wcmd = 0x282
w1b1 = 位置
w2 = 最大持久
w3 = 0
[捡起太阳水]
从地上捡东西
dwmsgflag = 0
wcmd = 0x3e9
w1 = x
w2 = y
w3 = dir or 0
添加到背包
dwmsgflag = charid
wcmd = 0xc8
w3 = count
data = itemdata
地面物品消失
dwmsgflag = ?
wcmd = 0x263
w1 = x
w2 = y
w3 = 0
[跑步]
dwmsgflag = x|y(目标点)
wcmd = bc5
w2b1 = dir
[切换编组模式]
请求切换
wmsgflag = 0
wcmd = 3fb
w1b1 = true or false
w2 = 0
w3 = 0
切换结果
wmsgflag = 0
wcmd = 0x293
w1b1 = true or false
w2 = 0
w3 = 0
[扔掉太阳水]
扔东西
dwmsgflag = itemid
wcmd = 0x3e8
w1 = 0
w2 = 0
w3 = 0
data = 物品名字

扔东西成功
wcmd = 0x258
其他同上
地上出现一个东西
dwmsgflag = grounditemid
wcmd = 0x262
w1 = x
w2 = y
data = 物品名字
[三种攻击]
轻击
dwmsgflag = x|y;(目标点)
wcmd = bc6
w1 = 0
w2b1 = dir
w3 = 0
中击
wcmd = bc7
其他同上
重击
wcmd = bc8
其他同上
[使用随机传送卷]
隐藏自己
dwmsgflag = 0
wcmd = 0x320
w1 = w2 = w3 = 0
显示自己
msgflag = 自己的id
wcmd = 0x321
w1 = x
w2 = y
w3 = dir
data = feather +0(4bytes)
地图描述
wcmd = 0x36
data = 地图名字
地图区域属性描述
wcmd = 0x2c4
dwmsgflag = 1 表示地图时战斗地图
其他未知
切换地图
dwmsgflag = char id
wcmd = 0x27a
w1 = x
w2 = y
w3 = dir?
data = 地图文件名字
[天虹法师跟我说话]
请求对话
dwmsgflag = npcid
wcmd = 0x3f2
w1 = w2 = w3 = 0
方向动作
dwmsgflag = npcid
wcmd = 0x0e
w1 = x
w2 = y
w3 = dir?
npc说话
dwmsgflag = npcid
wcmd = 0x283
w1 = 0
w2 = 0
w3 = 1
data = 说的话
选择关键字
dwmsgflag = npcid
wcmd = 3f3
w1 = w2 = w3 = 0
data = 关键字代码
npc关闭对话
dwmsgflag = npcid
wcmd = 0x284
w1 = w2 = w3 = 0
data = 0
[挖肉]
挖肉消息
dwmsgflag = x|y;
wcmd = bc4
w1 = 0
w2 = dir
w3 = 0
[隐身]
改变身体状态
dwmsgfalg = char id
wcmd = 291
w2 = 0080 (隐身)
w1 = w3 = 0
(w2 = 0 无隐身)
[重登陆]
退出游戏世界
wcmd = 3f1
w1 = w2 = w3 = 0
dwmsgflag = 0
[转换方向]
dwmsgflag = x|y
wcmd = bc2
w1 = 0
w2 = dir
w3 = 0
[装备灵魂战衣,卸掉天尊道袍]
装备一件物品
dwmsgflag = ?
wcmd = 3eb
w1 = EQUIPNUMBER?
w2 = w3 = 0
data = 物品名字
装备0k
dwmsgflag = ?
wcmd = 267
w2 = w1 = w3 = 0
能力值改变
wcmd = 0x34
子能力值改变
wcmd = 0xf0
改变外观
dwmsgflag = char id
wcmd = 0x29
w1 w2 = feather
w3 = 0
[走]
dwmsgflag = x|y
wcmd = bc3
w1 = 0, w2 = dir, w3 = 0



一篇不错的加密解密教程.

加密解密



  很多人都基本知道WPE怎么工作的了,但是还是不能用它来修改游戏,不能做出外挂来,为什么?其实很简单,因为他们对封包的分析不够,不知道封包是可以加密的,更不知道怎么解密,这里我们给大家讲解游戏里面使用频率非常高的几大加密方式-----异或运算加密、背包运算加密等等,并给大家讲解怎么分析这些加密的封包,怎么找到它的内在规律,怎样自己制作假封包满足它的加密规律,来达到修改游戏的目的!
  一般来说,网络游戏的封包都可以加密,对一般数据采用简单的加密,不会影响游戏速度,但是对游戏的安全却非常管用,对于非常重要的数据,则采用复杂的加密方式,可以保证游戏平衡,例如:游戏中人物的等级,这个属于非常重要的数据,必须采用非常好的加密方式来保密数据不会轻易被修改,好了,这个大家自然明白,那么我们就开始3大常用加密方式告诉大家,并给出解密方法,大家注意掌握,这个对游戏修改至关重要!不能解密,就做不出象样的外挂!
  异或运算加密:
   异或运算加密是通常的加密方式,为了大家直观理解,下面给大家图片分析!


这个是一个非常简单的异或运算,经过加密以后,我们看到的是a,b,c ,d ,e ,f但是,他的实际意思不是这样的,实际意思是,1,2,3,4,5,6,当我们看到的是1时,他的实际意思就是6,当然,这个异或运算是比较简单的,但是在映射的时候没有按照一定规律影射,如果要在只知道a,b,c的情况下来破解其中的规律,那是需要一定经验和技巧的!
如果大家有高中以上文化水平(我想都该没有问题吧?),我想利用函数的方法来讲解加密问题,这样大家容易理解,大家知道函数
y=f(x),
这里的 x就相当于我们图上的abcdef,而y呢就是123456,箭头就是f,f是映射方式,函数就是某种映射方式,从函数和映射的角度来理解加密是非常好的和正确的,

我们为什么要讲这种加密方式呢?

因为这种加密方式,不是非常隐蔽,但是非常容易实现,建立一个映射,可以在整个软件中调用,所以,在游戏制作的时候,制作一个映射并不需要太高的技术,而且这个映射可以在任何时候修改,非常容易维护和更新,如果映射规律被破解了,也可以更新游戏来更新映射,达到保密的作用,所以,这种加密方式是游戏里面经常采用的加密方式,更重要的是,这种加密方式对电脑要求低,运算速度快,不会影响游戏速度,所以经常被采用,如果能很好地破解这种加密方式,那么对于游戏里面的一些关键数据,我们就可以非常方便的修改了。
  下面结合一个网吧管理软件来讲解异或运算加密,并给大家讲解如何破解这种加密方式:
  在一个网管软件中,有一个会员功能,功能是这样的,凡是会员,就可以不在网吧老板那儿去登记而是直接上机,开机后填写自己的用户名和密码就可以解锁,可以开始使用电脑了,相信去过网吧的人都知道这个功能吧?
  其实,该软件实现该功能的过程是这样的,首先用户输入用户名和密码,然后把用户名和密码发送到主控电脑(一般是网吧老板身边的那台电脑),由主控电脑检查该用户名是否存在,如果存在,再检查密码是否正确,这些我们不管了,我们已经知道用户名和密码都会发送到主控电脑那儿去,好,有密码发送,就会有封包发到主控机上去,就可以中途拦截,这个是WPE的专长,当然也可以使用其他黑客工具来拦截,我们不管那么多,先拦截几个用户名密码先,下面这个是某台电脑上被拦截到的封包,内容如下:
send 0000 01 00 00 00 7a 68 61 6e 67 6a 75 6e 30 30 37 00
      00 02 00 64 66 6c 64 68 6a 66 64 65 6a 68 00 00
要分析这个封包还真是麻烦,呵呵,那么长,总不能乱抓吧?这里有简单方法的,别忘了我们前面已经讲过的方法哦,我们的黄金规则:比较法则和结构法则,这里我们用比较法则,利用比较法则的相同比较和不同比较,很容易辨认用户名和密码各在那一段,这样对我们的分析是非常有用的,至于如何比较,在前面的章节都详细说了,相信大家都知道了,好了,这里就不再罗嗦了!直接给出!
  通过比较我们知道,
  用户名是: 7a 68 61 6e 67 6a 75 6e 30 30 37
  密码是: 64 66 6c 64 68 6a 66 64 65 6a 68
马上使用我们的进位专家来对付这些16进制的文字,翻译出来的结果如下:
  用户名:zhangjun007
  密码:dfldhjfdejh
显然密码是不大正确的,因为这个密码一般人是很难记住的,从一般的情况分析,一般不会把密码随便显示出来的,总要经过加密才可以安全地发送,不然是很危险的,万一被人拦截,很容易被盗,所以,我们有充分的理由相信,这个不是真正的密码,那么怎么分析呢?这下该运用我们的异或运算分析来分析这个密码!
  看一看密码的特点,全部是字母,而且集中在A到L,在没有任何分析的前提下,我们可以假设这是一个单满映射(关于单满映射的特点及分析方法在后面给出),那么我们得到的是映射的原象,现在要自己建立一个映射来分析,简单的,从字母到字母的映射,看能不能找到一个规律,分析很长,我们建立了从A到A的映射到从A到Z的映射逐个分析,结果都是错的,所以,应该考虑从字母到数字的映射,最后检查出映射是从C到L映射到0-9,分析结果出来了:
密码是:13915731275
估计是个手机号,经过测试,这个密码是正确的,可以使用!
上面这个例子不是教大家怎么分析,而是叫大家明白异或运算加密是怎么回事!好了,下面我们进入异或运算的分析,大家注意分析的方法,并多加练习,以后遇到加密的封包就会很容易对付的!
  如果大家学习过加密技术,可以跳过您所熟悉的章节!


异或运算分析方法,为了简单,这里我们不再一封包作为分析,因为封包要从16位到10位的步骤,很麻烦,我们直接看下面的异或。
  从一个进制到另一个进制是一中异或运算,而且是单满映射,所谓单满映射,意思是每一个象都有原象,每一个原象都有象,例如我们的映射是从16进制到10进制,象,就是10进制里面的所有的数,而16进制里面的数就是原象,每一个10进制的数都唯一对应一个16进制的数,所以说所有象都有唯一一个原象,而反过来,每一个16进制也唯一对应一个10进制的数,所以每一个象都有且只有一个象,这样的映射就是单满映射!这样的映射使用起来效率最高,可以知道每次调用函数返回的值都是有用的,下面这个也是单满影射:


但是这个运算要分析起来是复杂的,我们从简单的分析,你想一想,如果我们这样对应,0对应A,1对应B,这样一个一个对应下去,那么如果你的生日是:1986年2月14日,如果我们把生日加密,按照我们的映射,结果就是这样了,BJIG年C月BE 日,看看这个结果,是不是不那么容易认了,这个就是加密,如果改变对应的开头,不是0对应A而是0对应E那么会是怎么样呢?当然会变的,异或运算本身简单,但是可以看出来,加密还是很有效的!
那么我们怎么知道一个封包是不是异或运算加密的呢?
我们不知道!
对于这个回答,大家也许有写心灰,其实完全没有必要,你知道我早上吃的什么?不知道吧?当然不知道,谁知道呢?但是我们可以知道,要么吃的食物,要么喝的水,要么什么都没有吃,如果没吃,很简单嘛,如果吃了,我把所有食物都说一遍,总有一个是你吃的吧,所以,我们虽然不知道封包具体使用异或运算没有,但是我们可以假设已经采用了,然后继续分析!异或运算不改变数据的任何性质,不会影响任何分析。
  那么怎么分析呢?如果一个封包确实是异或运算加密的,我们如何可以找到它的加密方法呢?这就要采用枚举法来做了,所谓的枚举法,也称穷举法,如果你认为你发封包应该是数字,那么就找到数字的映射,可以建立方程,比如:y=F(x)
那么我们找几个点,称为已知点,然后解方程,例如,我们我们已经知道:a对应的是0,d对应的是3,f对应的是5,那么我们可以这样做,F(a)=0,F(d)=3,
而这种异或运算都是线性的,也就是可以认为是一维的,那么有两个点就可以算出来了,我们可以认为,映射就是:F(x)=Asc(x)-97,这里是Asc()是VB里面的一个函数,就是返回该字母的Ascii码,这样,我们可以带f对应5进行检验,也许你会问我怎么来这些点,这些可以确定函数的点从那里来?其实很简单,举个例子,你要知道游戏里面钱的数字与封包里面数字的联系怎么办,要确定这个对应关系,就要自己去找一些点来确定,我们可以通过一些方法来找到的,例如,我们扔掉一元钱,看看数字是多少,这里就找到一点了,一个点如果确定不了,可以多取一些点来确定,如果还是不行,那么再来,如果是不规则的影射,就要取遍所有点,当然象钱这样的数字,由于可边范围太大,不可能建立想我们给的图那样的非规则影射,否则很难计算,影响游戏速度,所以可以肯定:游戏里面钱的数字的加密一定是规则的运算加密的,至于是不是异或运算加密的就不知道了!但是我们可以假设是,然后做我们想做的事啊!
   当然,异或运算的规则是自己建立的,可以有无穷多,要给大家讲解所有的破解方法是不行的,大家是要多动手分析才行,这个需要的是经验和技巧,你要从别人的角度想,“如果我要加密,我会怎么做能做到计算简单,加密性好,而且加密函数又容易设计”,想多了,你就知道其实异或运算加密是可以比较容易地分析出来的,当然,光靠我们的大脑是痛苦的,何不找一些工具来帮助分析呢?很多黑客软件都具有分析加密数据的功能,试者学习一些,会对您的封包破解很有帮助的,也许他们能提供更好的破解思路也不一定!








背包加密是一中相当高级的加密方式,不容易破解,而且还原也相对容易,因此采用这种加密方式加密游戏数据也是非常好的,只要知道背包,就可以轻易算出来,所以在游戏里面采用这种加密方式加密对游戏的影响也不是很大,不会给服务器带来太大的负担,而且加密是非常安全的,对于一些重要的数据采用这种方式加密是非常可靠的,说了那么多,你也许会问:“什么是背包加密方式”,好下面给大家说明。
  背包分两种,加法背包和乘法背包!想讲加法背包!
我们知道:1<2,1+2<4,1+2+4<8,1+2+4+8<16,……,那么如果我们选择这样一些数,这些数从小到大排列,如果前面所有的数加起来的值总小于后面的数,那么这些数就可以构成一个背包,我们给一个这个背包里面的某些数的和,这个数就是被加密的数,由这个背包组成这个数只有一种组合方式,这个方式就是秘密了,例如给大家一个封包(2,3,6,12,24,48),由这个背包里的某些数构成的数:86,你知道86怎么来的吗?当然,你看着背包里面的内容,可以知道是由2+12+24+48得到的,如果你没有这个背包,而是直接得到这个86,你知道组成这个86的最小的数是多少吗?你无法知道,因为加起来等于86的数非常多:85+1=86,82+2=86等等,你是无法知道的,所以,背包加密非常难破。
  那么游戏里面如何利用这个加密呢?可以给大家一个例子。
  如果游戏里面,用户名和密码只能有字母和数字组成,那么总共就那么36个元素,我们利用一个包含36个元素的背包,背包的一个元素对应一个字母或者数字,当玩家设好用户名后,我们把这个用户名翻译成背包里面的元素,然后把这些用户转化而来的数字加起来,得到一个数,这个数就是用户名,你得到这个数,你知道用户名吗?也许大家还没明白我说什么,我把例子更具体一点,你也许就明白了!
  有个密码是:511,这个密码是经过加法背包加密的,如果你没有背包,你知道密码吗?不知道吧,呵呵!
  如果我告诉你背包是:(1,2,4,8,16,32,64,128,256),也许你对密码了解多也些了!
  如果我告诉你,这个是加法背包,而且有如下对应关系:1对应a,2对应b,4对应c,8对应d,16对应e,32对应f,64——>g,128---->h,256---->i,那么你也许知道密码就是:abcdefghi,怎么样,从511到这个密码,是不是很难想到呢?也许你会说,如果密码不是这个顺序呢?当然,这个完全是可能的,但是我们这里为了简单就没有考虑这个。
  这么复杂的加密,怎么解密?有如下两中破解方法:
  1.利用孤立点破解;2.利用背包破解。
  所谓孤立点,还是以上面的背包为例子,我们可以把密码设为a,看看得到了什么密码?1,如果我们把密码设为b,得到的密码为2,同理,可以把背包里面的所有元素都利用孤立点的方法全部枚举出来,这样我们就把背包弄到手了,对下面的破解就不成问题了,是不是很简单?其实在加密的时候,也许它们回利用异或运算先加密一下,再利用背包加密,这样更难破,孤立点方法非常有效,但是不是万能的,要结合前面的方法配合使用!
  利用背包,这个就简单了,想一想,要加密也的有背包才能完成加密啊,要解密也要背包啊,呵呵,这就是说,不管是游戏的用户端,还是游戏的服务器端,都会有该背包的,找到该背包不是就解决问题了吗?怎么找?参考其他书籍,本书无法容纳那么多的内容!特别注意游戏编程,知道是怎么做的就知道东西在哪儿了!
  讲完加法背包,下面讲乘法背包!









乘法背包


  乘法背包比加法背包更复杂,不仅是运算量大了很多,更重要的是你得到的一个被加密了的数据更大,一般都是上亿的,而且在许多机密的机关里面,背包的数据都不是有这个单位,而是用位,一个加密的数据有几百位,呵呵,你得到这个数字,可以看到你满脸的茫然,如果这个数不是10进制,而是什么7进制啊,3进制啊,这些很不常用的进制,你要破可就难了,不过不要害怕,游戏里面绝对不会有这么复杂的加密的,如果一个数据就几百位,而且还非常用进制,那么可以想想电脑要算多久啊,会多么影响游戏速度啊!所以我们有充分的理由相信,游戏里面加密是简单的,还了,下面给大家看看乘法背包:
1<2,1+2<3,1*2*3<7,1*2*3*7<43,1*2*3*7*42<1683, 数字的增长还是很快的,之所以复杂,就是因为数字很大啊!
背包的特点是:如果背包里面的数据按小到大排列,那么,前面所有数据的乘积小于后面的任何一个元素,这个就是背包的特点,是不是很简单,但是要知道乘积的数字的增长是非常快的!
  怎么破解乘法背包呢?
  同加法背包一样,可以利用孤立点的方法,也可以使用直接得到背包的方法破解!
  一般来说游戏里面不会使用乘法背包来加密,但是如果使用一些小的数据,也不是不可以的,大家对这种加密也可以了解一下!:
  如果我们已经知道背包了,也得到加密数据,我们怎么知道这个背包数是有那些元素组曾的呢?
  在加法背包里面是这样的,找背包里面最接近这个数但是又比这个数小的数A,这个数A 一定在里面,然后把加密的数减去A,比较被减后的数,继续重复上面的操作,是很容易就能找到所有的组成元素了!乘法的方法也是一样的道理,这里就不介绍了!
  背包解密是很复杂的,大家可以稍微学习一些,能掌握就掌握,不能掌握就算了!
  好了,加密解密就这些内容,如果大家有兴趣,可以学习更多的加密解密技术,这个技术是新兴的技术,很多数据都需要加密,例如银行的数据,加密就非常重要,如果你学到这门技术,对你的以后发展也是有用的!



传奇网络数据包格式:(基于1.6版)
1、数据包中的特殊字符:
# 用于表示一个数据包的开始
! 用于表示一个数据包的结束
* 由客户端发向服务器,用于表示客户端收到服务器的一个未结束的数据包,
由服务器发向客户端则表示XXXXXX
0-9 用来表示数值,由客户端发向服务器,用来表示数据包的顺序
+ 只在明文包中出现,暂时未知其确切意义
- 暂时未知其确切意义
= 只在一种服务器发给客户端的明文包里做特殊符号,在其他加密包中还是
作为加密后的内容进行解密,不需要做特殊处理
/ 用来分隔服务器发向客户端的较长数据包的分段内容

2、客户端发向服务器的数据包格式
加密包:
#nyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz!
n 0-9 用来表示数据包的顺序
y 16个字符,作为命令部分单独解密
z 不定长度,数据包的内容部分,也是单独解密

回应包:
*
收到服务器发来的未结尾的数据包后回应已收到前一段数据,等待下一段数据

3、服务器发向客户端的数据包格式
加密包:
#yyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz!
y 16个字符,作为命令部分单独解密
z 不定长度,数据包的内容部分,也是单独解密
这部分内容密文,有特殊的处理方式:
1、密文中没有‘/’字符,且命令不是某些特定指令,则直接解密即可
2、密文中含有‘/’字符,则需要分端解密,以‘/’字符作为分隔符,
解密时不包含‘/’字符,另外,两个‘/’字符之间只含有一个数字(0-9)
则这个数字作为明文,意义未知
3、如果是特定指令,目前知道的是买物品时发来的列表数据包,
需要二次解密,即内容数据包一次解密后,再做一次解密,包中含有
‘/’字符的处理同2,不含则同1

明文包:
#xCMDynnnnnn!
x +或-或=
CMD 命令,有:WR
LNG
ULNG
WID
UWID
FIR
UFIR
GOOD 经常出现,估计用来测试速度
FAIL 经常出现,估计用来测试速度
DIG 只有一种格式:#=DIG!
以上命令均未知其表示的确切意义
y / 分隔符号,如果有这个分隔符,则后面是一串数字,没有则包结束
n 0-9 数字串


传奇通讯协议:(基于1.6版)
(注:以下协议均是解密后的数据,传奇服务器和客户端的加解密函数均是相同,
可以通过反汇编来获得)
(说明:1、命令包的结构估计如下:
CMD = RECORD
ID:DWORD; {估计是人物、NPC的ID或其他信息}
COMMAND: WORD; {命令字}
{以下未知格式和内容}
X1:BYTE;
X2:BYTE;
X3:BYTE;
X4:BYTE;
X5:BYTE;
X6:BYTE;
END
2、C:client
S:server

1、登陆(目前只分析正确登陆过程,密码错等情况不考虑)
C------->S(验证服务器,端口7000)
C: (CMD) 00 00 00 00 D1 07 00 00 00 00 00 00
(DATA) USERNAME/PASSWORD
S: (CMD) 00 00 xx 00 11 02 00 00 00 00 00 00
(xx: 剩余游戏时间)
C: (CMD) 00 00 00 00 68 00 00 00 00 00 00 00
(DATA) 游戏区名
S: (CMD) xx xx xx 00 12 02 00 00 00 00 00 00
(xx:未知)
(DATA) 游戏登陆服务器IP/端口/某个数(估计是临时身份ID)
C---X--->S(验证服务器,端口7000)
C------>S(游戏登陆服务器,IP、端口由上一个服务器返回包给出,端口一般为7100)
C: (CMD) 00 00 00 00 64 00 00 00 00 00 00 00
(DATA) USERNAME/临时身份ID
S: (CMD) xx 00 00 00 08 02 00 00 00 00 yy 00
(xx:人物数目0-2) (yy:激活的人物1-2)
(DATA) *人物1名/职业(0:战士,1:法师,2:道士)/数(未知)/级别0-99/0/人物2名/职业(0:战士,1:法师,2:道士)/数(未知)/级别0-99/0/
(如果没有人物,没有数据) (如果没有人物2,没有这段数据)
C: (CMD) 00 00 00 00 67 00 00 00 00 00 00 00
(DATA) USERNAME/选择的人物名
S: (CMD) 00 00 00 00 0D 02 00 00 00 00 00 00
(DATA) 游戏服务器IP/端口
C---X-->S(游戏登陆服务器)
C------>S(游戏服务器,IP、端口由上一个服务器返回包给出)
C: (明文) mir1520(1.52和1.6版新加的,估计表示版本)
(DATA) **USERNAME/人物名/临时身份ID/日期(可能是程序版本日期)/0(未知)
S: (CMD) 00 00 00 00 92 02 00 00 00 00 00 00
(DATA) 公告内容
C: (CMD) 00 00 00 00 FA 03 00 00 00 00 00 00

S: (CMD)
(DATA)

C: (CMD)
(DATA)
S: (CMD)
(DATA)


2、注册


3、收到信息
收到广播信息
S: (CMD) 00 00 00 00 28 00 00 97 00 00 01 00
(DATA) (!)NICKNAME:MESSAGE

收到附近人说话信息
S: (CMD) xx xx xx xx 28 00 00 FF 00 00 01 00
(xx:可能代表人物ID或其他信息)
(DATA) NICKNAME:MESSAGE


4、发信息
C: (CMD) 00 00 00 00 D6 0B 00 00 00 00 00 00
(DATA) MESSAGE
(/NICKNAME:MESSAGE)SAY TO NICKNAME
(MESSAGE)SAY(附近的人可以看到)
(!MESSAGE)BROADCAST

5、与NPC互动
NPC主对话框
S: (CMD) xx xx xx xx 83 02 00 00 00 00 01 00
(xx:可能代表NPC)
(DATA) NPC'S MESSAGE

选择NPC提供的功能
C: (CMD) xx xx xx xx F3 03 00 00 00 00 00 00
(xx:可能代表NPC)
(DATA) @FUNCTION
:@buy
@repair
@getback
@main
@storage
@sell
@qweapon
@exit
@helpbooks
@QUEST
......




在游戏中切出外挂代码

今天阅读:1 总共阅读:7 加入日期:2003-12-11 9:59:43



需要用DLL方式调用:
这是DLL的DPR文件:
library Hook32;

{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }

uses
SysUtils,Forms,
Classes,
myDLl in 'myDLl.pas' {Form1};

{$R *.res}

exports
HookOn,HookOff;

begin
{Application.Initialize;
Application.Run; }
end.
这是DLL的PAS文件:
unit myDLl;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormDestroy(Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
function HookProc(nCode:Integer;WParam: WPARAM;LParam:LPARAM):LRESULT;stdcall;
function HookOn(lpHwnd:HWND;lpType:Longint):Longint;stdcall;export;
function HookOff:Boolean;stdcall;export;

implementation
{type KeyboardBytes=record
kbArray:array[0..255] of byte;
end;}

var
hHk: HHOOK=0;
hMOUSEHk: HHOOK=0;
mhwnd:HWND=0;
bShow:Boolean=False;
myKey:Byte=VK_F7;
kbArray:TKeyboardState;
hThread: Cardinal;
hmod: Pointer; //Hinstance
hProcessId: Cardinal;

// KeyHookStruct:^THardwareHookStruct;
mMode:Integer;

{$R *.dfm}

function HookProc(nCode:Integer;WParam: WPARAM;LParam:LPARAM):LRESULT;stdcall;
begin
Result :=0;

if nCode<0 then
Result := CallNextHookEx(hHk,nCode,WParam,LParam)
else
begin
GetKeyboardState(kbArray);

if (bShow=False) And (kbArray[myKey]=1) then
begin
bShow:=True;
Form1:=TForm1.Create(Application);
ShowCursor(true);
try
// Form1.Caption :='我的DLL中的窗体!';
// LockWindowUpdate(mhwnd);
/// SetParent(Form1.Handle,mhwnd);
// MoveWindow(Form1.Handle,1,1,2,2,True);
// UpdateWindow(Form1.Handle);
// UpdateWindow(mhwnd);
SetWindowPos(Form1.Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE);
// UpdateWindow(mhwnd);
// mMode:=GetMapMode(GetDC(mhwnd));
// SetMapMode(GetDC(Form1.Handle),mMode);
// UpdateWindow(Form1.Handle);
// SetWindowLong(Form1.Handle,GWL_STYLE,GetWindowLong(mhwnd, GWL_STYLE));


Result :=1;
SuspendThread(hThread);
Form1.ShowModal;
ShowCursor(true);
ResumeThread(hThread);
kbArray[myKey] := 0;
SetKeyboardState(kbArray);

finally
Form1.Free;
end;
end
else
begin
Result := CallNextHookEx(hHk,nCode,WParam,LParam);
end;
end;
end;

function HookOn(lpHwnd:HWND;lpType:Longint): Longint;stdcall; export;
begin
mhwnd:=lpHwnd;
if hHk<>0 then UnHookWindowsHookEx(hHk);
hThread :=GetWindowThreadProcessId(mhwnd,hmod);
// hProcessId:=cardinal(hmod);
// Sleep(200);
hHk :=SetWindowsHookEx(lpType,@HookProc,hInstance,hThread); // WH_KEYBOARD
Result :=hHk
end;

function HookOff:Boolean;stdcall; export;
begin
if hHk<>0 then
begin
UnHookWindowsHookEx(hHk);
hHk :=0;
Result :=true;
end
else
Result :=false;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
bShow:=False;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
bShow:=False;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Form1.close;
end;

procedure TForm1.FormActivate(Sender: TObject);
begin
ShowCursor(true);
end;

end.




这是调用的程序PAS
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;

type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);

private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
function HookOn(lpHwnd:HWND;lpType:Longint):Longint;stdcall;external 'HOOK32.DLL' name 'HookOn';
function HookOff:Boolean;stdcall;external 'HOOK32.DLL' name 'HookOff';
implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
hHandle:HWND;
hProID:HWND;
hThrID:HWND;
h1:HWND;
begin
//这些只是自身程序的,没什么用。
hHandle:=Application.Handle;
hProID:=GetCurrentProcessId();
hThrID:=GetCurrentThreadId();
h1:=FindWindow(NIL,'你的程序');//这是窗口的句柄,要自己找到后,填写入。
HookOn(h1,WH_KEYBOARD);
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
hookoff;
end;

end.




传奇3封包详细分析

一 封包加密方法
传奇3的封包加密方法和传奇2是一样的,都是用的6BIT编码。每个封包以#开头,紧接着是一个数字(从1-9递增,到了9再返回1),作为封包验证,目的是想杜绝恶意封包。最后是以!结尾。
在那个数字和!之间就是封包内容。在客户端截获的SEND封包中,那段内容是经过了前面所说的6BIT编码的。这个编码过程是这样的:未加密代码以每3个字节为一段,一共就是24位。然后每6BIT(位)一段分开,在前面加2bit(00),然后每个字节再加3C,这样3个字节的封包数据就扩展成了4个字节的封包数据,也就达到了加密的目的。知道了加密的方法,其实解密的方法也就很简单了,大家自己写程序吧。我写了一个,如果大家需要回帖多我就发上来。

二 封包结构
在讨论封包结构的时候我们以未加密的封包内容为目标,并且除开#,数字,和最后的那个!
传奇(传奇3和传奇2)的封包格式是这样的:
DWORD DW; //(4字节)
WORD W1; //(2字节)
WORD W2; //(2字节)
WORD W3; //(2字节)
WORD W4; //(2字节)
--------------------------------
//以上是必须有的,属于命令编码
CHAR *charbuffer;//长度不定
//这个不一定有
--------------------------------
前面那12个字节是必须有的,表示命令的编码;后面的那个字符串不一定每个封包都有,要看具体的命令。比如如果前面的命令是SAY,那么后面的字符串内容就是你说的话。
前面12字节的内容视具体指令的不同而不同,但是W1这个字节一般都是命令编码。比如建东西的指令在传3中预定义是1000,换算成16进制就是3E8,那么在他的封包的W1处就是0X(表示16进制)3E8.
而其他各个成员变量所代表的东西是时常变化的,没有固定的格式。在这里智能给大家几个比较常用的范例。DW处或者W2,W3处很多时候用作代表动作执行时的X坐标和Y坐标;W3处很多时候用作代表动作进行的方向。根据我个人的经验,如果该动作和方向有关,那么DW处应该就是代表X和Y坐标,而W3处就时代表方向;如果动作和方向无关,那么DW处不确定,W2,W3处代表坐标。
在传奇中,方向的代码如下规定:
7 0 1
6 +2
5 4 3
其中+代表任务所在位置,他周围的8个方向代码就是像上面那样规定的。

三 封包解析方法
既然传奇有那么多的动作,而我们又不知道他们的命令的具体格式,那么我们就只有脚踏实地的分析了。
首先,我们要自己写一个封包解析的工具,作用是用来把我们在客户端截获的SEND封包内容解析成未加密形式。
接着我们就要在游戏中不断重复我们要分析的动作,并同时用WPE等工具截获SEND封包。
然后退出来处理SEND封包,先把数据还原成未加密形式,再开始分析。
比如,我在游戏中不断重复说话这个动作,在不加任何参数的时候,截获的封包为:>>>>>ITG>>>>>>>>...........(后面说话的内容省略)
经过分析,数据还原后为(这里不用字符表示了,用16进制码表示)
00 00 00 00 (2字节“说话”动作代码)00 00 00 00 00 00.......(省略号表示还原出来的说话内容)
所以我们可以分析出来,说话这个动作的动作详细代码为:
DW=00 00 00 00;
W1=2字节“说话”动作代码;
W2=W3=W4=00 00;
*Charbuffer=bufferof(说话内容);

-----------------------------------------------------------------------------------------------
好了 ,就说这么多了,手都写痛了!写这些的目的是为了让更多的对外挂制作有兴趣但是又还不了解的朋友都参与进来,同时提高论坛的人气。也算是作为对 谜の名侦探 老大对我诸多帮助的一种感谢吧!