Assembly 在汇编和c+中弄乱堆栈+;
我想做以下工作: 我有一个不是我的函数(这里真的没关系,只是说我对它没有控制权),我想修补它,以便它调用我的函数,保留参数列表(跳跃不是选项) 我试图做的是,将堆栈指针放在调用该函数之前的位置,然后调用我的堆栈指针(就像返回并再次执行相同的操作,但使用不同的函数)。这无法直接工作,因为堆栈变得混乱。我相信当我打电话时,它会取代回信地址。所以,我做了一个步骤来保留返回地址,并将其保存在一个全局变量中,它可以工作,但这不好,因为我希望它抵制递归,你知道我的意思。不管怎么说,我是个装配新手,所以我来这里 请不要告诉我已经制作了软件来做这件事,因为我想按自己的方式做 当然,此代码必须独立于编译器和优化 我的代码(如果大于可接受的值,请告诉我如何发布): 打印(并且必须): 论点100 69 1337 666 编辑: 我正在按照Vlad建议测试的代码(仍然不起作用)Assembly 在汇编和c+中弄乱堆栈+;,assembly,hook,Assembly,Hook,我想做以下工作: 我有一个不是我的函数(这里真的没关系,只是说我对它没有控制权),我想修补它,以便它调用我的函数,保留参数列表(跳跃不是选项) 我试图做的是,将堆栈指针放在调用该函数之前的位置,然后调用我的堆栈指针(就像返回并再次执行相同的操作,但使用不同的函数)。这无法直接工作,因为堆栈变得混乱。我相信当我打电话时,它会取代回信地址。所以,我做了一个步骤来保留返回地址,并将其保存在一个全局变量中,它可以工作,但这不好,因为我希望它抵制递归,你知道我的意思。不管怎么说,我是个装配新手,所以我来这
您有一次
添加esp,8次和一次添加esp,16次。其中一个肯定是错的
编辑:
哦,我明白了,在添加esp之后,8
您必须从堆栈中删除ebp
之前推送的2条指令,并返回地址
在[ebp+4]处,必须有呼叫\u tmain
的回音地址
编辑2:
您可以通过以下方式分配“内部”变量:
call next
dd 0
next:
pop eax
mov [eax], yourinfo
但仍然不清楚为什么我们需要保存这些价值
编辑3:(删除,是错误的)
编辑4:
另一个想法是:
__declspec( naked ) void patch()
{
_asm
{
call next
// here we temporarily save the arguments
dd 0
dd 0
dd 0
dd 0
next:
pop eax
// eax points to the first dd
// now store the args
pop edx
mov [eax], edx
pop edx
mov [eax+4], edx
pop edx
mov [eax+8], edx
pop edx
mov [eax+12], edx
// now we can push the value
mov edx, [ebp+4]
push edx
// now, push the args again
mov edx, [eax+12]
push edx
mov edx, [eax+8]
push edx
mov edx, [eax+4]
push edx
mov edx, [eax]
push edx
// now continue with the old code
// --------------------------------
// restore the arguments
push ebp
mov ebp, esp
// Make that the stack becomes as it were before the real function was called
add esp, 8
// Calls our receiver
call receiver
mov esp, ebp
pop ebp
// ----------------------------
pop edx
mov [ebp+4], edx
ret
}
}
这个解决方案幸存于递归,但不是2个不同线程的同步执行。
< P>我从来没有用C++来处理低级的东西,所以我不会去讨论你的例子的细节,但是一般来说,如果你想拦截一个调用并有逻辑支持递归,你有两个选项:要么复制整个堆栈框架(参数)并使用新副本调用“钩住”原始文件,或者如果这不可行,则维护您自己的小堆栈以保存原始返回值(例如,作为根链接列表)在基于TLS的数据结构中。在正常执行期间,函数的操作数按相反顺序推送到堆栈上。当执行调用时,处理器首先推送EIP(或CS/IP)注册到堆栈上。这是返回地址。当执行到达要替换的函数时,股票的外观如下:
Return address 1
Operand 1
Operand 2
Operand 2
此时,您将调用自己的函数,该函数将具有如下堆栈:
Return address 2
Return address 1
Operand 1
Operand 2
Operand 3
RET 3
您的函数需要知道堆栈上有一个额外的DWORD,因为它正在执行您想要的操作。如果您也编写了替换函数程序集,这很容易处理,只要在引用时添加4即可,尤其是在函数中调用RET时,将弹出第一个返回地址,执行将返回函数您正在替换。堆栈将再次为:
Return address 1
Operand 1
Operand 2
Operand 3
在此函数中调用RET将再次弹出堆栈中的返回地址,并将控件返回给调用函数。这会使操作数仍留在堆栈上,从而导致损坏。我建议使用如下函数操作数调用RET:
Return address 2
Return address 1
Operand 1
Operand 2
Operand 3
RET 3
这将从堆栈中弹出3(在我的示例中),或者不管有多少操作数。以下是一些您可能会发现有用的参考:
以下代码摘录已通过mingw-g++检查,但应在VC++中使用,只需稍作修改。完整源代码可从Launchpad获得:
我们可以安全地保存特定于调用的数据的唯一方法是将其存储在堆栈上。一种方法是旋转堆栈的一部分
patch.s摘录(patchfun rollstack):
我们在这里省略了ebp
。如果我们添加它,我们将需要使用8字节的暂存空间,并保存和恢复ebp
以及eip
。请注意,当我们恢复返回指针时,我们会覆盖a
参数。为了避免再次旋转堆栈
另一种方法是让被调用方知道堆栈上的额外数据并忽略它
patch.s(patchfun ignorepointers):
receiver.cc:
void receiver(const void *epb, const void *eip, int a,int b,int c,int d)
{
printf("Arguments %d %d %d %d\n",a,b,c,d);
}
这里我包括了epb,如果您将其从asm中删除,剩下的就是呼叫
和ret
,接收方只需要接受并忽略eip
当然,所有这些主要是为了好玩和好奇。与简单的解决方案相比,没有什么主要优势:
void patch(int a,int b,int c,int d)
{
receiver(a,b,c,d);
}
生成的程序集将比我们的堆栈卷短,但需要多16个字节的堆栈,因为这些值被复制到patch()
堆栈帧下方的新区域
(实际上,gcc生成的asm在堆栈上分配了28个字节,尽管它只使用了16个。我不知道为什么。可能额外的12个字节是某些堆栈破坏保护方案的一部分。)如果在asm中没有显式调用,你的函数很可能是由编译器内联的。这就是为什么没有调用它。谢谢你让我觉得不好,因为我没有这样想:D.开玩笑。谢谢,这很有意义。你的函数真的是空的吗?如果是,它可能短于5个字节,因此你的补丁可能会覆盖下一个函数。我不知道想一想,但我猜它有处理帧指针的通用代码。无论如何,我们可以用nops填充它。我想知道的是如何从代码中删除“helper”变量。(我不知道我是否清楚,但代码按预期工作。只是我不想使用“helper”变量)这段代码不会独立于编译器和优化,因为它
void receiver(const void *epb, const void *eip, int a,int b,int c,int d)
{
printf("Arguments %d %d %d %d\n",a,b,c,d);
}
void patch(int a,int b,int c,int d)
{
receiver(a,b,c,d);
}