某品会app逆向—so文件逆向

逆向目标:请求头authorization参数

1、jadx参数来源分析

我们直接打开jadx搜索OAuth api_sign关键字

直接进入关键加密位置

直接找到str的赋值位置

追到最里层发现sign的值是通过这个gs方法返回的
我们分析一下这个gs方法,它这里是通过invoke进行调用, 在 Java 中,invoke 是反射机制中的一个方法,用于调用某个对象的指定方法

1
2
3
gsMethod = clazz.getMethod("gs", Context.class, Map.class, String.class, Boolean.TYPE);

gsMethod.invoke(object, context, map, str, Boolean.valueOf(z));
  • clazz保存的是类的地址
  • object保存的是类实例的地址
  • gsMethod用于获取clazz类的gs方法,然后通过invoke进行调用
    那么我们需要找到object在哪赋值,我们直接往下看initInstance方法,发现object = cls.newInstance();
    Class<?> cls = Class.forName(“com.vip.vcsp.KeyInfo”);
    那么说明object是com.vip.vcsp.KeyInfo类的实例对象
    那么实际上gs方法做的就是调用了com.vip.vcsp.KeyInfo类下面的gs方法,我们直接搜索com.vip.vcsp.KeyInfo


    这里最终调用了gsNav,gsNav对象是一个native方法,那么我们就要开始分析so文件了

2、frida hook

hook gsNav方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function hook() {
let KeyInfo = Java.use("com.vip.vcsp.KeyInfo");
KeyInfo["gsNav"].implementation = function (context, hashMap, str, z) {
console.log(`KeyInfo.gsNav is called: context=${context}, map=${hashMap}, str=${str}, z=${z}`);
// 想看值的话需要进行迭代取值
var key = hashMap.keySet() // 这一行获取了Map对象的键集合
var it = key.iterator();
while (it.hasNext()) {
var keystr = it.next()
var valuestr = hashMap.get(keystr)
console.log('key --> ' + keystr + ' : ' + 'value --> ' + valuestr)
}
let result = this["gsNav"](context, hashMap, str, z);
console.log(`KeyInfo.gsNav result=${result}`);
return result;
};
}

打印日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
KeyInfo.gsNav is called: context=com.achievo.vipshop.common.VipApplicationLike@27bfb62, map=[object Object], str=null, z=false
key --> api_key : value --> 23e7f28019e8407b98b84cd05b5aef2c
key --> app_name : value --> shop_android
key --> app_version : value --> 7.45.6
key --> channel_flag : value --> 0_1
key --> client : value --> android
key --> client_type : value --> android
key --> context : value --> {"615":"1","872":"1"}
key --> darkmode : value --> 0
key --> deeplink_cps : value -->
key --> did : value --> 0.0.e80313b7448e29336a6fc838d4e67584.d8017d
key --> extParams : value --> {"showSellPoint":"1","adsPids":"6920895740872648005,6921337635528653194,6921297453826790085,6921337635528624522","mclabel":"1","cmpStyle":"1","ic2label":"1","reco":"1","vreimg":"1","floatwin":"1","preheatTipsVer":"4","exclusivePrice":"1","stdSizeVids":"","rank":"2","couponVer":"v2","live":"1"}
key --> fdc_area_id : value --> 104104101
key --> mars_cid : value --> f0c88e16-2565-3703-84b6-347d072bb5c0
key --> mobile_channel : value --> 8rdlo74r:::
key --> mobile_platform : value --> 3
key --> other_cps : value -->
key --> page_id : value --> page_te_commodity_search_1750513635464
key --> phone_model : value --> M2007J17C
key --> productIds : value --> 6921294374461796241,6921416187592487955,6920060793359708379,6920895740872648005,6920888542375498199,6921459846930120072,6920953788349858973,6921295990137481171,6921337635528653194,6921365611972858771,6921024956490935131,6921227774783873802,6921259346178565854,6921297453826790085,6921291748170981343,6921333554660816643,6921416187644527635,6920974089399252571,6921337635528624522,6920990186128641162
key --> province_id : value --> 104104
key --> referer : value --> com.achievo.vipshop.search.activity.VerticalTabSearchProductListActivity
key --> rom : value --> Dalvik/2.1.0 (Linux; U; Android 11; M2007J17C Build/RKQ1.200826.002)
key --> scene : value --> search
key --> sd_tuijian : value --> 0
key --> session_id : value --> f0c88e16-2565-3703-84b6-347d072bb5c0_shop_android_1750490050337
key --> skey : value --> 2d30297ff20ec9b7442dc4f3c335abdc
key --> source_app : value --> android
key --> standby_id : value --> 8rdlo74r:::
key --> sys_version : value --> 30
key --> timestamp : value --> 1750513638
key --> warehouse : value --> VIP_NH
KeyInfo.gsNav result=507c2e13857df44c0dfe37fa1f92b3023adca6da

