正确的方法将装配中的寄存器值从一个位置移动到另一个位置

时间:2014-01-05 09:32:12

标签: c++ assembly struct mov eip

我目前正遇到一些无法解释的寄存器问题,我无法想象我正在将寄存器从一个寄存器移动到另一个寄存器。

我正在尝试将EDX的值转换为test.myEDX,其中EDX的值通常总是test.myEDX EDX,但是EDX的一部分EDX 1}}值似乎是正确的,这是非常奇怪的某种Hi / Lo Bits问题我假设。

让我解释一下我想做什么 首先,我在内存中挂钩一个包含一些任意汇编代码的位置。

我通过在该位置放置硬件断点来挂钩它然后我可以及时看到该位置的所有寄存器值。
现在我做了一个简单的程序,每当cpu进入断点位置时,我将EDX值放入C ++中的结构中。
据我所知,在将EDX寄存器放入结构体时,除非将值放入结构中修改我测试的EAX并非如此,我通过移动测试EAX首先进入EDX,然后将EAX传递给结构,该结构应该与EDX具有相同的值,并且它仍然是错误的,也可能会使用EIP数据进入struct?我没有进一步说,在测试所有寄存器以找到哪一个未被使用时,怀疑这甚至是问题。

另外要考虑实际做任何操作,例如将PUSHAD放入我必须在C ++中生成的结构中,当前PUSHFD进入我的裸函数,我从以前做过这个东西就知道了裸机函数根本不修改任何寄存器,它们只是用来将当前asm代码扩展到更大的asm代码,而没有C ++在进入子程序时会添加的任何垃圾。

当我完成将EDX转储到struct中时,我还使用PUSH/POPPOPFD来恢复以前设置的寄存器值,这一切都在安全的环境中(只要我不使用{在我致电POPAD然后MOV EBX, EDX MOV ECX, EDX

之前,{1}}当然不正确)

我几乎不知道任何ASM,但从看过很多例子我从未看到寄存器像这样移动。 (显然复制一个寄存器没有任何意义,但即使是同一个寄存器也移到了两个不同的地址之后,我还没有看到它。)

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

但实际上我看到的是这样的东西(我认为这是我的问题,为什么它不起作用),(它不是,我仍然无能为力,我做错了)

EAX

除了现在AL丢失之外我没有看到任何差别,但我仍然认为我必须通过反复移动同一个寄存器的每一次多次移动到实际移动到原来的位置,愚蠢,但这就是我认为你必须做的事情我仍然认为这是这项工作的正确方法。
无论哪种方式,这仍然没有帮助我,我仍然得到错误的价值。

我认为这些选项的BL EDX注册了TEST或者JEEDX搞砸了我无法说清楚,我将此代码粘贴到OllyDBG中,似乎没有修改EDX谜,为什么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. } } 错误,也许地址更新它的值太慢了?因为它基于RAM速度而不与CPU速度同步(当然所有愚蠢的理论)。

无论如何,我在这里解释的一切都是代码。

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;
}

编辑:好的,我还没有解释我是如何测试这段代码的。

我不打算解释我是如何处理硬件断点的,我不希望这种方法在互联网上公开,出于我自己的安全原因。 但它的工作原理是调用另一个与系统驱动程序通信的DLL。

但这应该解释我是如何测试的。

我在这个DLL注入中运行了一个不同的线程。

{{1}}

2 个答案:

答案 0 :(得分:1)

我在代码中注意到的一些事情:

首先,我认为你真的不需要pushadpushfd你真的只使用一个寄存器,因为甚至不需要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

完全错了。您可以根据需要随时移动相同的寄存器,没有限制。 MIGHT 唯一需要考虑的是因为指令调度而寻求最大性能,但这是一个完全不同的问题,无论如何都不适用于此。

如此简单地做

 MOV EBX, EDX 
 MOV ECX, EDX 

没关系。

您正在使用ESI,但它尚未初始化。也许这是正确的,因为我真的不明白你的代码应该做什么。

您在这里使用变量test

MOV test.myEDX, EAX
MOV EAX, test.mySetEDX

但是这个变量甚至都没有声明,那么编译器应该从哪里知道它的地址呢?如果将它用作偏移量,则必须使用寄存器作为基址。

答案 1 :(得分:1)

老实说,这在很多层面都是错误的,我的意思是代码本身。 你要做的是强行进入C领域告诉编译器退出,你通过手写代码做自己的事情。这有时可能会派上用场,但你必须记住,你还会丢失一些C功能,特别是那些为你自动完成的事情,据我所知,微软的编译并不像你在内联时那样搞乱(不要搞定)我错了,MASM编译器很棒,但它与C编译器不兼容)

// 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

通常在ASM中没有范围这样的东西,就像你告诉它从测试中获取myEDX的值,这意味着获取指向值测试的指针,然后通过指针获取值。它可能有用,但由C编译器来帮助你。

通常你需要得到一个指针并使用它来移动数据,如

LEA ecx,test // get the address of test
MOV DWORD PTR[ecx],edx // use that address to move the data into the struct

现在你告诉它把这些数据放到结构中,但是这会做出一些假设,比如我们知道第一个元素就是我们想要将数据放入的元素(myEDX),我们假设DWORD是与C中的int相同(在Windows上通常是这样),但同样的假设也很糟糕!