shanlihou 发表于 2021-3-13 17:46

从零开始教你做辅助--拾取篇(三 完结篇)

大家好,我是山里猴.
哈哈,好久不见不知道还有人记得我吗,最近是真的忙,距离上一次更新估计有一两个月了.
这次更新也是这个系列最后一篇,主要是本系列后面更新重复内容会逐渐增多,新的可以和大家分享的内容就不多了.
我本人主要是做开发的,所以在做逆向的过程中,比较喜欢通过自己做一些插件等方式来提升逆向的效率.
其实最初选调试器的时候也用过OD,但是OD编写扩展相对其他调试器缺乏官方支持,后来也编译过x64Dbg,不过当时使用过程中有bug,可能是我编译问题.
后来发现immunity debugger这个调试器原生支持python,相信大家都听说过"人生苦短,我用python",python这语言开发效率确实高,很适合用来做插件
再后来发现CheatEngine 这个神器不止能查找内存,修改内存,也有调试功能.
而且我看论坛之前好像对于CheatEngine + lua方式,还有immunity debugger这方面的帖子也不多,所以就做了这个系列.

自己做辅助的过程也让自己又提升不少,为了分析方便,最近又学了下IDA pro的插件开发,做了个根据调用堆栈辅助分析基址的插件,之后把这个也分享下.
这是这个IDA Pro插件工程地址,用c++开发的 https://github.com/shanlihou/ida_get_base
用起来就如下图这样,下图显示的就是我用来查拾取函数参数基址时候的效果


下面是前两篇的导航
从零开始教你做辅助一
从零开始教你做辅助二
---------------------------------------------- 废话分割线 -----------------------------------------------------
# 物品拾取

## 0x01 找到函数调用地址

上期我们在找攻击的调用函数地址时候其实已经找到了这个函数 76A670
这个函数里面有一个switch case 在case 10006的时候会走到拾取逻辑,最终走到769b30这个函数中

