C++ 将程序集中的寄存器值从一个位置移动到另一个位置的正确方法
我目前有一些无法解释的寄存器问题,我猜我是在不正确地将寄存器从一个寄存器移动到另一个寄存器 我试图将C++ 将程序集中的寄存器值从一个位置移动到另一个位置的正确方法,c++,assembly,struct,mov,eip,C++,Assembly,Struct,Mov,Eip,我目前有一些无法解释的寄存器问题,我猜我是在不正确地将寄存器从一个寄存器移动到另一个寄存器 我试图将EDX的值放入test.myEDX中,它通常会将EDX的错误值放入test.myEDX中,但是EDX值的一部分似乎是正确的,这是非常奇怪的,我假设存在某种高/低位问题。 让我解释一下我想做什么。 首先,我在内存中钩住一个包含一些任意汇编代码的位置。 我通过在该位置放置一个硬件断点来钩住它,然后我可以及时看到该位置的所有寄存器值。 现在,我做了一个简单的程序,每次CPU进入那个断点位置,我把 EDX
EDX
的值放入test.myEDX
中,它通常会将EDX
的错误值放入test.myEDX
中,但是EDX
值的一部分似乎是正确的,这是非常奇怪的,我假设存在某种高/低位问题。让我解释一下我想做什么。
首先,我在内存中钩住一个包含一些任意汇编代码的位置。
我通过在该位置放置一个硬件断点来钩住它,然后我可以及时看到该位置的所有寄存器值。
现在,我做了一个简单的程序,每次CPU进入那个断点位置,我把
EDX
寄存器放入结构时,不应修改它,除非将值放入结构会修改我测试的EDX
,情况并非如此,我首先将EDX
移动到EAX
中,然后将EAX
传递到struct中进行测试,该struct应具有与EDX
相同的值,这仍然是错误的,将数据放入struct时可能也使用了EAX
?我没有进一步说明,在测试所有寄存器以确定哪一个未被使用时,我甚至怀疑这是问题所在。另一件事是要考虑把任何操作如把代码> EDX <代码>放入结构中,我必须在C++中做当前的代码> EIP<代码>进入我的裸函数,我知道从以前做过这些事情,裸函数根本不修改任何寄存器,它们被简单地用作将当前的EASP代码扩展到更大的ASM代码的方式,而不需要在进入子程序时添加C++的任何垃圾。 我还使用
PUSHAD
和PUSHFD
来恢复以前设置的寄存器值,当我将EDX
转储到struct中时,在调用POPFD
然后POPAD
之前,这一切都在一个安全的环境中(只要我没有错误地使用PUSH/POP
)
我几乎不知道任何ASM,但通过查看许多示例,我从未看到寄存器像这样移动。(复制一个寄存器显然没有任何意义,但即使是同一个寄存器,也会一个接一个地移动到两个不同的地址,我没有看到)
但事实上,我看到了类似的情况(我认为这是我的问题,为什么它不起作用),(它不起作用,我仍然不知道我做错了什么)
我看不出有什么不同,只是现在EAX
丢失了,但我仍然认为,在实际移动到原始位置之前,我必须将同一寄存器的每次多次移动重新路由到多个位置,这很愚蠢,但我认为你必须这样做,我仍然认为这是完成此工作的正确方法。不管怎样,这仍然没有帮助我,我仍然得到了错误的值。
我想要么是那些选项的
AL
BL
寄存器把EDX
搞砸了,要么是TEST
或者JE
把它搞砸了,我真的说不清,我把这个代码粘贴到OllyDBG中,看起来好像什么都没有修改过EDX
为什么EDX
是错误的,也许地址更新它的值太慢了?因为它基于RAM速度,而RAM速度与CPU速度不同步(当然是所有愚蠢的理论)
总之,我能解释的就是代码
struct TestStruct {
int myEDX;
int mySetEDX;
} test;
extern bool optionOne = false;
extern bool optionTwo = false;
DWORD gotoDumpBackAddress = 0x40012345;
void __declspec( naked ) dump(void) {
__asm {
PUSHAD
PUSHFD
XOR EAX, EAX //zero EAX, used below as AL (optionOne)
XOR EBX, EBX //zero EBX, used below as BL (optionTwo)
MOV AL, optionOne //optionOne set
MOV BL, optionTwo //optionTwo set
TEST EAX, EAX //Tests if optionOne equals == 0, then je will be equal.
je gotoOptionOne //Jumps if optionOne equals 0.
TEST EBX, EBX //Tests if optionTwo equals == 0, then je will be equal.
je gotoOptionTwo //Jumps if optionTwo equals 0.
gotoOptionOne:
//This the default case (improper value in EDX..., I could just use address at [ESI+0x2] which is old EDX, which is risky since it's delayed (outdated)
MOV DWORD PTR DS:[ESI+0x2], EDX //(normal operation)
MOV test.myEDX, EDX //Stores freshest EDX to test.myEDX (wrong EDX value)
JMP finish //Clear temporary used registers and go back to next asm code
//Special case. (works mostly properly)
//Thing is EDX gets updated very frequently, Causes no side-effect only because
//[ESI+0x2] gets updated in a different location as well a bit later to renew the value.
//So it's not even noticeable, but when I run this at it's peak speeds, you start to see the flickering effect, which isn't normal, if I run peak speeds without hook.
//I eliminated the problem that the hook could cause the flicker effect since after
//I call a empty naked Hook with just return address to same location and disable hook
//Then re-enable hook and repeat step above (no flicker occurs).
gotoOptionTwo:
//MOV DWORD PTR DS:[ESI+0x2], EDX //(normal operation), omitted
MOV EAX, DWORD PTR DS:[ESI+0x2] //Old EDX, but atleast it's correct.
MOV test.myEDX, EAX //Stores old EDX into struct test.myEDX
MOV EAX, test.mySetEDX //Replace old EDX with what I wish it should be.
MOV DWORD PTR DS:[ESI+0x2], EAX //nySetEDX into what EDX should of did.
JMP finish //Clear temporary used registers and go back to next asm code
finish:
POPFD
POPAD
JMP gotoDumpBackAddress //return to starting location before dump + 1.
}
}
编辑:好的,我还没有解释如何测试这段代码
我不打算解释如何使用硬件断点,出于我自己的安全考虑,我不希望这种方法在互联网上公开。但它通过调用另一个与系统驱动程序通信的DLL来工作 但这应该解释我是如何测试它的 在这个DLL注入中,我在不同的线程中运行
void diffThread() {
while(1) {
if(GetAsyncKeyState(VK_NUMPAD0)) {
optionOne != optionOne;
Sleep(1000);
}
if(GetAsyncKeyState(VK_NUMPAD1)) {
optionTwo != optionTwo;
Sleep(1000);
}
Sleep(10);
}
}
bool __stdcall DllMain(HINSTANCE hInst,DWORD uReason,void* lpReserved)
{
if(uReason == DLL_PROCESS_ATTACH)
{
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)&diffThread,0 ,NULL,NULL);
}
else if(uReason == DLL_PROCESS_DETACH)
{
ExitThread(0);
}
return true;
}
我在您的代码中注意到一些事情: 首先,我不认为您真的需要执行
pushad
和pushfd
您实际上只使用了一个寄存器,因为ebx
甚至不需要,而且eax
编译器也不认为需要保留
XOR EAX, EAX //zero EAX, used below as AL (optionOne)
MOV AL, optionOne //optionOne set
TEST EAX, EAX //Tests if optionOne equals == 0, then je will be equal.
je gotoOptionOne //Jumps if optionOne equals 0.
MOV AL, optionTwo //optionTwo set
TEST EAX, EAX //Tests if optionTwo equals == 0, then je will be equal.
je gotoOptionTwo //Jumps if optionTwo equals 0.
你的假设
MOV EBX, EDX
MOV EAX, EDX //Theory: in order to move EDX a second time into ECX, I must not move EDX directly into ECX.
MOV ECX, EAX
这完全是错误的。您可以随时移动同一寄存器,没有限制。可能考虑的唯一一件事是,由于指令调度,您需要寻求最大性能,但这是一个完全不同的问题,在这里并不适用
这么简单
MOV EBX, EDX
MOV ECX, EDX
好的
您正在使用ESI
,但它没有初始化。也许这是正确的,因为我不太明白你的代码应该做什么
您在此处使用变量test
MOV test.myEDX, EAX
MOV EAX, test.mySetEDX
但是这个变量甚至没有声明,所以编译器应该从哪里知道它的地址呢?如果要将其用作偏移量,则必须使用寄存器作为基址。好的,老实说,这在很多级别上都是错误的,我指的是代码本身。 您试图做的是强行进入C领域,告诉编译器退出,然后您自己写代码。这有时可能很方便,但你必须记住,你也失去了一些C特性,特别是那些自动为你完成的事情,据我所知,微软的编译软件不喜欢你在内联代码中胡闹(别误会,MASM编译器很棒,但它与C编译器配合不好) 那么回答你的问题, 我认为这里最大的问题是这条线
MOV test.myEDX, EDX
通常在ASM中,没有范围这样的东西,就像你告诉它给你一个
MOV test.myEDX, EAX
MOV EAX, test.mySetEDX
// doing this is not the best since the compiler may
// do some nasty stuff like aligning the struct that
// you have to take watch out for in asm
struct TestStruct {
int myEDX;
int mySetEDX;
} test;
extern bool optionOne = false;
extern bool optionTwo = false;
DWORD gotoDumpBackAddress = 0x40012345; // static memory address? this should fail like instantly, I dont really know what this is supposed to be
void __declspec( naked ) dump(void) {
__asm {
PUSHAD // you shouldn't normally push all registers only the ones you actually used!
PUSHFD
XOR EAX, EAX // this is how you zero out but honestly you can use EAX's low and high parts as 2 16bit regsiters instead of using 2 32bits for 2 bits of data
XOR EBX, EBX
MOV AL, optionOne //optionOne set
MOV BL, optionTwo //optionTwo set
TEST EAX, EAX // This check is meaning less because if gotoOptionTwo is false you move on regardless of gotoOpeionOne's value
je gotoOptionOne
TEST EBX, EBX // This test should always be true isn't it?
je gotoOptionTwo
gotoOptionOne:
// Assuming that ESI is coming from the system as you said that there is a
// breakpoint that invokes this code, do you even have access to that part of the memory?
MOV DWORD PTR DS:[ESI+0x2], EDX
MOV test.myEDX, EDX // this is just a C idom 'struct' this doesnt really exist in ASM
JMP finish
gotoOptionTwo:
MOV EAX, DWORD PTR DS:[ESI+0x2]
MOV test.myEDX, EAX
MOV EAX, test.mySetEDX
MOV DWORD PTR DS:[ESI+0x2], EAX
JMP finish
finish:
POPFD
POPAD
JMP gotoDumpBackAddress //return to starting location before dump + 1.
}
}
MOV test.myEDX, EDX
LEA ecx,test // get the address of test
MOV DWORD PTR[ecx],edx // use that address to move the data into the struct