我正在尝试了解DLL劫持。 如果我要创建一个包含两个函数的DLL文件;加减,说这叫做mathics.dll。 现在我创建一个加载mathics.dll的程序,这样我就可以添加&减去。
然后我用新的DLL文件劫持了原始的dll文件,这个文件可以繁殖和除法,但我还必须实现被劫持的DLL的两个原始函数,这些函数无论出于什么原因都是未知的。我怎样才能通过原始函数传递调用DLL使程序仍然可以加载原始的DLL文件。
即便如此,我如何将钩子注入到我的代码中,以便我可以使用我新发现的乘法和除法函数?
如果所有这些都是胡说八道而且我试图追究这个错误,请详细说明这个问题,因为我对它并不熟悉。
答案 0 :(得分:1)
基本思想很简单,但是细节可能会有些棘手。
因此,要使该程序隐式链接到新DLL而不是旧DLL,新DLL必须具有与旧DLL相同的文件名。在典型情况下,它也需要位于Windows加载程序可以找到它的位置,而不是旧的加载程序。
同时,新DLL需要能够链接到旧DLL。一种简单(但易碎)的方法是,它调用LoadLibrary
来加载旧的DLL,当这样做时,它指定旧DLL的完整路径。
我看到有时使用的另一种方法是在安装新DLL时查找并重命名旧DLL。然后,新DLL将以旧DLL的原始名称安装,因此程序将找到它,并且新DLL可以找到旧DLL,因为它知道它的新名称。
从那里开始,通常很简单的事情是让新的DLL加载旧的DLL,并用GetProcAddress
获取每个适用函数的地址,并具有一个通过该指针转发到旧函数的函数。一个功能。
例如:
Old_dll.cpp:
extern "C" {
__declspec(dllexport) int Add(int a, int b) { return a + b; }
__declspec(dllexport) int Sub(int a, int b) { return a - b; }
}
new_dll.cpp:
#include <windows.h>
int(*pAdd)(int a, int b) = NULL;
int(*pSub)(int a, int b) = NULL;
HMODULE mod = NULL;
extern "C" {
__declspec(dllexport) int Mul(int a, int b) { return a * b; }
__declspec(dllexport) int Div(int a, int b) { return a / b; }
__declspec(dllexport) int Add(int a, int b) { return pAdd(a, b); }
__declspec(dllexport) int Sub(int a, int b) { return pSub(a, b); }
BOOL WINAPI DllMain(_Out_ HINSTANCE hInstance, _In_ ULONG ulReason, LPVOID Reserved)
{
if (mod == NULL) {
mod = LoadLibrary("old_dll.dll");
typedef int(*arith)(int, int);
pAdd = (arith)GetProcAddress(mod, "Add");
pSub = (arith)GetProcAddress(mod, "Sub");
}
return true;
}
}
new_dll.h:
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
int Add(int, int);
int Sub(int, int);
int Mul(int, int);
int Div(int, int);
void init(void);
#ifdef __cplusplus
}
#endif
use_dll.cpp:
#include <iostream>
#include "new_dll.h"
int main() {
std::cout << Add(Mul(2, 3), Div(8, 4));
}
Makefile:
use_dll.exe: use_dll.cpp new_dll.lib
cl use_dll.cpp new_dll.lib
new_dll.lib: new_dll.cpp old_dll.dll
cl /LD new_dll.cpp
old_dll.dll: old_dll.cpp
cl /LD old_dll.cpp
因此,假设我没有跳过任何内容,则应该可以将它们放在目录中,键入nmake
,然后全部构建。完成后,您应该有几个DLL,一个可执行文件以及一些其他内容(.lib,.exp等)。如果运行可执行文件,则应打印出8
,该文件是通过使用new_dll获得的,而new_dll使用new_dll中的函数。
关于使旧的主程序使用您添加的功能(如果不是为了这样做而编写的),首先:要进行任何详细的介绍,都远远超出了此处的答案范围。真正简短的答案是,它将需要某种反射机制,以便程序可以找到可用的函数以及如何调用它们。
这对于诸如为某种脚本语言嵌入解释器,并允许用户使用脚本语言中可用的新功能编写新程序的操作非常实用。至于修改原始程序本身的工作方式,并且以某种方式知道以前从未听说过的新功能可以为它做一些有用的事情,并修改其代码以自动使用该功能...嗯,这可能是可行的,但肯定不是我可以在这里讨论的事情。
如果您确实想使用反射使功能可用(即使它们不是自动使用的),则可能需要研究COM。它为这类工作的大多数部分提供了机制(但要注意:使用COM可能会导致整个程序发生重大变化,远远超出了用一个DLL替换另一个DLL的简单任务)。