Skip to main content

12月安卓折腾

·1284 words·7 mins

“你怎么搞安卓逆向连个能抓包的机器都没有”

呵呵,花了两天把吃灰的pixel掏出来折腾,难也不难就是折腾

感觉基础知识差好多啊

设备介绍 #

pixel3 安卓12 版本号:blueline-sp1a.210812.016.c1

啊还得是pixel官网就有资料终于不用导出找一加的包了

已安装环境:(点点点工程师)

暂时用magisk了 装不上apatch(有bug) 没时间搞kernelsu +=

reqable 证书

很普通的 frida 16

tips #

  1. 开机检查时区和时间不然大概率连不上网,也可以运行
adb shell settings put global captive_portal_https_url https://www.google.cn/generate_204
  1. fastboot刷magisk boot先用

fastboot boot .\magisk_patched-28000_KhnpS.img

再用

fastboot flash boot .\magisk_patched-28000_KhnpS.img

flash的修改是永久的,boot的修改只会作用于当前启动

  1. pixel3安卓版本12 sdk版本是31,在用安卓死丢丢的时候要改以下参数

在app目录下的build.gradle.kts修改 targetSdk = 31


android {
    namespace = "com.example.myapplication"
    compileSdk = 34         编译时使用的sdk版本

    defaultConfig {
        applicationId = "com.example.myapplication"
        minSdk = 31
        targetSdk = 31      目标机器使用的sdk版本这两个不一样的
        versionCode = 1
        versionName = "1.0"

抓到包了 #

Untitled

教程很详细但有几个注意事项

  1. 建议magisk一件安装reqable官方模块
  2. 扫码链接的时候注意网段选择,不然扫半天二维码扫不上很沙比
  3. 用户证书的安装位置在 设置->安全->加密与凭证->安装证书 or 用户凭证

Alt text

复现文章草稿 #

原文已经相当详细了

https://bbs.kanxue.com/thread-284444.htm#msg_header_h2_4

  • frida模板x2
  • 内存映射

“iRrL63tve+H72wjr/HHiwlVu5RZU9XDcI7A=”

native code 想要访问java VM的特性就需要调用JNI函数,调用JNI函数需要JNI interface pointer

JNI interface pointer是一个pointer to pointer,具体来说就是一个指针数组,这个数组保存着JNI函数的地址,包括: JNI 接口的组织方式类似于 C++ 虚拟函数表或 “COM 接口”?

所以,ida看到的table实际上是一个类似于虚表的东西,表嘛,有个表头+偏移然后引用就好了

其中一个libnative的 LOAD segment

LOAD:0000000000000288 ; ELF Symbol Table
LOAD:0000000000000288                 Elf64_Sym <0>
LOAD:00000000000002A0                 Elf64_Sym <aCxaFinalize - byte_608, 0x12, 0, 0, dword_0, 0> ; "__cxa_finalize"
LOAD:00000000000002B8                 Elf64_Sym <aCxaAtexit - byte_608, 0x12, 0, 0, dword_0, 0> ; "__cxa_atexit"
LOAD:00000000000002D0                 Elf64_Sym <aRegisterAtfork - byte_608, 0x12, 0, 0, dword_0, 0> ; "__register_atfork"
LOAD:00000000000002E8                 Elf64_Sym <aSrand - byte_608, 0x12, 0, 0, dword_0, 0> ; "srand"
LOAD:0000000000000300                 Elf64_Sym <aMalloc - byte_608, 0x12, 0, 0, dword_0, 0> ; "malloc"
LOAD:0000000000000318                 Elf64_Sym <aMemcpyChk - byte_608, 0x12, 0, 0, dword_0, 0> ; "__memcpy_chk"
LOAD:0000000000000330                 Elf64_Sym <aRand - byte_608, 0x12, 0, 0, dword_0, 0> ; "rand"
LOAD:0000000000000348                 Elf64_Sym <aStackChkFail - byte_608, 0x12, 0, 0, dword_0, 0> ; "__stack_chk_fail"
LOAD:0000000000000360                 Elf64_Sym <aFmod - byte_608, 0x12, 0, 0, dword_0, 0> ; "fmod"
LOAD:0000000000000378                 Elf64_Sym <aFree - byte_608, 0x12, 0, 0, dword_0, 0> ; "free"
LOAD:0000000000000390                 Elf64_Sym <aMar0ssaa7ze9 - byte_608, 0x12, 0, 0xD, MaR0Ssaa7zE9, \ ; "MaR0Ssaa7zE9"
LOAD:0000000000000390                            0x2B0>
LOAD:00000000000003A8                 Elf64_Sym <aBegy76tpey0a - byte_608, 0x12, 0, 0xD, beGy76TPEy0a, \ ; "beGy76TPEy0a"
LOAD:00000000000003A8                            0x274>
LOAD:00000000000003C0                 Elf64_Sym <aHtxbdoojl03v - byte_608, 0x12, 0, 0xD, htxBdOoJL03V, \ ; "htxBdOoJL03V"
LOAD:00000000000003C0                            0x270>
LOAD:00000000000003D8                 Elf64_Sym <aInit - byte_608, 0x12, 0, 0xD, init, 0x1C> ; "init"
LOAD:00000000000003F0                 Elf64_Sym <aIusp9avayomi - byte_608, 0x12, 0, 0xD, iusp9aVAyoMI, 0xB4> ; "iusp9aVAyoMI"
LOAD:0000000000000408                 Elf64_Sym <aEeg0quqiztro - byte_608, 0x12, 0, 0xD, eeg0QuqIZtRO, 0xD0> ; "eeg0QuqIZtRO"
LOAD:0000000000000420                 Elf64_Sym <aGenerateChaosS - byte_608, 0x12, 0, 0xD, \ ; "generate_chaos_sequence"
LOAD:0000000000000420                            generate_chaos_sequence, 0xA0>
LOAD:0000000000000438                 Elf64_Sym <aDoubleToByte - byte_608, 0x12, 0, 0xD, double_to_byte, \ ; "double_to_byte"
LOAD:0000000000000438                            0x3C>
LOAD:0000000000000450                 Elf64_Sym <aZwfl19atrzaj - byte_608, 0x12, 0, 0xD, zWfl19ATrZaj, 0xD0> ; "zWfl19ATrZaj"
LOAD:0000000000000468                 Elf64_Sym <aSz3pmtldta7q - byte_608, 0x12, 0, 0xD, SZ3pMtlDTA7Q, \ ; "SZ3pMtlDTA7Q"
LOAD:0000000000000468                            0x2BC>
LOAD:0000000000000480                 Elf64_Sym <aH4aqfgsoe2df - byte_608, 0x12, 0, 0xD, H4AQFGSOe2Df, \ ; "H4AQFGSOe2Df"
LOAD:0000000000000480                            0x284>
LOAD:0000000000000498                 Elf64_Sym <aUqhyy0f049n5 - byte_608, 0x12, 0, 0xD, UqhYy0F049n5, \ ; "UqhYy0F049n5"
LOAD:0000000000000498                            0x2FC>
LOAD:00000000000004B0                 Elf64_Sym <aT6aahj6zpxwi - byte_608, 0x12, 0, 0xD, T6AAHJ6ZpxWI, \ ; "T6AAHJ6ZpxWI"
LOAD:00000000000004B0                            0x2FC>
LOAD:00000000000004C8                 Elf64_Sym <aZkgafawta806 - byte_608, 0x12, 0, 0xD, zkGaFawtA806, \ ; "zkGaFawtA806"
LOAD:00000000000004C8                            0x2DC>
LOAD:00000000000004E0                 Elf64_Sym <aNcajgg9kvem4 - byte_608, 0x12, 0, 0xD, NCaJGG9kvem4, \ ; "NCaJGG9kvem4"
LOAD:00000000000004E0                            0x364>
LOAD:00000000000004F8                 Elf64_Sym <aAe7kmlpkuubb - byte_608, 0x12, 0, 0xD, aE7KMLpKuUbB, \ ; "aE7KMLpKuUbB"

frida:

1 版本需要一致(搞半天又忘看版本了)

2 download目录没有执行权限 传到 /data/local 就行

输入 n1ctf{111111}

多按几次出现

GetStringUTFChars called from:
0x7099fcf17c libnative1.so!0x1b17c
0x711f0d9a48 libart.so!art_quick_generic_jni_trampoline+0x98
0x711f0d0168 libart.so!art_quick_invoke_stub+0x228
0x711f256c50 libart.so!_ZN3art17InvokeWithVarArgsIP10_jmethodIDEENS_6JValueERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectT_St9__va_list+0x1b8
0x711f3b4b00 libart.so!_ZN3art3JNIILb0EE26CallNonvirtualObjectMethodEP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDz+0x15c
0x71055d07e0

进libNative1里面找 000000000001B17C 是在000000000001B148里面调用的

.text:000000000001B148
.text:000000000001B148 ; =============== S U B R O U T I N E =======================================
.text:000000000001B148
.text:000000000001B148 ; Attributes: bp-based frame fpd=0x20
.text:000000000001B148
.text:000000000001B148 ; __int64 __fastcall sub_1B148(__int64, __int64, __int64)
.text:000000000001B148 sub_1B148                               ; DATA XREF: .data:0000000000040F10↓o
.text:000000000001B148
.text:000000000001B148 var_20          = -0x20
.text:000000000001B148 var_18          = -0x18
.text:000000000001B148 var_10          = -0x10
.text:000000000001B148 var_s0          =  0
.text:000000000001B148 var_s8          =  8
.text:000000000001B148 var_s10         =  0x10
.text:000000000001B148 var_s18         =  0x18
.text:000000000001B148
.text:000000000001B148 ; __unwind {
.text:000000000001B148                 STP             X29, X30, [SP,#-0x20+var_20]!
.text:000000000001B14C                 STR             X23, [SP,#0x20+var_10]
.text:000000000001B150                 STP             X22, X21, [SP,#0x20+var_s0]
.text:000000000001B154                 STP             X20, X19, [SP,#0x20+var_s10]
.text:000000000001B158                 MOV             X29, SP
.text:000000000001B15C                 LDR             X8, [X0]
.text:000000000001B160                 MOV             X1, X2
.text:000000000001B164                 MOV             X2, XZR
.text:000000000001B168                 MOV             W21, #0x1094
.text:000000000001B16C                 MOV             X19, X0
.text:000000000001B170                 MOVK            W21, #0xB9F5,LSL#16
.text:000000000001B174                 LDR             X8, [X8,#0x548]
.text:000000000001B178                 BLR             X8
.text:000000000001B17C                 ADRL            X8, dword_40FA0

libhook

[Pixel 3::com.n1ctf2024.ezapk ]-> enc called with input: 111111
enc returned: 9VKJjnks
enc called with input: 111111
libnative2.so function called at 0x7102fa506c iusp9aVAyoMI
libnative2.so function returned at 0x7102fa506c iusp9aVAyoMI
libnative2.so function called at 0x7102fa52c0 SZ3pMtlDTA7Q
libnative2.so function returned at 0x7102fa52c0 SZ3pMtlDTA7Q
libnative2.so function called at 0x7102fa5ab0 UqhYy0F049n5
libnative2.so function returned at 0x7102fa5ab0 UqhYy0F049n5

调用顺序
iusp9aVAyoMI xor
SZ3pMtlDTA7Q rc4
UqhYy0F049rc base64

关于rand函数的替换为返回固定值233,这会影响xor

.init_array:000000000003F930                 ; ORG 0x3F930
.init_array:000000000003F930                 DCQ sub_3679C
.init_array:000000000003F938                 DCQ sub_36C6C
.init_array:000000000003F940                 DCQ sub_1B540 改rand

读 /proc/self/maps 最基本的so注入的操作

啊但是后边一堆结构体的偏移操作看不太懂了

  sub_1B6C4(filename);
  v0 = fopen(filename, "r");
  if ( v0 )
  {
    while ( fgets(filename, 4096, v0) )
    {
      if ( strstr(filename, "libnative2.so") )
      {
        v1 = strtok(filename, "-");
        v2 = strtoull(v1, 0LL, 16);
        goto LABEL_6;
      }
    }
  }
  v2 = 0LL;

ok然后让ai帮忙写个rc4

脚本存档 #

rc4脚本

def rc4_init(key: bytes) -> tuple:
    """
    初始化RC4的状态向量S和临时向量T。
    
    :param key: RC4秘钥,bytes类型
    :return: (S, T) 状态向量和临时向量
    """
    S = list(range(256))
    T = [key[i % len(key)] for i in range(256)]
    j = 0
    for i in range(256):
        j = (j + S[i] + T[i]) & 0xFF
        S[i], S[j] = S[j], S[i]
    return S, T

def rc4_cipher(data: bytes, S: list, T: list) -> bytes:
    """
    执行RC4加密或解密操作。
    
    :param data: 明文或密文,bytes类型
    :param S: 状态向量
    :param T: 临时向量
    :return: 加密后的密文或解密后的明文
    """
    result = bytearray()
    i = j = 0
    for byte in data:
        i = (i + 1) & 0xFF
        j = (j + S[i]) & 0xFF
        S[i], S[j] = S[j], S[i]
        t = (S[i] + S[j]) & 0xFF
        k = S[t]
        result.append(byte ^ k)
    return bytes(result)

def rc4_encrypt(plaintext: bytes, key: bytes) -> bytes:
    """
    RC4加密函数。
    
    :param plaintext: 明文,bytes类型
    :param key: 秘钥,bytes类型
    :return: 密文,bytes类型
    """
    S, _ = rc4_init(key)
    return rc4_cipher(plaintext, S, [])

def rc4_decrypt(ciphertext: bytes, key: bytes) -> bytes:
    """
    RC4解密函数。
    
    :param ciphertext: 密文,bytes类型
    :param key: 秘钥,bytes类型
    :return: 明文,bytes类型
    """
    # RC4是对称加密,加密和解密使用相同的函数
    S, _ = rc4_init(key)
    return rc4_cipher(ciphertext, S, [])

# 示例用法
if __name__ == "__main__":
    # key = b'example_key'
    # plaintext = bytes([233])*16
    #     enc = b"iRrL63tve+H72wjr/HHiwlVu5RZU9XDcI7A="
    # import base64 

    # dec1 = base64.b64decode(enc)
    # print(dec1)

# b'\x89\x1a\xcb\xeb{o{\xe1\xfb\xdb\x08\xeb\xfcq\xe2\xc2Un\xe5\x16T\xf5p\xdc#\xb0'
    key = b'\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9\xe9'
    encrypted = b'\x89\x1a\xcb\xeb{o{\xe1\xfb\xdb\x08\xeb\xfcq\xe2\xc2Un\xe5\x16T\xf5p\xdc#\xb0'

    # encrypted = rc4_encrypt(plaintext, key)
    # print(f"Encrypted: {encrypted}")

    decrypted = rc4_decrypt(encrypted, key)
    tmp = b''
    for i in decrypted:
        tmp += bytes([i^233])
    print(tmp)
    
    # b'MysT3r10us_C0d3_2024N1CTF!'

hook脚本

Java.perform(() => { // 进入Java上下文,确保可以访问Java类和方法
    const MainActivity = Java.use("com.n1ctf2024.ezapk.MainActivity"); // 使用Frida的Java.use方法来获取目标类的引用
    
    MainActivity.enc.implementation = function(input) { // 重写enc方法的实现
        console.log("enc called with input:", input); // 打印调用enc方法时的输入参数
        const result = this.enc(input); // 调用原始的enc方法,并保存返回值
        // startHook(); // 调用自定义的startHook函数
        startHooklib(); // 
        console.log("enc returned:", result); // 打印enc方法的返回值
        
        return result; // 返回enc方法的原始结果
    };
});

function startHook() { // 定义startHook函数
    const lib_art = Process.findModuleByName('libart.so'); // 使用Frida的Process模块找到libart.so模块
    const symbols = lib_art.enumerateSymbols(); // 枚举libart.so模块中的所有符号
    for (let symbol of symbols) { // 遍历所有符号
        var name = symbol.name; // 获取符号名称
        if (name.indexOf("art") >= 0) { // 如果符号名称包含"art"
            if ((name.indexOf("CheckJNI") == -1) && (name.indexOf("JNI") >= 0)) { // 并且不包含"CheckJNI"但包含"JNI"
                if (name.indexOf("GetStringUTFChars") >= 0) { // 并且包含"GetStringUTFChars"
                    console.log('start hook', symbol.name); // 打印开始hook的符号名称
                    Interceptor.attach(symbol.address, { // 使用Frida的Interceptor模块附加到符号地址
                        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()) // 打印返回值
                        }
                    })
                }
            }
        }
    }
}

function startHooklib(){

    // 枚举 libnative1.so 模块导出的所有函数
    var functions_lib1 = Module.enumerateExports("libnative1.so");
    // 将 functions_lib1 重新赋值为空数组(这行代码似乎是多余的,可能是逻辑错误)
    functions_lib1 = []
    // 枚举 libnative2.so 模块导出的所有函数
    var functions_lib2 = Module.enumerateExports("libnative2.so");
   
    // 为 libnative1.so 中的每个函数添加模块名称属性
    functions_lib1 = functions_lib1.map(item => {
        return { ...item, module: "libnative1.so" };    
    })

    // 为 libnative2.so 中的每个函数添加模块名称属性
    functions_lib2 = functions_lib2.map(item => {
        return { ...item, module: "libnative2.so" };    
    })

    // 合并两个模块的函数数组
    var functions = [...functions_lib1,...functions_lib2];

    // 遍历所有函数
    functions.forEach(function(func) {
        // 查找 libnative1.so 模块的基础地址
        var moduleBase_lib1 = Module.findBaseAddress(func.module);
        // 查找 libnative2.so 模块的基础地址(这里重复查找,可能是逻辑错误,应该根据模块名称区分查找)
        var moduleBase_lib2 = Module.findBaseAddress(func.module);
        // 如果两个模块的基础地址都找到了
        if ( moduleBase_lib1 && moduleBase_lib2) {
            // 获取函数的地址
            var address = func.address
            // 使用 Interceptor.attach 附加到函数地址上,以便监控函数的调用和返回
            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);
                }
            });
        } else {
            // 如果模块未找到,打印提示信息
            console.log("Module " + func.module + " not found!");
        }
    });
}

tips:看arm的可以先关注

BL BLX BLR BX 等函数调用的命令 其余的查字典

看起来手动注入不是个轻松活,能用框架还是用框架吧,纯c\cpp写着太难受了55