C 代码:用于运行时仿真与设备指纹生成

C 代码:用于运行时仿真与设备指纹生成

📚 前置知识

本配方涉及以下核心技术,建议先阅读相关章节:

  • IDA Pro 指南 - 从 IDA 中提取算法逻辑
  • C/C++ 基础 - 理解指针、内存操作等概念

在逆向工程中,直接使用 C/C++ 编写一些辅助工具或重现目标逻辑是一种非常高效的策略。这可以帮助我们脱离复杂的 App 环境,对核心算法进行独立的测试、Fuzzing 或仿真。


1. 运行时仿真 (Runtime Emulation)

当我们在 SO 文件中定位到一个关键的核心算法(如自定义加密、签名生成)后,如果该算法逻辑清晰且依赖较少,最好的方法就是将其逻辑用 C/C++ “翻译"一遍。

场景示例:重现一个简单的 XOR 加密算法

假设在 IDA Pro 中看到如下伪代码:

1
2
3
4
5
6
// Decompiled pseudo-code from IDA
void encrypt_data(char* data, int len) {
    for (int i = 0; i < len; ++i) {
        data[i] = (data[i] ^ 0x5A) + 5;
    }
}

重现与验证代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// emulate_encrypt.c
#include <stdio.h>
#include <string.h>
#include <stdint.h>

// Re-implementation of the encryption algorithm
void simulate_encrypt(char* data, size_t len) {
    for (size_t i = 0; i < len; ++i) {
        data[i] = (data[i] ^ 0x5A) + 5;
    }
}

// Corresponding decryption for our own testing
void simulate_decrypt(char* data, size_t len) {
    for (size_t i = 0; i < len; ++i) {
        data[i] = (data[i] - 5) ^ 0x5A;
    }
}

// Helper function: Print hexadecimal
void print_hex(const char* label, uint8_t* data, size_t len) {
    printf("%s: ", label);
    for (size_t i = 0; i < len; i++) {
        printf("%02x ", data[i]);
    }
    printf("\n");
}

int main() {
    char my_data[] = "this_is_a_test_message";
    size_t len = strlen(my_data);

    printf("=== XOR Encryption Algorithm Test ===\n\n");
    printf("Original: %s\n", my_data);
    print_hex("Original HEX", (uint8_t*)my_data, len);
    printf("\n");

    // Encrypt it
    simulate_encrypt(my_data, len);
    printf("After encryption:\n");
    print_hex("Encrypted HEX", (uint8_t*)my_data, len);
    printf("\n");

    // Decrypt it
    simulate_decrypt(my_data, len);
    printf("After decryption: %s\n", my_data);
    print_hex("Decrypted HEX", (uint8_t*)my_data, len);

    return 0;
}

编译与运行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 编译
gcc emulate_encrypt.c -o emulate

# 运行
./emulate

# 输出:
# === XOR Encryption Algorithm Test ===
#
# Original: this_is_a_test_message
# Original HEX: 74 68 69 73 5f 69 73 5f 61 5f 74 65 73 74 5f 6d ...
#
# After encryption:
# Encrypted HEX: 29 37 38 2e 0a 38 2e 0a 3e 0a 29 3a 2e 29 0a 32 ...
#
# After decryption: this_is_a_test_message

2. 设备指纹生成 (Device Fingerprint Generation)

许多 App 会通过读取 Android 系统的 build.prop 或其他系统属性来生成设备指纹,用于识别和跟踪设备。在进行自动化操作时,我们需要能够模拟这些指纹。

getprop 是 Android shell 中的一个命令,可以读取系统属性。我们也可以用 C 代码在 Native 层实现类似的功能,从而生成可以乱真的指纹数据。

场景示例:用 C 读取关键设备属性并生成 JSON

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// device_fingerprint.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

// A simple wrapper to execute a shell command and get its output
// In a real scenario, you might use direct system calls for better performance/stealth
char* get_prop(const char* key) {
    char command[256];
    snprintf(command, sizeof(command), "getprop %s", key);

    FILE* fp = popen(command, "r");
    if (fp == NULL) {
        return NULL;
    }

    char* line = malloc(256);
    if (fgets(line, 256, fp) == NULL) {
        free(line);
        pclose(fp);
        return NULL;
    }

    // Remove trailing newline
    line[strcspn(line, "\n")] = 0;
    pclose(fp);
    return line;
}

