转载一篇好的博客,原始链接 https://mp.weixin.qq.com/s/5kUDmlPvIOw-6mdzywvyFA

推荐一波,这是博主的网站:https://www.qinless.com/ 有兴趣看看,多学习学习。

今天我们要研究的app是美图秀秀,版本号:v9080,下载链接:https://www.wandoujia.com/apps/37577/history_v9080

同系列文章推荐下:
1.聚美app之 _sign参数分析
2.大润发优鲜app之paramsMD5参数分析
3.美图秀秀 sig参数分析
4.贝壳app Authorization参数分析
5.豆瓣app sig参数分析
6.半次元app之data参数分析

转载请注明出处:
https://blog.csdn.net/weixin_38819889/article/details/122307928?spm=1001.2014.3001.5501

美图秀秀请求接口中有一个sig参数加密

1.来我们先抓个包:

2.java 层分析

「直接全局搜索 sig 关键词即可」

最终是定位到了这个函数 com.meitu.secret.SigEntity.generatorSig

调用 SigEntity.nativeGeneratorSig 执行加密逻辑。

native 函数在 librelease_sig.so 文件里

3.so 分析

「librelease_sig so 文件都是静态注册的函数,也没啥对抗分析,打开即可分析」


打开 so 可以看到符号都没混淆,逻辑还是比较清晰的。先来看看 ValidateKey::getValidateResult 函数逻辑。

进来这里调用了 JavaHelper::getAndroidAPKKeyHash 函数,看起来像是校验 apk 签名。

点进来逻辑很清晰一眼望去,确实是获取签名。下面再来看 GeneratorSIG sig 生成函数。

刚开始掉用了一个 GetSecretKey 函数,获取 key,接着是拼接一些不明的字符串,最后调用了 MD5_Calculate 计算函数。着重分析下这个。

md5 init update final 函数一应俱全,应该是标准的。


点进 init 函数,这些常量确实是标准的

4.frida hook

hook java 函数

var SigEntity = Java.use("com.meitu.secret.SigEntity");
SigEntity.generatorSig.overload('java.lang.String', '[Ljava.lang.String;', 'java.lang.String', 'java.lang.Object').implementation = function (a, b, c, d) {printLog('SigEntity.generatorSig.a: ', a);printLog('SigEntity.generatorSig.b: ', b);printLog('SigEntity.generatorSig.c: ', c);printLog('SigEntity.generatorSig.d: ', d);var res = this.generatorSig(a, b, c, d);printLog('SigEntity.generatorSig.res: ', res.sig.value);return res;
}

hook so 函数
「里面的一部分 hook 逻辑,在后面使用 unidbg 跑 so 时会用的」

var soAddress = Module.findBaseAddress("librelease_sig.so");
console.log('soAddress: ', soAddress);var sub_6794 = soAddress.add(0x6794 + 1);
console.log('sub_6794: ', sub_6794);Interceptor.attach(sub_6794, {onEnter: function (args) {console.log('\nsub_6794 onEnter r0: ', this.context.r0);},onLeave: function (retval) {}
})var sub_6790 = soAddress.add(0x6790 + 1);
console.log('sub_6790: ', sub_6790);Interceptor.attach(sub_6790, {onEnter: function (args) {console.log('\nsub_6790 onEnter r0: ', hexdump(this.context.r0, {length: 64, header: false}));console.log('\nsub_6790 onEnter r1: ', hexdump(this.context.r1, {length: 64, header: false}));},onLeave: function (retval) {}
})var sub_6730 = soAddress.add(0x6730 + 1);
console.log('sub_6730: ', sub_6730);Interceptor.attach(sub_6730, {onEnter: function (args) {console.log('\nsub_6730 onEnter r0: ', hexdump(this.context.r0, {length: 64, header: false}));},onLeave: function (retval) {}
})var sub_4A68 = soAddress.add(0x4A68 + 1);
console.log('sub_4A68: ', sub_4A68);Interceptor.attach(sub_4A68, {onEnter: function (args) {console.log('\nmd5_update onEnter r0: ', hexdump(args[0], {length: 32, header: false}));console.log('\nmd5_update onEnter r1: ', hexdump(args[1], {length: args[2].toInt32(), header: false}));console.log('\nmd5_update onEnter r2: ', args[2].toInt32());console.log('\nmd5_update onEnter r3: ', args[3]);},onLeave: function (retval) {}
})

frida hook 命令跑起来,把结果保存到文件中,generatorSig 函数入参, 参数比较简单,也就是请求的一些参数。

so md5 update 函数

hook 结果 md5 update 函数一共调用了三次。这对 md5 算法有了解的小伙伴就会看的出来,只有第一次是真正的数据,后面两次全是填充的数据

「Tips: 不了解的可以看看之前分享的密码学文章:https://www.qinless.com/1246」

最终结果:

就是这个 390b028d37b588d2b4380c4aa215be72, 我们来加密下看看是否相同。


最终加密结果相同。

「下面分享下怎么使用 unidbg 跑起来」

5.unidbg

