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时改变,所以这非常脆弱,让我再说一遍,不是一个好主意.
我意识到这个问题相当古老,但shf301在这里有正确的想法.我要添加的唯一内容是在目标库上实现模式搜索.如果您有IDA或OllyDbg,则可以搜索该函数并查看该函数起始地址周围的二进制/十六进制数据.
在大多数情况下,会出现某种很少改变的二进制签名.签名可能包含可能在构建之间发生变化的通配符,但最终在搜索此模式时应至少有一次成功命中,除非构建之间发生了极大的变化(此时,您可以为此找出新的签名)特别版).
您实现二进制模式搜索的方式如下:
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')和哪些被忽略('?').然后,该函数将遍历已加载模块的内存空间,搜索匹配项.在某些情况下,可能会有多个匹配,在这种情况下,明智的做法是让您的签名更加明显,只有一个匹配.
同样,你需要在反汇编应用程序中进行初始二进制搜索,以便知道这个签名是什么,但是一旦你有了这个,那么这个方法应该比每次构建目标时手动查找函数偏移更好一些.希望这可以帮助.
如果引用的库没有显式导出其对象(类/函数),恐怕没有“安全”的方法可以做到这一点。因为您不知道所需的对象映射到代码内存中的位置。
但是,通过使用 RE 工具,您可以找到库中感兴趣的对象的偏移量,然后将其添加到任何已知的导出对象地址以获得“真实”内存位置。之后,准备一个函数原型等并转换为本地结构以供使用。