【安卓逆向】某二手平台反调试请求头分析

admin 2025年5月22日00:59:35评论6 views字数 5522阅读18分24秒阅读模式

恰饭

【安卓逆向】某二手平台反调试请求头分析

简介

【安卓逆向】某二手平台反调试请求头分析

抓包

【安卓逆向】某二手平台反调试请求头分析

过程

hook下请求头的okhttp的 addHeader

Java.perform(function () {
    var Builder = Java.use('okhttp3.Request$Builder');
    Builder.addHeader.overload('java.lang.String''java.lang.String').implementation = function (key, value) {
if (key != null && key === 'zzReqSign') {
                console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()))
                console.log('Header added: ' + key + ': ' + value);
            }
        var original = this.addHeader(key, value);
return original;
    };
});

frida检测并且app没闪退

【安卓逆向】某二手平台反调试请求头分析

应该是开线程检测的,hook下dlopen以及pthread_create

functionhook_dlopen() {
    Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
        {
            onEnter: function (args) {
                var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
                    var path = ptr(pathptr).readCString();
                    console.log("enter: " + path);
                }
            },
            onLeave: function (retval) {
                console.log("leave ");
            }
        }
    );
}
hook_dlopen()

跟贝壳和某书的监测点相似,都是libmsaoaidsec.so开三个线程检测

【安卓逆向】某二手平台反调试请求头分析
【安卓逆向】某二手平台反调试请求头分析

hook下JNI_OnLoad发现在其执行之前,脚本如下

function hook_dlopen(soName = '') {
    Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
        {
            onEnter: function (args) {
                var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
                    var path = ptr(pathptr).readCString();
if (path.indexOf(soName) >= 0) {
                        this.is_can_hook = true;
                    }
                }
            },
            onLeave: function (retval) {
if (this.is_can_hook) {
                    hook_JNI_OnLoad(soName)
                }
            }
        }
    );
}

function hook_JNI_OnLoad(soName) {
let module = Process.findModuleByName(soName)
let jniOnloadAddr = module.findExportByName(soName, "JNI_OnLoad");
    Interceptor.attach(jniOnloadAddr, {
        onEnter(args) {
            console.log("call JNI_OnLoad")
        }
    })
}

setImmediate(hook_dlopen, "libmsaoaidsec.so")

加载so->链接so-初始化(init_proc)-数组初始化(init_array)-jni_onLoad

监测点还在前面,根据hook到的偏移量找到位置:

【安卓逆向】某二手平台反调试请求头分析

几次交叉引用查找到了init_proc

【安卓逆向】某二手平台反调试请求头分析

在这里选择一个较早执行的外部函数hook

【安卓逆向】某二手平台反调试请求头分析
【安卓逆向】某二手平台反调试请求头分析
function hook_dlopen(soName = '') {
    Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
        {
            onEnter: function (args) {
                var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
                    var path = ptr(pathptr).readCString();
if (path.indexOf(soName) >= 0) {
                        locate_init()
                    }
                }
            }
        }
    );
}

functionlocate_init() {
let secmodule = null
    Interceptor.attach(Module.findExportByName(null, "__system_property_get"),
        {
            onEnter: function (args) {
                secmodule = Process.findModuleByName("libmsaoaidsec.so")
                var name = args[0];
if (name !== undefined && name != null) {
                    name = ptr(name).readCString();
if (name.indexOf("ro.build.version.sdk") >= 0) {
                        hook_pthread_create()
                    }
                }
            }
        }
    );
}

let i = 0;
functionhook_pthread_create() {
if (i >= 2) {
       console.log("成功过检测")
return
    }
let baseAddress = Process.findModuleByName("libmsaoaidsec.so").base;
    console.log("libmsaoaidsec.so --- " + baseAddress);

    Interceptor.replace(Module.findExportByName("libc.so""pthread_create"), new NativeCallback(function (attr, start_routine, arg1, arg2) {
let func_addr = arg1.sub(baseAddress);
if (func_addr.equals(ptr(0x1c544)) || func_addr.equals(ptr(0x1b8d4) ) || func_addr.equals(ptr(0x26e5c) )) {
            i++
            console.log(func_addr, i)
return 0
        }
let pthread_create = new NativeFunction(Module.findExportByName("libc.so""pthread_create"), 'int', ['pointer''pointer''pointer''pointer']);
return pthread_create(attr, start_routine, arg1, arg2);
    }, 'int', ['pointer''pointer''pointer''pointer']));
}


