某游戏盒子 so层加密 逆向分享

前言:

本文章仅学习研究,如若侵权,请联系第一时间进行删除

准备:

app版本:8.1.0.33

工具:frida

一般我们都用jadx查看xml文件,得到app的包名,还可以使用adb命令,来查看包名

adb shell pm list packages -3

Frida检测:

使用frida spawn 模式启动app,app直接闪退了,猜测存在frida检测

这里有几篇大佬的过检测文章,感兴趣的可以研究一下,强烈推荐宝藏论坛 -- 看雪论坛

通过bilibili分析libmsaoaidsec.so的环境检测-Android安全-看雪-安全社区|安全招聘|kanxue.com

[原创] frida常用检测点及其原理--一把梭方案-Android安全-看雪-安全社区|安全招聘|kanxue.com

[原创]绕过爱奇艺新版libmsaoaidsec.so Frida检测-Android安全-看雪-安全社区|安全招聘|kanxue.com

不过 本次app 的frida检测并不是很变态,使用hluda就可以轻松绕过,感谢大佬的分享

Releases · hzzheyang/strongR-frida-android · GitHub

分析:

通过登录接口,发现再表单里有个sign字段,每次请求登录都不一样,这个就是校验的参数,我们来看看再哪里生成的

逆向:

因为加密在so,我们直接关注so,先试试通用脚本,能否找到这个生成位置

function hook_register(name, address) {
  Interceptor.attach(address, {
    onEnter: function (args) {
      console.log(" vvv-------------------------------------------------vvv ")
      console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n"))
      console.log(name, ": ", args[1].readCString())
      console.log(" ^^^-------------------------------------------------^^^ \n\n")

    }, onLeave: function (retval) {

    }
  })
}

function hook_NewStringUTF() {
  var hkFunc = "NewStringUTF"
  var symbols = Process.getModuleByName("libart.so").enumerateSymbols();  // 枚举所有符号
  for (let i = 0; i < symbols.length; i++) {
    var symbol = symbols[i]
    if (symbol.name.indexOf("CheckJni") == -1 && symbol.name.indexOf(hkFunc) != -1) {

      console.log(symbol.name, symbol.address)
      hook_register(hkFunc, symbol.address)
    }
  }
}

直接就可以hook到,省了不少事情,通过堆栈,可以看到他是在libNativeHelper.so中生成的,我的手机是64位的,所以将 lib/arm64-v8a 文件夹下的libNativeHelper.so 拖出来,并使用ida64打开,直接搜索位置

将a1一下数据类型,再看一下代码就清晰了

我们先简单的分析一下,v4是由s而来,s由v14和v15拼接而来,再向上,v15只能看到初始化,没有别的有用线索了,重点关注一下v14

从表面看,他好像是一个md5,是不是标准的呢,hook一下MD5Final看看,查看日志 ,注意我的这个版本还有一层,需要再进去

function print_arg(addr) {
  var module = Process.findRangeByAddress(addr)
  if (module != null) return hexdump(addr, {length: 32}) + "\n";
  return ptr(addr) + "\n";
}
function hook_1() {
  var soAddr = Module.findBaseAddress("libNativeHelper.so")
  var sub_11668 = soAddr.add(0x12588)
  Interceptor.attach(sub_11668, {
    onEnter: function (args) {
      this.arg0 = args[0]
      this.arg1 = args[1]
      this.arg2 = args[2]
      console.log(" 进入时 ----------------------------------------  ")
      console.log(print_arg(this.arg0))
      console.log(print_arg(this.arg1))
      console.log(print_arg(this.arg2))
    }, onLeave: function (retval) {
      console.log(" 离开时 ----------------------------------------  ")
      console.log(print_arg(this.arg0))
      console.log(print_arg(this.arg1))
      console.log(print_arg(this.arg2))
    }
  })
}

通过日志分析,参数1,也就是buffer,将来的结果存放地,参数2,是上下文,参数3是长度

再去hook MD5Update

function print_arg(addr) {
  // 这里可能有bug,如果传来的是一个jString,可能会报错
  var module = Process.findRangeByAddress(addr)
  if (module != null) return hexdump(addr) + "\n";
  return ptr(addr) + "\n";
}
function hook_1() {
  var soAddr = Module.findBaseAddress("libNativeHelper.so")
  var sub_11668 = soAddr.add(0x11668)
  Interceptor.attach(sub_11668, {
    onEnter: function (args) {
      this.arg0 = args[0]
      this.arg1 = args[1]
      this.arg2 = args[2]
      console.log("12588 :  进入时 ----------------------------------------  ")
      console.log(print_arg(this.arg0))
      console.log(print_arg(this.arg1))
      console.log(print_arg(this.arg2))
    }, onLeave: function (retval) {
      console.log("12588 :  离开时 ----------------------------------------  ")
      console.log(print_arg(this.arg0))
      console.log(print_arg(this.arg1))
      console.log(print_arg(this.arg2))
    }
  })

  var sub_12588 = soAddr.add(0x12588)
  Interceptor.attach(sub_11668, {
    onEnter: function (args) {
      this.arg0 = args[0]
      this.arg1 = args[1]
      this.arg2 = args[2]
      console.log("12588 :  进入时 ----------------------------------------  ")
      console.log(print_arg(this.arg0))
      console.log(print_arg(this.arg1))
      console.log(print_arg(this.arg2))
    }, onLeave: function (retval) {
      console.log("12588 :  离开时 ----------------------------------------  ")
      console.log(print_arg(this.arg0))
      console.log(print_arg(this.arg1))
      console.log(print_arg(this.arg2))
    }
  })
}

再次观察日志,参数1是上下文,参数2,看起来像明文,中间有我输入的手机号,拿到加密网站试试,看看是否是标准md5

CyberChef

aaghcaptchaReqbf86bb305f98419985417153313461WOOpS79PEz4=1888888ef2vx#sf*^FlklSD*9sdf(m$&qw%d7po

可以看到,加密结果与抓包的一致,那么他就是一个标准md5了

通过对明文的分割,发现与抓包的data相同,只是多了一串东西,再次请求一下看看最后是否会变

再次hook的结果

最后一串是固定的,那么参数的来源我们就都知道了,对表单中 captcha、captchaid、dateline、password、usename、固定字符串 进行拼接,再进行md5,就可以得到sign值了

结语:

回到一开始观察的部分,因为我们hook md5 直接就看到了结果,后续的操作,并没有影响结果,所以我们可以不用关心他,也就不用关心v14和v15怎么来的,

在逆向so的时候,可以从结果向上hook的方式,来快速定位关键部分,有时候的so很长很吓人,但是关键点可能就一小部分,ida提供的伪c也只是参考,要耐心,敢于尝试,才能拨云见日

  • 26
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值