这个故事要从儿时开始讲起,记得我还是个小孩的时候,有一天接到了一个任务,就是搞懂车帝。我心想:我之前搞过汽车之家、易车之类的,既然他们都是车相关的app,四舍五入就等于一样呗。我摆出了个OK的姿势,对方以为我只收0K的钱,就欣然的说交给你了。
规范性动作,抓个包吧
返回值加密了,不慌,我之前搞过易车,易车也返回值加密过,只要jadx反编译一下,找到解密位置,把解密的java代码复制出来就可以了,这个我熟,搜索关键词errorMessage找一下。
需要调用JNI,那时的我知道有个叫unidbg的宝剑,你可以拿它斩杀恶龙,你不需要会什么ida武功,只要挥舞起unidbg,剑指恶龙即可。我试着拿起宝剑,传入方法名和参数,启动。妈蛋,竟然报错了,这个项目bug真多,垃圾。
-2000 years later-
此时的我长大了,一个男人也出生了,他就是千与千寻里浪里白条的白龙(简称龙哥(其实性别我没有考证过))
龙哥:宝,想学unidbg斩杀恶龙呀,我教你啊。
宝:不行啊,龙王,我很傻的。
龙哥:傻宝,没事的,我传你心法,你把上衣脱了。
傻宝:这样不太好吧(- -)脱衣中...
龙哥:好了,现在你已经拥有我1天的功力了(龙哥今年30好几岁)
从此以后我起早贪黑,勤加练习,挥汗如雨、浪里白嫖。终于在此刻1629949911,我拿起了屠龙宝刀,向恶龙发起最后的冲锋(可以脑补下詹姆·蘭尼斯特冲向恶龙的场景)
如今,两千年过去了,当时的少年是否斩杀恶龙呢,他究竟经历了什么奇妙之旅呢,那我们就开始一探究竟吧。
---正文---
首先先frida hook下native方法的参数
知道了参数,运行下yang神脚本,看一下用的哪个so
OK,是libcjtfcc.so,那把基础的unidbg代码写好,运行一下
没有报错,native方法地址偏移量也拿到了0xa2ac,这也太顺利了,接着把调用native方法的业务逻辑写上,调用一下吧。
报错了,这个错误也没见过呀,把DEBUG开启重新运行,打印一下bt
最近的错误调用栈就一条,ida总查看下吧
啥!啥!这都是啥,只看懂一个R2,装模作样看看R2的值吧
嗯,我知道了,我是个菜逼
-郁闷了 2000 years later-
傻宝:龙哥,这次真的搞不出来了
龙哥:样本
傻宝:文件传输中....(1%)250B/s
-2 seconds later-
龙哥:把调用native方法的第二个参数,改成传jobject或jobject,别传0
傻宝:为什么?
龙哥:第二个参数被用到了,不被用到的时候传0
傻宝:你怎么知道第二个参数被用到了?
龙哥:我先去拍个小视频?
傻宝:不太好吧?
我:可以和家人一起看吗?
龙哥:学习资料,自己学吧
-2000 seconds later-
我悟了
小视频不太好光明正大的分享,感兴趣的可以在底部加星球看呦,也可以加我交流技术呦。
在龙哥的指点过后,改完代码再执行一次
这个报错咱们就熟悉了,补环境嘛
再次运行
unidbg竟然不报错了,才补了一个就没有了?这样太顺利了吧。但是虽然unidbg不报错了,但这个报错应该是so层打印的错误日志,看着像是说解码失败了。哎,这就麻烦了,补环境最好弄了,闭着眼就能补。那就进ida看下伪代码吧
看这逻辑是有三个判单,判单不通过就打印东西,感觉这三个判断像校验之类的。我用console debugger验证了一下,前两个判断都通过了,最后一个判单的时候没有通过,打印了日志,也就是说只要把最后一个校验让他通过就可以了。tab切换到汇编看下最后一个校验是怎么判断的
cmp ro, #0,如果r0是0就走下面的打印错误日志逻辑。那我们就把他改成如果r0是1,才走,这样他就永远不会走那个逻辑,转而走解密逻辑(聪明脸)
运行一下
不提示解密失败了,但解密数据为空肯定也是不对的,这个应该是解密的什么逻辑出问题了,经过群友@NZ大神的分析,解密函数应该在if判断之前的那个函数,我们看看if判断前的那个函数什么样
既然又走错逻辑了,我们再次用魔法棒,让函数强行走上面那个解密逻辑
(我是图,图中有返回值了,但是返回值是乱码)图丢了
看来不能这样强制换逻辑
到目前为止,我已经灰心了,经过漫长岁月的试错,我总感觉关键点就在解密函数的第一个参数上面
private native String tfccDecrypt(int i, int i2, String str, String str2);
第一个参数 是引用计数的一个值,每次调用会加1(我严重怀疑这个参数,但是有不知道它有什么问题)
第二个参数 一直是1(良民)
第三个参数 一个是一个字符串(良民)
第四个参数 待解密的字符串
就目前试过的结果来看,参数1和参数4可能有某种关联,因为123不变,4变就不行,或234不变,1变也不行。
通过看jadx源码,好像没什么特别的地方,就是初始化Tfcc对象后,对象中的b字段(也就是第一个参数)就会有一个默认值,然后解密时候就把这个值传过去。
然后我frida也新建一个对象,然后用这个对象里的b值作为参数调用native方法解密,不行。也就是参数1和参数4确实存在某种关联,不单单是新建一个Tfcc对象,把里面的b作为参数就行,而是这个b必须和参数4关联起来才行。
而参数4是服务器返回的待解密数据,也即是服务器是知道参数4对应的b是多少,那它怎么能知道呢?
我操,可能有一种可能,客户端发给服务器的,这样服务器返回数据的时候,就可以给数据动手脚,让这个数据只能有客户端发送过来的那个b给结了,也就是下图的逻辑
那我们就去看下发送url的地方,有没有发送什么特别的参数吧
**,jadx又打不开了(改死的小内存mac)
我直接说吧,就是在发送请求的时候,客户端给服务器发送了一个body t_key
而这个t_key正是我们解密的那个对象,调用加密natvie生成的,生成这个t_key的时候,它的参数1和解密的时候的参数1是一样的。
private native String tfccEncrypt(int i, int i2, String str, String str2);
private native String tfccDecrypt(int i, int i2, String str, String str2);
也即是说,解密的参数1和返回值关联了起来
ok,那基本就明白了,他们的加密逻辑应该是这样的(狂喜中...)
1,新建一个Tfcc对象D1(此对象会根据引用计数,将其字段b设置为某个数字)
2,用D1对象调用native tfccEncrypt方法加密(b, 1, "固定字符串S", "dongchedi+时间戳"),得到加密字符串t_key
3,发起http请求(将上一步生成的t_key作为body传过去),得到加密的返回值R1
4,还是用步骤1生成的D1对象调用它的native ftccDecrypt方法解密(b, 1, "固定字符串S", R1),得到解密结果
我们实际验证一下
我们生成一个t_key
然后用这个t_key请求数据
那这个数据,还有相同的第一个参数去解密(要成功啦,我能感觉到)
妈蛋,不玩啦,不玩啦,睡觉,头发不多了。
-the second morning-
我的第一个参数没错啊,绝地的没错啊,我都把源码看看的清清澈澈,它就是这么加密解密的,难道让我看so?你瞧瞧现在哪有会so的呀,这都是只有龙哥才会so。
有经过长时间的试错,我还是感觉我分析他们的逻辑没错,我的第一个参数绝对没问题,却察觉出了问题。而第一个参数关联的是t_key,而t_key本身有没有参与到native方法到调用(我用unidbg模拟执行了一次,返现jni层没有调用java层多少方法,都是getBytes啊之类的,没有调用获取t_key的值)。那不是t_key本身,难道是t_key的相关属性?啊?难道是t_key传到服务器的时间?
但感觉如果是时间也太简单了,头条系的水平就这?但是也没有其他思路了,是一下吧。这次我们获取到t_key后立马用代码发送请求数据,拿到数据后,立马解密,看下效果。
欢迎加我微信交流技术:zhoutianxing_
也欢迎加龙哥星球学习unidbg:https://t.zsxq.com/NVVrBYJ
https:
/
/
ib.snssdk.com
/
motor
/
owner_price_api
/
car_price_list?car_id
=
50223
&owner_price_region_name
=
%
E5
%
8C
%
97
%
E4
%
BA
%
AC&sort_type
=
time_reverse&offset
=
0
&page_size
=
10
&iid
=
3035074666959895
&device_id
=
1469370100097725
&ac
=
wifi&channel
=
wandoujia&aid
=
36
&app_name
=
automobile&version_code
=
651
&version_name
=
6.5
.
1
&device_platform
=
android&ab_client
=
a1
%
2Cc2
%
2Ce1
%
2Cf2
%
2Cg2
%
2Cf7
&ab_group
=
2589722
&ssmix
=
a&device_type
=
Pixel&device_brand
=
google&language
=
zh&os_api
=
29
&os_version
=
10
&manifest_version_code
=
651
&resolution
=
1080
*
1794
&dpi
=
420
&update_version_code
=
6512
&_rticket
=
1629894505066
&cdid
=
f3034d66
-
09c7
-
4a46
-
a724
-
b1334073e9b4&city_name
=
%
E5
%
8C
%
97
%
E4
%
BA
%
AC&gps_city_name
=
%
E5
%
8C
%
97
%
E4
%
BA
%
AC&selected_city_name&rom_version
=
29
&longi_lati_type
=
1
&longi_lati_time
=
1629894505693
&content_sort_mode
=
0
&overall_score
=
6.8389
&cpu_score
=
6.8915
&host_abi
=
armeabi
-
v7a
{
"status"
:
"ok"
,
"errorMessage"
: "",
"data"
:
"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"
}
https:
/
/
ib.snssdk.com
/
motor
/
owner_price_api
/
car_price_list?car_id
=
50223
&owner_price_region_name
=
%
E5
%
8C
%
97
%
E4
%
BA
%
AC&sort_type
=
time_reverse&offset
=
0
&page_size
=
10
&iid
=
3035074666959895
&device_id
=
1469370100097725
&ac
=
wifi&channel
=
wandoujia&aid
=
36
&app_name
=
automobile&version_code
=
651
&version_name
=
6.5
.
1
&device_platform
=
android&ab_client
=
a1
%
2Cc2
%
2Ce1
%
2Cf2
%
2Cg2
%
2Cf7
&ab_group
=
2589722
&ssmix
=
a&device_type
=
Pixel&device_brand
=
google&language
=
zh&os_api
=
29
&os_version
=
10
&manifest_version_code
=
651
&resolution
=
1080
*
1794
&dpi
=
420
&update_version_code
=
6512
&_rticket
=
1629894505066
&cdid
=
f3034d66
-
09c7
-
4a46
-
a724
-
b1334073e9b4&city_name
=
%
E5
%
8C
%
97
%
E4
%
BA
%
AC&gps_city_name
=
%
E5
%
8C
%
97
%
E4
%
BA
%
AC&selected_city_name&rom_version
=
29
&longi_lati_type
=
1
&longi_lati_time
=
1629894505693
&content_sort_mode
=
0
&overall_score
=
6.8389
&cpu_score
=
6.8915
&host_abi
=
armeabi
-
v7a
{
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-10-21 11:56
被贪吃虫编辑
,原因: 细节调整