
智能摘要 AI
本文介绍了通过逆向工程破解某安卓应用中的hkey生成机制。作者使用了NetKeeper、jeb和IDA等工具进行分析,并利用Frida进行动态调试。通过对网络请求的抓包,发现hkey随时间戳变化。进一步分析发现,hkey是通过将特定路径与时间戳拼接后进行MD5加密生成的。具体过程为:将路径`/game/all_recommend`、时间戳及固定字符串拼接,计算出32位小写MD5值。最终得出hkey的生成原理,并提供了相关Hook代码用于验证。该研究展示了如何利用逆向工程技术破解反爬虫机制。
前言
这盒子有个抽奖功能,但是中奖率感人~
故,hack!
用到的工具:
分析处理
先抓个包包吧
主要请求部分如下:
POST /account/data_report/?type=13&time_=1595993115&heybox_id=17584182&imei=xxxxx&os_type=Android&os_version=6.0&version=1.3.114&_time=1595993115&hkey=bd34ee4c62&channel=heybox_yingyongbao HTTP/1.1
经过测试hkey会随time_值变动而变动
jeb分析
搜寻hkey
v1与v2的值来源似乎不好处理,
为了减少脑细胞与头发的消耗,可以尝试从NDKTools->encode方法入手。
但是,到达encode位置后(Ctrl+双击),
啊,这得上ida了
ida分析
进入ida的encode中,
看似没什么大问题,来hook:
console.log("========Hook Start==========")
String.prototype.format = function () {
var values = arguments;
return this.replace(/\{(\d+)\}/g, function (match, index) {
if (values.length > index) {
return values[index];
} else {
return "";
}
});
}
var JNI_LOAD_POINTER = Module.getExportByName('libnative-lib.so', 'JNI_OnLoad'); // 首先拿到 JNI_OnLoad方法的地址
var BASE_ADDR = parseInt(JNI_LOAD_POINTER) - parseInt('0x1C6C'); // 用程序运行中JNI_OnLoad的绝对地址减去它的相对地址得到基址
// encode
Java.perform(function() {
var hookpointer = '0x' + parseInt(BASE_ADDR + parseInt('0x1B00')).toString(16) // 获取要hook方法的地址
var pointer = new NativePointer(hookpointer) // 根据方法地址构建NativePointer
console.log('[encode] hook pointer: ', pointer)
var arg0, arg1, arg2, arg3
Interceptor.attach(pointer, {
onEnter: function(args) {
arg0 = args[0]
arg1 = args[1]
arg2 = args[2]
arg3 = args[3]
console.log('\n')
console.log('=====> [encode] -> [方法调用前]')
console.log('参数1: {0} => {1}'.format(arg0, Memory.readCString(arg0)))
console.log('参数2: {0} => {1}'.format(arg1, Memory.readCString(arg1)))
console.log('参数3: {0} => {1}'.format(arg2, Memory.readCString(arg2)))
console.log('参数4: {0} => {1}'.format(arg3, Memory.readCString(arg3)))
console.log('参数5: {0} => {1}'.format(args[4], Memory.readCString(args[4])))
console.log('\n')
},
onLeave: function(retval) {
console.log('\n')
console.log('=====> [encode] -> [方法调用后]:')
console.log('返回值: ', retval)
console.log('参数1: {0} => {1}'.format(retval, Memory.readCString(retval)))
console.log('\n')
}
}
)
})
但是,还是出了些问题:
额,我不觉得肉眼能看出这是什么东西 #(流汗滑稽)
既然程序处理了一些不能看的东西,那就尝试去找出能看的东西吧 #(摊手)
我觉得选择MDString比较好,因为可以看到什么东西被拿去算md5了。
hook MDString:
console.log("========Hook Start==========")
String.prototype.format = function () {
var values = arguments;
return this.replace(/\{(\d+)\}/g, function (match, index) {
if (values.length > index) {
return values[index];
} else {
return "";
}
});
}
var JNI_LOAD_POINTER = Module.getExportByName('libnative-lib.so', 'JNI_OnLoad'); // 首先拿到 JNI_OnLoad方法的地址
var BASE_ADDR = parseInt(JNI_LOAD_POINTER) - parseInt('0x1C6C'); // 用程序运行中JNI_OnLoad的绝对地址减去它的相对地址得到基址
// MDString
Java.perform(function() {
var hookpointer = '0x' + parseInt(BASE_ADDR + parseInt('0x15C4')).toString(16) // 获取要hook方法的地址
var pointer = new NativePointer(hookpointer) // 根据方法地址构建NativePointer
console.log('[MDString] hook pointer: ', pointer)
var arg0, arg1, arg2, arg3
Interceptor.attach(pointer, {
onEnter: function(args) {
arg0 = args[0]
arg1 = args[1]
arg2 = args[2]
console.log('\n')
console.log('=====> [MDString] -> [方法调用前]')
console.log('参数1: {0} => {1}'.format(arg0, Memory.readCString(arg0)))
console.log('\n')
},
onLeave: function(retval) {
console.log('\n')
console.log('=====> [MDString] -> [方法调用后]:')
console.log('返回值: ', retval)
console.log('返回: {0} => {1}'.format(retval, Memory.readCString(retval)))
console.log('参数1: {0} => {1}'.format(arg0, Memory.readCString(arg0)))
console.log('\n')
}
}
)
})
输出:
可以发现,返回值正是将/game/all_recommend/bfhdkud_time=1596004491进行MD5加密:
结合抓包到的请求,
可以得到NDKTOOL->encode的原理:
由路径/game/all_recommend与时间戳1596004491以及/bfhdkud_time= 拼接成/game/all_recommend/bfhdkud_time=1596004491算出32位 小写md5-> 837444501881f2af92b9cc0f0a9505fc
结论
hkey的处理流程:
注:Hook代码编写,参考:Python反反爬虫 – Frida破解某安卓社区token反爬虫




评论 (0)