【保姆级教学】某金融app FRIDA hook加解密算法+jsrpc=乱杀

首发于土司:https://www.t00ls.com/thread-68782-1-1.html

0x01 APP渗透测试

因为是经过授权的测试,所以拿到的这个包是没有加固的。

加固的话,也是有对策的,可以使用脱壳机脱壳,使用hluda绕过frida检测。

1.1 绕过root检测

  1. 打开app,发现提示:”系统已root,不允许操作“,然后软件进行退出,无法进行后续操作。

所以需要绕过root检测。

  1. 打开magisk,在设置中选择“隐藏magisk”,然后输入随机包名,根据提示进行重启

● 安装shamiko:https://github.com/LSPosed/LSPosed.github.io/releases

笑脸为启用状态。

● magisk设置——配置排除列表——勾选app(此处的操作是为app隐藏root,及没有通过root启动app)

需要关闭“遵循排除列表”

  1. 然后重启,便可以打开app了。

1.2 抓包(burp抓安卓app包)

电脑地址:192.168.1.39

手机端设置:

将手机wifi连接到与电脑同一网段下,然后修改wifi代理:

电脑burp设置:

注意:抓取https的包需要安装证书,需要注意的是,安卓7.0以下,系统信任用户安装的证书,而安卓7.0以上,系统不会信任用户安装的证书,导致无法抓取https的包,建议使用magisk+Move Certificates将证书移动到系统路径下。

  1. 然后开始抓包,还好app没有各种检测,能顺利抓包~

通过抓包,发现请求包和返回包都被加密了,但是通过观察多个请求包会发现请求体的内容都是以“#01开头”,而这正是突破口,我们可以通过反编译代码,全局搜索“#01”,来寻找加密的地方。

1.3 APP反编译分析

  1. 使用burp对app进行抓包,发现报文被加密,但是有个相同的特点——报文都是以”#01″开头,于是我们在jadx-gui中全局搜索(ctrl+shift+F)“#01”

  1. 在搜索结果中可以看到关键字“encrypteDataWithSM“(中文意思为:使用SM方式加密数据),然后双击点进去查看详细代码。

  1. 通过上图,可以看到encrypt与decrypt,通过分析代码推断此处为加密和解密函数,为了确定是加解密函数,我们对这两个方法进行hook,使用objection也可以。

0x02 Frida HOOK

2.1 编写hook代码

import frida, sys​jscode ="""Java.perform(function () {<!-- 获取CryptoUtil这个类的对象 -->    var CryptoUtil = Java.use('com.xxxx.security.CryptoUtil');<!-- 调用encryptDataWithSM方法,因为这个方法的参数有三个,所以在 function 中有三个参数,至于为什么没有类型修饰,因为 JavaScript 是弱语言 -->    CryptoUtil.encryptDataWithSM.implementation = function (app,dataStr,keyStr) {        console.log("Hook Start...");  //        console.log("app:",app); //打印app参数        console.log("dataStr",dataStr); //打印dataStr参数        console.log("keyStr",keyStr); //打印keyStr参数        var data = this.encryptDataWithSM(app,dataStr,keyStr);//使用this.encryptDataWithSM()再次调用原函数并把原本的参数传递给这个encryptDataWithSM()函数,然后通过return原封不动的返回。        return data;    }});"""# 这下面的代码的作用就是为了执行上面 JS 代码,输出 JS 中 send() 方法中的内容def message(message, data):    if message["type"] == 'send':        print("[*] {0}".format(message['payload']))    else:        print(message)# 获取这个 APK 的进程,可以使用frda-ps -Ua获取process = frida.get_usb_device().attach('xxxxxx银行')script= process.create_script(jscode)script.on("message", message)script.load()sys.stdin.read()

手机端运行frida,然后pycharm直接运行代码,操作手机,查看控制台输出。

从控制台可以看到encryptDataWithSM()函数被成功hook,而dataStr也是我们想要的报文。

2.2 hook 加解密函数

我们修改代码,只打印dataStr并同时hook解密函数。

import frida, sys​jscode ="""Java.perform(function () {    var CryptoUtil = Java.use('com.xxxx.security.CryptoUtil');    CryptoUtil.encryptDataWithSM.implementation = function (app,dataStr,keyStr) {        console.log("Hook Start...");         console.log("encrycptDataStr",dataStr);        var data = this.encryptDataWithSM(app,dataStr,keyStr);        return data;    }    CryptoUtil.decryptDataWithSM.implementation = function (app,dataStr,keyStr) {        console.log("Hook Start....");        console.log("decrycptDataStr:",dataStr)        var data = this.decryptDataWithSM(app,dataStr,keyStr);        return data;       }});"""def message(message, data):    if message["type"] == 'send':        print("[*] {0}".format(message['payload']))    else:        print(message)​process = frida.get_usb_device().attach('XXXXX银行')script= process.create_script(jscode)script.on("message", message)script.load()sys.stdin.read()

