[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OE94MTXP-1600281730856)(C:\Users\hcz\AppData\Roaming\Typora\typora-user-images\image-20200917013828865.png)]
发现函数无法f5,按p创建函数,发现如下错误信息:
0000A86A DCW 0xB686 a886出的opcode 无法翻译成指令,竟然ida 无法翻译,那么原so的流程肯定不会经过改分支,经过的话程序会崩,修复方法是直接patch nop。 nop之后可以p键创建函数,但f5 之后又报A872: positive sp value has been found 错误,定位到0xa872,指令为 ADD SP, SP, #0xF0
改指令导致堆栈不平衡,导致ida无法f5, 原so肯定不会运行到改分支,会崩溃,所以直接nop。nop之后u键->c键->p键创建函数,AAFA: positive sp value has been found 发现同理直接nop,nop堆栈之后的f5代码如下:
发现代码中出现大量jmpout。
1.2 修复jmpoup
截取一块jmpout代码块
可以看到导致jmpoup的原因是mov pc,r1,ida不知道目的地在哪,仔细观察从0000A9D0 push {r0-r3}开始至0000AA26 POP {R0-R3}结束,寄存器的值不会发生改变,这一段可以认为是花指令,直接在0000A9D0出patch 指令为b 0x0000AA28,0000A9D0- 0x0000AA28 中间的指令用nop填充,为方便修改,编写ida python脚本,脚本内容如下:
# -*- coding:utf-8 -*-
from idc import *
from idaapi import *
from keystone import *
ks = Ks(KS_ARCH_ARM, KS_MODE_THUMB)
def ks_dis_asm(dis_str):
global ks
encoding, count = ks.asm(dis_str)
return encoding
def un_junk_code(fun_start, fun_end):
patch_start = None
patch_end = None
jmp_addr = []
for i in range(fun_start, fun_end):
# PUSH {R0-R3} or PUSH {R0-R3,R7}
# MOV R0, PC
# MOV R1, PC
buf = "".join(['%02x' % ord(j) for j in get_bytes(i, 6)]).lower()
if buf == '0fb478467946' or buf == '8fb478467946':
patch_start = i
# end POP {R0-R3}
end_buf_one = "".join(['%02x' % ord(j) for j in get_bytes(i, 2)]).lower()
#
if end_buf_one == '0fbc':
patch_end = i + 2
# # end POP {R0-R2}
# # POP {R3,R7}
end_buf_two = "".join(['%02x' % ord(j) for j in get_bytes(i, 4)]).lower()
if end_buf_two == '07bc88bc':
patch_end = i + 4
if patch_start != None and patch_end != None:
for j in range(0, patch_end - patch_start, 2):
# patch nop
patch_byte(patch_start + j, 0x00)
patch_byte(patch_start + j + 1, 0xbf)
# 跳转
dis_str = 'b #{}-{}'.format(patch_end, patch_start)
jmp_addr.append(patch_start)
encoding = ks_dis_asm(dis_str)
print dis_str, encoding
for item in encoding:
print('{}'.format(hex(item)))
for k in range(len(encoding)):
patch_byte(patch_start + k, encoding[k])
print hex(patch_start), hex(patch_end)
patch_start = None
patch_end = None
if __name__ == '__main__':
# 函数开始位置与结束位置
un_junk_code(0x000551C0, 0x00057784)
修改un_junk_code 函数的参数,即可修复这类花指令。
2、定位leviathan 函数
2.1 Jni_onload 定位法
长话短说,经过分析sub_7B10函数为RegisterNative函数的关键处。
2.2 frida hook registernative
好用的一批,强推,不多bb 直接上代码
// hook register 打印动态注册的函数地址
function hook_register(){
// libart.so 所有导出函数表
var symbols = Module.enumerateSymbolsSync("libart.so");
var addr_register = null;
for(var i = 0; i < symbols.length; i++){
var symbol = symbols[i];
var method_name = symbol.name;
if(method_name.indexOf("art") >= 0){
if(method_name.indexOf("_ZN3art3JNI15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi") >= 0){
addr_register = symbol.address;
}
}
}
// 开始hook
if(addr_register){
Interceptor.attach(addr_register, {
onEnter: function(args){
var methods = ptr(args[2]);
var method_count = args[3];
console.log("[RegisterNatives] method_count:", method_count);
for(var i = 0; i < method_count; i++){
var fn_ptr = methods.add(i * Process.pointerSize * 3 + Process.pointerSize * 2).readPointer();
var find_module = Process.findModuleByAddress(fn_ptr);
if(i == 0){
console.log("module name", find_module.name);
console.log("module base", find_module.base);
}
console.log("\t method_name:", methods.add(i * Process.pointerSize * 3).readPointer().readCString(), "method_sign:", methods.add(i * Process.pointerSize * 3 + Process.pointerSize).readPointer().readCString(), "method_fnPtr:", fn_ptr, "method offset:", fn_ptr.sub(find_module.base));
}
}, onLeave(retval){
}
})
}
}
function main(){
hook_register();
}
setImmediate(main);
3、算法还原:
1、敏感话题, 不讨论。 算法的关键位置,f5之后的代码和汇编代码出入很多,要想还原得刚汇编。
附上xg03 算法的还原图:
成功获取数据
xg0408(最新版)算法的还原图:
4注意
还原出来的xgorgon只供学习研究,切勿商业用途及非法用途
5 发现bug
这两个发现了bug,unidbg还原出来的xg0408不登录的情况下不能搜索,正常设备能搜。经过验证发现是xgorgon中有设备检测,不正常的设备搜索会需要登录。 经过一系列的奋斗,已完善。