https://bbs.kanxue.com/thread-282657.htm
对init_array段调用的方法进行Hook
https://bbs.kanxue.com/thread-191092.htm
init_array原理简单说明
https://bbs.kanxue.com/thread-280135.htm
frida hook init_array自吐新解
init_array
的作用,以及是否可以没有 init_array
1. init_array
是什么?
init_array
是 ELF(Executable and Linkable Format)格式中的 初始化段(.init_array),用于存储在动态库(.so)或可执行文件(ELF)加载时自动执行的函数指针。
当程序启动时,动态链接器 (ld.so
) 会按照顺序执行 init_array
段中的所有函数,这通常用于全局变量初始化、动态 Hook、反调试等功能。
2. init_array
的作用
init_array
段的作用主要有:
-
全局构造函数初始化
- 在 C++ 代码中,全局对象的构造函数(
global constructor
)会自动加入init_array
,确保在main()
之前执行:#include <iostream> class Test { public:Test() { std::cout << "Test Constructor\n"; } }; Test t; // 这个构造函数会被放入 `.init_array` int main() {std::cout << "Main Function\n";return 0; }
- 执行顺序:
Test Constructor Main Function
- 在 C++ 代码中,全局对象的构造函数(
-
手动注册初始化函数
- 可以在
init_array
中手动添加函数:
等价于:#include <stdio.h> __attribute__((constructor)) void init_func() {printf("Init Function Executed\n"); }
void init_func() { printf("Init Function Executed\n"); } __attribute__((section(".init_array"))) void (*init_func_ptr)() = init_func;
- 可以在
-
反调试/保护代码执行
- 某些
so
文件会在init_array
中放入反调试代码,例如:
这样,调试器很难在void anti_debug() {if (ptrace(PTRACE_TRACEME, 0, 0, 0) == -1) {exit(1);} } __attribute__((section(".init_array"))) void (*ptr)() = anti_debug;
main()
之前附加到进程。
- 某些
3. init_array
是否可以没有?
可以没有,但会影响某些功能。
情况 1:C 语言程序(没有全局构造函数)
- 如果只用 C 语言,不使用
__attribute__((constructor))
,也不依赖init_array
进行初始化,那init_array
可以为空,不会影响程序运行。
情况 2:C++ 程序(有全局对象构造函数)
- 不能去掉
init_array
! - 因为 C++ 的全局对象 必须在
main()
之前构造,如果删除init_array
,global constructor
可能不会执行,导致程序异常。
情况 3:so
文件(共享库)
- 如果
so
里面不使用init_array
进行初始化(比如不涉及JNI_OnLoad
、全局构造、反调试等),可以去掉,但如果有全局变量初始化,可能会引起运行时错误。
4. 如何手动去掉 init_array
?
如果要移除 init_array
,可以:
-
重新编译时禁用
在gcc
/clang
编译时加上:gcc -nostartfiles -o no_init main.c
这样不会自动生成
init_array
。 -
使用
objcopy
修改 ELFobjcopy --remove-section .init_array target.elf
这样
init_array
就会被移除。 -
修改
ld
链接脚本- 可以修改
ld
链接脚本:
删除SECTIONS {.init_array : { KEEP(*(.init_array)) } }
KEEP(*(.init_array))
后init_array
就不会被包含。
- 可以修改
5. 如何动态 Hook init_array
?
如果想拦截 init_array
中的函数执行,可以:
- 使用
LD_PRELOAD
拦截:LD_PRELOAD=hook.so ./target
- 使用
Frida
动态 Hookvar baseAddr = Module.findBaseAddress("target.so"); var initArrayAddr = baseAddr.add(0x1234); // 具体偏移需要分析 Memory.scan(initArrayAddr, 0x100, "?? ?? ?? ??", {onMatch: function (addr, size) {console.log("Found init_array entry:", addr);} });
- 使用
gdb
断点调试gdb -q target break *0x400600 # init_array 入口地址 run
总结
是否可以没有 init_array | 影响 |
---|---|
纯 C 语言,没有全局变量构造 | 可以没有,不会影响 |
C++ 代码,有全局对象 | 不能去掉,否则 global constructor 不执行 |
共享库(.so),有 JNI_OnLoad 或 __attribute__((constructor)) | 不能去掉,否则初始化逻辑可能失效 |
共享库(.so),无全局初始化 | 可以没有,但需测试是否影响运行 |
结论:
- 普通 C 代码可以去掉
init_array
,但 C++ 代码、共享库一般不能去掉。 - 反调试、全局变量初始化、JNI 代码等 依赖
init_array
,去掉可能会导致程序异常。 - 逆向时可以 Hook
init_array
进行分析或修改初始化逻辑。
如果你是遇到 init_array
相关的问题,欢迎提供具体场景,我可以给出更详细的建议!🚀