在DLL中调用未导出的函数

Nil*_*ert 10 c++ dll assembly function

我有一个加载DLL的程序,我需要调用它包含的一个非导出函数.通过在调试器中搜索或其他方式,我有什么办法可以做到这一点吗?在有人问之前,是的,我有原型和功能的东西.

shf*_*301 10

是的,至少有一点,但这不是一个好主意.

在C/C++中,所有函数指针都是内存中的地址.所以,如果你能以某种方式找到这个函数的地址,你可以调用它.

让我问一些问题,你怎么知道这个DLL包含这个函数?你有源代码吗?否则我不知道你怎么知道这个功能存在或者是否可以安全地通话.但是如果您有源代码,那么只需公开该函数.如果DLL编​​写器没有公开这个函数,他们永远不会期望你调用它,并且可以随时更改/删除实现.

除了警告,您可以找到函数地址,如果您有调试符号或MAP文件,您可以在DLL中找到偏移量.如果除了DLL之外没有任何东西,那么就无法知道DLL中该函数的位置 - 它不存储在DLL本身中.

一旦你有了偏移量,你就可以将其插入到代码中,如下所示:

const DWORD_PTR funcOffset = 0xDEADBEEF;
typedef void (*UnExportedFunc)();

....
void CallUnExportedFunc() {
     // This will get the DLL base address (which can vary)
     HMODULE hMod = GetModuleHandle("My.dll"); 
     // Calcualte the acutal address 
     DWORD_PTR funcAddress = (DWORD_PTR)hMod + funcOffset;
     // Cast the address to a function poniter
     UnExportedFunc func = (UnExportedFunc)funcAddress;
     // Call the function
     func();
}
Run Code Online (Sandbox Code Playgroud)

还要意识到这个函数的偏移会在每次重建DLL时改变,所以这非常脆弱,让我再说一遍,不是一个好主意.


Rec*_*als 8

我意识到这个问题相当古老,但shf301在这里有正确的想法.我要添加的唯一内容是在目标库上实现模式搜索.如果您有IDAOllyDbg,则可以搜索该函数并查看该函数起始地址周围的二进制/十六进制数据.

在大多数情况下,会出现某种很少改变的二进制签名.签名可能包含可能在构建之间发生变化的通配符,但最终在搜索此模式时应至少有一次成功命中,除非构建之间发生了极大的变化(此时,您可以为此找出新的签名)特别版).


您实现二进制模式搜索的方式如下:

bool bCompare(const PBYTE pData, const PBYTE bMask, const PCHAR szMask)
{
    for(;*szMask;++szMask,++pData,++bMask)
            if(*szMask=='x' && *pData!=*bMask)  
                    return 0;
    return (*szMask) == NULL;
}

DWORD FindPattern(DWORD dwAddress, DWORD dwLen, PBYTE bMask, PCHAR szMask)
{
    for(DWORD i=0; i<dwLen; i++)
            if (bCompare((PBYTE)(dwAddress+i),bMask,szMask))  
                    return (DWORD)(dwAddress+i);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)


用法示例:

typedef void (*UnExportedFunc)();

//...
void CallUnExportedFunc()
{
    // This will get the DLL base address (which can vary)
    HMODULE hMod = GetModuleHandleA( "My.dll" );

    // Get module info
    MODULEINFO modinfo = { NULL, };
    GetModuleInformation( GetCurrentProcess(), hMod, &modinfo, sizeof(modinfo) );

    // This will search the module for the address of a given signature
    DWORD dwAddress = FindPattern(
        hMod, modinfo.SizeOfImage,
        (PBYTE)"\xC7\x06\x00\x00\x00\x00\x89\x86\x00\x00\x00\x00\x89\x86",
        "xx????xx????xx"
    );

    // Calculate the acutal address 
    DWORD_PTR funcAddress = (DWORD_PTR)hMod + dwAddress;

    // Cast the address to a function poniter
    UnExportedFunc func = (UnExportedFunc)funcAddress;

    // Call the function
    func();
}
Run Code Online (Sandbox Code Playgroud)


这种方法的工作方式是通过传入已加载库的基址GetModuleHandle,指定要搜索的长度(以字节为单位),要搜索的二进制数据,以及指定二进制字符串的哪些字节有效的掩码(' x')和哪些被忽略('?').然后,该函数将遍历已加载模块的内存空间,搜索匹配项.在某些情况下,可能会有多个匹配,在这种情况下,明智的做法是让您的签名更加明显,只有一个匹配.

同样,你需要在反汇编应用程序中进行初始二进制搜索,以便知道这个签名是什么,但是一旦你有了这个,那么这个方法应该比每次构建目标手动查找函数偏移更好一些.希望这可以帮助.


Yee*_*Fei 0

如果引用的库没有显式导出其对象(类/函数),恐怕没有“安全”的方法可以做到这一点。因为您不知道所需的对象映射到代码内存中的位置。

但是,通过使用 RE 工具,您可以找到库中感兴趣的对象的偏移量,然后将其添加到任何已知的导出对象地址以获得“真实”内存位置。之后,准备一个函数原型等并转换为本地结构以供使用。