这里传入了一个context对象,map中存放的就是接口的表单数据,str是一个null,z是一个布尔值

gsNav方法是一个native方法,往上查找翻到它的加载的so文件为keyinfo

3、so文件分析

我们找到对应的so文件,并将so文件托入至ida中进行分析,这个so文件是一个armeabi-v7a架构的,需要使用32位的ida打开进行分析
进入到ida后直接搜索gsNav方法


这里返回的是v9,我们直接进入到v9赋值的方法中去

进到最里层的方法后,我们直接将这里的代码全选赋值给ai帮我们分析一下
发现会提取map中的key和value,将键值对转为key=value形式,用&连接,然后拼接字符串给j_getByteHash放方法计算哈希值,我们直接跟进j_getByteHash方法中

发现这里似乎用的是SHA1的算法,我们直接hook getByteHash方法,我们直接按住tab键查看该方法的地址值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function hook_so() {
var addr = Module.findBaseAddress("libkeyinfo.so")
// 附加地址
var func = addr.add(0x69954 + 1) // 32位架构内存地址要+1
Interceptor.attach(func, {
onEnter: function (args) {
// 在函数执行前执行的代码
console.log('args1 --> ' + args[1].readCString())
console.log('args2 --> ' + args[2].readCString())
// console.log('args3 --> ' + args[3].readCString())
console.log('args4 --> ' + args[4].readCString())
},
onLeave: function (retval) {
console.log('result --> ' + retval.readCString())
}
});
}

由于是32位的架构,所以内存地址要+1
输出的日志结果:

1
2
3
4
5
6
7
8
args1 --> ���(����
args2 --> a84c5883206309ad076deea939e850dcapi_key=23e7f28019e8407b98b84cd05b5aef2c&app_name=shop_android&app_version=7.45.6&channel_flag=0_1&client=android&client_type=android&context={"615":"1","872":"1"}&darkmode=0&deeplink_cps=&did=0.0.e80313b7448e29336a6fc838d4e67584.d8017d&extParams={"showSellPoint":"1","adsPids":"6921337635528653194,6921337635528632714,6921337635528624522,6920543322000294543","mclabel":"1","cmpStyle":"1","ic2label":"1","reco":"1","vreimg":"1","floatwin":"1","preheatTipsVer":"4","exclusivePrice":"1","stdSizeVids":"","rank":"2","couponVer":"v2","live":"1"}&fdc_area_id=104104101&mars_cid=f0c88e16-2565-3703-84b6-347d072bb5c0&mobile_channel=8rdlo74r:::&mobile_platform=3&other_cps=&page_id=page_te_commodity_search_1750514916442&phone_model=M2007J17C&productIds=6921295990137481171,6920974089399252571,6921294374461796241,6921337635528653194,6921416187592487955,6920817343215758871,6920953788349858973,6921257911246055181,6921337635528632714,6920060793359708379,6920990186128641162,6921346563282405075,6921259346178565854,6921337635528624522,6921291748170981343,6921377229456667929,6921024956490935131,6921459846930120072,6920543322000294543,6921365611972858771&province_id=104104&referer=com.achievo.vipshop.search.activity.VerticalTabSearchProductListActivity&rom=Dalvik/2.1.0 (Linux; U; Android 11; M2007J17C Build/RKQ1.200826.002)&scene=search&sd_tuijian=0&session_id=f0c88e16-2565-3703-84b6-347d072bb5c0_shop_android_1750490050337&skey=2d30297ff20ec9b7442dc4f3c335abdc&source_app=android&standby_id=8rdlo74r:::&sys_version=30&timestamp=1750514920&warehouse=VIP_NH
args4 -->
result --> ac463f7d156a361aa731c99506dbdd007539118f
args1 --> ���( ����
args2 --> a84c5883206309ad076deea939e850dcac463f7d156a361aa731c99506dbdd007539118f
args4 -->
result --> e585d05a1af08912261113f55822575b51f9acb6

通过和抓包工具分析e585d05a1af08912261113f55822575b51f9acb6就是我们需要的值,args2是传入的明文值,我们分析这两个args2的值可以发现加密逻辑为:

  • 提取map中的key和value,将键值对转为key=value形式,用&连接
  • 将a84c5883206309ad076deea939e850dc和转换的map字符串进行拼接
  • 拼接后通过标准的sha1算法进行一次加密得到结果
  • 将a84c5883206309ad076deea939e850dc和第一次加密得到的字符串拼接再次进行sha1加密得到最终的值