SO保护处理
这里只讨论 so 自解密/动态释放的清空,一般这些步骤都是在 init 或者 init_array 阶段进行。所以我们可以等 so 加载完成之后,dump 内存中的数据即可实现 so 脱壳。
至于,那些拥有函数粒度的保护,在函数执行前解密,执行后再加密回去的,就不讨论了。
dump so 非常的简单:
rpc.exports = {
findmodule: function(so_name) {
var libso = Process.findModuleByName(so_name);
return libso;
},
dumpmodule: function(so_name) {
var libso = Process.findModuleByName(so_name);
if (libso == null) {
return -1;
}
Memory.protect(ptr(libso.base), libso.size, 'rwx');
var libso_buffer = ptr(libso.base).readByteArray(libso.size);
libso.buffer = libso_buffer;
return libso_buffer;
},
allmodule: function() {
return Process.enumerateModules()
},
arch: function() {
return Process.arch;
}
}
dump 出来之后,修复一下:
搜索系统调用
arm64的系统调用对应的汇编代码:
.text:00000000000A2F04 01 00 00 D4 SVC 0
我们可以搜索内存中所有的可执行段(也可以搜索 /proc/self/maps 文件),然后在段对应的内存中遍历指令:
Java.perform(function () {
var Runtime = Java.use('java.lang.Runtime');
Runtime.nativeLoad.implementation = function (arg0, arg1, arg2) {
console.log(Process.getCurrentThreadId() + "--load so:" + arg0);
var result = this.nativeLoad(arg0, arg1, arg2);
if (arg0.indexOf('libnative-lib.so') >= 0) {
findsyscall();
}
return result;
}
})
function findsyscall() {
Process.enumerateRanges('--x').forEach(function (range) {
if (JSON.stringify(range).indexOf('libnative-lib.so') >= 0) {
LogPrint(JSON.stringify(range));
search(range);
}
})
}
function search(range) {
Memory.scanSync(range.base, range.size, '01 00 00 D4').forEach(function (match) {
LogPrint(JSON.stringify(match));
disassemble(match.address, 10);
})
}
function disassemble(addr, count) {
var start = addr;
for (var i = 0; i < count; i++) {
try {
var ins = Instruction.parse(start);
LogPrint("addr:" + start + ",dis:" + ins.toString());
if (ins.toString() == 'svc #0') {
hookaddr(start);
}
start = ins.next;
} catch (e) {
}
}
}
function hookaddr(addr) {
Interceptor.attach(addr, {
onEnter: function (args) {
this.arg0 = this.context.r0;
this.arg1 = this.context.r1;
this.arg2 = this.context.r2;
this.syscallnum = this.context.r7.toInt32();
if (this.syscallnum == 290) {
//sendto
}
if (this.syscallnum == 292) {
//sendto
}
console.log(Process.getCurrentThreadId() + "--go into " + addr + ",syscallnumm:" + this.syscallnum);
printNativeStack(this.context, "go into " + addr + ",syscallnum:" + this.syscallnum);
}, onLeave(retval) {
var arg0 = this.context.r0;
console.log(Process.getCurrentThreadId() + "--leave " + addr + ",retval:" + arg0);
}
})
}
为了搜索的正确性,一般系统调用前面还会有一个 mov 指令,所以我们也可以校验前面四字节的指令是否为 mov 指令:
insnt & 0xFFE0001F == 0xD2800008
关注我的微信公众号:二手的程序员