如何在C/C++(Windows)中创建函数的文字副本?[关闭]

yruzcnhs  于 7个月前  发布在  C/C++
关注(0)|答案(7)|浏览(100)

已关闭。此问题需要details or clarity。目前不接受回答。
**要改进此问题吗?**通过editing this post添加详细信息并阐明问题。

9年前关闭。
Improve this question
我在想是否有任何方法可以直接复制一个函数。例如,如果我有一个函数foo(),它输出“Hello world!“,我想把这个行为复制到一个新的函数bar()中。如果我只是做bar = &foo,那么如果foo()在运行时改变,bar()也会改变(因为它只是一个指针)。也许CopyMemory()可以帮助我?
我说“在运行时更改”是因为我的代码挂钩foo()并将其替换为另一个函数,让我们称之为fooReplaced();所以如果现在我调用foo(),则会调用fooReplaced()。因此我需要保留对原始函数的引用,这就是为什么我需要在挂钩之前将foo()复制到bar()

我想现在很清楚我所说的“在运行时更改”的意思,不知道为什么这个问题是“[暂停]"

先谢了。

r1zk6ea1

r1zk6ea11#

你不能只让bar()调用foo()吗?这样不管foo做什么,bar也会做同样的事情

void bar() {
    foo();
}

字符串
你应该给予更多关于 * 为什么 * 你想这样做的信息,这样也许我们可以建议一种方法来实现它在一个明智的方式。
编辑:好了,现在你的编辑表明你正在使用钩子,改变了显着的东西。
我相信,如果你像上面那样 Package ,那么bar将沿着更新它的效果,否则当你在运行时用钩子修改一些东西时,就不会有效果了。
为了让函数保持foo的原始形式,可以这样设置它吗

void unchangedFoo() {
    //...
}

void foo() {
    unchangedFoo();
}


然后挂接到foo,但仍然保持原来的未更改的Foo()不变?

m4pnthwp

m4pnthwp2#

看起来,在钩住一个函数(用你自己的实现替换它)之后,你希望能够调用原来的函数。就像子类调用子类覆盖的方法的基类版本一样。我的回答的其余部分是基于这种假设的理解...
这个功能应该由你的钩子技术提供。一般来说,除了前序中的几条指令被跳转覆盖外,函数是保持不变的。
另一个常见的假设是,函数体不包含任何进入序言的向后分支,这几乎是普遍正确的。
因此,运行原始代码包括执行被覆盖的指令(钩子子程应该保存这些指令,如果没有其他原因,只是为了在钩子移除过程中将它们放回去),然后分支到函数的其余部分。
这是高度依赖指令集的,因为必须区分指令边界,这在某些架构(具有固定的指令大小)上非常容易,但在其他广泛使用的平台(例如Intel x86及其衍生产品)上就不那么简单了。
因为函数体的其余部分存在于原始位置,所以相对跳转,即使是跳转到其他函数,仍然有效。只有前导中的相对寻址必须调整。通常前导只是几个PUSH指令,用于保存寄存器。

h9a6wy2h

h9a6wy2h3#

有趣的问题!
答案是“可能”,但你需要注意编译器/平台的任何细节问题。
首先,如果你想在内存中复制整个函数,你需要两件事:
a:你必须对你的可执行代码有“读访问”权限。大多数操作系统通过DEP或类似的东西来阻止这一点。
B:您必须能够确定函数在内存中的位置。您知道入口点(&foo),但您能保证编译器/链接器/加载器不会将其分散,或重新排列(==优化)它,以便将一个公共尾部替换为另一段代码的JMP吗?
假设代码是连续的,如何找到结尾?解释机器代码指令,寻找RET,也许-或者查看符号表以确定下一个函数的地址。两者都可以工作,但两者都可能有缺陷......
最后,一旦你得到了你的副本-你真的能执行它吗?你又有了DEP的问题,但你也有二进制代码可能不是位置无关的问题。而不是说JMP to PC + n,指令可能有JMP to absolute address N的形式(其中N是你复制的空间中的地址.)
static数据这样的东西还有一个额外的复杂性。你希望你的函数的副本有不同的静态数据到原始的,如果不是,那怎么办?
总结:这在很多层面上都是一场噩梦。我会尝试找到一种替代方法!

wpx232ag

wpx232ag4#

我不是很擅长C++,所以这可能是错误的。
你就不能这样做吗

int function(){
    otherFunction();
}

字符串

ctehm74n

ctehm74n5#

函数通常不应该在运行时改变,至少在C/C++中不应该。
也许你需要重新思考你处理手头任务的方式。
FYI:如果没有更多关于你为什么要复制这段代码,或者函数可能会如何改变的细节,你可能不会得到你喜欢的答案。

snz8szmq

snz8szmq6#

据我所知,你想挂接一个函数,并能够调用原来的。
在C++中没有这样的东西。
但是,对于给定的平台,您可以使用平台特定的功能。例如,在Windows中,您可以使用**Microsoft Detours**。

okxuctiv

okxuctiv7#

在C / C++中,foo是一个指针,指向函数foo()的指针。理论上,当然这取决于代码是如何编写的,foo可以在运行时更改为另一个地址,当foo()被调用时调用另一个函数,但foo的原始机器码应该在相同的内存空间和地址中保持不变。
事实上,试图在运行时更改代码不仅是一件愚蠢的事情,而且还会在尝试写入代码区域时引发分段错误异常。
即使你在编译后知道foo在内存中的大小,这是高度特定于编译器和平台的,如果你把它复制到另一个地址,它可能不会工作,因为绝对和相对寻址不匹配,所以这是完全没有问题的。

相关问题