android 逆向 28 / 38
- app OAuth api_sign 分析
- app sig 参数加密分析 unidbg 模拟黑盒调用
- app sign so 加密参数破解|unidbg
- sgmain x-sign 分析 - unidbg
- androidAsync fridaManager sgmain 70102 rpc 远程调用
- app edata 参数 so aes 加密分析破解|unidbg
- frida 加载 sekiro dex 文件 实现与服务端交互
- frida sekiro 实现 sgmain 70102 远程 rpc 调用
- xposed sekiro hook 获取 wx 万能 key
- unidbg console debugger 使用
- unidbg hook inline hook 使用
- app 公众号文章列表 so 加解密算法分析还原 | 简单分析
- app 公众号文章列表 so 加解密算法分析还原 | 加密 rsa base64 分析
- app 公众号文章列表 so 加解密算法分析还原 | 加密 zip aes 分析
- app 公众号文章列表 so 加解密算法分析还原 | response 内容解密分析
- app sign so 加密算法分析还原|简单分析
- app sign so 加密算法分析还原|so 算法分析
- app sign so 加密算法分析还原|so sub_126AC 函数算法还原
- app so signkeyV1 参数分析
- ida 动态调试 android so 文件|基础入门环境搭建
- app so newSign 参数分析破解
- app tzRgz52a 参数分析破解
- app sign-v2 签名算法 aes 加解密分析
- app so 加密参数分析|protocbuf 分析
- mxtakatak android app 加解密分析
- android app so 加密算法分析破解|mtgsig unidbg
- android app so 加密算法分析破解|siua unidbg
- android app nsign so 加密算法分析
- android app sig 参数 so 加密逻辑逆向分析
- android app so sig 加密参数 unidbg
- 狗狗音乐登陆协议加密参数逆向分析
- android sign so 加密参数分析|unidbg
- android app X-SS-QUERIES 参数分析
- unidbg android app xgorgon 加密参数 leviathan
- sgmain 6.4.x xsign 加密算法分析研究
- sgmain 6.4.x xminiwua 加密算法分析研究
- 某 app mas 算法分析还原 cms so
- 某东登陆协议 tlv 逻辑分析
推荐阅读
前言
- app https://www.wandoujia.com/apps/280945
- 版本 15.25.1
charles 抓包
就是这个 nsign
参数
java 分析
全局搜索 "nsign"
关键词,随便定位一个,使用 jeb
打开这个类
nsign = v2; v2 = SignUtil.c
点进 SignUtil.c
看看
这里调用了 SignUtil.getSign0
函数
SignUtil.getSign0
是个 native
函数
往前翻,回到 nsign = v2
的位置,往上翻可以看到加载了 signutil.so
文件
frida hook
function getMapData(mapSet) {
try {
var result = {};
var key_set = mapSet.keySet();
var it = key_set.iterator();
while (it.hasNext()) {
var key_str = it.next().toString();
result[key_str] = mapSet.get(key_str).toString();
}
return result
} catch (error) {
return mapSet
}
}
function getProcessId() {
var androidProcess = Java.use('android.os.Process');
return 'processId: ' + androidProcess.myTid() + ' - ';
}
function printLog() {
var result = '';
for (var i = 0; i < arguments.length; i++) {
result += arguments[i] + ' ';
}
console.log(getProcessId(), result)
}
Java.perform(function () {
var SignUtil = Java.use('com.anjuke.mobile.sign.SignUtil');
SignUtil.getSign0.implementation = function (a, b, c, d, e) {
printLog('SignUtil.getSign0.a: ', a);
printLog('SignUtil.getSign0.b: ', b);
printLog('SignUtil.getSign0.c: ', c);
printLog('SignUtil.getSign0.c: ', JSON.stringify(getMapData(c)));
printLog('SignUtil.getSign0.d: ', d);
printLog('SignUtil.getSign0.e: ', e);
var res = this.getSign0(a, b, c, d, e);
printLog('SignUtil.getSign0.res: ', res);
return res;
}
})
使用 frida hook com.anjuke.mobile.sign.SignUtil
这个类的输入输出
参数3,是个 map
,value
是 byte[]
没打印出来,不过问题不大,根据 key
可以去 url params
里查找对应的 value
so 分析
前面是 md5 逻辑的分析,可以参考下上面的推荐阅读文章
md5
加密完之后,还有这个小逻辑,拼接上 1000
字符
接着还会拼接上这些计算的结果,最后才是最终的结果
unidbg
这个样本的逻辑比较简单,使用 frida hook so 也是足够了,不过博主还是比较喜欢使用 unidbg 辅助算法还原
@Override
public int callIntMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
switch (signature) {
case "java/util/HashMap->size()I": {
HashMap hashMap = (HashMap) dvmObject.getValue();
return hashMap.size();
}
}
return super.callIntMethod(vm, dvmObject, signature, varArg);
}
@Override
public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
switch (signature) {
case "java/util/HashMap->keySet()Ljava/util/Set;": {
HashMap map = (HashMap) dvmObject.getValue();
return vm.resolveClass("java/util/Set").newObject(map.keySet());
}
case "java/util/Set->toArray()[Ljava/lang/Object;": {
Set thisSet = (Set) dvmObject.getValue();
StringObject[] obj = new StringObject[thisSet.size()];
for (int i = 0; i < thisSet.size(); i++) {
obj[i] = new StringObject(vm, (String) thisSet.toArray()[i]);
}
return new ArrayObject(obj);
}
case "java/util/HashMap->get(Ljava/lang/Object;)Ljava/lang/Object;": {
HashMap map = (HashMap) dvmObject.getValue();
Object key = varArg.getObjectArg(0).getValue();
Object obj = map.get(key);
return new ByteArray(vm, (byte[]) obj);
}
}
return super.callObjectMethod(vm, dvmObject, signature, varArg);
}
大概需要补以上的这些环境
public void runGetSign0() {
DvmClass SignUtil = vm.resolveClass("com/anjuke/mobile/sign/SignUtil");
DvmObject<?> ret = SignUtil.callStaticJniMethodObject(
emulator, "getSign0(Ljava/lang/String;Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;[B>;>;Ljava/lang/String;I)Ljava/lang/String;",
new StringObject(vm, "/xinfang/m/android/1.3/loupan/newlistv2/"),
new StringObject(vm, "d41d8cd98f00b204e9800998ecf8427e"),
vm.resolveClass("java/util/HashMap").newObject(getMapData()),
new StringObject(vm, "ab4f9779-6108-430d-a083-338bbb5154db"),
DvmInteger.valueOf(vm, 0)
);
System.out.println(ret.getValue().toString());
}
然后运行 getSign0
函数
过程很流畅,没啥问题,结果也是正常出来了
public void inlineHook() {
final Pointer[] md5updater0 = {null};
emulator.getBackend().hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
if (address == (module.base + 0x11D4)) {
Arm32RegisterContext ctx = emulator.getContext();
md5updater0[0] = ctx.getR0Pointer();
Inspector.inspect(md5updater0[0].getByteArray(0, ctx.getR2Int() + 10), " md5 update params r0 ");
Inspector.inspect(ctx.getR1Pointer().getByteArray(0, ctx.getR2Int() + 10), " md5 update params r1 ");
System.out.println("md5 update params r2 " + ctx.getR2Int());
}
}
@Override
public void onAttach(UnHook unHook) {
}
@Override
public void detach() {
}
}, module.base + 0x11D4, module.base + 0x11D4, null);
}
使用 inline hook
一下 md5 update
函数
第一次 update
就是我们输入的数据
第二次 update
是 800000......
应该是填充的数据
第三次 update
是八个字节,刚好是明文的长度
结果验证
复制明文到 cyberchef
验证一下,结果是对的
使用 python
跑一下,结果也是正常出来了
tofu
2022-01-28大佬 已加你好友vx