某电商app的sign分析
定位sign关键位置
抓包后看到sign的值为16个字节,猜测应该是一个md5
可以通过hookNewStringUTF
利用字符串长度筛选定位到关键位置,也可以通过url中的关键字段去定位组包位置。这里利用后者,搜索x-api-eid-token
字段,注意这里最好使用此url特有的字段,这样好筛选。jadx中看到此字段被CHANGEABLE_PARAM_LIST
引用
继续往上回溯引用CHANGEABLE_PARAM_LIST
的地方getChangeableParamStr
向上回溯getChangeableParamStr
的引用有多个,经过打印筛选得到实际的引用为getQueryParamsStr
。此函数调用getStaticParamStr
函数得到UNCHANGEABLE_PARAM_LIST
中包含的固定的参数值,调用getChangeableParamStr
得到CHANGEABLE_PARAM_LIST
中包含的不固定会变化的参数值。最后将固定的参数和不固定的参数拼接后返回。
继续往上回溯getQueryParamsStr
引用,最后得到addStatQueryParam--->getStatisticReportString--->getQueryParamsStr
。查看addStatQueryParam
函数其会将getStatisticReportString
返回的固定和不固定参数拼接后的结果作为参数调用addStateQueryParamInternal
查看addStateQueryParamInternal
,其先会调用getUrl
获取当前url,然后与传入的参数进行拼接,最后调用setUrl
重新设置url
此时还没有涉及到sign
,继续回溯addStatQueryParam
的引用得到setupParams
。其会调用addStatQueryParam
添加查询参数,调用addCustomQueryParam
添加自定义业务参数
对setUrl
和上述分析过程中的关键函数进行hook观察url的变化情况,可以发现在调用了addCustomQueryParam
添加了自定义参数后(并且在调用doSignUsingJdGuard函数之前)紧接着又添加了sign
参数。
所以最后找到是signature
函数,其会调用com.jingdong.common.network.JDNetworkDependencyFactory$3.signature
计算得到sign
等参数,最后调用seturl
冲新设置url。
com.jingdong.common.network.JDNetworkDependencyFactory$3.signature
函数会调用jni函数getSignFromJni
分析sign算法
getSignFromJni
对应的native函数在libjdbitmapkit.so
中
- 先调用
gettimeofday
添加时间戳st=
- 调用
lrand48() % 3
生成encryptId
和offset
,并与1拼接得到参数sv=
- 添加了
st=
和sv=
的url和encryptId
,offset
作为参数调用自定义的加密函数 - 返回的加密数据进行
base64
编码 base64
编码后的结果进行md5
并转化为字符串得到sign
值
分析3中的自定义加密函数,其首先会利用之前随机生成的offset
和encryptId
选择对应的生成算法和key。
最后得到offset
,encryptId
,sv
和生成算法的对应关系如下表
分析TenSeattos
生成算法,其会轮询加密每一个字符
用c将加密算法还原如下
void TenSeattosEncrypt(char* input, int input_len)
{
int v4, v5;
char v6;
const char* TenSeattos_key = "80306f4370b39fd5630ad0529f77adb6";
unsigned char table[0x10] = { 0x37, 0x92, 0x44, 0x68, 0xA5, 0x3D, 0xCC, 0x7F, 0xBB, 0xF, 0xD9, 0x88, 0xEE, 0x9A, 0xE9, 0x5A };
for (int i = 0; i != input_len; ++i) {
v4 = i & 7;
v5 = table[i & 0xF];
v6 = (v5 + (*(unsigned char*)(input + i) ^ *(unsigned char*)(TenSeattos_key + v4) ^ table[i & 0xF])) ^ table[i & 0xF];
*(unsigned char*)(input + i) = v6;
*(unsigned char*)(input + i) = *(unsigned char*)(TenSeattos_key + v4) ^ v6;
}
}
知道其中一种算法就可以固定sv参数去请求接口了,frida hook打印一下整个加密过程