吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 24860|回复: 105
收起左侧

[调试逆向] 战矛在线外挂制作经验分享(用脚本语言python+lua来节省分析时间)

    [复制链接]
shanlihou 发表于 2021-1-24 18:03
本人是一名从事游戏开发的程序猿,平时也喜欢玩游戏,比如题目上的这款战矛在线warspear,是一款像素风的mmorpg。
不过有个问题就是这款游戏是真的肝,像我这种每天996的人是没有时间玩的,最终解决这个问题的方法,只能是做外挂了,让计算机自己玩自己{:1_886:}。
这个外挂开发过程中的相关代码,截止到放弃时候完成了,获取怪物信息,打怪,捡物品等功能(之所以放弃因为我发现要想完整做一个外挂,也挺肝的,基本方法学会了,后面就是前面操作的重复了)。
https://github.com/shanlihou/hack_warspear
当然也要感谢郁金香老师,我也是看了他的视频后学到了很多东西后才开始做的,我这里介绍一些他视频里没有的东西吧。

hack_warspear

0x01:必要工具

1.immunity debugger

之所以选择 immunity 而不用od是因为这个调试器支持python,实现用python来简化一些重复性工作,比如在断点时候打印一些寄存器信息,内存信息等等,虽然od应该也可以做,不过对我这种不太习惯的人来说学习成本有点高。

2.cheat engine

郁金香老师的外挂视频教程中也经常使用的一个查基址,甚至调试的软件。而且我发现ce也支持脚本语言,你是可以通过编写lua脚本来实现很多功能,比如查看堆栈等等

0x02:目录结构

GameData
InjectDll
UpdateBase
hackWarspear
immunity:该目录存放为immunity debugger写的一些python脚本
info:该目录存放一些lua脚本及其他一些调试中间的信息记录
上面前面4个目录是当时写的外挂软件目录,不过本文不涉及

0x03:ce + lua

先给大家分享下ce的lua api网址,里面有ce提供的各种lua接口
https://wiki.cheatengine.org/index.php?title=Lua  

ce通常的使用方法相信应该很多朋友是知道的,比如我想知道怪物的血量信息:
1.找到怪物血量信息在内存中的地址
2.再attach到游戏进程中查看是哪里改写了这个地址
3.然后根据堆栈找到基址
但是我不知道ce当前代码对应的堆栈,所以就需要用到调试器附加到进程加上断点之后再重复上述操作。  

不过有了ce+lua后,我们就能通过编写lua脚本来直接从ce里获取堆栈,然后就可以直接根据堆栈用ida或其他查看静态汇编的软件来分析了。

function print_stack(ebp, deep)
    --deep:堆栈深度
    --ebp:栈低寄存器
    if deep == 0 
    then
        return ""
    end
    local ebp_4 = readInteger(ebp + 4) --ebp + 4 就是调用过来时候的eip寄存器存的地址,根据这个就能倒推堆栈
    local str_ret = string.format("%x->", ebp_4)
    local next_ebp = readInteger(ebp) -- 然后当前ebp里面存的就是上一个栈低的地址,也就是上一个ebp里面存的地址
    str_ret = str_ret .. print_stack(next_ebp, deep - 1) --剩下的就是递归调用了,最终会把堆栈保存到str_ret里面
    return str_ret
end

上面代码中用到了几个ce提供的lua接口,比如readInteger(addr)就是从传入的addr对应的地址中读出内存中四字节数,其他都是lua语法了.

当然少不了加断点,以及断点自动执行lua脚本的方法了,毕竟ce可是具备了调试器的.

function debugger_onBreakpoint() --通用断点回调,每个断点都会进到这里
         local ebp_4 = readInteger(EBP + 4)
         if EIP == 0x77E114 -- 根据当前eip地址来做函数分发
         then
             debugger_0077E114()
         else
             debugger_784a21()
         end
  return 1
end

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
clear_debug()
debug_setBreakpoint(0x77E114) --设置断点到地址0x77E114

如上代码展示了ce的断点相关操作,其实debug_setBreakpoint,我看官方文档最近版本应该是可以指定函数做入参了,之前我写这个脚本时候貌似还没有.

这里只是展示了ce+lua脚本的一小部分功能,还有很多很方便的用法,有兴趣的同学可以试一试.毕竟人生苦短,我用(python or lua or ...),能提高一点效率是一点.

0x04: immunity debugger

