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<代码>值放入C++中的一个结构中。 就我所知,当我将
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