2

私は現在、レジスターをあるレジスターから別のレジスターに不適切に移動しているのではないかと推測できない、説明のつかないレジスターの問題を抱えています。

の値を に取得しようとしていますがEDXtest.myEDXほとんどの場合、 の間違った値を に入れていますがEDXtest.myEDXそのEDX値の一部は正しいように見えます。

私がやろうとしていることを説明しましょう。
まず、任意のアセンブリ コードを含むメモリ内の場所をフックします。

その場所にハードウェア ブレークポイントを配置してフックすると、その場所にあるすべてのレジスタ値を時間内に確認できます。
ここで、CPU がそのブレークポイントのある場所に入るたびに、EDX値を C++ の構造体に入れる単純なプログラムを作成しました。
私が知ってEDXいる限り、構造体に値を入れてEDXテストしたものを変更しない限り、レジスターEDXを構造体に入れている間は何も変更しないでください。と同じ値で、まだ間違っている可能性がありますEAXEAXEDXEAXデータを構造体に入れるときにも使用されますか? すべてのレジスタをテストして、使用されていないレジスタを見つけることについては、それ以上は進めませんでした。

EDXC ++で作成する必要がある構造体に入れるなどの操作を実際に実行するために考慮すべきもう1つのことはEIP、ネイキッド関数に入る前にこのことを行ったことから、ネイキッド関数はレジスタをまったく変更しないことを知っています。サブルーチンに入るときにC++が追加するゴミなしで、そのEIPの現在のasmコードをはるかに大きなasmコードに拡張する方法として単純に使用されます。

また、構造体へのダンプが終了したときに、以前に設定したレジスタ値を復元するためPUSHADに andを使用します。PUSHFDEDXPUSH/POPPOPFDPOPAD

私は ASM をほとんど知りませんが、多くの例を見ても、レジスタがこのように移動したことはありません。(明らかに、レジスターを複製しても意味がありませんが、同じレジスターでさえ、私が見たことのないアドレスの後に 2 つの異なるアドレスに移動しました)。

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 に貼り付けたところ、何も変更されていないように見えます。なぜ間違っているのか、アドレスの更新の値が遅すぎるのかもしれません。CPU速度と同期しないRAM速度に基づいているためです(もちろん、すべて愚かな理論です)。TESTJEEDXEDX

とにかく、ここで説明できるのはコードだけです。

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