找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

32

积分

0

好友

6

主题
发表于 2025-10-25 01:26:39 | 查看: 16| 回复: 0

题目背景

题目界面

题目要求输入n1ctf{flag}格式的字符串,点击check按钮进行验证,是典型的Android逆向题目。

初始分析:DEX反编译

使用jadx-gui分析APK主逻辑:

public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
    public native String enc(String str);
    public native String stringFromJNI();

    @Override 
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        ActivityMainBinding inflate = ActivityMainBinding.inflate(getLayoutInflater());
        this.binding = inflate;
        setContentView(inflate.getRoot());
        this.binding.CheckButton.setOnClickListener(new View.OnClickListener() {
            @Override 
            public final void onClick(View view) {
                MainActivity.this.m157lambda$onCreate$0$comn1ctf2024ezapkMainActivity(view);
            }
        });
    }

    public void m157lambda$onCreate$0$comn1ctf2024ezapkMainActivity(View view) {
        String obj = this.binding.flagText.getText().toString();
        if (obj.startsWith("n1ctf{") && obj.endsWith("}")) {
            if (enc(obj.substring(6, obj.length() - 1)).equals("iRrL63tve+H72wjr/HHiwlVu5RZU9XDcI7A=")) {
                Toast.makeText(this, "Congratulations!", 1).show();
                return;
            } else {
                Toast.makeText(this, "Try again.", 0).show();
                return;
            }
        }
        Toast.makeText(this, "Try again.", 0).show();
    }

    static {
        System.loadLibrary("native2");
        System.loadLibrary("native1");
    }
}

核心加密逻辑位于enc函数中,该函数通过JNI调用两个native库:libnative1.so和libnative2.so。

JNI机制分析

IDA打开libnative.so会发现大量指针数组调用,这是典型的JNI调用模式。

JNI调用机制说明:

  • Native代码访问Java VM特性需要通过JNI函数
  • JNI interface pointer是native函数的第一个参数
  • JNI interface pointer是指向指针的指针,包含JNI函数地址数组

JNI函数调用示例:

jdouble Java_pkg_Cls_f__ILjava_lang_String_2(
     JNIEnv *env,        // interface pointer
     jobject obj,        // "this" pointer
     jint i,             // argument #1
     jstring s)          // argument #2
{
     const char *str = (*env)->GetStringUTFChars(env, s, 0);
     // process the string
     (*env)->ReleaseStringUTFChars(env, s, str);
     return ...;
}

Frida动态Hook分析

定位enc函数

Hook enc函数并监控JNI调用:

Java.perform(() => {
    const MainActivity = Java.use("com.n1ctf2024.ezapk.MainActivity");
    MainActivity.enc.implementation = function(input) {
        console.log("enc called with input:", input);
        const result = this.enc(input);
        startHook();
        console.log("enc returned:", result);
        return result;
    };
});

function startHook(){
    const lib_art = Process.findModuleByName('libart.so');
    const symbols = lib_art.enumerateSymbols();
    for (let symbol of symbols) {
        var name = symbol.name;
        if (name.indexOf("art") >= 0) {
            if ((name.indexOf("CheckJNI") == -1) && (name.indexOf("JNI") >= 0)) {
                if (name.indexOf("GetStringUTFChars") >= 0) {
                    console.log('start hook', symbol.name);
                    Interceptor.attach(symbol.address, {
                        onEnter: function (arg) {
                            console.log('GetStringUTFChars called from:\n' + 
                                Thread.backtrace(this.context, Backtracer.ACCURATE)
                                .map(DebugSymbol.fromAddress).join('\n') + '\n');
                        },
                        onLeave: function (retval) {
                            console.log('onLeave GetStringUTFChars:', ptr(retval).readCString())
                        }
                    })
                }
            }
        }
    }
}

运行结果确认sub_1b148为enc函数。

函数调用追踪

Hook所有native函数调用:

Java.perform(() => {
    const MainActivity = Java.use("com.n1ctf2024.ezapk.MainActivity");
    MainActivity.enc.implementation = function(input) {
        console.log("enc called with input:", input);
        const result = this.enc(input);
        startHooklib();
        console.log("enc returned:", result);
        return result;
    };
});

