bang加固简单分析
本文为看雪论坛优秀文章
看雪论坛作者ID:falconnnn
一
dex
二
libSecShell.so
system call : 7d 70
addr : c0783
Func Name : __NR_mprotect
c0 70 a0 e3 00 00 00 ef
用frida去hook这个函数。
var mprotect_cnt = 0
//frida -U --no-pause -f com.testlinker.ty -l hook.js
function sleep(delay) {
var start = (new Date()).getTime();
while ((new Date()).getTime() - start < delay) {
continue;
}
}
function hook_svc_mprotect() {
let base_svc_mprotect = Module.findBaseAddress("libSecShell.so");
if (base_svc_mprotect != null) {
console.log("base_svc_mprotect : " + base_svc_mprotect)
}else{
return ;
}
let svc_mprotect = base_svc_mprotect.add(0xC0778);//32位
Interceptor.attach(svc_mprotect, {
onEnter: function(args) {
console.log("==========================================")
console.log("svc_mprotect: start = " + args[0] + " , len = " + args[1] + " , ATTRIBUTES = " + args[2])
mprotect_cnt += 1
console.log(hexdump(base_svc_mprotect.add(0x281B4)))
},
onLeave: function(){
console.log("svc_mprotect leave")
console.log("==========================================")
}
})
}
function dis(address, number) {
for (var i = 0; i < number; i++) {
var ins = Instruction.parse(address);
console.log("address:" + address + "--dis:" + ins.toString());
address = ins.next;
}
}
//libc->strstr() 从linker里面找到call_function的地址
function hook() {
//call_function("DT_INIT", init_func_, get_realpath());
var linkermodule
if (Process.pointerSize == 4) {
linkermodule = Process.findModuleByName("linker");
}else if (Process.pointerSize == 8) {
linkermodule = Process.findModuleByName("linker64");
}
// var linkermodule = Process.getModuleByName("linker");
var call_function_addr = null;
var symbols = linkermodule.enumerateSymbols();
for (var i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
//LogPrint(linkername + "->" + symbol.name + "---" + symbol.address);
if (symbol.name.indexOf("__dl__ZL13call_functionPKcPFviPPcS2_ES0_") != -1) {
call_function_addr = symbol.address;
//LogPrint("linker->" + symbol.name + "---" + symbol.address)
}
}
Interceptor.attach(call_function_addr, {
onEnter: function (args) {
var type = ptr(args[0]).readUtf8String();
var address = args[1];
var sopath = ptr(args[2]).readUtf8String();
console.log("loadso:" + sopath + "--addr:" + address + "--type:" + type);
if (sopath.indexOf("libSecShell.so") != -1) {
var libnativemodule = Process.getModuleByName("libSecShell.so");//call_function正在加载目标so,这时就拦截下来
var base = libnativemodule.base;
hook_svc_mprotect()
}
}
})
}
function main() {
hook();
}
setImmediate(main)
[Pixel 3::com.example.cryptotest ]->
base_svc_mprotect : 0xcfaea000
==========================================
svc_mprotect: start = 0xcfaea000 , len = 0xa1000 , ATTRIBUTES = 0x7
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
cfb121b4 9b 66 a6 75 82 ab ba fb 1a 80 e6 75 d7 0e 7f 1b .f.u.......u....
svc_mprotect leave
==========================================
==========================================
svc_mprotect: start = 0xcfb8b000 , len = 0x1f000 , ATTRIBUTES = 0x3
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
cfb121b4 2d e9 f0 4f ad f6 ac 4d df f8 44 4e df f8 44 3e -..O...M..DN..D>
svc_mprotect leave
==========================================
==========================================
svc_mprotect: start = 0xcfaea000 , len = 0xa1000 , ATTRIBUTES = 0x7
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
cfb121b4 2d e9 f0 4f ad f6 ac 4d df f8 44 4e df f8 44 3e -..O...M..DN..D>
svc_mprotect leave
==========================================
ida_funcs.add_func(0x281B4,0x2A5CC)
.data:00085E24 DD F9 97 E4 off_85E24 DCD 0xE497F9DD ; DATA XREF: sub_DF08+8↑r
from idautils import *
from idaapi import *
from idc import *
f = open("./func.txt",'w')
for func_addr in Functions(0,0x5B18BC):
func_name = get_func_name(func_addr)
print(func_addr , func_name)
f.write(str(func_addr) + "," + func_name + "\n")
# f.writelines()
f.close()
from idautils import *
from idaapi import *
from idc import *
f = open(r"CryptoTest_32\CryptoTest\lib\func.txt",'r')
func_info = {}
while True:
info = f.readline().strip('\n')
if not info:
break
addr, func_name = info.split(',')
# print(addr + func_name)
func_info[int(addr,10)] = func_name
# print(func_info)
f.close()
textStart = 0xA2984
textEnd = 0xC2000
# textStart = 0xA2DE0
# textEnd = 0xA2E04
libc_dump_base = 0xe494b000
for i in range(textStart,textEnd,4):
dword_ = get_dword(i)
if dword_ > libc_dump_base:
libc_func = dword_ - libc_dump_base
# print(dword_,libc_func)
func_name = func_info.get(libc_func)
if not func_name:
func_name = func_info.get(libc_func-1) #thumb
if not func_name:
continue
raw_name_off = get_name(i)
patch_name_off = func_name + "_ptr_" + raw_name_off
set_name(i,patch_name_off)
xrefaddrs = XrefsTo(i, flags=0)
for xrefaddr in xrefaddrs:
raw_name = get_func_name(xrefaddr.frm) #拿到函数原名称
patch_fun_addr = get_name_ea_simple(raw_name) #拿到函数地址
# print(get_func_name(xrefaddr.frm))
if raw_name and patch_fun_addr:
break
if raw_name and patch_fun_addr:
patch_name = func_name + "_" + raw_name
print("patch_name : ",patch_name)
set_name(patch_fun_addr,patch_name)
print(dword_,func_name)
三
init_array
四
JNI_Onload
字符串解密
function hook_decode_str(){
let base_secShell = Module.findBaseAddress("libSecShell.so");
let decode_str = base_secShell.add(0x12B94+1);
Interceptor.attach(decode_str, {
onEnter: function(args) {
console.log("=======decode_str========="+ " size = " + args[1] + " op = " + args[2]," return addr = " + this.context.lr.sub(base_secShell))
this.args0 = args[0]
this.args1 = args[1]
},
onLeave: function(){
console.log(hexdump(this.args0,{length:this.args1.toInt32()}))
// console.log(hexdump(args[0],{length:0x10}))
}
})
}
function hook_svc_mprotect() {
let base_secShell = Module.findBaseAddress("libSecShell.so");
if (base_secShell != null) {
console.log("base_secShell : " + base_secShell)
}else{
return ;
}
let svc_mprotect = base_secShell.add(0xC0778);//32位
// let svc_mprotect = base_secShell.add(0x1541A0);//64位
//private native void jniLoadScriptFromAssets(AssetManager assetManager, String assetURL, boolean loadSynchronously);
Interceptor.attach(svc_mprotect, {
onEnter: function(args) {
console.log("==========================================")
console.log("svc_mprotect: start = " + args[0] + " , len = " + args[1] + " , ATTRIBUTES = " + args[2])
mprotect_cnt += 1
console.log(hexdump(base_secShell.add(0x281B4)))
},
onLeave: function(){
console.log("svc_mprotect leave")
console.log("==========================================")
if(mprotect_cnt == 2){
hook_decode_str()
// hook_elf_hook()
// sleep(1000000)
}
}
})
}
大概流程
先执行case 0:初始化JNIEnv,解密得到com/SecShell/SecShell/H字符串
然后case8(0x29e00):
跳到sub_13E48,获取libc.so一些函数指针,从java类获取PKGNAME = "com.example.cryptotest",
后面在case8里的case分支干了一些不知道在干啥,好像是在配置环境
然后是case9:
调用android/app/ActivityThread类的currentActivityThread方法
调用ActivityThread对象的getSystemContext方法
调用ContextImpl的getPackageManager方法
调用PackageManager的getPackageInfo方法
获取PackageInfo对象的applicationInfo字段
获取ApplicationInfo对象的sourceDir字段
获取ApplicationInfo对象的nativeLibraryDir字段
拼接出/proc/%d/fd/%d,遍历fd找到base.apk路径
然后是case3:创建了线程(没执行到),验证了签名
case1->case10
case10:打开/proc/self/maps,找到lib/libart.so,比较是否是r-xp权限,通过格式化字符串%lx-%lx读取地址
case11:把libart.so改为可读写,两个箭头前后对比
然后到case13里面的case10,打开这个base.odex
会打开打开classes.dve进行校验
然后会执行sub_260BC,这里会调用0x4D7DC(hook_libc_so_func),对hook部分说的那些函数进行hook,然后读classes.jar写到内存里的时候就调用这些函数进行解密(dex加载)
case4:把libart.so权限改回去
case12:弄了好多inlinehook,但是好像也没有执行(不知道是不是我系统版本过高)
具体见附件给的idb吧
dex加载
调用com/SecShell/SecShell/H的ff加载/data/user/0/com.example.cryptotest/.cache/v1filter.jar
通过dump maps来比较加载前和加载后的差异
inlineHook
检测
check_root:0x17D9C
其他
is_miuiinstaller_process对小米手机进行适配
JNI_Onload里兼容性适配:
def createFunction(start,end):
len_func = end - start
begin = start
del_items(start,0,len_func) #先undefine
while len_func:
cnt = idc.create_insn(begin)
if cnt == 0:
break #遇到比如off_31F40 DCD __stack_chk_guard_ptr - 0x31D78这种就不解析了,一般是连续的,如果不连续需要跳过,继续解析
begin += cnt
len_func -= cnt
print(len_func)
#idc.create_insn(start)
return idc.add_func(start,end)
看雪ID:falconnnn
https://bbs.pediy.com/user-home-803962.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!