某某虾App加密参数分析
0x01 写在前面
对于AppHook这项技术,说难不难,说简单也不简单,唯一的特点就是比较费头发。因为你需要在别人浩如烟海的代码中推导出你想要的东西,而且最终的推导结果还不一定如你所愿。所以搞这种东西之前,我们优先考虑的是自己的发量,而不是对它的研究兴趣。
0x02 所用工具
系统环境
网易mumu模拟器 + Android 6.0【系统版本是模拟器自定义的】
链接工具
adb 这是文档
应用软件
某某虾 version3.0
抓包工具
Fiddler
反编译工具
jadx-gui
Hook工具:
python + frida
安卓编写工具
Android Studio version4.0
调用工具:
nanohttpd + xposed
这里需要注意两点:
1. 目前github xposed作者将下载请求的协议改成了HTTPS,所以安装过程中会出现激活失败的情况,这里是我的解决该问题的参考方案
2. 在选择模拟器时尽量选择版本比较低的进行安装,如果最新版本在安装完xposed后,开机动画会可能会卡在94%无法载入,这种情况不明所以,这里是我的选择的模拟器版本
0x03抓包分析
通过抓包我们不难发现,请求header里有两个加密参数:X-SS-QUERIES、X-Gorgon ,而这两个参数正是我们今天的研究的对象。
0x04 逆向源码
这款App没有进行加壳,我们借助jadx-gui工具轻而易举就能将它扒光逆向,然后直接全局搜索X-SS-QUERIES关键字,记得勾选code
搜索到结果后对代码进行跟进
直接对它所对应的函数a进行hook,分析一下的传入值和返回值
/* code for javascript */Java.perform(function () {let RequestEncrypt = Java.use("com.bytedance.frameworks.core.encrypt.RequestEncryptUtils");// overload functionRequestEncrypt.a.overload("java.lang.String", "java.lang.String").implementation = function (x1, x2){console.log("【INPUT x1】:", x1);console.log("【INPUT x2】:", x2);let result = this.a(x1, x2);console.log("【OUTPUT 】:", result);return result;}})
}
运行结果
从Hook后的结果我们发现一大堆乱码的数据,但如果你细心研究一下,就会有更为惊奇的发现:它们不止乱码而且看起来还挺费眼睛。
结论:【input x1】传入值属于加密数据,【input x2】传入的UTF8编码格式。所以,我们要从x1入手去推导它是如何进行加密的,只有把这一步整明白,那么整套算法流程就会不攻自破。
根据上图所圈点的代码区块,我们可以把大致的加密流程整理出来
从上述流程中我们可以分析出加密的初始值,也就是String 类型的a2变量,换句话来解释:加密的源头是a2所对应的数值。
看一下a2对应的函数
直接Hook
/* code for javascript */RequestEncrypt.a.overload('java.util.List', 'boolean', 'java.lang.String').implementation = function (x1, x2, x3){console.log("【INPUT x1】:", x1);console.log("【INPUT x2】:", x2);console.log("【INPUT x3】:", x3);let result = this.a(x1, x2, x3);console.log("【OUTPUT 】:", result);return result;};
从这次Hook结果来看,我们得到了一种明文数据,而且这种明文数据又符合urlencode编码格式。所以我们直接给定结论或是提出大胆的假设:它正是执行加密的原始数据。但对于这种假设我们一会儿拿到xposed模板统一去验证,这里先按下暂停键。
0x05 分析X-Gorgon参数
应该是版本迭代或者是官方故意隐藏,这个参数我们直接在逆向工具上搜索很难搜到,而且用我老师提供的堆栈追踪法也无法定位。最终得益于神通广大的网友助力,才让此参数的研究思路初现端倪。(PS:具体哪篇文章我忘了,如果有侵权烦请告知。)
切入到函数内部
再次定位方法
直接Hook
// code for javascript
let NetworkParams = Java.use("com.bytedance.frameworks.baselib.network.http.NetworkParams");NetworkParams.tryAddSecurityFactor.overload('java.lang.String', 'java.util.Map').implementation = function (x1, x2){console.log("【input x1】:", x1);console.log("【input x2】:", x2);let result = this.tryAddSecurityFactor(x1, x2);console.log("【output 】:", result);return result;}})
分析一下:
【input x1】传入值是网络请求的链接
【input x2】应该是一种Java专有的数据类型叫做:HashMap(见识少,勿喷)
【output 】正是我们想要的X-Gorgon加密参数
值得注意的是:x2的传入值里面有很多密文,这些是App程序对你系统信息的一些收录,在之后调用的过程中可以模拟出来,不用刻意研究。
0x07 浅谈android编辑器用法
关于安卓程序的编写,我也是刚学不久,属于初级菜鸟,如果你对此也感兴趣的话,我会在文末给出我老师的知识星球坐标,共勉。至于有什么收获,你所看到的既是我的收获。
选择创建的模块(不知道这样叫准不准确,勿喷)
选择安卓版本
这里需要注意几点:
1. 你所选择的android版本要跟SDK版本一一对应上,不然编译成App时直接报错。
2.创建App签名时,一定要选用系统方式创建,不然即使编译成功也会安装失败。
0x08 Xposed编写
项目app目录下
AndroidMainfest.xml
文件中增加几行代码
<-- code for android !--><meta-dataandroid:name="xposedmodule"android:value="true" /><meta-dataandroid:name="xposeddescription"android:value="your defined description" /><meta-dataandroid:name="xposedminversion"android:value="53" />
项目app目录下
build.gradle
文件增加几行代码
dependencies {...compileOnly 'de.robv.android.xposed:api:82'compileOnly 'de.robv.android.xposed:api:82:sources'
}
项目app目录下
src/main/java/com.example.xxx/
目录中增加Java文件命名为HookLoader
项目app目录下
src/main/
创建assets
文件夹并在文件夹创建xposed_init
文件添加一行代码:com.example.xxx.HookLoader
编译成App成功后执行adb安装指令:
adb install -t app-release.apk
,然后我们就会发现我们的程序就推送到xposed模板中了
重启模拟器验证效果
打开App后打印拦截提示
0x09 搭建android服务
晾晒在文档开头的nanohttpd工具终于可以轮到它抛头露面了,我们把它下载完成后添加到项目app目录下
libs
文件夹中
再次在项目app目录下
build.gradle
文件增加一行代码
compileOnly 'org.nanohttpd:nanohttpd:2.3.1'
重新回到HookLoader文件中编写代码如何搭建服务,nanohttpd文档中有详细介绍我这里就张贴代码了,见谅
重新编译App并推送到模拟器中,当模拟器重启后,打开某某虾App会发现我们的服务启动成功了。
执行
adb forward tcp:8889 tcp:8889
将端口映射到本地,然后在本地浏览器访问http://127.0.0.1:8889/hello
成功!
0x10 xposed调用
这一步要做的跟frida实现的功能有异曲同工之妙,只不过frida对内部函数进行拦截用于我们分析,而这一步我们使用xposed对内部函数进行调用,据为己用
处理请求
//code for java
@Override
public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms, Map<String, String> files) {log(uri);//register ClassClass<?> clazzPPx = null;String postData = files.get("postData");log("postData=" + postData);// direct return errorif (StringUtils.isEmpty(postData)) {return newFixedLengthResponse("postData is null.");}//if (StringUtils.containsIgnoreCase(uri, "get-queries")) {try {clazzPPx = lpparam.classLoader.loadClass("com.bytedance.frameworks.encryptor.EncryptorUtil");} catch (ClassNotFoundException e) {e.printStackTrace();}return getQueries(clazzPPx, postData);} else if (StringUtils.containsIgnoreCase(uri, "get-gorgon")) {try {clazzPPx = lpparam.classLoader.loadClass("com.bytedance.frameworks.baselib.network.http.NetworkParams");} catch (ClassNotFoundException e) {e.printStackTrace();}try {JSONObject json = new JSONObject(postData);log("json------------------" + json);String str = (String) json.get("uri");log("uri------------------" + uri);JSONObject params = new JSONObject((String) json.get("params"));log("params-------------- " + params);HashMap hashMap = (HashMap) Utils.jsonToMap(params);log("hashMap-------------" + hashMap);return getGorgon(clazzPPx, str, hashMap);} catch (JSONException e) {e.printStackTrace();return newFixedLengthResponse("invalid json type.");}}return super.serve(uri, method, headers, parms, files);}
处理响应以及xposed调用
// code for java
// get x-ss-queries function
public Response getQueries(Class<?> classUse, String strData) {// get bytesbyte[] dataBuf = strData.getBytes();// get bytes lengthint length = dataBuf.length;// callback native ttEncrypt functionbyte[] dataEnc = (byte[]) XposedHelpers.callStaticMethod(classUse, "ttEncrypt", dataBuf, length);// base64 encodeString dataBase64 = Base64.encodeToString(dataEnc, 2);// url encodeString dataUrlEncode = null;try {dataUrlEncode = URLEncoder.encode(dataBase64, "UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}log("X-SS-QUERIES:" + dataUrlEncode);return newFixedLengthResponse(dataUrlEncode);
}// get x-gorgon function
public Response getGorgon(Class<?> classUse, String str, Map<String, List<String>> map) {log("getGorgon uri=" + str);log("getGorgon map=" + map);Map<String, String> mapGorgon = (Map<String, String>)XposedHelpers.callStaticMethod(classUse,"tryAddSecurityFactor", str, map);log("Map gorgon=" + mapGorgon);String gorgon = mapGorgon.get("X-Gorgon");log("gorgon=" + gorgon);return newFixedLengthResponse(gorgon);
}
使用python模拟请求
import requests
import json
# code for python
uri = "http://127.0.0.1:8889"def get_queries_params():queries_uri = f"{uri}/get-queries"postData = "cell_id=7006574890992015629&count=2&api_version=1&iid=2524899796857758&device_id=61993742510055&ac" \"=wifi&mac_address=08%3A00%3A27%3AE4%3AE3%3AB1&channel=store_tengxun_wzl&aid=1319&app_name=super" \"&version_code=300&version_name=3.0.0&device_platform=android&ssmix=a&device_type=MI+6&device_brand" \"=Xiaomi&language=zh&os_api=23&os_version=6.0.1&uuid=300000000218617&openudid=ab925ec9fb32a36d" \"&manifest_version_code=300&resolution=810*1440&dpi=270&update_version_code=30050&_rticket" \"=1631346727236&cdid=6eb9b3b7-4651-4038-a48e-107dc0f75c71&app_region=CN&sys_region=CN&time_zone=Asia" \"%2FShanghai&app_language=ZH&carrier_region=&last_channel=&last_update_version_code=0 "res = requests.post(url=queries_uri, data=postData)if res.status_code == 200:return res.textdef get_gorgon_params(x_ss_queries):gorgon_uri = f"{uri}/get-gorgon"hashMap = {"accept-encoding": "gzip","cookie": "odin_tt=1c9cb7f29b113286bcf3ddd0e7a3d117cc88da5926f098f4fa09c1bc690efeffff835ecaf4fa53c35158071f87239f1f74a1545586d7acd74917405bc034b92f; passport_csrf_token_default=6237903143a1db30225c2f7e60e76afc; install_id=2524899796857758; ttreq=1$a5fa436781e70b00229f4e96ba6aa97fa8471fc1","sdk-version": "1","user-agent": "ttnet okhttp/3.10.0.2","x-ss-queries": x_ss_queries,"x-ss-req-ticket": "1631346727238"}postData = {"uri": "https://i.snssdk.com/bds/cell/immersion_comment/?cell_type=1&cell_id=7006574890992015629&count=2""&api_version=1&iid=2524899796857758&device_id=61993742510055&ac=wifi&mac_address=08%3A00%3A27%3AE4""%3AE3%3AB1&channel=store_tengxun_wzl&aid=1319&app_name=super&version_code=300&version_name=3.0.0""&device_platform=android&ssmix=a&device_type=MI+6&device_brand=Xiaomi&language=zh&os_api=23""&os_version=6.0.1&uuid=300000000218617&openudid=ab925ec9fb32a36d&manifest_version_code=300&resolution""=810*1440&dpi=270&update_version_code=30050&_rticket=1631346727236&cdid=6eb9b3b7-4651-4038-a48e""-107dc0f75c71&app_region=CN&sys_region=CN&time_zone=Asia%2FShanghai&app_language=ZH&carrier_region""=&last_channel=&last_update_version_code=0&ts=1631346727&as=a111111111111111111111&cp""=a000000000000000000000&mas=01950e0f880e41b17ece8e51d3ddfbe6a08c8c8c8c8c8c8c8c8c8c ","params": json.dumps(hashMap)}res = requests.post(url=gorgon_uri, data=json.dumps(postData))if res.status_code == 200:print(f"X-Gorgon 请求成功:", res.text)def main():x_ss_queries = get_queries_params()print(f"X-SS-Queries 请求成功{x_ss_queries}")get_gorgon_params(x_ss_queries)if __name__ == '__main__':main()
完美!!!
差点忘了,鉴于python没有HashMap的数据类型,在向服务端请求的时候,我只用使用json去传递数据,Java端需要把我传递的json数据转成HashMap类型, 这里是转换的方法:
// code for java
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
public class Utils {public static Map jsonToMap(JSONObject json) throws JSONException {HashMap hashMap = new HashMap();JSONObject items = new JSONObject();Iterator<String > it = json.keys();while (it.hasNext()){String key = it.next();items.put(key, new ArrayList<>());;ArrayList itemArr = (ArrayList) items.get(key);itemArr.add(json.get(key));hashMap.put(key, itemArr);}return hashMap;}
}
0x11 结语
这是本人一次尝试写技术博客,如果有错误之处还望大家多多评判指正,因为这样的话,我就能明白自己的脸皮到底有多厚。最后,如果大家想学习这项技术的话,我推荐一个星球给大家,星主不光对这方面有很深得造诣,而且人长得还很帅。
某某虾App加密参数分析相关推荐
- 贝壳app Authorization参数分析
嗯,2021年快结束了,学习忙碌一年了,年底了写点文章,总结性学习成果吧! 今天我们要分析的app是贝壳 版本号:v2.66.0,小伙伴们可以去各大应用商定自行下载,也是一个很好的逆向分析案例. 转载 ...
- 豆瓣app sig参数分析
今天我们要分析的app 是豆瓣, 版本号7.18.0,下载链接小伙伴们可以去各大应用商定自行下载.怕自己忘,赶紧记录下分析流程,是作为一个逆向分析的很好案例. 参考链接: https://blog.c ...
- 安卓逆向小案例——阿里系某电影票务APP加密参数还原-Unidbg篇
安卓逆向小案例--阿里系某电影票务APP加密参数还原-Unidbg篇 一.前期准备 使用unidbg还原参数时,首先需要找到指定的native方法和对应的so文件.而锁定生成加密参数的native方法 ...
- 某盾验证加密参数分析——cb,data参数(终篇)
文章目录 前言 一.cb参数 二.data参数 总结 前言 话不多说,上次易盾未破解的参数最终篇,因为写个博客详单与重新再追一遍,今天抽了空算是交差了. 上篇文章某盾验证加密参数分析--fp参数(一) ...
- 知乎App加密流量分析初探
如果努力没有结果,那就要再努力一点.对面HTTPS加密流量,还是束手无策啊.因为我们得到的信息太少了,只有握手过程是明文的,后续数据部分什么都分析不了.便纵有千种风情,更与何人说? 第一步:在安卓虚拟 ...
- 网站图片全自动加密_外卖优惠平台内容加密参数分析!你见过一块钱买外卖的吗?...
Js 加密的内容其实大同小异,目前咸鱼也在不断学习 APP 逆向的知识,之后会出一部分关于 APP 逆向在爬虫中的应用相关的文章,这部分设计的技能栈较广,大家可以先预习 Java 基础与 Androi ...
- 某美颜app sig参数分析
之前转载过该app的文章,今天翻版重新整理下,版本号:576O5Zu+56eA56eAYXBwIHY5MDgw (base64 解码). 上来先抓个包: jadx搜索关键词 "sigTime ...
- acw_sc__v2加密参数分析(XX找房、36氪、亿X欧、大TANGs商务)
看到很多网站都用到了acw_sc__v2 这个参数,于是,决定把某一个站点研究出来,之后发现,这些站点都能解决. 我们发现,目标url第一次请求都没有得到数据,得到的是类似于下面的js. 站点还会进入 ...
- 第28篇-某商盟登录加密参数分析【2022-02-28】
提前声明:该专栏涉及的所有案例均为学习使用,如有侵权,请联系本人删帖! 文章目录 一.前言 二.网站分析 三.完整js代码 一.前言 先送大家一个头像 然后进入今天的文章主题,嘻嘻. 今天要分析的网站 ...
最新文章
- Linux 启动mysql
- 北京交通大学研究生教务处爬虫
- 每日程序C语言24-回文数字的判断
- mysql optimization
- 作者:邹本友,男,中国人民大学信息学院博士生, CCF学生会员。
- 翻译 Real-Time Global Localization of Robotic Cars in Lane Level via Lane Marking Detection and Shape
- 为什么年龄大了近视还增加_都是做近视手术,为什么价格区别这么大?
- synchronized的实现原理用法详解
- Logistic(逻辑)回归分析
- Google earth engine(GEE)——LANDSAT8统计不同点的DN值
- Java 蓝桥杯 基础练习 01字串(循环)
- CDN 是什么 、CDN 引入
- linux课程--实验二 Linux 基本命令操作2
- javaweb之Html/Hss/JavaScript/BootStrap小结
- Clamav使用及规则库详解
- redis存取list<T>,及bug:Failed to serialize object of type: class com.google.common.collect.Lists$Revers
- 威盾IIS防火墙升级到V3.7
- 分布式系统中使用分布式session和token的区别
- 懒汉模式在多线程中的问题
- 自动化测试助力“无边界办公”