一、问题来源
https://www.52pojie.cn/thread-2108051-1-1.html
动态修改段数据,实现函数不导出调用。
二、动态库1
typedef const char* (*EncryptFunc)(const char*, const char*);
// 不导出,但我们需要让加密模块能找到它
// 我们可以放置一个特殊的标记(例如一个已知的字符串)来定位
struct CallbackHolder {
EncryptFunc callback; // 目标指针
char marker[16]; // 标记 "CRYPTO_CB_MARK"
} g_holder = { nullptr, "CRYPTO_CB_MARK" };
void DoHttpRequest(const char* url, const char* body)
{
if (g_holder.callback)
{
const char* headers = g_holder.callback(url, body);
printf("[Network] Encrypted: %s\n", headers);
}else {
printf("[Network] No callback.\n");
}
}
extern "C" __declspec(dllexport) void SendRequest(const char* url, const char* body)
{
DoHttpRequest(url, body);
}
三、动态库2
typedef const char* (*EncryptFunc)(const char*, const char*);
const char* MyEncrypt(const char* url, const char* body)
{
static char buf[256];
sprintf_s(buf, "Encrypted[adv]:%s-%s", url, body);
return buf;
}
// 在目标模块的 data 段中搜索标记字符串,然后修改紧邻的 callback 指针
void RegisterToNetworkByScan()
{
HMODULE hNetwork = GetModuleHandleA("MFCLibrary1.dll");
if (!hNetwork)
return;
// 获取模块的 DOS 头和 NT 头
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hNetwork;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((BYTE*)hNetwork + pDos->e_lfanew);
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
// 遍历所有节,找到 .data 节
for (WORD i = 0; i < pNt->FileHeader.NumberOfSections; ++i)
{
if (memcmp(pSection->Name, ".data", 5) == 0)
{
DWORD dataRVA = pSection->VirtualAddress;
DWORD dataSize = pSection->Misc.VirtualSize;
BYTE* pData = (BYTE*)hNetwork + dataRVA;
// 在 .data 段中搜索标记 "CRYPTO_CB_MARK"
const char* marker = "CRYPTO_CB_MARK";
size_t markerLen = strlen(marker);
for (DWORD offset = 0; offset < dataSize - markerLen; ++offset)
{
if (memcmp(pData + offset, marker, markerLen) == 0)
{
// 找到了标记,回调指针紧邻在标记之前(结构体定义中 callback 在前)
// 结构体: { EncryptFunc callback; char marker[16]; }
// 所以 callback 的地址 = pData + offset - sizeof(EncryptFunc)
EncryptFunc* pCallback = (EncryptFunc*)(pData + offset - sizeof(EncryptFunc));
// 修改内存保护属性
DWORD oldProtect;
VirtualProtect(pCallback, sizeof(EncryptFunc), PAGE_READWRITE, &oldProtect);
*pCallback = MyEncrypt;
VirtualProtect(pCallback, sizeof(EncryptFunc), oldProtect, &oldProtect);
printf("[Crypto] Found marker at offset %d, callback set.\n", offset);
return;
}
}
}
++pSection;
}
printf("[Crypto] Marker not found.\n");
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID) {
if (reason == DLL_PROCESS_ATTACH) {
RegisterToNetworkByScan();
}
return TRUE;
}
四、应用程序
typedef void (*SendRequestFunc)(const char*, const char*);
HMODULE hNetwork = LoadLibraryA("MFCLibrary1.dll");
HMODULE hCrypto = LoadLibraryA("MFCLibrary2.dll");
// 获取网络模块的发送函数
SendRequestFunc SendRequest = (SendRequestFunc)GetProcAddress(hNetwork, "SendRequest");
if (SendRequest)
{
SendRequest("https://api.example.com/login", "username=test");
}
FreeLibrary(hCrypto);
FreeLibrary(hNetwork);
五、结论
该方法可实现不导出DLL函数,在运行时实现调用。动态修改的函数地址可经过二次加密,应用于防破解或保护算法模块等场景。