!(https://gitee.com/shanlihou/image_bed/raw/master/warspear_03/open_monster_bag.png)

汇编代码就如上图,咱们就在调用的时候加个断点,来看下esi和edi究竟是什么,其实esi可以根据之前的攻击函数才出来,应该是玩家实体,毕竟这里应该调用的是玩家类的成员函数,所以ecx相当于this这个指针.

```lua
function clear_debug()
    local tbl = debug_getBreakpointList()
    if tbl == nil
    then
      return
    end
    for i,v in ipairs(tbl) do
      print(string.format("%4d 0x%X",i,v))
      debug_removeBreakpoint(v)
    end
end

function debugger_onBreakpoint()
    if EIP == 0x76a7a5
    then
      print(string.format("enter in 0x76a7a5, esi:0x%X, edi:0x%X", ESI, EDI))
    else
      print(2)
    end
    return 1
end

clear_debug()
print(1)
debug_setBreakpoint(0x76a7a5)
```

打印这两个值得代码其实如上,前面已经展示很多遍了,其实这种逻辑比较简单的,大家加断点,在断下来之后直接查看寄存器,也可以,只不过我个人不太喜欢让游戏停下来,因为大部分游戏,停下来之后再运行可能要重新走下登录流程,很麻烦.
最后打印出如下
enter in 0x76a7a5, esi:0xEF4F4C8, edi:0xD623270
这个esi经过我和玩家实体指针对比,确实是玩家没错.这个edi并不是之前的怪物实体指针,需要在确认下具体是什么.
这个貌似没什么捷径,只能一层一层往上找,网上看到下图位置,看到edi是从函数的入参传进来的,现在只能继续逆流而上查找入参是从哪里来的.

!(https://gitee.com/shanlihou/image_bed/raw/master/warspear_03/modify_edi_1.png)

## 0x02: 分析参数来源

然后我们用老方法,获取调用过程如下.

76a5c6->5c72b5->7af2f9->7e8c79->5c77df->7bf179->747b18->8699b2->87b817->4be438->

!(https://gitee.com/shanlihou/image_bed/raw/master/warspear_03/modify_edi_4.png)
从上层传入的第一个参数也是edi,再找这个edi的来源
!(https://gitee.com/shanlihou/image_bed/raw/master/warspear_03/modify_edi_2.png)
可以看到,edi来自ecx+8
!(https://gitee.com/shanlihou/image_bed/raw/master/warspear_03/modify_edi_3.png)
这里一顿操作,发现eax来自esi
!(https://gitee.com/shanlihou/image_bed/raw/master/warspear_03/modify_edi_5.png)
esi又来自ecx
!(https://gitee.com/shanlihou/image_bed/raw/master/warspear_03/modify_edi_6.png)
ecx又来自ebx
!(https://gitee.com/shanlihou/image_bed/raw/master/warspear_03/modify_edi_7.png)
好了,这里真相了,ebx来自eax, eax又来自基址9A45B0,这个基址仔细一看跟当初的怪物列表最终的基址一模一样,应该出自同一个全局变量之类的.

废了老大功夫,找到这个edi,我把它这个逻辑写到咱们的辅助程序里,试了下,结果直接异常退出了,这时候我把获取到的参数和ce里面获取到的比较了一下,发现这么深的一个变量只是用来临时存储用的.
只能从头来过了,其实还是重复上面的操作,下面还是直接贴个结果吧,主要还是让大家了解向上追踪的一个方法.

## 0x03: 编写打开怪物掉落列表的窗口的代码

```cpp
void EntityBase::searchDead()
{
    try
    {

      DWORD tmp_ptr = *(DWORD*)(BASE_MONSTER_LIST_ROOT);
      log_debug("before get ptr :%p\n", tmp_ptr);
      tmp_ptr = *(DWORD*)(tmp_ptr + 0x10);
      log_debug("before get ptr :%p\n", tmp_ptr);
      tmp_ptr = *(DWORD*)(tmp_ptr + 0x3c);
      log_debug("before get ptr :%p\n", tmp_ptr);
      tmp_ptr = *(DWORD*)(tmp_ptr + 0x38c);
      log_debug("before get ptr :%p\n", tmp_ptr);
      tmp_ptr = *(DWORD*)(tmp_ptr + 0xecc);
      DWORD tmp2 = tmp_ptr;

      //下面这些是根据咱们第一次向上追踪写出来的一个获取参数的逻辑
      //log_debug("before get ptr :%p\n", tmp_ptr);
      //tmp_ptr = *(DWORD*)(tmp_ptr + 0x78);
      //log_debug("before get ptr :%p\n", tmp_ptr);
      //tmp_ptr = *(DWORD*)(tmp_ptr + 0x0);
      //log_debug("before get ptr :%p\n", tmp_ptr);
      //tmp_ptr = *(DWORD*)(tmp_ptr + 0x8);
      //log_debug("after get tmp %p\n", tmp_ptr);

      //如下是tmp2是咱们在第一次失败后第二次追踪后写出来的
      tmp2 = *(DWORD*)(tmp2 + 0x48);
      tmp2 = *(DWORD*)(tmp2 + 0x44);
      tmp2 = *(DWORD*)(tmp2 + 0x0);
      DWORD node = tmp2;
      DWORD next_node = *(DWORD*)(node + 0x4);
      while (next_node) { //这边应该是获取了列表里最后一个节点,但是这个列表存了什么暂时还不清楚
            node = next_node;
            DWORD next_node = *(DWORD*)(node + 0x4);
      }
      tmp2 = *(DWORD*)(node + 0x14);
      if (tmp2 == 0)
      {
            log_debug("search but tmp_ptr:0\n");
            return;
      }
      
      DWORD selfPtr = this->basePtr;
      log_debug("will searchDead tmp2 %p %p\n", selfPtr, tmp2);
      DWORD searchDropCall = SEARCH_DEAD_DROP;
      __asm
      {
            mov eax, tmp2
            push eax //怪物的地址
            mov ecx, selfPtr //自己的地址
            mov eax, searchDropCall
            CALL eax
      }
    }
    catch (...)
    {
    }
}
```

上面的代码流程中吧第一次找的地址tmp_ptr这个也包含了进来,失败了就给注释掉了,tmp2才是咱们要的
编译运行后就能正确的打开掉落列表了

!(https://gitee.com/shanlihou/image_bed/raw/master/warspear_03/drop_list.png)

## 0x04: 寻找拾取的代码地址(就是点击如上下一页)

为了找到拾取代码的地址,只能用老方法,通过send函数调用堆栈来找,这里跟上一章碰到的问题也一样,send函数只是从消息队列中取出消息来发送,要想找到拾取代码位置,还是需要找到插入消息队列的函数地址.

分析后发现如下三个插入到消息队列的操作,调用堆栈如下,在看这三个堆栈都经过这个6f0e25地址.
并且这个地址只会经过一次,所以到这里已经得到拾取函数的地址了,就是这个地址6F00B0.

eax:0x8
50dd22->7a714e->7a44ca->6eea24->6f0110->6f0e25->7e8b65->7a7222->7af33c->7a73e0->7bf0a7->747b18->8699b2->87b817->4be438->860521->76b647ab->76b42aac->
eax:0x14
50dd1c->7a714e->6eeb25->6f0110->6f0e25->7e8b65->7a7222->7af33c->7a73e0->7bf0a7->747b18->8699b2->87b817->4be438->860521->76b647ab->76b42aac->76b444db->
eax:0x14
50dd1c->7a714e->6eeeb8->6f0fa2->7e8e48->6f0110->6f0e25->7e8b65->7a7222->7af33c->7a73e0->7bf0a7->747b18->8699b2->87b817->4be438->860521->76b647ab->

## 0x05: 分析拾取代码的调用参数

现在函数已经有了,下面就是传参了,首先在6f0e20这里下断,观察发现,入参eax一直为0,剩下的就是ecx的获取了.
根据上面已经获取的堆栈分析后,得到如下获取ecx值得过程.

```cpp
      DWORD a = 0x9A5798;
      a = *(DWORD*)(a + 0xc);
      a = *(DWORD*)(a + 0x0);
      a = *(DWORD*)(a + 0x7c);
      a = *(DWORD*)(a + 0x40);
      a += 0x550c;
      a = *(DWORD*)(a + 0x1A98);
      a = *(DWORD*)(a + 0x18);
      __asm
      {
            mov ecx, 0x0
            push ecx
            mov ecx, a
            mov eax, 0x6f00b0
            CALL eax
      }
```

可惜编写了如上代码后,发现这个最终获取到的a并不是我们想要的,看来这个值是在另一个流程里赋值进去的.
现在再用ce对如上获取到a这个内存地址查是谁改写了这个值,得到如下这个地址,可以看到这里把值放进了1a98,那应该就是这个地址了.

007BECA1 - 8B EC- mov ebp,esp
007BECA3 - 8B 45 08- mov eax,
007BECA6 - 89 81 981A0000- mov ,eax <<
007BECAC - 5D - pop ebp
007BECAD - C2 0400 - ret 0004

EAX=0A1F0AF8
EBX=00000003
ECX=00CD0614
EDX=000000DA
ESI=0A1F0AF8
EDI=0EDE24F0
ESP=0019F85C
EBP=0019F85C
EIP=007BECAC

再对如上地址加断点获取到如下堆栈.

7a74f8->7af2f9->7a73e0->7ae053->7af2a3->7e8c79->6f0c50->7bf179->747b18->8699b2->87b817->4be438->

最终我们根据堆栈分析出了如下这个调用过程

```cpp
      DWORD a = 0x9A5798;
      a = *(DWORD*)(a + 0xc);
      a = *(DWORD*)(a + 0x0);
      a = *(DWORD*)(a + 0x7c);
      a = *(DWORD*)(a + 0x40);
      a += 0x550c;
      a = *(DWORD*)(a + 0x10);
      a = *(DWORD*)(a + 0x8);
      a += 8;
      a = *(DWORD*)(a + 0x8);
      a = *(DWORD*)(a + 20);
      a = *(DWORD*)(a + 0x18);

      __asm
      {
            mov ecx, 0x0
            push ecx
            mov ecx, a
            mov eax, 0x6f00b0
            CALL eax
      }
```

用了如上代码后,大功告成

scghjl 发表于 2021-3-13 22:10

完全看不懂啊,大佬,我想学习写辅助,主要是能够遍历角色周围地上的物品,就传奇那类的游戏,不知道要学习哪方面的知识,望指点一下,谢谢了

shanlihou 发表于 2021-3-14 11:58

scghjl 发表于 2021-3-13 22:10
完全看不懂啊,大佬,我想学习写辅助,主要是能够遍历角色周围地上的物品,就传奇那类的游戏,不知道要学习 ...

写辅助推荐用c++吧,也有人用易语言
遍历物品要找到基址,需要cheatengine先分析下

lyl610abc 发表于 2021-3-13 17:47

前来给大佬提鞋{:301_975:}
评分用完了,明天补上{:301_978:}

lsy832 发表于 2021-3-13 17:50

我就喜欢这样的帖子

wylksy 发表于 2021-3-13 17:56

先感谢下

x1188 发表于 2021-3-13 18:50

学习一下,谢谢了

liyingqiu123 发表于 2021-3-13 19:00

学习一下6666666666

vbhvhgh 发表于 2021-3-13 19:07

大佬 厉害赶紧学习

Mdr 发表于 2021-3-13 19:12

不错楼主,支持下

yoyoma211 发表于 2021-3-13 20:48

不明觉厉,进来跟高手学习了

debug_cat 发表于 2021-3-13 20:56

萌新来给大佬打call了。现在分享技术的这么详细的真不多。
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 从零开始教你做辅助--拾取篇(三 完结篇)