相信immunity 的python调试功能很多同学都用过,我在分析这款游戏时候,首先也是用的immunity,不过后来发现ce能支持lua后大部分工作就放在ce上了.两个各有优缺点吧.
immunity:
优点: 调试功能还是比较全的,毕竟是名字里带着debugger的调试器
缺点: 断点之后+python着实优点肿了,很多时候他的断点函数执行会导致进程变的极度缓慢.但是如果你用ce+lua就完全不会,一秒钟进入几十次断点游戏运行速度几乎不受影响
ce+lua:
优点: 如上所说,断点调试非常顺滑,甚至调用频率比较高的接口都敢放心加断点.
缺点: lua跟python比提供的功能还是太简单了.而且这个lua的import貌似有问题,import之后的函数还是找不到,所以基本上代码只能写到一个文件中,一些自己封装的接口也没法通用.  

其实大部分功能还是相似的,比如常用的断点执行某个函数等功能,主要区别是因为python的库比较多,可以实现一些自己的小工具等.  

比如根目录下immunity/rec_down.py这个文件是我实现的一个方便查看内存的脚本.不过我也是在网上找了一个现成的别人对递归下降算法的实现,然后改了一下,让他能支持对简单的语法解析,里面内容有点长,这里就不展示了,原理是利用递归下降算法来做语法解析,有兴趣的同学可以了解下.
用法就是在immunity的命令行窗口下
!rec_down [eax]: 这个命令可以用来查看eax寄存器对应地址的内容
!rec_down [ebx + [eax + 4]] 这个命令是先读取eax+4地址对应内容再 加上ebx的值,得到的地址,再从中获取值
!rec_down [ecx+[ebx + [eax+4]]]  哈哈,这个主要就是可以无限嵌套.
immunity 的脚本执行需要通过在命令窗口执行 "!脚本名 脚本参数1 脚本参数2" 以这种方式执行(不带双引号)

在有了这个小工具后,你就可以把他当做一个库来用在其他脚本里了,比如

from immlib import *
import rec_down # 引入刚才咱们介绍的小工具
import utils

class HookStack(LogBpHook):
    def __init__(self, desp):
        LogBpHook.__init__(self)
        self.desp = desp

    #########################################################################
    def run(self, regs):
        imm = Debugger()
        #ret = rec_down.parse_exprs(imm, regs, '[esp],[esp+8],[esp+12],[esp+16]') 这是之前注释的一个,
        data_len = rec_down.parse_expr(imm, regs, '[esp+12]') # 这个就是获取 esp+12 再取地址内容存入data_len
        if data_len != 6:
            data_addr = rec_down.parse_expr(imm, regs, '[esp+8]') 
            ret = imm.readMemory(data_addr, data_len) #从地址data_addr 处读取 data_len长度的数据到ret中
            ret = list(map(lambda x:ord(x), ret))
            imm.log("data_addr:{:x}, len:{}, ret:{}".format(data_addr, data_len, ret))

        if data_len == 30:
            stacks = imm.callStack() # 如果数据长度等于30的话,就获取堆栈并把堆栈输出到imm的log窗口中
            for stack in stacks:
                imm.log(str(stack))
        # stack_set = imm.getKnowledge('stack')
        # if not stack_set:
        #     stack_set = set()

        # stack_set.add(rec_down.parse_expr(imm, regs, '[esp+4]'))
        # imm.addKnowledge('stack', stack_set)

    @staticmethod
    def cat_stack_set():
        imm = Debugger()
        ret = imm.getKnowledge('stack') # getKnowledge是获取用户自定义的之前存入stack中的值,相当于一个全局变量
        imm.log(str(ret))

def main(args): # immunity的可执行脚本都要有main函数,然后参数从这里传入
    imm = Debugger()

    utils.clear_hooks(imm)
    opr = args[0]
    if opr == 'hook': 
        # ---------------------------- send start ------------------------------------
        addr = '76C76C19' # bp send
        #addr = '008563D1' # 8418a6
        #addr = '841871'
        # ---------------------------- send end --------------------------------------
        #addr = '5a926a' # HookClick
        #addr = '5a9210' # click header
        h = HookStack(addr) # 这个钩子是我们刚刚定义的,现在把它实例化
        ret = h.add(h.desp,  int(addr, 16)) # 这个就是加断点了,当断下来的时候就会回调到我们的钩子函数里面
        if ret == -1:
            imm.log("hook send failed!")
    elif opr == 'cat':
        HookStack.cat_stack_set()
    elif opr == 'clear':
        utils.clear_hooks(imm)

    return "ok"

