免责声明:本篇文章仅用来学习交流,请勿用于商业用途,如因违反规定产生任何法律纠纷,本人概不负责。假如本文影响到官方任何利益,请联系文章作者告知,我会在第一时间将文章删除,感谢~
# 记录下某 c电新闻apk
的逆向,技术点包括 sslpinning
,脱壳及静态分析, hmac sha256
, md5
算法还原。因为之前研究过一次,这次顺便帮个兄弟看看 search
接口
# 抓包分析接口
此
app
有sslpinning
,所以使用frida
脚本或者objection
封装的sslpinng
过掉,这里我用的objection
,因为方便😁# 一条指令解决,-P 是载入插件,后面脱壳用
objection -g 包名 explore -s "android sslpinning disable --quiet" -P .objection/plugins
关于 objection 的用法和脱壳插件使用,之前的文章有介绍,可以翻阅。
猛一看有点劝退,不过不要慌,大部分加密参数不是必须的,
postman
测试了一下,发现加密的参数只有signature
是必须的
# 脱壳并静态分析
将
apk
拖到jadx
不难发现是360
加固,或者拖进PKID
看一下frida-dexdump
一把梭,再次鼓吹一波,葫芦娃大佬的插件真特么好用,这里再推荐一波葫芦娃大佬的一键解ollvm
混淆神器:Obpo
# 用 objection 配合 frida_dexdump 使用的,输入以下指令等待脱壳完成
plugin dexdump dump
忽略上面的报错,先
jadx
分析一哈,大部分时候这些报错的dex
不影响我们分析,实在不行上寒冰大佬的fart
那么多参数,随便一搜,参数都怼一起了,真就随便搜,
q
方法生成signature
,str4
逻辑在红框位置先用
objection hook
下这个类,发现确实调用了一些函数,说明位置没错android hooking watch class com.touchtv.internetSDK.network.d # hook 类下面的所有 method
再
hook
具体method
确定了加密位置,刚开始搜到的那个s
方法和生成signature
的q
方法,明文就这么呈现在眼前;s
方法传入四个参数:url
,param
,post
和extraParams
类实例,q
方法传入两个参数:第一个是url
,第二个是post
,url
,时间戳
,base64密文
和\n
连接的字串再看刚才的逻辑,
str
就是url
,str4
是带base64
的参数,所以只能是中间那里生成,就是md5
的base64
结果跟进
q
看生成signature
的逻辑,是Hmac SHA256
又跟进去确认了一下
key
的值是固定的,且频道页和详情页不同上面那个
base64
的密文还没确定是什么参数,既然是MessageDigest
加密,直接通用加密脚本hook
var messageDigest = Java.use("java.security.MessageDigest");
console.log("success")
messageDigest.update.overload('byte').implementation = function (data) {
console.log("MessageDigest.update('byte') is called!");
return this.update(data);
}
messageDigest.update.overload('java.nio.ByteBuffer').implementation = function (data) {
console.log("MessageDigest.update('java.nio.ByteBuffer') is called!");
return this.update(data);
}
messageDigest.update.overload('[B').implementation = function (data) {
console.log("MessageDigest.update('[B') 被调用了!");
// 调用 getAlgorithm 得到算法名,因为 getAlgorithm 为静态方法用 this 调用
var algorithm = this.getAlgorithm();
var tag = algorithm + "调用update得到的数据:";
//tag 为标签,data 为数据
toUtf8(tag, data);
toHex(tag, data);
toBase64(tag, data);
console.log("=======================================================");
return this.update(data);
}
messageDigest.update.overload('[B', 'int', 'int').implementation = function (data, start, length) {
console.log("MessageDigest.update('[B', 'int', 'int') 被调用了!");
var algorithm = this.getAlgorithm();
var tag = algorithm + "调用update得到的数据:";
toUtf8(tag, data);
toHex(tag, data);
toBase64(tag, data);
console.log("=======================================================", start, length);
return this.update(data, start, length);
}
messageDigest.digest.overload().implementation = function () {
console.log("MessageDigest.digest() 被调用了!");
var result = this.digest();
var algorithm = this.getAlgorithm();
var tag = algorithm + " 调用digest返回输出的数据:";
toHex(tag, result);
toBase64(tag, result);
console.log("=======================================================");
return result;
}
messageDigest.digest.overload('[B').implementation = function (data) {
console.log("MessageDigest.digest('[B') 被调用了!");
var algorithm = this.getAlgorithm();
var tag = algorithm + " 调用digest得到的数据:";
toUtf8(tag, data);
toHex(tag, data);
toBase64(tag, data);
var result = this.digest(data);
var tags = algorithm + " 调用digest返回输出的数据:";
toHex(tags, result);
toBase64(tags, result);
console.log("=======================================================");
return result;
}
messageDigest.digest.overload('[B', 'int', 'int').implementation = function (data, start, length) {
console.log("MessageDigest.digest('[B', 'int', 'int') 被调用了!");
var algorithm = this.getAlgorithm();
var tag = algorithm + " 调用digest得到的数据:";
toUtf8(tag, data);
toHex(tag, data);
toBase64(tag, data);
var result = this.digest(data, start, length);
var tags = algorithm + " 调用digest返回输出的数据:";
toHex(tags, result);
toBase64(tags, result);
console.log("=======================================================", start, length);
return result;
}
可以看到明文就是参数,密文是
base64
的结果,注意这里不是md5
结果再转base64
!!!
# 算法还原
思路已经理顺
①请求参数进行
md5
加密,并获取base64
的结果,②然后再和
post
,url
,时间戳
拼接生成字串,③最后用固定的
key
对这段字串进行HmacSHA256
加密python
还原算法,详情页也跟了下加密逻辑一样,只是公钥不同import base64
import hmac
from hashlib import sha256
def get_hash():
param = """{"pageSize":"15","keyword":"苏州","pageNum":1,"searchType":0,"type":0}"""
md5 = hashlib.md5()
md5.update(param.encode("utf-8"))
ret = base64.b64encode(md5.digest()).decode() # 注意这里不是 hexdigest
def get_signature(url, ts, ret, ch):
"""
:param ts: 请求头的时间戳
:param ch: 判断是频道页还是详情页
:type url: post请求的url
:return: 返回签名
"""
hash_code = get_hash()
if ch:
key = "****************************************************************".encode('utf-8') # 频道页公钥
else:
key = "****************************************************************".encode('utf-8') # 详情页公钥
final = "POST\n%s\n%s\n%s" % (url, ts, ret) # 拼接明文
data = final.encode()
signature = base64.b64encode(hmac.new(key, data, digestmod=sha256).digest())
return signature