x64 ASM mit C++ problem

10/12/2014 17:37 XxharCs#1
Hallo.

Wie ihr sicherlich wissts lässt VisualStudio kein inline ASM zu wenn man x64-Bit Applikationen erstellt.

Ich habe dann ein bisschen rumgegoogelt und gefunden wie man in VS ASM mit x64 Code verbinden kann.
Soweit so gut, aber ich bekomme einen error und ich weiß nicht wieso :confused:

Die Errors sind als comment zu meinem Code aufgeschrieben:
Code:
.code
noReload proc
	mov [rax+98],1e ; Fehler	1	error A2048: nondigit in number
	mov eax,[rax+98]
	jmp 7FF95517C177 ; Fehler	2	error A2206: missing operator in expression
noReload endp
end
Ich hoffe um Hilfe den wenn ich ähnlichen Code in x86 inline ASM schreibe, bekomme ich solche Errors nicht..

Danke in vorraus!
10/12/2014 20:40 Omdi#2
0x vor bzw. 'h' hinter jede Nummer.

Btw.
Code:
jmp 7FF95517C177
Geht sowas überhaupt ôo?
10/12/2014 20:51 XxharCs#3
Ich habe etwas länger rumgegoogelt und rausgefunden das MASM64 irgendwie Base-10 nutzt statt Base-16.
Also sieht mein neuer Code so aus: (Trotzdem Compiler Error beim jmp, siehe unten wieso)
Code:
.code
noReload proc
	mov [rax+152], DWORD PTR 30
	mov eax, [rax+152]
        jmp 7FF95517C177h
noReload endp
end
Was ich ebenfalls durchlesen konnte ist das man keine direkten jmps machen kann wie im obrigen Beispiel.
Also versuche ich es mit Umwegen die aber das Programm/Spiel crashen.
Code:
.code
noReload proc
	mov [rax+152], DWORD PTR 30
	mov eax, [rax+152]
	mov rax, 7FF95517C177h
	jmp rax
noReload endp
end
Villt ist ja auch nur mein writeJmp Falsch :confused:
Code:
#define BYTECODE_JMP 0xE9
#define BYTECODE_NOP 0x90

void WDFuncs::writeJMP(BYTE* src, BYTE* dst, const int len)
{
	DWORD64 dwRelAddr;
	DWORD dwOldProtect;

	VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	dwRelAddr = (DWORD64)(dst - src) - 5;

	src[0] = BYTECODE_JMP;

	*((DWORD64*)(src + 0x1)) = dwRelAddr;

	for(DWORD64 c = 0x5; c < len; c++)
		*(src + c) = BYTECODE_NOP;

	VirtualProtect(src, len, dwOldProtect, &dwOldProtect);
}

void WDFuncs::EnableNoReload(bool enable)
{
	DWORD dwOldProtect;
	BYTE origBytes[] = {0x8B, 0x80, 0x98, 0x00, 0x00, 0x00};
	//BYTE hackBytes[] = {0xC7, 0x80, 0x98, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x8B, 0x80, 0x98, 0x00, 0x00, 0x00, 0xE9, 0x62, 0xC1, 0x56, 0x00};

	if(enable == true)
	{
		WDFuncs::writeJMP((BYTE*)0x7FF95517C171, (BYTE*)noReload, 6);
	}else
	{
		VirtualProtect((LPVOID)0x7FF95517C171, sizeof(origBytes), PAGE_EXECUTE_READWRITE, &dwOldProtect);
		RtlMoveMemory((LPVOID)0x7FF95517C171, origBytes, sizeof(origBytes));
		VirtualProtect((LPVOID)0x7FF95517C171, sizeof(origBytes), dwOldProtect, &dwOldProtect ); 
	}
}
Im header die noReload definition:
Code:
extern "C" void noReload();
Es ist zu beachten das mein Target eine x64 Anwendung ist.
10/12/2014 21:29 Dr. Coxxy#4
falsch, falsch, falsch, guck dir bitte erstmal nen bissle den assembler code an, lern wie ein detour funktioniert und dann mach das ganze mal im debugger per hand, oder guck dir an wie der cheatengine autoassembler und dessen templates funktionieren.
10/12/2014 21:41 XxharCs#5
Im debugger per Hand funktioniert es. Bzw. ich besitze einen autoassembler template in CE, also hab ich eine Vorlage, aber dieses übersetzen klappt irgendwie nicht.

Ich bin nicht auf ein Detour richtig eingegangen, da ich ja eine Codecave erstellen möchte, und ich deshalb nur einen JMP zur Funktion mache und die Funktion dann springt dann wieder zurück.