老规矩,先搭架子

package com.xiayu.meituxiuxiu;import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.debugger.BreakPointCallback;
import com.github.unidbg.debugger.Debugger;
import com.github.unidbg.debugger.DebuggerType;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.memory.Memory;
import unicorn.ArmConst;import java.io.File;
import java.io.IOException;
import java.security.MessageDigest;public class GetSig_v9080Test extends AbstractJni {private final AndroidEmulator emulator;private final Module module;private final VM vm;public String apkPath = "/Volumes/T7/android/android-file/meituxiuxiu-9.0.8.0.apk";public String soPath2 = "unidbg-android/src/test/resources/test_so/meituxiuxiu/librelease_sig-9.0.8.0.so";GetSig_v9080Test() {emulator = AndroidEmulatorBuilder.for32Bit().build();final Memory memory = emulator.getMemory();memory.setLibraryResolver(new AndroidResolver(23));vm = emulator.createDalvikVM(new File(apkPath));vm.setVerbose(true);vm.setJni(this);DalvikModule dm2 = vm.loadLibrary(new File(soPath2), true);dm2.callJNI_OnLoad(emulator);module = dm2.getModule();}public static void main(String[] args) {GetSig_v9080Test getSig = new GetSig_v9080Test();getSig.destroy();}private void destroy() {try {emulator.close();} catch (IOException e) {e.printStackTrace();}}
}


又是熟悉的 bug,这里依赖了 libgnustl_shared.so 我们给他加上。

跑起来,发现啥都没有输出,这没啥事,只要没报错就是好事。下面直接 call 函数
「注意这里的 byte[][] 类型,千万不要直接 vm.resolveClass("[[B") 这样在后面取值会报错,需要使用 ArrayObject + ByteArray 构建(龙哥指点------->ps:龙哥也是一位大佬)」

public ArrayObject getByteByte() {String[] arg5 = {这里大家自行填充 frida hook 出来的参数即可};DvmObject<?>[] dvm = new DvmObject<?>[arg5.length];for (int v1 = 0; v1 < arg5.length; ++v1) {if (arg5[v1] == null) {arg5[v1] = "";}dvm[v1] = new ByteArray(vm, arg5[v1].getBytes());}return new ArrayObject(dvm);
}public void call() {String methodId = "nativeGeneratorSig(Ljava/lang/String;[[BLjava/lang/String;Ljava/lang/Object;)Lcom/meitu/secret/SigEntity;";DvmClass SigEntity = vm.resolveClass("com/meitu/secret/SigEntity");SigEntity.callStaticJniMethodObject(emulator, methodId,new StringObject(vm, "search/feeds.json"),getByteByte(),new StringObject(vm, "6184556633574670337"),vm.resolveClass("android/content/Context").newObject(null));
}


报错,正常补环境

又报错,但是看不出来啥问题,日志全开看看。

发现这里是调用 GetSuperClass JNI 函数报错的。不知道啥问题点进源码看看 DalvikVM.java:150

这里调用了 dvmClass.getSuperclass 函数,返回空,才报错的。跟进去看看

这个函数返回 superClass 字段,该字段的值来源于初始化的时候。在哪里初始化的呢。这个跟一下源码就可以了,我这里就不再继续分析了。是在 vm.resolveClass 的时候

这里可以传递多个参数,正常都是传递一个,如果有两个第二个参数就是 superClass


咱们加上试试

跑起来,成功了,没有报 superClass 错误了,但是又出现其他的了,不慌点进源码一探究竟

发现这里是个未实现的 JNI 函数,这里为了简单,就直接返回需要的值就行了。使用 jnitrace 看看返回值是啥

这里是个 0 直接返回

上面正常补环境即可

这里需要注意下,android base64java base64 还是有点区别的(龙哥的精讲课程里也说到过),这里为了不出问题,就直接复制了 android base64 的代码,大家自行复制就行。


环境补完,但是出奇的是结果尽然为空就很奇怪。不慌继续分析

「复制最后一次 NewStringUTF 的地址,ida 里跳过去」

是在这里操作的,x 查看该函数的交叉引用。


跟到了这里 LABEL_8 代码块,很明显是在 ValidateKey::getValidateResult 函数之后的 if 判断里调用的,在分析该函数逻辑。
这里有多地方会返回 -1 那就应该是该函数出了问题

「Tips: 这里应该是单步调试或者 hook 排除,博主就不去一步步分析了,大家可以自行尝试(具体啥原因也是没分析出来,直接使用 patch 大法搞定它)」

还记得前面的 frida hook 吗,里面就有 hook while 循环里 v11 的值,来看一下

这里循环了三次,最后一次结果为 0,刚好前面的 if 判断不成立就可以继续了

这里直接使用 unidbg hook 强制修改函数返回值为 0,最后也是成功运行出了结果。

6.最后end:

嗯,这篇文章好长啊,转载了我半天时间,后来qinless大哥给了一个固定url 的demo,前前后后分析了半天请求url参数差别,最后还是给还原成Python,测试 详情,评论和搜索接口都能用。