int main() {
    // List of properties we want to fetch
    const char* props_to_fetch[] = {
        "ro.product.brand",
        "ro.product.model",
        "ro.product.manufacturer",
        "ro.product.device",
        "ro.build.version.release",
        "ro.build.version.sdk",
        "ro.build.fingerprint",
        "ro.serialno",
        "ro.boot.serialno"
    };
    int num_props = sizeof(props_to_fetch) / sizeof(props_to_fetch[0]);

    printf("{\n");
    printf("  \"timestamp\": %ld,\n", time(NULL));
    printf("  \"device\": {\n");

    for (int i = 0; i < num_props; ++i) {
        char* value = get_prop(props_to_fetch[i]);
        if (value) {
            // Extract last part of property name for cleaner key
            const char* last_dot = strrchr(props_to_fetch[i], '.');
            const char* key = last_dot ? last_dot + 1 : props_to_fetch[i];

            printf("    \"%s\": \"%s\"", key, value);
            if (i < num_props - 1) {
                printf(",");
            }
            printf("\n");
            free(value);
        }
    }

    printf("  }\n");
    printf("}\n");

    return 0;
}

编译与运行

使用 Android NDK 进行交叉编译:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 设置 NDK 路径 (根据实际安装位置修改)
export NDK_PATH=~/Android/Sdk/ndk/25.1.8937393

# 编译 (arm64 架构)
$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang \
    device_fingerprint.c -o fingerprint

# 推送到设备
adb push fingerprint /data/local/tmp/
adb shell chmod +x /data/local/tmp/fingerprint

# 运行
adb shell /data/local/tmp/fingerprint

输出示例


3. 更复杂的算法仿真

示例:HMAC-SHA256 签名仿真

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// hmac_sign.c
#include <stdio.h>
#include <string.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>

void hmac_sha256(const char* key, const char* data, unsigned char* result) {
    unsigned int len = 32;
    HMAC(EVP_sha256(),
         key, strlen(key),
         (unsigned char*)data, strlen(data),
         result, &len);
}

void print_hex(unsigned char* data, size_t len) {
    for (size_t i = 0; i < len; i++) {
        printf("%02x", data[i]);
    }
    printf("\n");
}

int main() {
    const char* key = "secret_key_12345";
    const char* data = "user=test&timestamp=1234567890";

    unsigned char result[32];
    hmac_sha256(key, data, result);

    printf("HMAC-SHA256 Signature:\n");
    print_hex(result, 32);

    return 0;
}

编译

1
2
3
4
5
# 需要链接 OpenSSL
gcc hmac_sign.c -o hmac_sign -lssl -lcrypto

# 运行
./hmac_sign

4. 使用 Unidbg 进行 SO 仿真

对于复杂的 Native 算法,可以使用 Unidbg 进行完整仿真,而不需要手动翻译代码。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Java 代码示例 (Unidbg)
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.VM;

public class EmulatorExample {
    private AndroidEmulator emulator;
    private VM vm;
    private DalvikModule dm;

    public EmulatorExample() {
        // 创建模拟器
        emulator = AndroidEmulatorBuilder
            .for64Bit()
            .setProcessName("com.example.app")
            .build();

        // 创建虚拟机
        vm = emulator.createDalvikVM();

        // 加载 SO 文件
        dm = vm.loadLibrary("libnative-lib.so", false);

        // 调用 JNI_OnLoad
        dm.callJNI_OnLoad(emulator);
    }

    public String callEncrypt(String input) {
        // 调用 Native 方法
        // ... (具体实现取决于目标函数签名)
        return "";
    }
}

总结

使用 C/C++ 进行运行时仿真的优势:

  1. 独立测试: 脱离 App 环境,可以快速迭代测试
  2. 便于 Fuzzing: 可以对算法进行大规模模糊测试
  3. 性能优越: 相比在 App 内 Hook,独立运行效率更高
  4. 便于分享: 可以将仿真代码分享给团队其他成员

在实践中,建议结合动态分析(Frida Hook)和静态分析(IDA Pro)的结果,逐步完善仿真代码,直到输出与目标函数完全一致。


相关内容

如果这篇文章对你有帮助,请我喝杯咖啡吧~
+5 支付宝支付宝
+5 微信微信