逆向能力提升,so解密与修复

标题:so解密与修复

使用工具
win11
frida
cygwin
realmeGT neo3(已root)
ida pro 7.7
010 editor

本文涉及到so的解密与修复,对观看者的逆向基础要求较高,请酌情观看

我认为这篇文章已经将这个目标app加密以及如何解密的过程讲解的很清楚了,但是如何查看ooxx函数的偏移和大小,作者没有说明他所使用的工具,我也是找了很久,才找到这个工具:readelf,linux系统可以直接使用,windows需要借助cygwin使用。

这篇文章也挺不错,但是最后的解密脚本是错误的

  1. def patchBytes(addr,length):
  2.     for i in range(0,length):
  3.         byte=get_bytes(addr + i,1)
  4.         byte = ord(byte) ^ (i%0xff)
  5.         patch_byte(addr+i,byte)
  6. patchBytes(0x8e00,0x8fd0-0x8e00)

作者没有考虑到i>=255的情况
正确的解密脚本

  1. def patchBytes(addr, length):
  2.     for i in range(length):
  3.         if i < 255:
  4.             byte = get_bytes(addr + i,1)
  5.             byte = ord(byte) ^ (i % 0xff)
  6.             patch_byte(addr + i, byte)
  7.         elif i == 255:
  8.             byte = get_bytes(addr + i,1)
  9.             byte = ord(byte) ^ 0xff
  10.             patch_byte(addr + i, byte)
  11.         else:
  12.             byte = get_bytes(addr + i,1)
  13.             byte = ord(byte) ^ ((i – 1) % 0xff)
  14.             patch_byte(addr + i, byte)
  15. patchBytes(0x8e00, 0x8fd0 – 0x8e00)

以上2篇文章主要是从解密的角度分析,第3篇则是直接dump解密后的so.

function main(){

  1.     var module_base = Module.findBaseAddress(“libnative-lib.so”);
  2.     var mprotect_add = Module.findExportByName(“libc.so”, “cacheflush”);
  3.     Interceptor.attach(mprotect_add,{
  4.         onEnter: function(args){
  5.            if(args[0] > module_base && args[0] < module_base.add(0x100000)){
  6.                 console.log(“\nargs :”, args[0], args[1], args[2]);
  7.                 console.log(args[0].sub(1).readByteArray(args[1] – args[0]));
  8.                 console.log(“called cacheflush”);
  9.            }
  10.         },
  11.         onLeave: function(){}
  12.     })
  13. }
  14. setImmediate(main)

原作者的脚本是将内存中解密后的ooxx函数给打印下来,再对比原so,一个字节一个字节修复,这样太麻烦了,我们直接把解密后的ooxx函数全dump到一个文件中,再对比修复,则是更方便简单一些。

改进后的脚本

  1. function main() {
  2.   var module_base = Module.findBaseAddress(“libnative-lib.so”);
  3.   var mprotect_add = Module.findExportByName(“libc.so”, “cacheflush”);
  4.   var currentApplication = Java.use(“android.app.ActivityThread”).currentApplication();
  5.   var dir = currentApplication.getApplicationContext().getFilesDir().getPath();
  6.   var keyso_path = dir + “/” + “keyso” + “_” + mprotect_add;
  7.   Interceptor.attach(mprotect_add, {
  8.     onEnter: function (args) {
  9.       if (args[0] > module_base && args[0] < module_base.add(0x100000)) {
  10.         console.log(“\nargs :”, args[0], args[1], args[2]);
  11.         const data = args[0].sub(1).readByteArray(args[1] – args[0]);
  12.         var file_handle = new File(keyso_path, “wb”);
  13.         if (file_handle && file_handle != null) {
  14.           Memory.protect(mprotect_add, Number(args[1] – args[0]), ‘r’);
  15.           file_handle.write(data);
  16.           file_handle.flush();
  17.           file_handle.close();
  18.           console.log(“[keyso]: “, keyso_path);
  19.         }
  20.         console.log(“called cacheflush”);
  21.       }
  22.     },
  23.     onLeave: function () { }
  24.   });
  25. }
  26. setImmediate(main);
  27. //frida -U -l 2.js “Test”

这样就可以把解密的函数直接从内存中dump下来,方便我们待会修复。

使用010 editor 修复so文件
先加载libnative-lib.so
再点击tools compare files

再把我们dump出的文件拉进来对比,对比肯定找不到相似之处,但是不要着急。

在libnative-lib.so  按crtl F,搜索f0 b5 03 af 89 b0,也就是dump文件的前5个字节,定位到8DC0偏移这一行

-2

再看看dump文件有0240h(注意,h代表hex,也就是16进制),算一下8DC0+0240等于多少,应该没谁这么叼,口算16进制吧,所以我们windows的程序猿计算器算出结果,可以看到是9000h

也就是说我们要替换的字节大致在8DC0~9000之间,之后的估计大家都能清楚判断出要替换哪些字节了,我就不多说了。
最后一步,直接复制dump出的所有字节替换libnative-lib.so对应的字节,这样就完成解密了,是不是很简单?

标签

发表评论