我们看到encryptDataWithSM加密前的数据以及decryptDataWithSM解密前的数据被打印出来。现在我们是数据都拿到了,但是怎么篡改数据那,那么我们就要rpc了。

0x03 编写转发脚本

感谢pyth0n大佬的项目

3.1 修改hook代码

修改hook脚本,将hook出来的数据,发送到burp便于篡改数据,篡改后的请求包发送到服务端,服务端处理后,返回响应包。

666.js

Java.perform(function () {    var CryptoUtil = Java.use('com.xxxx.security.CryptoUtil');    CryptoUtil.encryptDataWithSM.implementation = function(app,dataStr,keyStr) {        console.log("Hook Start...");         console.log("encrycptDataStr",dataStr,keyStr);        var data;        send({from:"/http",payload:dataStr,api_path:"request"}); //将dataStr发送到burpTracer.py        <!-- 接收burp篡改后返回的数据 ↓-->        var op = recv("input",function(value){              data = value.payload        });        op.wait();        console.log("经过burp修改后的参数:" +data);        var ret = this.encryptDataWithSM(app,data,keyStr);        return ret;    }//hook解密函数    CryptoUtil.decryptDataWithSM.implementation = function (app,dataStr,keyStr) {        var getVal = this.decryptDataWithSM(app, dataStr, keyStr);        send({ from: "/http", payload: getVal, api_path: "response" });            var op = recv('input',function (value) {             console.log(value.payload);                         });        op.wait();                 return getVal;}    });

3.2 burpTracer.py,直接使用

from codecs import ignore_errorsimport fridaimport requestsimport timeimport sysimport osimport socketimport argparsefrom log import *​print ('''\033[1;31m \n  _____    _     _         ___       _             ____           _|  ___| __(_) __| | __ _  |_ _|_ __ | |_ ___ _ __ / ___|___ _ __ | |_| |_ | '__| |/ _` |/ _` |  | || '_ \| __/ _ \ '__| |   / _ \ '_ \| __||  _|| |  | | (_| | (_| |  | || | | | ||  __/ |  | |__|  __/ |_) | |_|_|  |_|  |_|\__,_|\__,_| |___|_| |_|\__\___|_|   \____\___| .__/ \__|                       #pyth0n                             |_|                     Intercept Api in Android Application''')​print ("\033[1;34m[*]___author___: @Pyth0n\033[1;37m")print ("\033[1;34m[*]___version___: 1.0\033[1;37m")print ("")​BURP_HOST = "127.0.0.1"BURP_PORT = 26080​def check_platform():    try:        platforms = {        'linux'  : 'Linux',        'linux1' : 'Linux',        'linux2' : 'Linux',        'darwin' : 'OS X',        'win32'  : 'Windows'        }        if sys.platform not in platforms:            sys.exit(logger.error("[x_x] Your platform currently does not support."))    except Exception as e:        logger.error("[x_x] Something went wrong, please check your error message.\n Message - {0}".format(e))​def check_ps_for_win32():    try:        if sys.platform == "win32":            PROCESSNAME = "iTunes.exe"            for proc in psutil.process_iter():                try:                    if proc.name() == PROCESSNAME:                        return True                except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess) as e:                    pass            return sys.exit(logger.error("[x_x] Please install iTunes on MicrosoftStore or run iTunes frist."))                  except Exception as e:        logger.error("[x_x] Something went wrong, please check your error message.\n Message - {0}".format(e))​def check_echo_server():    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    result = sock.connect_ex(('127.0.0.1',27080))    if result == 0:       logger.info("[*] Connect to echoServer successfully.")    else:        sock.close()        sys.exit(logger.error("[x_x] Please start echoServer."))    def run():    #check platform support    check_platform()    #check process iTunes for Win32s    #check_ps_for_win32()    #check python version    if sys.version_info < (3, 0):        logger.error("[x_x] iOS hook requires Python 3.x")        sys.exit(0)    else:        handle_del_log()        main()​def handle_del_log():    try:        pwd = os.getcwd()        path = pwd + '/errors.log'        file_stats = os.stat(path)        if (file_stats.st_size > 1024000000): #delete errors.log if file size > 1024 MB            os.remove(path)        else:            return True    except Exception as e:        logger.error("[x_x] Something went wrong when clear error log. Please clear error log manual.\n [Error Message] - {0}".format(e))​def main():    def frida_process_message(message, data):                handled = False               if message['type'] == 'input':            handled = True                   elif message['type'] == 'send':            body = message['payload']                       API_PATH = body['api_path']                       if body['from'] == '/http':                try:                    # 把数据发给 本地burp 监听的 26080端口                    req = requests.request('FRIDA', 'http://%s:%d/%s' % (BURP_HOST, BURP_PORT, API_PATH), headers={'content-type':'application/json'}, data=body['payload'].encode('utf-8'))                    script.post({ 'type': 'input', 'payload': req.text }) # 把修改后的数据传输回给js                    handled = True                except requests.exceptions.RequestException as e:                    logger.error("[x_x] Connection refused, please check configurage on BurpSute.\n [Error Message] - {0}".format(e))​    parser = argparse.ArgumentParser()    parser.add_argument("-p", "--package")    parser.add_argument("-n", "--name")    parser.add_argument("-s", "--script", help='custom handler script')    parser.add_argument("-r", "--remote",help="远程主机")    args, leftovers = parser.parse_known_args()​    try:        #Spawning application with default script        if args.package is not None and args.script is None:            #check echoServer            check_echo_server()            #            logger.info('[*] Spawning: ' + args.package)            logger.info('[*] Script: ' + 'handlers.js')            time.sleep(2)            device = frida.get_usb_device()            pid = device.spawn(args.package)            device.resume(pid)            time.sleep(1)            session = device.attach(pid)            with open("handlers.js") as f:                script = session.create_script(f.read())            script.on("message", frida_process_message)            script.load()            input()        #Attaching default script to application        if args.name is not None and args.script is None:            #check echoServer            check_echo_server()            #            logger.info('[*] Attaching: ' + args.name)            logger.info('[*] Script: ' + 'handlers.js')            time.sleep(2)            process = frida.get_usb_device().attach(args.name)            with open("handlers.js") as f:                script = process.create_script(f.read())            script.on("message", frida_process_message)            script.load()            input()        # Spawing application with custom script        if args.package is not None and args.script is not None:            #check echoServer            check_echo_server()            #            if os.path.isfile(args.script):                logger.info('[*] Spawning: ' + args.package)                logger.info('[*] Script: ' + args.script)                time.sleep(2)                device = frida.get_remote_device()                               pid = device.spawn(args.package)                device.resume(pid)                            ​                time.sleep(1)                session = device.attach(pid)                with open(args.script,encoding='utf-8',errors='ignore') as f:                    script = session.create_script(f.read())                script.on("message", frida_process_message)                script.load()                input()            else:                logger.error('[?] Script not found!')                #Attaching default script to application        if args.name is not None and args.script is not None:            #check echoServer            check_echo_server()            #            logger.info('[*] Attaching: ' + args.name)            logger.info('[*] Script: ' + args.script)            time.sleep(2)            process = frida.get_usb_device().attach(args.name)            with open(args.script,encoding='utf-8',errors='ignore') as f:                script = process.create_script(f.read())            script.on("message", frida_process_message)            script.load()            input()                if args.remote is not None and args.script is not None:            #check echoServer            check_echo_server()            #            logger.info('[*] Attaching: ' + args.remote)            logger.info('[*] Script: ' + args.script)            time.sleep(2)            process = frida.get_remote_device().attach(args.remote)            with open(args.script,encoding='utf-8',errors='ignore') as f:                script = process.create_script(f.read())            script.on("message", frida_process_message)            script.load()            input()  ​    #EXCEPTION FOR FRIDA    except frida.ServerNotRunningError:        logger.error("Frida server is not running.")    except frida.TimedOutError:        logger.error("Timed out while waiting for device to appear.")    except frida.TransportError:        logger.error("[x_x] The application may crash or lose connection.")    #EXCEPTION FOR OPTIONPARSING​    #EXCEPTION FOR SYSTEM    except Exception as e:        logger.error("[x_x] Something went wrong, please check your error message.\n Message - {0}".format(e))​    except KeyboardInterrupt:        logger.info("Bye bro!!")        # sys.exit(0)​if __name__ == '__main__':    run()

3.3 echoServer.py 直接使用

from http.server import HTTPServer, BaseHTTPRequestHandlerfrom optparse import OptionParserfrom log import *​print ('''\033[1;31m \n            _           _____                                     | |         / ____|                            ___  ___| |__   ___| (___   ___ _ ____   _____ _ __   / _ \/ __| '_ \ / _ \\___ \ / _ \ '__\ \ / / _ \ '__| |  __/ (__| | | | (_) |___) |  __/ |   \ V /  __/ |     \___|\___|_| |_|\___/_____/ \___|_|    \_/ \___|_|                                                                                                                        ''')print ("\033[1;34m[*]___author___: @Pyth0n\033[1;37m")print ("\033[1;34m[*]___version___: 1.0\033[1;37m")print ("")​ECHO_PORT = 27080​class RequestHandler(BaseHTTPRequestHandler):​    def do_FRIDA(self):        request_path = self.path​        request_headers = self.headers        content_length = request_headers.get('content-length')        # length = int(content_length[0]) if content_length else 0        length = int(content_length) if content_length else 0​        self.send_response(200)        self.end_headers()​        self.wfile.write(self.rfile.read(length))​def main():    try:        logger.info('[*] Listening on 127.0.0.1:%d' % ECHO_PORT)        server = HTTPServer(('', ECHO_PORT), RequestHandler)        server.serve_forever()​    except KeyboardInterrupt:        logger.info("Stop echoServer!!")​if __name__ == "__main__":    logger.info('[*] Starting echoServer on port %d' % ECHO_PORT)    main()

3.4 使用

● burp的proxyoptions导入burpsuite_configuration_proxy.json,

burpsuite_configuration_proxy.json

{    "proxy":{        "request_listeners":[            {                "certificate_mode":"per_host",                "custom_tls_protocols":[],                "enable_http2":true,                "listen_mode":"loopback_only",                "listener_port":8080,                "running":true,                "use_custom_tls_protocols":false            },            {                "certificate_mode":"per_host",                "custom_tls_protocols":[                    "SSLv3",                    "TLSv1",                    "TLSv1.1",                    "TLSv1.2",                    "TLSv1.3"                ],                "enable_http2":true,                "listen_mode":"specific_address",                "listen_specific_address":"127.0.0.1",                "listener_port":26080,                "redirect_to_host":"127.0.0.1",                "redirect_to_port":27080,                "running":true,                "support_invisible_proxying":true,                "use_custom_tls_protocols":false            }        ]    }}

● 手机上开启frida-server

● 运行echoServer.py

● 运行burpTracer.py

adb forward tcp:27042 tcp:27042 #转发端口burpTracer.py -s 666.js -p com.xxx.xxx.bank

此时手机会自己打开app,如果burp打开监听,也会收到数据包,便可以愉快的改包测试了。.

下面是流程原理:

0x04 乱杀

敏感信息泄露

  1. 登录时,输入账号然后抓包,将抓到的请求包放行。

  1. 从返回包中,可以看到

接下来就是各种乱杀:

转载请注明:Adminxe's Blog » 【保姆级教学】某金融app FRIDA hook加解密算法+jsrpc=乱杀

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Frida是个轻量so别的hook框架,它可以帮助逆向人员对指定的进程的so模块进行分析。它主要提供了功能简单的python接口和功能丰富的js接口,使得hook函数和修改so编程化,值得一提的是接口中包含了主控端与目标进程的交互接口,由此我们可以即时获取信息并随时进行修改。使用frida可以获取进程的信息(模块列表,线程列表,库导出函数),可以拦截指定函数和调用指定函数,可以注入代码,总而言之,使用frida我们可以对进程模块进行手术刀式剖析。 它主要的工作方式是将脚本库注入到目标进程,在目标进程执行脚本。这里需要注意的是,它是将脚本库注入到已经启动的进程,但并不是说,对于进程初始化所做的动作,frida无能为力,frida提供了一个接口spawn,可以启动并暂时挂起进程,然后待我们布置好hook代码后再恢复进程运行,但是这个时间很短,大概2秒,也可能是我的使用姿势不对,求大佬指正。 此外,frida提供了相关的文档,但是frida官网提供的关于python接口的文档实在是少的可怜,连工具命令行的参数都没有,这点需要下载frida的python接口的源代码自己去分析了。值得高兴的一点是,Frida官网提供的js接口的文档稍微详细一些,并附有一些可喜的例子。 除了用于脚本编程的接口外,frida还提供了一些简单的工具,比如查看进程列表,追踪某个库函数等。 剩下就是关于frda学习路线了,Frida的学习还是蛮简单的,只需要了解两方面的内容: 1)主控端和目标进程的交互(message) 2)Python接口和js接口(查文档)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Adminxe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值