Meinen alten Detour Code habe ich noch nicht ausprobiert:
Code:
void *DetourFunction (BYTE *src, const BYTE *dst, const int len)
{
    BYTE *jmp;
    DWORD dwback;
    DWORD jumpto, newjump;

    VirtualProtect(src,len,PAGE_READWRITE,&dwback);
    
    if(src[0] == 0xE9)
    {
        jmp = (BYTE*)malloc(10);
        jumpto = (*(DWORD*)(src+1))+((DWORD)src)+5;
        newjump = (jumpto-(DWORD)(jmp+5));
        jmp[0] = 0xE9;
       *(DWORD*)(jmp+1) = newjump;
        jmp += 5;
        jmp[0] = 0xE9;
       *(DWORD*)(jmp+1) = (DWORD)(src-jmp);
    }
    else
    {
        jmp = (BYTE*)malloc(5+len);
        memcpy(jmp,src,len);
        jmp += len;
        jmp[0] = 0xE9;
       *(DWORD*)(jmp+1) = (DWORD)(src+len-jmp)-5;
    }
    src[0] = 0xE9;
   *(DWORD*)(src+1) = (DWORD)(dst - src) - 5;

    for(int i = 5; i < len; i++)
        src[i] = 0x90;
    VirtualProtect(src,len,dwback,&dwback);
    return (jmp-len);
}
Edit: Mit meiner alten DetourFunktion funktioniert es teilweise, irgendwie hat er die Munition in die Hälfte geschnitten :/
Edit2: Was mich brainfuckt ist, wieso ich normales DWORD verwenden kann und nicht DWORD64 da ja die Anwendung x64-Bit ist und deshalb eine x64-Bit Addressierung hat :confused:
10/12/2014 22:08 Dr. Coxxy#6
du kannst nicht einfach von 32bit nach 64bit alles übernehmen, das klappt so nicht.
benutz mal das ce autoassembler codecave template und guck dir mit dem debugger genau an, was ce da gemacht hat.
10/12/2014 22:17 XxharCs#7
Das was der autoassembe macht, will ich ja im ASM machen, nur da ich MASM verwenden muss im VS, kann ich keinen direkten Address jmp aufrufen.

autoassemble-script
Code:
[ENABLE]
//
alloc(_noReload,128,"game.dll")
//
label(_noReload_exit)
label(_noReload_aob_jmp)
//
registersymbol(_noReload_aob_jmp)
//
aobscanmodule(_noReload_aob,game.dll,E8 B4 C0 03 01 48 85 C0 74 0C 8B 80 98 00 00 00) //"game.dll"+55C167

_noReload:
mov [rax+98],1e //30
mov eax,[rax+98]
jmp _noReload_exit

_noReload_aob+a: //"game.dll"+55C171
_noReload_aob_jmp:
jmp _noReload
db 90
_noReload_exit:

[DISABLE]
dealloc(_noReload)
_noReload_aob_jmp:
db 8B 80 98 00 00 00
unregistersymbol(_noReload_aob_jmp)
Und ursprünglich sollte ja mein code so aussehen:
(Im Base-10 grad geschrieben)
Code:
.code
noReload proc
	mov [rax+152], DWORD PTR 30
	mov eax, [rax+152]
	jmp 7FF95517C177h
noReload endp
end
Nur leider geht kein direkter call. Und wenn ich
Code:
mov rax, 7FF95517C177h
jmp rax
Verwende klappt es ca., die Munition wird ja gecuttet usw.
10/12/2014 23:22 Dr. Coxxy#8
ok, ich umreiße eben kurz, was du falsch machst:
1. writejump schreibt einen e9 jump - unter 64bit ist das nur ein short jump -> springt maximal 32bit weit, du brauchst aber (wenn du es sauber machst) einen far jump - immediate jumps sind unter x86 relativ, und @ 64bit gibts keinen 64bit jump immediate - musst also über nen register springen.
2. in deiner no reload "funktion" in assembler hast du zuerst versucht nen immediate absolute jump zu machen, was aus obengenannten 2 gründen nicht geht, das hast du dann korrigiert - allerdings dabei aber rax überschrieben, wo zu dem zeitpunkt allerdings anscheinend etwas sinnvolles drinsteht (zmdst benutzt dus 2 zeilen drüber) -> crash.

Lösung:
writejmp neu für 64bit schreiben.
in deinem assembler entweder ein register nehmen was nicht benutzt wird, oder die registerwerte wieder herstellen.
10/13/2014 08:24 XxharCs#9
Ich bedanke mich, das klärt widderum viel auf!