function startHooklib(){
    var functions_lib1 = Module.enumerateExports("libnative1.so");
    functions_lib1 = []
    var functions_lib2 = Module.enumerateExports("libnative2.so");

    functions_lib1 = functions_lib1.map(item => {
        return { ...item, module: "libnative1.so" };
    })
    functions_lib2 = functions_lib2.map(item => {
        return { ...item, module: "libnative2.so" };
    })
    var functions = [...functions_lib1,...functions_lib2];

    functions.forEach(function(func) {
        var moduleBase_lib1 = Module.findBaseAddress(func.module);
        var moduleBase_lib2 = Module.findBaseAddress(func.module);
        if ( moduleBase_lib1 && moduleBase_lib2) {
            var address = func.address
            Interceptor.attach(address, {
                onEnter: function(args) {
                    console.log(func.module + " function called at "  + func.address + " " + func.name);
                },
                onLeave: function(retval) {
                    console.log(func.module + " function returned at "+ func.address + " " + func.name);
                }
            });
        }
    });
}

调用顺序分析结果为:EOR → RC4 → Base64加密流程。

随机数函数劫持分析

libnative2.so中的加密函数使用rand()生成密钥:

_BYTE *__fastcall iusp9aVAyoMI(__int64 a1, size_t a2)
{
    size_t i;
    _BYTE *v4;
    v4 = malloc(a2);
    __memcpy_chk(v4, a1, a2, -1LL);
    for ( i = 0LL; i < a2; ++i )
        v4[i] ^= rand();
    return v4;
}

_BYTE *__fastcall SZ3pMtlDTA7Q(__int64 a1, int a2)
{
    // RC4密钥生成
    for ( i = 0; i < 16; ++i )
        *((_BYTE *)v20 + i) = rand();
}

libnative2.so的.init.array初始化随机种子:

void init(){
    srand(0x134DAD5u);
}

但实际解密失败,因为rand函数被libnative1.so劫持替换为恒定返回233的函数sub_1B140。

动态分析方案

方案一:Cheat Engine + Frida Hook

环境配置要点:

  • CE server版本匹配问题
  • ADB端口转发:adb forward tcp:52736 tcp:52736

Hook dlopen监控so加载时机:

Interceptor.attach(Module.findExportByName('libc.so', 'android_dlopen_ext'), {
    onEnter: function(args) {
        var libraryPath = Memory.readUtf8String(args[0]);
        console.log('android_dlopen_ext called to load library: ' + libraryPath);
        if (libraryPath.indexOf('native1.so') !== -1) {
            console.log('Pausing for 10 seconds before loading native1.so...');
            var sleep_string = Module.findExportByName('libc.so', 'sleep');
            var sleep_address = parseInt(sleep_string, 16);
            new NativeFunction(ptr(sleep_address), 'void', ['int'])(20);
        }
    },
    onLeave: function(retval) {
        console.log('android_dlopen_ext returned: ' + retval);
    }
});

CE扫描结果

方案二:Frida Hook mprotect(推荐)

监控内存保护修改:

function hook_mprotect(){
    var module = Process.findModuleByName('libnative2.so');
    console.log('libnative2.so loaded at: ' + module.base);

    Interceptor.attach(Module.findExportByName("libc.so", 'mprotect'), {
        onEnter: function(args) {
            this.addr = args[0];
            this.len = args[1];
            this.prot = args[2];
            console.log('mprotect called');
            console.log('Address: ' + this.addr,'Length: ' + this.len + 'Protection: ' + this.prot);
        }
    });
}

运行结果显示mprotect修改了0x6d55c3c3f8地址,偏移0x43f8对应rand_ptr位置。

CE验证结果

技术总结

本次分析涉及的关键技术点:

  • DEX静态反编译分析
  • JNI机制理解与动态Hook
  • Frida多场景应用
  • 函数劫持与GOT表修改检测
  • 加密算法识别(EOR、RC4、Base64)

通过综合运用静态分析和动态调试技术,成功解析了加密流程并定位了关键函数劫持点。

标签: Android逆向,Frida,JNI,hook,CE,libnative,加密分析

来自圈子: 面试专业户
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|云栈社区(YunPan.Plus) ( 苏ICP备2022046150号-2 )

GMT+8, 2025-11-5 21:24 , Processed in 0.072002 second(s), 37 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 CloudStack.

快速回复 返回顶部 返回列表