原文件下载路径:
【移动安全与逆向工程】得物app破解
【移动安全与逆向工程】得物app破解源码
【移动安全与逆向工程】得物app破解所需软件及插件
说明:文中所示案例仅供学习使用,请勿进行商业用途!!!
1 得物目标
# 采集推荐信息
# 版本
-4.74.5
2 绕过强制更新
# 先断网,打开app,再联网即可
3 抓包
3.1 使用SocksDroid
# 该app禁用了手机端代理,所以我们使用SocksDroid代理
# 操作步骤
- 手机系统代理删除
- 基本配置
切记:在使用前删除手机上设置的系统代理。
# 根据抓包分析,我们发现/sns-rec/v1/recommend 地址为首页推荐接口
# 地址如下:https://app.dewu.com/sns-rec/v1/recommend/all/feed
-地址:https://app.dewu.com/sns-rec/v1/recommend/all/feed
-请求方式:get
-请求头:
-X-Auth-Token:必须带
-请求参数:
-newSign:这个接口不需要带,学习破解它,后续别的接口需要带
4 破解newSign
4.1 反编译搜索newSign
# 1 把app拖入 jadx中,反编译,搜索 "newSign
# 2 搜出8个,但实际上只有5个有效,5个都在同一个类中---》随便点一个进去看--》hook验证
# 3 发现它是写在拦截器中---》所有请求,都会带这个newSign
# 4 最终调用了RequestUtils.c
# 5 hook一下 RequestUtils.c, 确认有没有走
4.2 Hook-RequestUtils.c
import frida
import sys
rdev = frida.get_remote_device()
session = rdev.attach("得物(毒)")
scr = """
Java.perform(function () {
var RequestUtils = Java.use("com.shizhuang.duapp.common.utils.RequestUtils");
RequestUtils.c.implementation = function(map,j){
console.log("----------------------------------------");
console.log('1.参数字典为:',map); // 此处直接打印map,发现打印的是对象,我们需要转换一下
console.log('1.参数字典为:',JSON.stringify(map)); // 查看一下类型 :<instance: java.util.Map, $className: java.util.HashMap>,把HashMap值取出来,做个转换,如下
var Map = Java.use('java.util.HashMap');
var obj = Java.cast(map, Map);
console.log('1.参数字典为:',obj.toString());
var res = this.c(map,j);
console.log("4.newSign结果:", res);
return res;
}
});
"""
script = session.create_script(scr)
def on_message(message, data):
print(message, data)
script.on("message", on_message)
script.load()
sys.stdin.read()
'''
### 参数字典为:
{abValue=1, deliveryProjectId=0, abRectagFengge=0, abType=social_brand_strategy_v454, limit=20, lastId=, abRecReason=0, abVideoCover=2}
### newSign结果:
16aa2e250ac6c28c3166cce3d0561be8
### 拿到newSign和 抓包抓包的newSign比较发现是一样的,确定位置
'''
*/
4.3 分析RequestUtils.c核心逻辑
public static synchronized String c(Map<String, String> map, long j2) throws UnsupportedEncodingException {
synchronized (RequestUtils.class) {
PatchProxyResult proxy = PatchProxy.proxy(new Object[]{
map, new Long(j2)}, null, changeQuickRedirect, true, 6612, new Class[]{
Map.class, Long.TYPE}, String.class);
if (proxy.isSupported) {
return (String) proxy.result;
} else if (map == null) {
return "";
} else {
// 1 参数拼接
// 把uuid,platform,v,loginToken,timestamp放入map中
map.put("uuid", DuHttpConfig.d.getUUID());
map.put("platform", "android");
map.put("v", DuHttpConfig.d.getAppVersion());
map.put("loginToken", DuHttpConfig.d.getLoginToken());
map.put("timestamp", String.valueOf(j2));
ArrayList arrayList = new ArrayList(map.entrySet());
// 2 把map转成ArrayList,并进行排序
Collections.sort(arrayList, new Comparator<Map.Entry<String, String>>() {
public static ChangeQuickRedirect changeQuickRedirect;
@Override
public int compare(Map.Entry<String, String> entry, Map.Entry<String, String> entry2) {
PatchProxyResult proxy2 = PatchProxy.proxy(new Object[]{
entry, entry2}, this, changeQuickRedirect, false, 6618, new Class[]{
Map.Entry.class, Map.Entry.class}, Integer.TYPE);
return proxy2.isSupported ? ((Integer) proxy2.result).intValue() : entry.getKey().toString().compareTo(entry2.getKey());
}
});
// 3 构建字符串
StringBuilder sb = new StringBuilder();
for (int i2 = 0; i2 < arrayList.size(); i2++) {
Map.Entry entry = (Map.Entry) arrayList.get(i2);
sb.append(((String) entry.getKey()) + ((String) entry.getValue()));
}
String sb2 = sb.toString();
DuHttpConfig.LogConfig logConfig = DuHttpConfig.f15800h;
String str = f16243a;
logConfig.d(str, "StringToSign " + sb2)