几周前我对某个手机银行应用进行测试。这个app用了一个框架,这个框架能够混淆、加密app与服务器进行的TLS连接。我用Frida截获了加密环节之前的明文请求/响应。我希望能够修改截获的API调用,然后看看远程服务器是如何响应的,但这样的话我每次都得修改Frida脚本。

Burp是用来进行web测试的工具,而对于移动应用的测试应该也大致相同。我想要让burp跟frida配合起来,截获/修改burp中的API调用。尽管我没做Burp的插件,但是用一个小脚本我们就可以截获API调用了,这省去了修改脚本中API参数的大量时间。

截获API请求并不麻烦:

1.    建立Burp监听端口(如26080端口)将流量转发到回显服务器(如27080端口)

2.    回显服务器在27080端口监听

3.    用Frida同步发送HTTP请求到Burp监听端口

Burp就会接收到Frida发送的API请求。我们就可以修改Burp中收到的调用,然后把数据转发到回显服务器。回显服务器会将修改请求后收到的响应发还给Frida。

设置Burp监听端口

我们先开始设置Burp的监听端口。我已经在隐匿代理模式中启用了26080作为监听端口:

使用Frida配合Burp Suite追踪API调用-RadeBit瑞安全

Python tracer代码

我选择对已有的frida-trace代码进行修改和拓展,让其拥有trace工具那样的灵活性。

以下的这段Python代码能够让frida-trace代码与你转发API调用的服务器配合。它能够将HTTP请求发送到我们本地的Burp监听端口。你也可以在HTTP头部加入API调用的元数据,或者加入URL路径加以区分。

现在的代码可能依赖版本号,我不知道未来的版本会不会有变化,至少现在它能兼容Frida 8.2.2。

from frida import tracer
import requests

BURP_HOST = "localhost"
BURP_PORT = 26080

def frida_process_message(self, message, data, ui):
    handled = False

    if message['type'] == 'input':
        handled = True
    elif message['type'] == 'send':
        stanza = message['payload']

        if stanza['from'] == '/http':
            req = requests.request('FRIDA', 'http://%s:%d/' % (BURP_HOST, BURP_PORT), headers={'content-type':'text/plain'}, data=stanza['payload'])
            self._script.post({ 'type': 'input', 'payload': req.content })
            handled = True

    if not handled:
        self.__process_message(message, data, ui)

tracer.Tracer.__process_message = tracer.Tracer._process_message
tracer.Tracer._process_message = frida_process_message

if __name__ == '__main__':
    print("[x] To intercept in Burp, set up an invisible proxy listening on port %d, forwarding to the echo server." % BURP_PORT)
    tracer.main()

Trace handler脚本

下面的脚本可以用来截获onLeave 函数中的read()调用。你可以根据自己需求修改。我们只需要转发/截取你想要修改的那些参数。

我通过Frida的send() API实现了功能。这个调用被Python脚本接收,然后发送到handler脚本,然后当我们在BurpSuite中修改请求时,脚本会等待响应,然后执行回调函数。

{
    onEnter: function (log, args, state) {
        log("read(" + "fd=" + args[0]+ ", buf=" + args[1]+ ", count=" + args[2] + ")");
        state.buf = args[1]
    },

    onLeave: function (log, retval, state) {
        send({from: '/http', payload: Memory.readUtf8String(state.buf)})
        var op = recv('input', function(value) { // callback function
            log("Forwarding mitm'ed content: " + value.payload)
            Memory.writeUtf8String(state.buf, value.payload)
        });
        op.wait();
    }
}

回显服务器

以下脚本实现的功能是对Frida发出的请求做出响应,会先request payload。你也可以根据自己的需求进行修改。

from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from optparse import OptionParser

ECHO_PORT = 27080

class RequestHandler(BaseHTTPRequestHandler):

    def do_FRIDA(self):
        request_path = self.path

        request_headers = self.headers
        content_length = request_headers.getheaders('content-length')
        length = int(content_length[0]) if content_length else 0

        self.send_response(200)
        self.end_headers()

        self.wfile.write(self.rfile.read(length))

def main():
    print('Listening on localhost:%d' % ECHO_PORT)
    server = HTTPServer(('', ECHO_PORT), RequestHandler)
    server.serve_forever()

if __name__ == "__main__":
    print("[x] Starting echo server on port %d" % ECHO_PORT)
    main()

演示

以下为实际演示:

使用Frida配合Burp Suite追踪API调用-RadeBit瑞安全
使用Frida配合Burp Suite追踪API调用-RadeBit瑞安全
使用Frida配合Burp Suite追踪API调用-RadeBit瑞安全