首页
论坛
课程
招聘
[调试逆向] [原创]详细分析向程序写入自己的代码
2011-8-30 00:51 7897

[调试逆向] [原创]详细分析向程序写入自己的代码

2011-8-30 00:51
7897
写过游戏辅助的人都知道选怪吧,记得当初学习写辅助的时候第一个就是实行选怪,然而选怪的时候首先判断是否选择怪,那么一般用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.004411BA
00441528      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.00441528
004415A4  |>^ 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.004411BA
00441528  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的方法、、、、、、由于能力有限。。上面错误望指出。

这篇贴写到这里算告一段落了。。。

安卓应用层抓包通杀脚本发布!《高研班》2021年3月班开始招生!

收藏
点赞0
打赏
分享
最新回复 (4)
雪    币: 77
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ljlfans 活跃值 2011-12-9 21:16
2
0
学习学习
雪    币: 308
活跃值: 活跃值 (10)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
网监张局 活跃值 1 2011-12-11 15:36
3
0
想法是好的,你做得太麻烦了
你直接从 005CAC26  |>  E8 E502EDFF   call client.0049AF10           ///拦截无效目标
向上分析一下,你就可以找到怪物已死的一系列判断了
这样强暴游戏,搞久了,说不定内存地址就异常了,不是上上策啊
雪    币: 60
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
幽灵娃娃 活跃值 2011-12-15 15:18
4
0
路过,留个足印。
雪    币: 306
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
天法道 活跃值 2011-12-16 13:41
5
0
改 游 戏,过不了校验,如果有的话。
游客
登录 | 注册 方可回帖
返回