占时没跟新完,一点点更新把
本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除!
挑战下TT,当然是站在大佬的肩膀上去弄,大佬就是大佬写的文章都让人难懂,所以本文比较适合逆向萌新看。
版本:39.3.3
抓包
先去网上找找,找到了。。。所以说逆向之前先去网络上找。hook之后就可以抓到包了
tiktok加密参数分析_tiktik iid-CSDN博客
frida hook代码
function main() {
try {
console.log('66666');
var android_dlopen_ext = Module.getExportByName(null, 'android_dlopen_ext');
console.error("android_dlopen_ext -->", android_dlopen_ext);
if (android_dlopen_ext) {
console.log('55555');
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
var soName = args[0].readCString();
console.log("soName -->", soName);
console.log(soName.indexOf('libsscronet.so'));
if (soName.indexOf('libsscronet.so') != -1) {
console.log('77777');
this.hook = true;
}
}, onLeave: function () {
if (this.hook) {
console.log('88888');
console.error('启动成功');
var scronet = Module.findBaseAddress("libsscronet.so");
var ver = Module.findExportByName("libsscronet.so", "SSL_CTX_set_custom_verify");
var custome_verify = new NativeFunction(ver, 'pointer', ['pointer', 'int', 'pointer']);
var self = new NativeCallback(function (arg1, arg2, arg3) {
console.log("custom_verify is calling", arg2, arg3);
return custome_verify(arg1, 0, arg3);
}, 'pointer', ['pointer', 'int', 'pointer']);
Interceptor.replace(ver, self);
console.log('注入成功')
}
}
});
}
} catch (error) {
console.error("find error: ", error);
}
}
setImmediate(main);
加密参数定位
老样子先去网上找找,找到了一篇文章[原创]unidbg直接调用tiktok so生成签名-Android安全-看雪-安全社区|安全招聘|kanxue.com,写的很详细,但还是有很多地方是没写过程直接写结果的,所以自己弄的时候会蒙蔽,但好在还是靠自己得出了结果。
Java层文章里说了是找不到的,那就不用浪费时间去看了,咋们直接上so层。
文章说TikTok使用 cronet网络库 。将libsscronet.so拖IDA反编译,搜索 x-gorgon 关键字,确实找到了:

再之后大佬说结合 cronet源码,找到Native层设置请求Header函数就定位到了0x2C44BC,通过hook就得到了全部请求头和值。那么这个0x2C44BC他是如何具体得到的呢?
我将这段代码交给了DeepSeek去分析:结果是说loc_329A48部分在设置请求头,我hook了下然而并不是。&99是请求头的key,并没有对应value传入。但是函数存在"../../net/http/http_request_headers.cc"字符串,猜测肯定和请求头有关系值得留意下。
既然上面没找到赋值的地方,那么就继续往下找:找到了sub_329848,点进去再次看到了"../../net/http/http_request_headers.cc"字符串


将这段代码丢给DeepSeek分析,得出的代码和 cronet源码居然差不多

同时hook了下sub_329848得出的结果就是请求头中的内容。那么就找到了设置请求Header函数的偏移地址:0x329848(也就是上面所说的0x2C44BC,版本不同地址也不同)
function main() {
var lib = Module.findBaseAddress("libsscronet.so");
Interceptor.attach(lib.add(0x329848), {
onEnter: function (args) {
var key = args[1].readPointer().readUtf8String();
var val = args[2].readPointer().readUtf8String();
console.log(key + " : " + val);
if (key.indexOf("X-Gorgon") !== -1) {
console.log('SetHeaders called from:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');
console.log("---------------------------end----------------------------------");
}
},
onLeave: function (retval) {
}
});
}
setImmediate(main);
打印出来的调用确实有两个地方,快捷键g键进行跳转,然后反编译

v67就是某个函数调用返回的结果,鼠标放到v64按tab键看到在0x413C5C处进行了跳转,因此用frida hook这里,然后输出调用时的两个参数查看

直接对0x413C5C地址进行HOOK,并打印出x0和x1寄存器的值,同时打印出X23寄存器的值:
function main() {
var lib = Module.findBaseAddress("libsscronet.so");
Interceptor.attach(lib.add(0x413C5C), {
onEnter: function (args) {
console.log('===================start==========================');
console.log(args[0].readUtf8String());
console.log('=============================================');
console.log(args[1].readUtf8String());
console.log("X23:" + this.context.x23);
console.log('===================end==========================');
},
onLeave: function (retval) {
}
});
}
setImmediate(main);

参数1是https请求的url,参数2是https请求的header,x23的地址是0x71df0986c0。
原文这里直接得出了函数在libmetasec_ov.so模块的0x88ee0处,那么这个0x88ee0又是怎么知道的呢?
hook了这么多我们知道函数在内存的具体地址=so文件基地址+函数偏移量。实际上0x88ee0就是函数偏移量。
既然我们提前知道了so文件为libmetasec_ov.so,那么就可以知道他的基地址(我这边是0x71df008000)。函数的具体地址就是x23:0x71df0986c0。
0x71df0986c0 - 0x71df008000 = 0x906C0
这个0x906C0就是我们要找地址了(对应0x88ee0)
hook下这个函数的参数和输出
function main() {
var lib = Module.findBaseAddress("libmetasec_ov.so");
console.log("libmetasec_ov.so:" + lib);
Interceptor.attach(lib.add(0x906C0), {
onEnter: function (args) {
console.log("arg0:" + args[0].readUtf8String());
console.log("arg1:" + args[1].readUtf8String());
},
onLeave: function (retval) {
console.log("retval: " + retval.readUtf8String());
}
});
}
setImmediate(main);

到这里就走完了函数的定位了
调用
调用部分原文也有,测试出来能用,但是没批量爬取过,想要批量爬取原文作者也说了还要点东西,这边测试完没问题再写了。