我想从一个可执行文件中调用一个函数。到达该进程的唯一方法是在父进程中注入一个dll。我可以在父进程中注入一个dll,但是如何从子进程中调用一个函数呢?类似于
_asm
{
call/jmp address
}
不工作。我希望你能理解我的意思。
如果你在进程内部运行,你需要知道你要调用的函数从包含该函数的模块(exe)的基础上的偏移量。 然后,你只需要做一个函数指针并调用它。
// assuming the function you're calling returns void and takes 0 params
typedef void(__stdcall * voidf_t)();
// make sure func_offset is the offset of the function when the module is loaded
voidf_t func = (voidf_t) (((uint8_t *)GetModuleHandle('module_name')) + func_offset);
func(); // the function you located is called here
如果你知道函数的地址,你的解决方案将在32位系统上工作(64位系统不允许使用内联汇编),但你需要确保你正确地实现调用约定。 上面的代码使用GetModuleHandle来解析你想调用其函数的模块的当前加载库。
一旦你把你的模块注入到正在运行的进程中,ASLR并不是一个真正的问题,因为你可以直接向windows询问包含你想调用的代码的模块的基数。 如果你想找到运行当前进程的exe的基础,你可以调用GetModuleHandle,参数为NULL。 如果你确信函数的偏移量不会改变,你可以在反汇编器或其他工具中找到偏移量后,硬编码你想调用的函数的偏移量。 假设包含函数的exe没有被改变,那么这个偏移量将是不变的。
正如注释中提到的,函数typedef中的调用约定很重要,确保它与你要调用的函数的调用约定一致。
要调用一个函数,你需要一个地址或一个中断号。 地址被加载到程序计数器寄存器中,并转移执行。 有些处理器允许 "软件中断",即程序执行一条特殊指令,调用软件中断。 这是执行功能的基础。
常见的可执行文件有两种形式。 绝对寻址和相对寻址(或位置独立代码,PIC)。 在绝对寻址中,函数是在硬编码的地址上。 函数不会移动。 通常用在嵌入式系统中。
在相对寻址模型中,地址与程序计数器寄存器中的值相对。 例如,你的函数可能在1024字节之外,所以编译器会发出一个相对的分支指令,地址为1024字节(远离)。
许多操作系统在每次调用时,都会在不同的地方加载程序,这意味着你的可执行文件可能在地址1000处启动,而下一次在地址127654处启动。 这意味着你的可执行文件可能从地址1000开始,而下一次则从地址127654开始。 在这些操作系统中。不能保证每次都能在同一地点启动可执行程序.
在你的程序中执行函数很容易。 链接器决定所有函数的位置,并决定如何执行它们;是使用绝对寻址,PIC还是混合使用。
有了上述知识,在另一个程序中执行函数就会出现问题。
大多数可执行文件不包含任何关于其函数位置的信息,所以你需要知道它在哪里。 你还需要知道该函数是绝对寻址还是PIC。 你还需要知道当你需要它时,该函数是否在内存中,或者操作系统是否有 页状 的功能到硬盘上。
知道函数的位置是必要的。 但是,如果操作系统没有加载可执行文件,那么这个位置就没有用了。 在你调用另一个可执行文件中的函数之前,你需要知道当调用被执行时,它是否存在于内存中。
最后,你需要知道外部函数使用的协议。 例如,值是通过寄存器传递的吗? 它们是在堆栈上吗? 它们是通过指针(地址)传递的吗?
操作系统(OS)已经发展到允许动态共享函数。 这些函数存在于动态链接库(DLL)或共享库(.SO)中。 你的程序告诉操作系统将库加载到内存中,然后你告诉操作系统执行函数,给它函数的名称。
需要注意的是,你想要的函数必须在一个库中。 如果可执行文件没有使用共享库,或者你需要的函数不在库中,那么你的任务就比较困难。