一种hook libc库函数的简易方案

一、概述

有时候我们分析/逆向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 -1;
    }

    pfnConnect = ADDFUNC(PFN_connect, "connect" );              
    return 0;
}

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 == 1 ) {
            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 -1;
}

3:测试

设置LD_PRELOAD环境变量的值为我们的so文件

adb push libcc.so /data/local/tmp
export LD_PRELOAD=/data/local/tmp/libcc.so

在这个shell中执行目标ELF文件,查看日志文件即可获取详细信息

这里写图片描述

四、问题

1:这种方法暂时不能直接适用于静态编译的ELF文件

一个idea:参考IDA识别静态函数的方式 不知是否可行?

2:垃圾信息太多,需要过滤

只对特定的进程(process name)、路径等才输出到日志文件
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在C语言中编写一个hook(钩子)可以实现对特定函数或代码块的拦截、修改或扩展功能。下面是一个简单的用C编写的hook的示例: ```c #include <stdio.h> #include <stdlib.h> #include <dlfcn.h> // 要拦截的函数的原始定义 void original_func() { printf("原始函数被调用\n"); } // hook函数,用于代替原始函数 void hooked_func() { printf("Hook函数被调用\n"); // 调用原始函数 original_func(); } int main() { // 获取要拦截的函数的地址 void *handle = dlopen(NULL, RTLD_LAZY); void (*original)() = dlsym(handle, "original_func"); // 修改原始函数为hook函数 *((void **)(&original)) = (void *)hooked_func; // 调用原始函数(实际上会调用hook函数) original(); dlclose(handle); return 0; } ``` 这个示例中的hook函数使用`dlopen()`和`dlsym()`获取了要拦截的函数的地址,然后通过修改函数指针的方式将原始函数修改为了hook函数。最终,当原始函数被调用时,实际上会执行hook函数。 需要注意的是,hook技术在一些场景中可以用来进行调试、代码注入或代码修改等操作,但在实际应用中要谨慎使用,以免引发软件安全性或稳定性问题。 ### 回答2: 在C语言中编写一个hook,可以通过拦截API调用来修改或者监控系统的行为。 首先,我们需要了解一个基本概念,即hook的实现依赖于动态链接库(DLL)注入技术。DLL注入是将自定义的DLL文件加载到目标进程中,并将DLL中的函数替换为自己定义的函数,从而改变程序的行为。 以下是一个简单的C语言hook的示例: ```c #include <windows.h> // 定义一个自定义函数指针类型,用于替换原始API函数 typedef int(WINAPI* ORIGINAL_FUNCTION)(int); // 定义一个指向原始API函数的指针 ORIGINAL_FUNCTION OriginalFunction; // 自定义的Hook函数,替代原始API函数的功能 int WINAPI HookedFunction(int arg) { // 在这里进行你想要的操作 // 调用原始API函数 return OriginalFunction(arg); } // DLL入口函数 BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // 获取原始API函数的地址 OriginalFunction = (ORIGINAL_FUNCTION)GetProcAddress(GetModuleHandle("target_module.dll"), "target_function"); // 替换原始API函数的地址为自定义Hook函数的地址 // 注意:这里需要禁用线程保护机制(DEP)和代码签名验证(Digital Signature Verification)才能进行内存写入操作。 DWORD oldProtect; VirtualProtect(OriginalFunction, sizeof(HookedFunction), PAGE_EXECUTE_READWRITE, &oldProtect); memcpy(OriginalFunction, &HookedFunction, sizeof(HookedFunction)); VirtualProtect(OriginalFunction, sizeof(HookedFunction), oldProtect, &oldProtect); break; case DLL_PROCESS_DETACH: // 恢复原始API函数的地址 DWORD oldProtect; VirtualProtect(OriginalFunction, sizeof(HookedFunction), PAGE_EXECUTE_READWRITE, &oldProtect); memcpy(OriginalFunction, &HookedFunction, sizeof(HookedFunction)); VirtualProtect(OriginalFunction, sizeof(HookedFunction), oldProtect, &oldProtect); break; } return TRUE; } ``` 以上代码以动态链接库的形式进行编写,通过DllMain函数在目标进程中加载并注入hook函数。等到目标进程调用原始API函数时,实际上会执行我们自定义的HookedFunction函数。在这个函数中,你可以做任何你想要的操作,比如修改函数参数,监控函数调用等等。 注意,由于hook操作需要对目标进程进行内存写入操作,因此在某些情况下可能导致目标进程奔溃或者产生其他不可预知的错误。所以在实际使用中,需要对目标进程进行充分的测试和评估,以确保hook操作的稳定性和安全性。 ### 回答3: 在 C 语言中,编写一个 hook(钩子)实际上是指在程序运行过程中拦截和处理特定事件的方式。下面是一个简单的示例,用 C 语言编写一个钩子函数: ```c #include <stdio.h> #include <stdlib.h> void hookFunction() { printf("Hook function called!\n"); // 在这里添加处理逻辑 // ... } void originalFunction() { printf("Original function called!\n"); // 在这里添加原始函数的逻辑 // ... } int main() { // 将钩子函数与原始函数绑定 void (*original)() = originalFunction; originalFunction = hookFunction; // 调用原始函数,实际上会触发钩子函数 originalFunction(); // 恢复原始函数 originalFunction = original; // 再次调用原始函数,不会触发钩子函数 originalFunction(); return 0; } ``` 在以上示例中,我们先定义了一个 hookFunction() 函数,它是我们的钩子函数。然后,我们定义了一个原始函数 originalFunction(),它是我们要绑定钩子的目标函数。 在 main() 函数中,我们将原始函数的地址赋给一个函数指针 original,然后将钩子函数 hookFunction() 绑定到 originalFunction。 当我们调用 originalFunction() 时,实际上会触发钩子函数 hookFunction(),从而输出 "Hook function called!"。 接着,我们将 original 函数指针重新赋给 originalFunction,从而恢复原始函数。 最后,再次调用 originalFunction(),输出 "Original function called!",此时不会触发钩子函数。 这就是一个简单的用 C 语言编写的钩子示例。根据实际需求,可以根据事件来编写不同的钩子函数和处理逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值