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
相關的問題,歡迎提供具體場景,我可以給出更詳細的建議!🚀