Quote:
Originally Posted by Dr. Coxxy View Post
ok, ich umreiße eben kurz, was du falsch machst:
1. writejump schreibt einen e9 jump - unter 64bit ist das nur ein short jump -> springt maximal 32bit weit, du brauchst aber (wenn du es sauber machst) einen far jump - immediate jumps sind unter x86 relativ, und @ 64bit gibts keinen 64bit jump immediate - musst also über nen register springen.
Das hatte ich gefunden, es hieß auch das ein JMP im 64-Bit den Bytecode 0xFF verwendet, aber laut den ganzen gesammelten Informationen kam es mir dann so rüber, als würde dieser Fall nicht immer auftreten müssen(?)

Quote:
Originally Posted by Dr. Coxxy View Post
2. in deiner no reload "funktion" in assembler hast du zuerst versucht nen immediate absolute jump zu machen, was aus obengenannten 2 gründen nicht geht, das hast du dann korrigiert - allerdings dabei aber rax überschrieben, wo zu dem zeitpunkt allerdings anscheinend etwas sinnvolles drinsteht (zmdst benutzt dus 2 zeilen drüber) -> crash.

Lösung:
writejmp neu für 64bit schreiben.
in deinem assembler entweder ein register nehmen was nicht benutzt wird, oder die registerwerte wieder herstellen.
Werde mich an die korrektur setzen und dementsprechend melden! :)
10/19/2014 15:28 XxharCs#10
Sry für den doppelpost, aber ich denke jetzt darf ich es, sonst lösch ich den obrigen post :)

Mein kumpel und ich haben uns hingesetzt um jetzt den x64 jump zu machen, jedoch ist es broken irgendwie.. (Typ von hooken: mid function hook)

Ich hoffe jemand kann hier weiter aushelfen :)
(irgendwie schafft es der VirtualAlloc nicht immer)

Code:
const short MOV_R11_BYTES = 0xBB49;
const short PUSH_R11_BYTE = 0x5341;
const char RET_BYTE = 0xC3;

