mfw sigh signature 请求协议分析
环境
python: 3.8
frida: 12.8.0
objection: 1.8.4
app version: 9.3.7
oauth_signature

通过抓包,可以看到有个oauth_signature_method
字段为HMAC-SHA1
,由此初步推断oauth_signature
的加密方法为HMAC-SHA1
。
HMAC算法
这里给一下标准HMAC的大致实现,以sha1为例,需要传入key和要加密的文本text
# 伪python代码
def hmac_sha1(key, text):
if len(key) > 64:
key = sha1(key)
key = ljust(64, b'\x00') # 在后面填充\x00,直到长度为64
inner = sha1(key ^ 0x36 + text)
outer = sha1(key ^ 0x5C + inner)
return outer
Java层
之前已经提到过oauth_signature
的加密方法是在com.mfw.tnative.AuthorizeHelper
类的xAuthencode
方法。

frida hook一下
// mfw_hmac.js
function dump(name, addr, legnth) {
console.log("======================== " + name + " ============");
console.log(hexdump(addr, {length:legnth||32}));
}
Java.perform(function(){
var helper = Java.use("com.mfw.tnative.AuthorizeHelper");
helper.xAuthencode.implementation = function(ctx, str1, str2, str3, z) {
console.log("xAuthencode-str1", str1);
console.log("xAuthencode-str2", str2);
console.log("xAuthencode-str3", str3);
console.log("xAuthencode-z", z);
var ret = this.xAuthencode(ctx, str1, str2, str3, z);
console.log("xAuthencode-ret", ret);
return ret;
}
})

小结一下,第二个参数str2就是要加密的参数,由url和params构造,其他的暂时不分析
固定参数
由于url和params参数经常变动,不利于分析,将加密的参数修改为ever
。
// mfw_hmac.js
function dump(name, addr, legnth) {
console.log("======================== " + name + " ============");
console.log(hexdump(addr, {length:legnth||32}));
}
Java.perform(function(){
var helper = Java.use("com.mfw.tnative.AuthorizeHelper");
helper.xAuthencode.implementation = function(ctx, str1, str2, str3, z) {
str1 = "ever";
console.log("xAuthencode-str1", str1);
console.log("xAuthencode-str2", str2);
console.log("xAuthencode-str3", str3);
console.log("xAuthencode-z", z);
var ret = this.xAuthencode(ctx, str1, str2, str3, z);
console.log("xAuthencode-ret", ret);
return ret;
}
})
此时hook的结果为

so层
之前提到xAuthencode
的真实函数为sub_2E3B8
。ida查看

同样的,修改a1
为_JNIEnv

通过Java层的分析,我们知道a4
是要加密的参数(native函数的参数是从第二个开始)

由于v29 = a4
,因此v20
就是要加密的字符串。那么sub_2F518
就是对v20
进行加密,看看sub_2F518

有点复杂,先不分析,hook看看它的输入
Java.perform(function(){
var bptr = Module.findBaseAddress("libmfw.so");
var ptr_0x2F518 = bptr.add(0x2F518 + 1);
Interceptor.attach(ptr_0x2F518, {
onEnter: function(args) {
this.arg0 = args[0];
console.log(args[0], args[1], args[2], args[3]);
dump("0x2F518-arg0", args[0]);
dump("0x2F518-arg1", args[1]);
},
onLeave: function(retval) {
dump("0x2F518-arg0-ret", this.arg0);
console.log("\n");
}
})
})


从图中可以看出,函数执行了多次,不过我们还是能够看到输入的文本ever
。可以看到sub_2F518
前面还执行了几次,而且看它的输入有点像key,既然这样,我们看一下前面的函数sub_2EA60

可以看到函数里面也执行了sub_2F518
,hook一下sub_2EA60
Java.perform(function(){
var bptr = Module.findBaseAddress("libmfw.so");
Interceptor.attach(bptr.add(0x2EA60+1),{
onEnter: function(args){
dump("0x2EA60-arg0", args[0]);
console.log("0x2EA60-arg1", args[1].readCString(parseInt(args[2])));
}
})
})

我们试着把它作为key,用标准HMAC-SHA1试一试输出
import base64
import hashlib
import hmac
KEY = b'9c2d2df9a049d1c489d084ca535daada&b51c30e9ec2b3beb549cbb2f6e766abd'
s = b'ever'
digest = hmac.new(KEY, s, hashlib.sha1).digest()
s0 = base64.b64encode(digest).decode()
print(s0)
assert s0 == 'gwOgWNY2tQTPMyJ4GIgoBLk9eCQ=', 'Not equal'

直接就出来了,说明并没有修改HMAC算法,而且使用的也是标准的sha1算法。
实现
import base64
import hmac
import hashlib
from functools import partial
from urllib.parse import urlencode, quote
KEY = b'9c2d2df9a049d1c489d084ca535daada&b51c30e9ec2b3beb549cbb2f6e766abd'
quote = partial(quote, safe='')
def calc_hmac(url, params, method='GET'):
data = method + '&' + quote(url) + '&' + quote(urlencode(sorted(params)))
data = data.encode('utf8')
digest = hmac.new(KEY, data, hashlib.sha1).digest()
sign = base64.b64encode(digest).decode()
return sign
验证

其他
事实上,我们可以在Java层通过hook得出,app是不验证zzzghostsigh
参数的,也就是说,zzzghostsigh
可以随便赋值,甚至可以不要这个字段,只要oauth_signature
是正确的就能正常返回结果。还有就是,请求里面有个oauth_token
字段,这个token是不能缺少的,也不能随机生成,暂时没有去研究它的有效时长以及生成的机制。
以上代码仅供把玩,本人并未采集任何数据。
网友评论