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加密参数分析相关推荐

  1. 贝壳app Authorization参数分析

    嗯,2021年快结束了,学习忙碌一年了,年底了写点文章,总结性学习成果吧! 今天我们要分析的app是贝壳 版本号:v2.66.0,小伙伴们可以去各大应用商定自行下载,也是一个很好的逆向分析案例. 转载 ...

  2. 豆瓣app sig参数分析

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

  3. 安卓逆向小案例——阿里系某电影票务APP加密参数还原-Unidbg篇

    安卓逆向小案例--阿里系某电影票务APP加密参数还原-Unidbg篇 一.前期准备 使用unidbg还原参数时,首先需要找到指定的native方法和对应的so文件.而锁定生成加密参数的native方法 ...

  4. 某盾验证加密参数分析——cb,data参数(终篇)

    文章目录 前言 一.cb参数 二.data参数 总结 前言 话不多说,上次易盾未破解的参数最终篇,因为写个博客详单与重新再追一遍,今天抽了空算是交差了. 上篇文章某盾验证加密参数分析--fp参数(一) ...

  5. 知乎App加密流量分析初探

    如果努力没有结果,那就要再努力一点.对面HTTPS加密流量,还是束手无策啊.因为我们得到的信息太少了,只有握手过程是明文的,后续数据部分什么都分析不了.便纵有千种风情,更与何人说? 第一步:在安卓虚拟 ...

  6. 网站图片全自动加密_外卖优惠平台内容加密参数分析!你见过一块钱买外卖的吗?...

    Js 加密的内容其实大同小异,目前咸鱼也在不断学习 APP 逆向的知识,之后会出一部分关于 APP 逆向在爬虫中的应用相关的文章,这部分设计的技能栈较广,大家可以先预习 Java 基础与 Androi ...

  7. 某美颜app sig参数分析

    之前转载过该app的文章,今天翻版重新整理下,版本号:576O5Zu+56eA56eAYXBwIHY5MDgw (base64 解码). 上来先抓个包: jadx搜索关键词 "sigTime ...

  8. acw_sc__v2加密参数分析(XX找房、36氪、亿X欧、大TANGs商务)

    看到很多网站都用到了acw_sc__v2 这个参数,于是,决定把某一个站点研究出来,之后发现,这些站点都能解决. 我们发现,目标url第一次请求都没有得到数据,得到的是类似于下面的js. 站点还会进入 ...

  9. 第28篇-某商盟登录加密参数分析【2022-02-28】

    提前声明:该专栏涉及的所有案例均为学习使用,如有侵权,请联系本人删帖! 文章目录 一.前言 二.网站分析 三.完整js代码 一.前言 先送大家一个头像 然后进入今天的文章主题,嘻嘻. 今天要分析的网站 ...

最新文章

  1. Linux 启动mysql
  2. 北京交通大学研究生教务处爬虫
  3. 每日程序C语言24-回文数字的判断
  4. mysql optimization
  5. 作者:邹本友,男,中国人民大学信息学院博士生, CCF学生会员。
  6. 翻译 Real-Time Global Localization of Robotic Cars in Lane Level via Lane Marking Detection and Shape
  7. 为什么年龄大了近视还增加_都是做近视手术,为什么价格区别这么大?
  8. synchronized的实现原理用法详解
  9. Logistic(逻辑)回归分析
  10. Google earth engine(GEE)——LANDSAT8统计不同点的DN值
  11. Java 蓝桥杯 基础练习 01字串(循环)
  12. CDN 是什么 、CDN 引入
  13. linux课程--实验二 Linux 基本命令操作2
  14. javaweb之Html/Hss/JavaScript/BootStrap小结
  15. Clamav使用及规则库详解
  16. redis存取list<T>,及bug:Failed to serialize object of type: class com.google.common.collect.Lists$Revers
  17. 威盾IIS防火墙升级到V3.7
  18. 分布式系统中使用分布式session和token的区别
  19. 懒汉模式在多线程中的问题
  20. 自动化测试助力“无边界办公”

热门文章

  1. 制作画中画视频的方法分享
  2. Postman Flow功能浅尝,解析常用Block使用
  3. C++找对象的季节——常成员函数(点和对称点)
  4. 使用pytorch完成kaggle猫狗图像识别
  5. 在OpenGL中利用shader进行实时瘦脸大眼等脸型微调
  6. 让计算变简单 华为RH2288HV3服务器评测
  7. vant 关于IOS时间会出现NaN现象
  8. java实现在线预览功能(支持xlx,word,ppt,dwg等格式转Pdf)
  9. 黑白照片如何上色?AI智能一键上色
  10. Object.assign是浅拷贝还是深拷贝?