网络抓包08 - 搜索系统调用

200 阅读2分钟

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 出来之后,修复一下:

github.com/lasting-yan…

搜索系统调用

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.address10);
    })
}

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, {
        onEnterfunction (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

关注我的微信公众号:二手的程序员