一、概述
有時候我們分析/逆向ELF檔案時,可能想直接運作ELF看看效果,同時又想捕獲ELF檔案用了哪些字元串、回連位址&端口、操作了哪些檔案等等特征資訊。這時我們可以巧妙的借用LD_PRELOAD,來實作一種簡易的hook libc庫函數方案來列印我們想要的特征資訊。
當然我們還可以用APK/ELF沙箱、HOOK等方式捕獲更加詳細的資訊,我們這裡不讨論,隻就LD_PRELOAD來簡單介紹。
二、LD_PRELOAD介紹
它允許你定義在程式運作前優先加載的動态連結庫,用于有選擇性的載入不同動态連結庫中的相同函數。使用這個環境變量,我們可以在主程式和其動态連結庫的中間加載别的動态連結庫,甚至覆寫正常的函數庫(重寫)
三、簡易方案
1:初始化
在初始化階段,主要工作:擷取原始目标函數位址、擷取配置檔案資訊等。設定_main()為構造函數,優于其它函數之前執行
// .init
__attribute__((constructor))
void _main()
{
init();
}
接着,用dlsym擷取目标libc函數原始位址。
typedef int (*PFN_connect)(int, const struct sockaddr *, socklen_t);
#define ADDFUNC( FUNCTYPE, SYMBOL ) (FUNCTYPE)getFuncAddr(SYMBOL)
void *LibcHelper::getFuncAddr( const char* symbol )
{
if ( symbol == NULL ) {
return NULL;
}
void * handle = dlsym( libcHandle, symbol );
if ( handle == NULL ) {
LOGPRINT( "[-]dlsym fail: ", ".s", symbol );
}
return handle;
}
int LibcHelper::init()
{
libcHandle = dlopen( "/system/lib/libc.so", RTLD_NOW|RTLD_GLOBAL );
if ( !libcHandle ) {
LOGPRINT( "Load libc fail.");
return -;
}
pfnConnect = ADDFUNC(PFN_connect, "connect" );
return ;
}
2:實作hook函數
這是hook函數的主體部分,實作一個跟libc導出函數一緻的函數。
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
LOGFUNC();
// 擷取原connect位址
PFN_connect org_connect = LibcHelper::getInstance().getConnect();
if ( org_connect != NULL ) {
// 先調用原connect函數
int ret = org_connect( sockfd, addr, addrlen );
struct sockaddr_in *addr_in = (struct sockaddr_in *)addr;
std::ostringstream s;
if ( addr_in->sin_family == ) {
struct sockaddr_un *sun = (struct sockaddr_un *)addr;
s << "unix://" << sun->sun_path;
}
else {
s << "ip://" << inet_ntoa(addr_in->sin_addr) << ":" << ntohs(addr_in->sin_port);
}
// 過濾無效目标,隻對特定程序輸出日志
if ( CheckUtils::checkShowInfo() ) {
// 記錄到日志檔案中
LOGPRINT( "connect",
"sockfd", sockfd,
"addr", addr,
"addrlen", addrlen,
ret );
}
return ret;
}
LOGPRINT( "connect fail");
return -;
}
3:測試
設定LD_PRELOAD環境變量的值為我們的so檔案
adb push libcc.so /data/local/tmp
export LD_PRELOAD=/data/local/tmp/libcc.so
在這個shell中執行目标ELF檔案,檢視日志檔案即可擷取詳細資訊
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiQ3chVEa0V3bT9CX5RXa2Fmcn9CXwczLcVmds92czlGZvwVP9EUTDZ0aRJkSwk0LcxGbpZ2LcBDM08CXlpXazRnbvZ2LcRlMMVDT2EWNvwFdu9mZvwVPwcVWspESaVjRHpVeWdkW1ljMkZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39jN2cTO1ATM4EDNwcDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
四、問題
1:這種方法暫時不能直接适用于靜态編譯的ELF檔案
一個idea:參考IDA識别靜态函數的方式 不知是否可行?
2:垃圾資訊太多,需要過濾
隻對特定的程序(process name)、路徑等才輸出到日志檔案