App信息
包名:com.shizhuang.duapp
过强制更新
jadx反编译apk
搜索下载最新版
就可以找到相关字符串,然后双击跳转
如下是源码
1 | public Dialog a(Activity activity) { |
这个方法返回一个Dialog
,一个更新对话框。
方法里面使用MaterialDialog.Builder
构建对话框,设置对话框的标题和内容。
这里面有一行代码d.b(false);
,用来设置对话框不可通过点击外部区域或返回键取消。
frida hook
使用frida把参数改成true实现绕过
1 | function hook_update(){ |
然后进入app之后,点击空白处,更新窗口就不见了。
抓包首页推荐数据
找到相应的接口
抓包数据
1 | url: https://app.dewu.com/sns-rec/v1/recommend/all/feed |
参数分析
需要破解的参数newSign
、X-Auth-Token
参数还原
使用Jadx反编译
newSign参数还原
搜索newSign
c()
它有多种生成方式,这里面RequestUtils.c()
出现了多次,分析这个方法
尝试用frida对c()
进行hook
1 | function printHashMap(map){ |
hook 结果
1 | {abValue=1, deliveryProjectId=0, abRectagFengge=0, abType=social_brand_strategy_v454, limit=20, lastId=, abRecReason=0, abLiveEntranceClose=0, abVideoCover=2} |
在c()方法里,向map添加了几个内容之后拼接成字符串然后调用AESEncrypt.encode
方法,返回值又作为参数传给了a
方法
通过hook发现这里面添加的参数只有timestamp
不是定值,其他都是定值。
encode()
这里面出现了getByteValues()
方法并且返回encodeByte()
这个方法里把byteValues
的值进行了取反,就是0
改成1
,1
改成0
。
getByteValues()
它在native里
hook getByteValues()
1 | function hook_getbytevalue(){ |
hook 结果
1 | AESEncrypt.getByteValues is called |
这里打印出来的结果都是一样的
hook encodeByte()
1 | function bArrToString(bArr) { |
hook 结果
1 | AESEncrypt.encodeByte is called: bArr=108,111,103,105,110,84,111,107,101,110,112,97,103,101,104,111,109,101,112,108,97,116,102,111,114,109,97,110,100,114,111,105,100,116,101,115,116,97,116,105,109,101,115,116,97,109,112,49,55,52,48,48,53,52,51,54,50,54,53,54,117,117,105,100,98,99,49,49,101,101,97,102,99,53,49,52,102,97,54,98,118,52,46,55,52,46,53, str=010110100010001010010010000011000111001011101010101000101110111010011010101101101010001000101100010110100010001010011010110011001111001011100010101000100100110010110010100010101011110010111100 |
后面在SO中分析它。
hook encode()
1 | function hook_encode() { |
hook结果
1 | AESEncrypt.encode is called: obj=com.shizhuang.duapp.modules.app.DuApplication@ae024e1, str=abTest{"name":"Commodity_subject","value":"2"},{"name":"Commodity_chuanda","value":"1"},{"name":"recommend_line","value":"0"}homeVersion1lastIdlimit20loginTokenplatformandroidtimestamp1740029457724uuidbc11eeafc514fa6bv4.75.5 |
它的返回值传递给了a
方法
a()
a
方法是MD5
hook a
1 | function hook_a(){ |
hook 结果
1 | RequestUtils.a is called: str=3VUAf22pQJc12obDQnsqvYlw0+yJxzwTa9UlRxJdVEtJdsrt8Ug8vJMXbRvBmLHUMyPBrC5RsVvrAYDNVuobkC5NwI68VoN4V9+AtU6Ua9abLYBoLXkpb3lLQHC2PdcAu5MmZbMSM6XuYm7gCkfIAnbMm8+dsr3fzbG8BlIpUfh/vrBlCHJNuNhdqsLnjl0s2OzGQQs4AcuAogxYqrZ+KJpADsrG578j4yW18jF8/mg8giv5nPp2mKhwHVYxQFU+N9biMQihdFvvr/Aac6kCd4hPLAYREE5dF2Brg4bXY9EyDwto9jibBSjAgfR9FwV3 |
经验证,它是标准的MD5
SO分析
JNIEncrypt
getByteValues()
和encodeByte()
都是通过动态注册的
找到JNI_OnLoad,
找到动态注册函数的偏移地址
get_bytes()
这里的返回值就是hook得到的返回值
encode()
它返回的是AES_128_ECB_PKCS5Padding_Encrypt()
的返回值
AES_128_ECB_PKCS5Padding_Encrypt()
这里面调用了AES_128_ECB_PKCS5Padding_Encrypt()
在这里进行加密,加密结果进行base64编码
hook AES_128_ECB_PKCS5Padding_Encrypt
1 | function hook_encrypt(){ |
hook 结果
1 | onEnter |
这里出现了一个固定值d245a0ba8d678a61
,猜测它是密钥
密钥验证
Python生成newSign
hook与newSign有关的完整数据
1 | function printHashMap(map){ |
结果如下
1 | {abValue=0, deliveryProjectId=0, abRectagFengge=1, abType=social_brand_strategy_v454, limit=20, lastId=, abRecReason=0, abVideoCover=0} |
写Python
1 | import time |
X-Auth-Token参数还原
搜索X-Auth-Token
这里获取JwtToken,分析抓包抓到的JwtToken
1 | "X-Auth-Token": "Bearer eyJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE3NDAxMDMyOTgsImV4cCI6MTc3MTYzOTI5OCwiaXNzIjoiYmMxMWVlYWZjNTE0ZmE2YiIsInN1YiI6ImJjMTFlZWFmYzUxNGZhNmIiLCJ1dWlkIjoiYmMxMWVlYWZjNTE0ZmE2YiIsInVzZXJJZCI6MjQ5Njk3MjI1NSwidXNlck5hbWUiOiLlvpfniallci05SzFGMEI2SCIsImlzR3Vlc3QiOnRydWV9.u3DBybDzMvee-V9rWdGuDePZU1TvvbOvodPeJ88-lKmxMbJpK8U93jyxIdcQ8BzhdSahkWn29T6sG3S3-bq1dz9p_MWgfxw5yYSIOE4qd2NfAr1OQ4n-BGQXw7EYdC-eg0uDOIctGCJ2OSfyKlqzqeIx9RIQieJf2bkYeYFF1vkF8SKU2rf-1ESjWdpiUkvPuBN5NrafGHvL4VRkdxkCjT_NTezQ8tXm1PgrYy_73jGGma1nX8Vp49wLqoxhrimQwUremdcNUEWWdeTNsy7nSbobiArkONoRIknrO922QU4nOmFWBuPRnRRGmKHZ371Q3MobmAz7s7yCXt1uDsY4QQ" |
这个token分为三段,通过.
分开
1 | Bearer eyJhbGciOiJSUzI1NiJ9 |
这个token串主要用来做前后端登录认证,第一段是头,第二段是荷载,第三段是签名,三段都是base64
编码后的内容,其中签名是加密的
1 | import base64 |
token串是由后端生成的,可以通过伪造请求获取
这里我们直接使用抓包获取的token
Python抓取推荐数据
1 | import requests |
Unidbg模拟执行
模拟执行encode
函数,这里面有两个参数,其中的obj并没有使用,所以不用管它,只需要一个str参数,而str参数又被传入了encodeByte
方法里,它是一个native方法。这里面还有一个字段byteValues
,它经过异或处理之后作为参数传给了encodeByte
方法
接下来通过unidbg来实现
1 | public class dewu extends AbstractJni { |