需求:
目前想改造一个带有窗口的exe文件,将其使用WPF来实现。
这个exe文件中最重要的一段内容就是一个函数(int f(int a,int b)),使用这个f函数来实现其核心的计算功能。
问题:
当前,没有exe文件的源码,因此无法使用C#简单地重写这个f函数。但我们可以考虑在C#代码中直接调用这个函数来进行计算。
困难:
exe没有导出这个f函数,因此在C#中无法直接使用DllImport来进行PInvoke调用。
方法:
1、在C#中通过Windows Api中的LoadLibrary把这个exe文件加载到内存。
2、使用IDA找到f函数在这个exe文件中的偏移量。
3、在C#中通过f函数的地址直接调用。
C语言演示代码:
#include <iostream>
int f(int a,int b)
{
return a + b;
}
int main()
{
std::cout << "Hello World! C++\n";
}
这里有一个f函数,还有一个main函数,其中我们要关心的计算函数为f。main是启动函数,我们不用关心。
这里f函数,并未导出。也就是说,在程序的函数导出表中,不存在f函数。其它程序无法使用常用的方式来调用它。
IDA查看函数位置
C#演示代码
internal class Program
{
//定义委托,对应于C语言中的函数原型
//int f(int a,int b)
public delegate int F(int a, int b);
static void Main(string[] args)
{
IntPtr hLib = LoadLibrary("C:\\Users\\Zmrbak\\source\\repos\\ConsoleApplication1\\Debug\\ConsoleApplication1.exe");
if (hLib == IntPtr.Zero)
{
Console.WriteLine("加载exe文件失败!");
return;
}
//函数地址
var offset = hLib + 0x12380;
int a = 10;
int b = 20;
var a1 = (F)Marshal.GetDelegateForFunctionPointer(offset, typeof(F));
if (a1 != null)
{
//调用函数
var c = a1(a, b);
Console.WriteLine(c);
}
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadLibrary(string lpFileName);
}
在C#的启动函数main中,首先将这个exe文件加载到内存,这会得到一个加载的地址。
接下来通过偏移量 0x12380,计算出这个函数在内存中的实际位置。
然后,通过这个地址来调用。
这里需要注意,调用的时候,需要用到委托(delegate ),它可以将一个函数地址转换成一个函数,从而按照C#的标准来调用。
执行结果:
30
C:\Users\Zmrbak\source\repos\ConsoleApplication1\ConsoleApp1\bin\Debug\net6.0\ConsoleApp1.exe (进程 14816)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
后记
另外,你还可以看到,程序运行的时候,只调用了C语言编写的f函数,C语言编写的main函数并未调用。从而达到了我们的目的,完成了我们的需求。