写过游戏辅助的人都知道选怪吧,记得当初学习写辅助的时候第一个就是实行选怪,然而选怪的时候首先判断是否选择怪,那么一般用ce 进行查找。。当选择怪的时候ce填写大于0,当没怪时候填写等于0.....过了这么久还是记忆犹豫,当初记得是看郁金香老师的视屏学习的........
那么我首先用一个小列子来开始我这篇文章,目的是为了证明我的方法也是有用武之地的
最近写一款游戏辅助,当选择怪物后判断是否有怪的地址也找对了,但是当打死怪物以后,如果那个怪物掉东西的话那个地址的数据却还是怪物id的数据,如果不掉东西的话就离开为0,于是这样的话会影响打怪物的速率,因为你要捡取物品后那个地址数据才会变成 0 ,然而我却没找到其他地方标注有判断id的地方了,但是我每次发现当怪物死了后掉落物品,你在发送技能的话会出现‘无效目标’....于是我找到了这个出现无效目标的地址,于是我突发奇想如果当出现这个标志的时候我就把那个标志是否有id的数据地址写入0,不就行了吗?于是我就按照我想法这样做了,
***************************************************************
005CAC1D |> 85FF test edi,edi ; Default case of switch 005CABE5
005CAC1F |. 74 15 je short client.005CAC36
005CAC21 |. 68 CC90C700 push client.00C790CC ; ASCII "MSG_USESKILL_ERROR8"
005CAC26 |> E8 E502EDFF call client.0049AF10 ///拦截无效目标
一:改call
**************************************************
于是我在上面 005CAC26 这个地址改变了他call的地址
比如你吧那个call的地址跳到你自己定义的函数
你可以这样做
//假设你的函数
function A();
int call_addr;// 定义一个变量。。保持成写出的数据
_asm
{
push eax
lea eax,A //取函数A的内存地址
mov call_addr,eax
pop eax
};
//计算写入的实际地址
call_addr:=call_addr-0x005CAC26 -0x5;
现在就是写入的实际的数据 writeProcessMemory(pHandle,Pointer(0x005CAC26 +1),@call_addr,sizeof(call_addr),lpNumberOfBytesWritten); 0x005CAC26 +1 是由于中间有个call 的机器码
通过上面的办法就改变了call的地址
那么调用call的时候就会跑到你自己的函数 、、、、、、于是你只用在你的函数里面处理了
***************************************************************二:改jmp
**********************************************************
下面讲解一下改jmp 指令跳到你的函数地址
function A();
int call_addr;// 定义一个变量。。保持成写出的数据
byte EE9; ///jmp 机器码 0xe9
_asm
{
push eax
lea eax,A //取函数A的内存地址
mov call_addr,eax
pop eax
};
//计算写入的实际地址
call_addr=call_addr-0x005CAC26 -0x5;
EE9=0xe9
现在就是写入的实际的数据
writeProcessMemory(pHandle,Pointer(0x005CAC26),@EE9,sizeof(EE9),lpNumberOfBytesWritten); //写jmp
writeProcessMemory(pHandle,Pointer(0x005CAC26 +1),@call_addr,sizeof(call_addr),lpNumberOfBytesWritten);
但是这个又有不同上面改call 不用你在函数中处理跳回到原来call的下一句执行
但是改成jmp 需要你跳回来。。或者你在函数中加一个处理堆伐的代码。。不然会引错误
*************************************************************方式三:直接在游戏中处理 ;
*************************************************************
由于上面2中方法必须要你的函数加载到原来程序中去,所以在一定方面上来说是有局限性的
二下面这个方法就不用加载你的函数中处理了
要求:在程序内存中找一篇空白处。。。。所谓空白处就是如nop
00466452 90 nop
00466453 90 nop
00466454 90 nop
00466455 90 nop
00466456 90 nop
00466457 90 nop
00466458 90 nop
00466459 90 nop
0046645A 90 nop
0046645B 90 nop
0046645C 90 nop
0046645D 90 nop
0046645E 90 nop
0046645F 90 nop
00466460 90 nop
00466461 90 nop
00466462 90 nop
00466463 90 nop
00466464 90 nop
00466465 90 nop
00466466 90 nop
00466467 90 nop
00466468 90 nop
00466469 90 nop
0046646A 90 nop
0046646B 90 nop
0046646C 90 nop
或
00441528 CC int3
00441529 CC int3
0044152A CC int3
0044152B CC int3
0044152C CC int3
0044152D CC int3
0044152E CC int3
0044152F CC int3
或者是
00466486 . 0000 add byte ptr ds:[eax],al
00466488 . 0000 add byte ptr ds:[eax],al
0046648A . 0000 add byte ptr ds:[eax],al
0046648C . 0000 add byte ptr ds:[eax],al
0046648E . 0000 add byte ptr ds:[eax],al
00466490 . 0000 add byte ptr ds:[eax],al
00466492 . 0000 add byte ptr ds:[eax],al
00466494 . 0000 add byte ptr ds:[eax],al
00466496 . 0000 add byte ptr ds:[eax],al
00466498 . 0000 add byte ptr ds:[eax],al
0046649A . 0000 add byte ptr ds:[eax],al
0046649C . 0000 add byte ptr ds:[eax],al
0046649E . 0000 add byte ptr ds:[eax],al 也就是说找一个地方 就是程序不会执行的这个代码。。。好像免杀里面用这个方法比较多
下面我也用一个列子来说明
00441524 . \BA114400 dd Lobby.004411BA
00441528 CC int3
00441529 CC int3
0044152A CC int3
0044152B CC int3
0044152C CC int3
0044152D CC int3
0044152E CC int3
0044152F CC int3
00441530 /$ 55 push ebp
00441531 |. 8BEC mov ebp,esp
00441533 |. 83EC 08 sub esp,0x8
00441536 |. 894D F8 mov [local.2],ecx
00441539 |. 8B4D F8 mov ecx,[local.2]
0044153C |. 81C1 C00C0000 add ecx,0xCC0
00441542 |. E8 A9D4FCFF call Lobby.0040E9F0
00441547 |. 85C0 test eax,eax
00441549 |. 75 5B jnz short Lobby.004415A6
0044154B |. C745 FC 000000>mov [local.1],0x0
00441552 |. EB 09 jmp short Lobby.0044155D
00441554 |> 8B45 FC /mov eax,[local.1]
00441557 |. 83C0 01 |add eax,0x1
0044155A |. 8945 FC |mov [local.1],eax
0044155D |> 8B4D F8 mov ecx,[local.2]
00441560 |. 81C1 C00C0000 |add ecx,0xCC0
00441566 |. E8 B5390400 |call Lobby.00484F20
0044156B |. 3945 FC |cmp [local.1],eax
0044156E |. 7D 36 |jge short Lobby.004415A6
00441570 |. 8B4D FC |mov ecx,[local.1]
00441573 |. 51 |push ecx
00441574 |. 8B4D F8 |mov ecx,[local.2]
00441577 |. 81C1 C00C0000 |add ecx,0xCC0
0044157D |. E8 CE9D0200 |call Lobby.0046B350
00441582 |. 8B08 |mov ecx,dword ptr ds:[eax]
00441584 |. E8 77530300 |call Lobby.00476900
00441589 |. 3945 08 |cmp [arg.1],eax
0044158C |. 75 16 |jnz short Lobby.004415A4
0044158E |. 8B55 FC |mov edx,[local.1]
00441591 |. 52 |push edx
00441592 |. 8B4D F8 |mov ecx,[local.2]
00441595 |. 81C1 C00C0000 |add ecx,0xCC0
0044159B |. E8 B09D0200 |call Lobby.0046B350
004415A0 |. 8B00 |mov eax,dword ptr ds:[eax]
004415A2 |. EB 04 |jmp short Lobby.004415A8 ******************* 004415A4 |>^ EB AE \jmp short Lobby.00441554
004415A6 |> 33C0 xor eax,eax
004415A8 |> 8BE5 mov esp,ebp
004415AA |. 5D pop ebp
004415AB \. C2 0400 retn 0x4
004415AE CC int3
004415AF CC int3
我由于写这个游戏的时候找一个数据找不到他的来源。。久了没找到也不想再去找了、、、于是我通过ce 找到了存放这个数据的地址。然后用od下内存访问断点就来到了上面给出的代码
于是我就对上面的函数进行了一个简单的hook
我发现上面有
00441528 CC int3
00441529 CC int3
0044152A CC int3
0044152B CC int3
0044152C CC int3
0044152D CC int3
0044152E CC int3
0044152F CC int3
于是我把我要实现的目的完成
我的目的是把ecx 保持到一个地址里面去,然后跳到下一句执行
我改成了
00441524 . \BA114400 dd Lobby.004411BA00441528 890D 00009000 mov dword ptr ds:[0x900000],ecx //把ecx保持到一个基址里面去。。这个基址需要是不影响程序的 找到 时候找个一直是0的,,。这个要自己分析
0044152E EB 78 jmp short Lobby.004415A8 //跳到004415A8执行
00441530 /$ 55 push ebp
00441531 |. 8BEC mov ebp,esp
00441533 |. 83EC 08 sub esp,0x8
00441536 |. 894D F8 mov [local.2],ecx
00441539 |. 8B4D F8 mov ecx,[local.2]
0044153C |. 81C1 C00C0000 add ecx,0xCC0
00441542 |. E8 A9D4FCFF call Lobby.0040E9F0
00441547 |. 85C0 test eax,eax
00441549 |. 75 5B jnz short Lobby.004415A6
0044154B |. C745 FC 000000>mov [local.1],0x0
00441552 |. EB 09 jmp short Lobby.0044155D
00441554 |> 8B45 FC /mov eax,[local.1]
00441557 |. 83C0 01 |add eax,0x1
0044155A |. 8945 FC |mov [local.1],eax
0044155D |> 8B4D F8 mov ecx,[local.2]
00441560 |. 81C1 C00C0000 |add ecx,0xCC0
00441566 |. E8 B5390400 |call Lobby.00484F20
0044156B |. 3945 FC |cmp [local.1],eax
0044156E |. 7D 36 |jge short Lobby.004415A6
00441570 |. 8B4D FC |mov ecx,[local.1]
00441573 |. 51 |push ecx
00441574 |. 8B4D F8 |mov ecx,[local.2]
00441577 |. 81C1 C00C0000 |add ecx,0xCC0
0044157D |. E8 CE9D0200 |call Lobby.0046B350
00441582 |. 8B08 |mov ecx,dword ptr ds:[eax]
00441584 |. E8 77530300 |call Lobby.00476900
00441589 |. 3945 08 |cmp [arg.1],eax
0044158C |. 75 16 |jnz short Lobby.004415A4
0044158E |. 8B55 FC |mov edx,[local.1]
00441591 |. 52 |push edx
00441592 |. 8B4D F8 |mov ecx,[local.2]
00441595 |. 81C1 C00C0000 |add ecx,0xCC0
0044159B |. E8 B09D0200 |call Lobby.0046B350
004415A0 |. 8B00 |mov eax,dword ptr ds:[eax]
004415A2 ^ EB 84 jmp short Lobby.00441528004415A4 |>^ EB AE \jmp short Lobby.00441554 跳上去00441554
004415A6 |> 33C0 xor eax,eax
004415A8 |> 8BE5 mov esp,ebp
004415AA |. 5D pop ebp
004415AB \. C2 0400 retn 0x4 你分析一下你就发现上面的函数自动把ecx的数据保存到了一个地址里面了。。所以你如果需要那个数的话直接从那个地址里面读取。。。。但是有人会问如果那个函数不执行怎么办?这个问题其实挺关键的、、、所以你在选取在那个地方hook挺关键的。比如我上面的那个地址他会一直执行。我虽然没分析他的作用但是知道他会自动执行就行了。。。在选取hook的地址的时候会很有用 上面只是我用od来完成的。。下面我要自己写代码完成了
于是我查看那个
dd 00441528
00441524 004411BA Lobby.004411BA00441528 00000D89
0044152C 78EB0090
00441530 83EC8B55
00441534 4D8908EC
00441538 F84D8BF8
0044153C 0CC0C181 于是我要写的就是向00441528 写入 00000D89
0044152C 写入 78EB0090 还有那个jmp的004415A2 ^\EB 84 jmp short Lobby.00441528 dd 004415A2
004415A2 AEEB84EB
004415A6 E58BC033
004415AA 0004C25D
于是我要写的就上面三个数
于是我要写的就是向00441528 写入 00000D89
0044152C 写入 78EB0090
004415A2 写入 AEEB84EB
其实第三个作用写一个byte就行了。。但是懒得麻烦。。。接写一个int一样
下面是代码 delhi 写的
function hook(h:hwnd):bool;stdcall //h:目标程序的句柄
var
pid: DWORD ;
phandle:hwnd ;
lpNumberOfBytesWritten:dword;
a1,a2:integer;
b1,b2:byte;
bo,bo1,bo2,bo3:bool;
begin
result:=true;
a1:=$89501589 ;// 00441528
a2:=$78EB0087 ;// 0044152C
b1:=$84 ; //004415A3; ///写的是一个byte 和写int 一样道理
try
GetWindowThreadProcessId(h,@pid);
phandle:=OpenProcess(PROCESS_ALL_ACCESS,false,pid);
bo:=writeProcessMemory(pHandle,Pointer($00441528),@a1,sizeof(a1),lpNumberOfBytesWritten); //
bo1:=writeProcessMemory(pHandle,Pointer($0044152C),@a2,sizeof(a2),lpNumberOfBytesWritten); //
bo2:=writeProcessMemory(pHandle,Pointer($004415A3),@b1,sizeof(b1),lpNumberOfBytesWritten); //
if bo and bo1 and bo2 and bo3 then
begin
result:=true;
end
else
result:=false;
except
result:=false;
end;
end; 于是你可以通过这个函数直接改他程序执行流程,达到你的目的
**********************************************************
对于方法3.。可以自己在od中改了再保存
有的人对于找空白区域。。如果写入的函数代码较多可以给程序加个区段。。。然后再写入 上面介绍了三种hook的方法、、、、、、由于能力有限。。上面错误望指出。 这篇贴写到这里算告一段落了。。。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)