前言

本节分析的是之前渗透时的一个APP,抓包后发现请求包和返回包都做了加密。

此处分析某保险的APP,具体分析情况如下:

开整开整开整~

抓包

目标接口:https://xxx.xxx.xxx/login.do

image.png

image.png

image.png

发现请求包和响应包的内容都是直接加密的,也没有什么提示信息,什么玩意???

分析

1、首先,查壳,未发现壳

image.png

2、将apk拖入到jadx中,搜索一些关键字符。如”encrypt”

image.png

发现在com.xxxx.core_library.util.encrypt这个package下,有AES和RSA的加密算法

image.png

查看相关代码后发现,疑似的关键代码是在AESUtilRSAUtils这两个类中的。

image.png

但具体加密逻辑是什么暂时不清楚。

3、这时候,可以用hook的神器objection来对这两个类下的所有方法hook,查看其加解密逻辑

android hooking watch class com.xxx.core_library.util.encrypt.AESUtil
android hooking watch class com.xxx.core_library.util.encrypt.RSAUtils

image-20241012112328744

4、点击登录,抓包,查看hook情况,如下:

image.png

整理一下,调用的函数有(按照调用顺序如下):

AESUtil.encrypt(java.lang.String)
AESUtil.makeKey(java.lang.String)
AESUtil.makeIv()
AESUtil.decrypt([B)
AESUtil.makeKey(java.lang.String)
AESUtil.makeIv()
AESUtil.decrypt([B)
AESUtil.makeKey(java.lang.String)
AESUtil.makeIv()
AESUtil.decrypt([B)
AESUtil.makeKey(java.lang.String)
AESUtil.makeIv()
AESUtil.decrypt([B)
AESUtil.makeKey(java.lang.String)
AESUtil.makeIv()
RSAUtils.actRsaPubkeyEncry(java.lang.String)
RSAUtils.decryptByPublicKey([B, java.lang.String)
RSAUtils.Base64Decode(java.lang.String)
AESUtil.decrypt([B, java.lang.String)
AESUtil.makeKey(java.lang.String)
AESUtil.makeIv()

整理去重后,主要是有以下几个函数

AESUtil.encrypt
AESUtil.makeKey
AESUtil.makeIv
AESUtil.decrypt
RSAUtils.actRsaPubkeyEncry
RSAUtils.decryptByPublicKey
RSAUtils.Base64Decode
RSAUtils.actResPubkeyDes

先看加密,根据函数名,可以看到重要的函数起始就值有2个AESUtil.encryptRSAUtils.actRsaPubkeyEncry

5、hook这两个关键函数(这块不用关注重载的情况,因为Objection默认hook一个函数是hook该函数的所有重载的)

android hooking watch class_method com.xxx.core_library.util.encrypt.AESUtil.encrypt --dump-args --dump-return --dump-backtrace
android hooking watch class_method com.xxx.core_library.util.encrypt.RSAUtils.actRsaPubkeyEncry --dump-args --dump-return --dump-backtrace

对比抓包获取的密文内容,可以成功定位到具体的加密函数RSAUtils.actRsaPubkeyEncry

image-20241012112515001

同理,利用如上方法,可以定位到具体解密函数为 RSAUtils.actResPubkeyDes:

image-20241012112545525

6、Frida编写脚本hook这两个函数

function printStack(name) {
Java.perform(function () {
var Exception = Java.use("java.lang.Exception");
var ins = Exception.$new("Exception");
var straces = ins.getStackTrace();
if (straces != undefined && straces != null) {
var strace = straces.toString();
var replaceStr = strace.replace(/,/g, "\r\n");
console.log("=============================" + name + " Stack strat=======================");
console.log(replaceStr);
console.log("=============================" + name + " Stack end=======================\r\n");
Exception.$dispose();
}
});
}

function main() {
Java.perform(function() {
var claz = Java.use('com.xxxx.core_library.util.encrypt.RSAUtils');
//加密函数
claz.actRsaPubkeyEncry.implementation = function(str) {
printStack('actRsaPubkeyEncry');
console.log("actRsaPubkeyEncry input: " + str);

var result = this.actRsaPubkeyEncry(str);
console.log("actRsaPubkeyEncry result: " + result);
return result;
}

//解密函数
claz.actResPubkeyDes.implementation = function(str) {
printStack('actResPubkeyDes');
console.log("actResPubkeyDes input: " + str);

var result = this.actResPubkeyDes(str);

console.log("actResPubkeyDes result: " + result);
return result;
}
});
}

// Usage:
setImmediate(main)

image.png

image.png

整理

主动调用

JS代码:

//加密
function encrypt(data) {
var result;
Java.perform(function() {
var claz = Java.use('com.xxx.core_library.util.encrypt.RSAUtils');
result = claz.actRsaPubkeyEncry(data)
});
return result;
}

//解密
function decrypt(data) {
var result;
Java.perform(function() {
var claz = Java.use('com.xxxx.core_library.util.encrypt.RSAUtils');
result = claz.actResPubkeyDes(data)
});
return result;
}

//导出接口
rpc.exports = {
encrypt: encrypt,
decrypt: decrypt
};

img

Python实现主动调用:

Python实现主动调用:

import frida
import time

def on_message(message, data):
if message['type'] =='send':
print(message['payload'])

# 准备工作

# 有线连接
device = frida.get_device_manager().add_remote_device("127.0.0.1:27042")
pid = device.spawn('com.xxx.xxxx')
device.resume(pid)
time.sleep(1)
session = device.attach(pid)

# 加载JS脚本
with open('rpc.js') as f:
script = session.create_script(f.read())

# 加载脚本并定义消息处理函数
script.on('message', on_message)
script.load()

# 现在可以通过 RPC 调用定义的函数
result = script.exports.encrypt('{"clientId":"65l2f2f0an526tc","imgCode":"1234","model":"SM-G988N","password":"e10adc3949ba59abbe56e057f20f883e","phone":"13888888888","phoneId":"08:00:27:51:bf:b2","runEnvironment":"2","system":"android","systemversion":"9"}')
print(result)

img

批量RPC

基于flask,创建对外接口,实现POST传参调用接口,最后实现批量化主动调用:

import frida
import time
import json
from flask import Flask, request, jsonify

def on_message(message, data):
if message['type'] =='send':
print(message['payload'])

# 准备工作
device = frida.get_device_manager().add_remote_device("127.0.0.1:27042")
pid = device.spawn('com.xxx.xxx')
device.resume(pid)
time.sleep(1)
session = device.attach(pid)

# 加载JS脚本
with open('rpc.js') as f:
script = session.create_script(f.read())

# 加载脚本并定义消息处理函数
script.on('message', on_message)
script.load()


app = Flask(__name__)

@app.route('/encrypt', methods=['POST'])
def encrypt():
data = request.get_data()
json_data = json.loads(data.decode('utf-8'))
result = script.exports.encrypt(str(json_data))
return result

@app.route('/decrypt', methods=['POST'])
def decrypt():
data = request.get_data()
string_data = data.decode('utf-8')
result = script.exports.decrypt(string_data)
return result

if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')

加密接口调用:

img

解密接口调用:

image.png