上面展示的就是加断点并且附带一个钩子函数,当断点断下来时候会执行成员函数run里面的内容.



如上只是一些个人在调试分析时候的一些经验分享吧.有错误或者有兴趣的朋友欢迎指正或交流,大家一起学习,一起进步.

点评

我也做过不少H5游戏的自动玩的JS脚本..但是..24小时挂机始终肝不过充钱每天只玩1小时的...  发表于 2021-1-25 16:54

免费评分

参与人数 42威望 +2 吾爱币 +138 热心值 +38 收起 理由
wuai130113 + 1 + 1 我很赞同!
weiye588 + 1 + 1 我很赞同!
z3264 + 1 热心回复!
34688347 + 1 热心回复!
victos + 1 + 1 谢谢@Thanks!
submarine1620 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
540776612 + 1 + 1 我很赞同!
zhoumeto + 1 + 1 用心讨论,共获提升!
Dicker + 1 + 1 已经处理,感谢您对吾爱破解论坛的支持!
借z雨点s爱你 + 1 + 1 用心讨论,共获提升!
MYlindaxia + 1 + 1 我很赞同!
羁o绊 + 1 + 1 用心讨论,共获提升!
wws天池 + 1 + 1 我很赞同!
ddr711 + 1 + 1 好帖子 学习了
lxj8378 + 1 + 1 这个游戏是手机端还是pc端的?
Par + 1 + 1 应该好好学一下,大赞
、﹏尐酒窩づ + 1 + 1 谢谢@Thanks!
soyiC + 1 + 1 用心讨论,共获提升!
fengbolee + 1 + 1 用心讨论,共获提升!
jackliqiao + 1 + 1 我很赞同!
quandu + 1 热心回复!
lookerJ + 1 谢谢@Thanks!
kubosec + 1 + 1 谢谢@Thanks!
eoven8 + 1 + 1 热心回复!
li221219 + 1 + 1 我很赞同!
又飘小雪 + 1 + 1 555,居然能在1202年看到战矛玩家
diaoes + 1 + 1 用心讨论,共获提升!
azcolf + 1 + 1 用心讨论,共获提升!
52jcool + 1 + 1 我很赞同!
dddl + 1 + 1 用心讨论,共获提升!
xdxf2000 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wth0411 + 1 + 1 谢谢@Thanks!
february + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
gogobn + 1 + 1 谢谢@Thanks!
o824 + 1 + 1 谢谢@Thanks!
759611050 + 1 我很赞同!
夏520 + 1 + 1 我很赞同!
ouyangjianping + 1 谢谢@Thanks!
willJ + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
绮梦 + 1 + 1 私信您了 请看一下 谢谢
精神科王医生 + 1 + 1 写的很详细,看完很感动
杨辣子 + 1 + 1 lua脚本是个好东西

查看全部评分

本帖被以下淘专辑推荐:

  • · Aarow|主题: 988, 订阅: 304

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| shanlihou 发表于 2021-1-27 09:38
苗浩东 发表于 2021-1-26 11:20
我也想学这个,但是无从学起,大佬可不可以给条路径

基础的话汇编 c或c++学一下,这些是必备的,还可以网上找下郁金香的视频,讲的还挺好,这周末我也做个从零开始的
宾少 发表于 2021-1-29 19:45
这游戏我有玩过,玩到10来级就不玩了,真心累人~手机象素类的游戏,只能通过任务才能获得经验,打怪只能得到钱和物品,跑图跑任务忒蛋疼了,玩了几天就放弃了~老实说,如果楼主大人要是能做出自动任务自动打怪的辅助出来的话,我一定会继续玩的
鲜血淋漓一舞倾 发表于 2021-1-25 10:58
cmd 发表于 2021-1-25 11:01
写的很饿详细  特别好
mokjf 发表于 2021-1-25 11:37
有没有原神的脚本?每天自己自动跑图,见到怪自己打,让游戏自己玩自己
whngomj 发表于 2021-1-25 11:58
感谢分享
cxj998 发表于 2021-1-25 12:17
感谢分享,正需要
Les丶 发表于 2021-1-25 13:58
感谢分享,正需要
金鍂鑫 发表于 2021-1-25 14:07
没有编译成品吗?
westlife73 发表于 2021-1-25 14:40
非常好 但是我看不懂···就看看能不能蹭个成品···
838384855 发表于 2021-1-25 14:58
这游戏我都放弃了 本人来自sea
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 11:34

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表