const int JMP_SMALL_LENGTH = 5;
const int JMP_BIG_LENGTH = 13;
smallJump
Code:
bool writeSmallJump(BYTE* src, BYTE* dst, const unsigned int length){
    
    if(length < JMP_SMALL_LENGTH)
        return false;

    DWORD dwOldProtect;

    VirtualProtect(src, length, PAGE_READWRITE, &dwOldProtect);
    
    src[0] = 0xE9;
    *(DWORD*)(src+1) = ((DWORD)dst - (DWORD)src - 5);

    VirtualProtect(src, length, PAGE_READWRITE, &dwOldProtect);

    return true;
}
bigJump
Code:
bool writeBigJump(BYTE* src, BYTE* dst, const unsigned int length){
    
    if(length < JMP_BIG_LENGTH)
        return false;

    DWORD oldProtect;
    VirtualProtect(src, length, PAGE_READWRITE, &oldProtect);
        
    int offset = 0;

    *(short*)(src + offset) = MOV_R11_BYTES;
    offset += 2;

    *(DWORD64*)(src + offset) = (DWORD64)dst;
    offset += sizeof(DWORD64);

    *(short*)(src + offset) = PUSH_R11_BYTE;
    offset += 2;

    *(char*)(src + offset) = RET_BYTE;
    offset += 1;

    VirtualProtect(src, length, oldProtect, &oldProtect);

    return true;
}
not working x64 jump lol
Code:
void writeNoWorkingX64Jump(BYTE* src, BYTE* dst, void** ret, const unsigned int length)
{
    if(length < JMP_SMALL_LENGTH){
        *ret = NULL;
        return;
    }

    const int totalLength = 13 + length + 13;
    //
    BYTE* trampoline = (BYTE*)malloc(totalLength);
    DWORD dwOldProt;
    BYTE* smallTrampoline = (BYTE*)VirtualAlloc((void*)src, JMP_BIG_LENGTH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    DWORD difference = (DWORD)((DWORD64)smallTrampoline - (DWORD64)src);

    if(difference > (DWORD)0xFFFFFFFF)
        MessageBox(0, "memory to far: DETOUR", 0,0);
            

    if(((DWORD64)trampoline & (DWORD64)smallTrampoline) == NULL){
        *ret = NULL;
        return;
    }
    

    //JMP to Hook
    //////////////////////////////////////////////////////////////
    writeBigJump(smallTrampoline, trampoline, JMP_BIG_LENGTH);
    writeBigJump(trampoline, dst, JMP_BIG_LENGTH);
    //////////////////////////////////////////////////////////////

    trampoline+=JMP_BIG_LENGTH;
    *ret = trampoline;

    //Save code
    memcpy(trampoline, (void*)src, length);
    trampoline += length;
    /////////////////////////////////


    writeBigJump(trampoline, src+length, JMP_BIG_LENGTH);

    writeSmallJump(src, smallTrampoline, length);

    for(int i = 12; i < length; i++)
        src[i] = 0x90;
}
All-in-one:
Code:
const short MOV_R11_BYTES = 0xBB49;
const short PUSH_R11_BYTE = 0x5341;
const char RET_BYTE = 0xC3;

const int JMP_SMALL_LENGTH = 5;
const int JMP_BIG_LENGTH = 13;

bool writeSmallJump(BYTE* src, BYTE* dst, const unsigned int length){
    
    if(length < JMP_SMALL_LENGTH)
        return false;

    DWORD dwOldProtect;

    VirtualProtect(src, length, PAGE_READWRITE, &dwOldProtect);
    
    src[0] = 0xE9;
    *(DWORD*)(src+1) = ((DWORD)dst - (DWORD)src - 5);

    VirtualProtect(src, length, PAGE_READWRITE, &dwOldProtect);

    return true;
}

bool writeBigJump(BYTE* src, BYTE* dst, const unsigned int length){
    
    if(length < JMP_BIG_LENGTH)
        return false;

    DWORD oldProtect;
    VirtualProtect(src, length, PAGE_READWRITE, &oldProtect);
        
    int offset = 0;

    *(short*)(src + offset) = MOV_R11_BYTES;
    offset += 2;

    *(DWORD64*)(src + offset) = (DWORD64)dst;
    offset += sizeof(DWORD64);

    *(short*)(src + offset) = PUSH_R11_BYTE;
    offset += 2;

    *(char*)(src + offset) = RET_BYTE;
    offset += 1;

    VirtualProtect(src, length, oldProtect, &oldProtect);

    return true;
}

void writeNoWorkingX64Jump(BYTE* src, BYTE* dst, void** ret, const unsigned int length)
{
    if(length < JMP_SMALL_LENGTH){
        *ret = NULL;
        return;
    }

    const int totalLength = 13 + length + 13;
    //
    BYTE* trampoline = (BYTE*)malloc(totalLength);
    DWORD dwOldProt;
    BYTE* smallTrampoline = (BYTE*)VirtualAlloc((void*)src, JMP_BIG_LENGTH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    DWORD difference = (DWORD)((DWORD64)smallTrampoline - (DWORD64)src);

    if(difference > (DWORD)0xFFFFFFFF)
        MessageBox(0, "memory to far: DETOUR", 0,0);
            

    if(((DWORD64)trampoline & (DWORD64)smallTrampoline) == NULL){
        *ret = NULL;
        return;
    }
    

    //JMP to Hook
    //////////////////////////////////////////////////////////////
    writeBigJump(smallTrampoline, trampoline, JMP_BIG_LENGTH);
    writeBigJump(trampoline, dst, JMP_BIG_LENGTH);
    //////////////////////////////////////////////////////////////

    trampoline+=JMP_BIG_LENGTH;
    *ret = trampoline;

    //Save code
    memcpy(trampoline, (void*)src, length);
    trampoline += length;
    /////////////////////////////////


    writeBigJump(trampoline, src+length, JMP_BIG_LENGTH);

    writeSmallJump(src, smallTrampoline, length);

    for(int i = 12; i < length; i++)
        src[i] = 0x90;
}
10/19/2014 16:41 Dr. Coxxy#11
Quote:
Code:
    BYTE* trampoline = (BYTE*)malloc(totalLength);
    DWORD dwOldProt;
    BYTE* smallTrampoline = (BYTE*)VirtualAlloc((void*)src, JMP_BIG_LENGTH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
warum einmal malloc und einmal virtualalloc?
außerdem brauchts execute rechte, entweder direkt bei virtualalloc angeben, oder per virtualprotect ändern.

Quote:
Code:
DWORD difference = (DWORD)((DWORD64)smallTrampoline - (DWORD64)src);

    if(difference > (DWORD)0xFFFFFFFF)
jetzt mal bitte ganz scharf nachdenken, ob ein DWORD größer als der größte wert in einem DWORD sein kann...

Quote:
Code:
if(((DWORD64)trampoline & (DWORD64)smallTrampoline) == NULL){
wat?

lass erstmal unnötigen mist wie das checken auf big/small jump weg, sondern nimm einfach erstmal immer big jumps, abstrahier noch nicht in funktionen (lohnt sich bei midfunction hooks eh kaum) und bekomm das ganze erstmal zum laufen.
danach kannste dich um feinheiten und optimierungen kümmern, das da ist aber erstmal spaghetticode, bei dem man kaum erkennen kann, was du da überhaupt probierst.