setImmediate(hook_dlopen, "libmsaoaidsec.so")

【安卓逆向】某二手平台反调试请求头分析

现在hook Builder.addHeader 就可以了

【安卓逆向】某二手平台反调试请求头分析

Header added: zzReqSign: e6ece0fc26af7c1c8f9561617b62090c java.lang.Throwable at okhttp3.Request$Builder.addHeader(Native Method) at com.wuba.zhuanzhuan.framework.network.interceptor.SignInterceptor.intercept(SignInterceptor.java:48) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:10) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:1) at com.wuba.zhuanzhuan.framework.network.interceptor.PPURefreshInterceptor.intercept(PPURefreshInterceptor.java:2) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:10) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:1)

【安卓逆向】某二手平台反调试请求头分析

到getSign native

【安卓逆向】某二手平台反调试请求头分析

静态

【安卓逆向】某二手平台反调试请求头分析

先改下环境指针

【安卓逆向】某二手平台反调试请求头分析

上Ai 先理下思路

【安卓逆向】某二手平台反调试请求头分析

第一部分:验证签名

if ( !a3 )
return 0LL;
  v6 = (const char *)get_app_sign_sha1(a1, a4);
if ( !v6 )
return 0LL;
if ( strcmp(v6, app_sing_str) )
return 0LL;
【安卓逆向】某二手平台反调试请求头分析

第二部分:明文转字节数组

v9 = (*a1)->FindClass(a1, "java/lang/String");
v10 = (*a1)->NewStringUTF(a1, "utf-8");
v11 = (*a1)->GetMethodID(a1, v9, "getBytes""(Ljava/lang/String;)[B");
v12 = (void *)_JNIEnv::CallObjectMethod(a1, mingwen, v11, v10);

第三部分:JNI掉java获取设备ID 然后转字节数组

【安卓逆向】某二手平台反调试请求头分析

第四部分:异或运算

【安卓逆向】某二手平台反调试请求头分析

理一下

v14: 明文字节数组 = mingwen
v18: 明文字节数组长度 = menwen_len
v23: 设备id字节数组 = device
v24: 设备id字节数组长度 = device_len


if ( (int)menwen_len >= 1 )
{
    v27 = 0LL;
    v28 = 0;
do
    {
        v29 = mingwen[(unsigned int)(v28 - v28 / (int)menwen_len * menwen_len)];

        v30 = device[v28 + 1 - (v28 + 1) / device_len * device_len];

        v25[v27++] = v30 ^ v29; 

        v28 += 2; // 步进2
    }
while ( menwen_len != v27 );
}

AI转一下

【安卓逆向】某二手平台反调试请求头分析
def xor_mix(mingwen: bytes, device: bytes) -> bytes:
    mingwen_len = len(mingwen)
    device_len = len(device)
    result = bytearray(mingwen_len)  # 预分配结果数组

for i in range(mingwen_len):
# 明文字节索引: i % mingwen_len
        mingwen_idx = i % mingwen_len

# 设备ID字节索引: (i + 1) % device_len
        device_idx = (i + 1) % device_len

# 异或操作
        result[i] = device[device_idx] ^ mingwen[mingwen_idx]

return bytes(result)

最后就是拼接个字符串实现md5

// 创建一个新的字节数组,长度 = 异或结果长度(v18) + 9(盐值长度)
v31 = (*a1)->NewByteArray(a1, (unsigned int)(v18 + 9));

// 异或结果v26写入新数组的前半部分
(*a1)->SetByteArrayRegion(a1, v31, 0LL, v18, v26);

// 固定字符串"smiletozz"写入新数组的后半部分(
(*a1)->SetByteArrayRegion(a1, v31, v18, 9LL, "smiletozz");

v32 = getMD5(a1, v31);
【安卓逆向】某二手平台反调试请求头分析

原文始发于微信公众号(爬虫逆向小林哥):【安卓逆向】某二手平台反调试请求头分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年5月22日00:59:35
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【安卓逆向】某二手平台反调试请求头分析http://cn-sec.com/archives/4091294.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息