可以看到计算出来的结果 美图秀秀sig: 9667703fa8bc772b436ad0b451de9a6d 和抓包拿到的一模一样。
核心代码 就不公布了毕竟对人家不太友善,有兴趣的朋友可以一起交流学习(扣扣: 519545433)

美图秀秀 sig参数分析相关推荐

  1. app逆向--美图秀秀sig参数

    声明 本文章中所有内容仅供学习交流,不可用于任何商业用途和非法用途,否则后果自负,如有侵权,请联系作者立即删除!由于本人水平有限,如有理解或者描述不准确的地方,还望各位大佬指教!! 抓包分析 今天我们 ...

  2. 豆瓣app sig参数分析

    今天我们要分析的app 是豆瓣, 版本号7.18.0,下载链接小伙伴们可以去各大应用商定自行下载.怕自己忘,赶紧记录下分析流程,是作为一个逆向分析的很好案例. 参考链接: https://blog.c ...

  3. 美图秀秀上传图片案例1

    2019独角兽企业重金招聘Python工程师标准>>> <!DOCTYPE html> <html> <head> <title>美图 ...

  4. 美图app sig分析

    本文案例是对美图秀秀app的sig参数和client_session参数生成分析. 案例环境:美图版本9.3.5 .工具Jadx.夜神安卓7.Frida APP包名: com.mt.mtxx.mtxx ...

  5. Android JNI实现Java与C/C++互相调用,以及so库的生成和调用(JNI方式调用美图秀秀so)

    前言 关于Android Studio如何继承JNI开发环境,请阅读上一篇博文 Android CMake集成JNI开发环境本篇博文将结合实例分别讲解Android中Java如何调用C/C++的方法, ...

  6. 从工具到社区,美图秀秀大规模性能优化实践

    导读:本文由演讲整理而成.美图秀秀社区自上线以来已经有近一年时间,不管是秀秀海量的用户还是图片社区特有的形态都给性能优化提出了巨大的挑战.本文将会结合这一年内我们遇到的具体案例和大家分享下美图秀秀社区 ...

  7. Python 图像 一样大小_教你把Python当美图秀秀用(一)

    本文作者:钱梦璇 文字编辑:孙晓玲 技术总编:张   邯 导读 Python中有一个第三方图像数据库,由于其强大功能和简单易用的API几乎被认为是Python平台上的官方图像处理库了,它就是PIL(P ...

  8. Qt Quick 图像处理实例之美图秀秀(附源码下载)

    在<Qt Quick 之 QML 与 C++ 混合编程详解>一文中我们讲解了 QML 与 C++ 混合编程的方方面面的内容,这次我们通过一个图像处理应用,再来看一下 QML 与 C++ 混 ...

  9. Qt Quick 图像处理实例之美图秀秀 附源码下载

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 在< ...

最新文章

  1. usr libexec java_osx – 如何更改从/ usr/libexec/java_home返回的Mac操作系统的默认Java虚拟机...
  2. java gpg_gpg的使用
  3. C# WebRequest 基础连接已关闭 连接意外关闭
  4. Python计算本周是第几周
  5. ISE中使用DDR3例程的生成步骤与仿真过程
  6. 【MATLAB统计分析与应用100例】案例018:matlab读取Excel数据,进行K均值聚类分析
  7. r语言ggplot2一夜多图_ggplot2绘图:多张图合并为一张
  8. 《算法竞赛进阶指南》数论篇
  9. poj 2318 TOYS amp; poj 2398 Toy Storage (叉积)
  10. 各地实时摄像头_你头顶的摄像头将变成有脑子的眼睛!亚马逊推出AWS Panorama,强监控时代来临?...
  11. c# log4net
  12. Delphi Form Designer (窗体设计器)之一
  13. CE教程:植物大战僵尸(太阳数值修改)
  14. 阿里云主要产品及功能介绍,阿里云产品分为6大分类:云计算基础/安全/大数据/人工智能/企业应用/物联网...
  15. 王长君:城市交通智能管控20年发展反思
  16. 怎么看微信公众号开发文档-微信开发教程14
  17. 苹果笔记本安装完系统win10后多了osxreserved_Mac重装双系统,苹果笔记本电脑重装系统教程...
  18. html5页面设计技术,H5页面设计技巧有哪些?-鱼爪网
  19. 基于局部平面拟合的法向估计
  20. INTRODUCING F#

热门文章

  1. Spring Boot整合Spring Data Redis-存取Java对象
  2. Tomcat集群快速入门:Nginx+Tomcat搭建集群
  3. 数据库分库分表的几种方式
  4. 操作系统对性能的影响-MySQL适合的操作系统
  5. 迭代器模式coding
  6. 气体润滑轴承matlab分析,气体润滑轴承的研究与发展
  7. .NET (c#)序列化和反序列化
  8. H5页面关于android软键盘弹出顶起底部元素的解决方案
  9. 如何在 jetbrick-template 中使用 debug函数?
  10. 配置集群Nginx+Memcached+Tomcat集群配置