2

我目前有一些无法解释的寄存器问题,我无法猜测我正在将寄存器从一个寄存器不正确地移动到另一个寄存器。

我试图获得EDXinto a的值,test.myEDX它通常总是将错误的值EDXinto test.myEDX,但该EDX值的一部分似乎是正确的,这对于我假设的某种高/低位问题非常奇怪。

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

我通过在该位置放置一个硬件断点来挂钩它,然后我可以及时看到该位置的所有寄存器值。
现在我做了一个简单的程序,每次 cpu 进入断点位置时,我都会将EDX值放入 C++ 中的结构中。
据我所知,EDX当我将寄存器放入结构中时,没有任何东西应该修改寄存器,除非将值放入结构中修改了EDX我测试的情况并非如此,我通过将第EDX一个移入EAX然后传递EAX到应该有的结构中进行测试相同的值EDX,但它仍然可能是错误的EAX将数据放入结构时也使用?那时我没有进一步测试所有寄存器以找出未使用的寄存器,怀疑这甚至是问题所在。

另一件要考虑的事情是实际执行任何操作,例如将EDX我必须在 C++ 中进行的当前EIP输入到我的裸函数中的结构中,我从以前做这些事情中知道裸函数根本不会修改任何寄存器,他们只是用作将 EIP 上的当前 asm 代码扩展为更大的 asm 代码的一种方式,而没有 C++ 在进入子例程时会添加的任何垃圾。

当我完成转储到 struct时,我还使用PUSHADandPUSHFD恢复以前设置的寄存器值EDXPUSH/POPPOPFDPOPAD

我几乎不知道任何 ASM,但通过查看许多示例,我从未见过寄存器像这样移动。(显然,复制一个寄存器没有任何意义,但即使是同一个寄存器也移动到两个不同的地址,一个接一个,我没有看到那个)。

MOV EBX, EDX
MOV ECX, EDX

但实际上我看到了这样的事情(我认为这是我的问题,为什么它不起作用),(不是,我仍然不知道我做错了什么)

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要么搞砸了,我真的说不出来,我把这段代码粘贴到 OllyDBG 中,似乎没有任何修改TEST的谜团为什么是错误的,也许地址更新它的值太慢了?因为它基于与 CPU 速度不同步的 RAM 速度(当然都是愚蠢的理论)。JEEDXEDX

无论如何,我可以在这里解释的所有内容都是代码。

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

2 回答 2

1

Some things that I noticed in your code:

First of all, I don't think that you really need to do the pushad and pushfd You are only using one register really, because ebx wouldn't even be needed, and eax is not considered by the compiler to be preserved anyway.

    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.

Your assumption

 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

is simply wrong. You can move the same register as often as you like, there is no limitation. The only thing that MIGHT be of consideration is when you are looking for maximum performance because of the instruction scheduling, but this is an entirely different issue and doesn't apply here anyway.

So simply doing

 MOV EBX, EDX 
 MOV ECX, EDX 

would be fine.

You are using ESI but it is nowhere initialized. Maybe this is correct, as I don't really understand what your code is supposed to do.

The you are using the variable test here

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

but this variable isn't even declared, so where should the compiler know it's address from? And for using it as an offset then you would have to use a register as the base address.

于 2014-01-05T09:56:19.803 回答
1

好吧,老实说,这在很多层面上都是错误的,我的意思是代码本身。您正在尝试做的是强行进入 C 领域,告诉编译器退后,您可以通过手写代码来做自己的事情。这有时可能会派上用场,但你必须记住,你也失去了一些 C 功能,特别是自动为你完成的事情,据我所知,微软编译只是不喜欢你在 inline 汇编中乱搞(不要得到我错了,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 中没有范围之类的东西,就像你告诉它从 test 中获取 myEDX 的值,这意味着获取指向值 test 的指针,然后通过指针获取值。它可能有效,但取决于 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 上通常是这样),但假设又是不好的!

于 2014-01-05T10:18:17.867 回答