Find Pattern wird dafür genutzt, bestimmte Funktionsadressen aus einem Prozess auszulesen.
Dazu wird der Byte-OpCode einer EXE, DLL, BIN oder dergleichen durchlaufen und nach einem vordefiniertem Byte-Muster durchsucht.
Da die Find Pattern Funktion einen bestimmten Suchbereich erwartet muss dieser zuvor ausgelesen werden. Normalerweise wird immer von der Modulbase bis zum Code-Ende alles durchsucht. Dazu braucht man den Anfangswert (Base) und den Endwert (SizeOfCode) , beides ist ganz einfach auslesbar. Der Anfang einer Windows Executable ist immer wie folgt aufgebaut (Tabelle unten).
DOS header
NT header
...
...
An der Modulbasis der DOS-Header, dieser ist Standardisiert und sieht im Debugger so aus (Bild Unten).
Gleich darauf folgt der NT-Header (auch PE-Header). Im DOS-Header steht der Offset an dem letzterer zu finden ist (oben rot markiert). In diesem Fall das Offset 148 (Hexadezimal) also Beginnt dieser bei 400148, wenn mal die Modulbase draufrechnet (Im Bild unten nochmal durch die grüne Markierung verdeutlicht).
Interessant sind hierbei wie zuvor geschrieben der Start und Endwert. Der Basiswert wird durch die GetModuleHandle Funktion ermittelt, der Endwert wird aus dem PE-Header ausgelesen (Oben rot markiert). Der Codeabschnitt unten zeigt eine mögliche Umsetzung in C++.
Ziel ist es ein bestimmtes Muster zu finden, in diesem Beispiel muss der Metin2 Client herhalten. Es sollen die Funktionsadressen automatisch gesucht werden, im Beispiel die „DropItem“ Funktion. Diese sieht im Debugger folgendermaßen aus (im Bild unten grau unterlegt).
Aus den OpCode muss jetzt eine Suchmaske erstellt werden, dazu sucht man sich ein Codefragment aus welches die gewünschte Funktion beinhaltet, es kann ruhig etwas weiter nach oben bzw. unten gehen (wie im Bild oben rot markiert), damit es später besser gefunden werden kann. Anschließend wird dieser Code in ein BYTE-Array kopiert und die Bytes, die sich von Version zu Version oder durch Relozierung des Moduls ändern könnten (also z.B. Adressen oder Offsets) und daher durch die noch folgende String Maske ignoriert werden, zur besseren Lesbarkeit durch 0-Bytes ersetzt.
Das Byte Array speziell für diese Funktion sieht so aus wie im Bild unten, alle Unbekannten wurden durch 0-Bytes ersetzt.
Jetzt muss noch eine Prüfmaske erstellt werden, in dieser werden alle bekannten Bytes durch ein „x“ gekennzeichnet, alle Unbekannten durch ein „-„. Das Beispiel unten zeigt eine solche Maske (zur besseren Veranschaulichung mit Zeilenumbrüchen).
PHP Code:
char mask[] = "x
xxxx
xxxx
xx---x
x
xx
x
x---x
x---x
x";
Nun ist alles Wichtige vorhanden um die Funktion aufzurufen.
Diese Funktion besteht aus zwei Bestandteilen, einmal die Funktion als solches und eine Unterfunktion DataCompare welche dafür zuständig ist die Bytes aus der Suchmaske mit dem OpCode aus dem Prozess zu vergleichen. Die Originalfunktion stammt von dom1n1k (gamedeception.net), ich habe noch den Ignore Parameter hinzugefügt.
DWORD FindPattern(DWORD StartAddress, DWORD CodeLen, BYTE* Mask, char* StrMask, unsigned short ignore)
{
unsigned short Ign = 0;
DWORD i = 0;
while (Ign <= ignore)
{
if(DataCompare((BYTE*)(StartAddress + i++), Mask,StrMask))
++Ign;
else if (i>=CodeLen)
return 0;
}
return StartAddress + i - 1;
}
Parameter:
Typ
Name
Beschreibung
DWORD
StartAddress
Offset von welchem begonnen wird zu durchsuchen
DWORD
CodeLen
Offset an dem die Suche gestoppt wird
BYTE*
Mask
Byte-OpCode Maske
char*
StrMask
String Maske, definiert welche Bytes bei der Suche beachtet werden(x) und welche ausgeschlossen werden(-).
unsigned short
ignore
Anzahl der Funde die ignoriert werden, 0 = erster Fund wird zurückgegeben
Rückgabe:
Zurückgegeben wird das Offset an dem die Funktion (die Suchmaske) gefunden wurde als DWORD Wert, in diesem Beispiel wäre das 0x41484E (auch auf dem Debugger-Codefragment im Bild oben ersichtlich, blau markiert).
Ein Aufruf könnte so aussehen
PHP Code:
DWORD Offset = FindPattern( Base + 0x1000, //BaseOfCode = Base + 1000(Hex)
SizeOfCode,
Mask,
"xxxxxxxxxxx---xxxxxx---xx---xx",
0);
Als StartAddress wurde nicht direkt Base sondern Base + 1000(Hex) übergeben, dass ist weil der eigentliche Code erst ab Offset 1000(Hex) beginnt (und zwar immer). Der Bereich davor ist für die Headers reserviert.
Wie bereits geschrieben sollte die Funktion im Beispiel das Offset 0x41484E liefern. Mit diesem Offset lassen sich die benötigten Funktionsadressen ermitteln, wie das funktioniert wird nun erklärt.
Damit nicht ständig hochgescrollt werden muss hier nochmal das Codefragment der DropItem Funktion von vorhin.
Um eine Adresse im Stil von DWORD PTR DS:[??????] auszulesen muss man folgendes tun. Man zählt die Bytes ab Beginn der Suchmaske bis zu der Stelle an dem die benötigte Adresse steht. Im Beispiel soll die Adresse 61C028 (Bild oben grün markiert) ausgelesen werden, also beginnt man ab Start der Suchmaske, in unserem Beispiel war das C3 die Bytes (1 Byte = Zahlenpaar aus 2 Zahlen) bis zu der Adresse zu Zählen. So kommt man auf 11 Bytes. Die gezählten Bytes wurden im Bild zur Veranschaulichung gelb hinterlegt. Der Blau hinterlegte Abschnitt ist die gewünschte Adresse. Das Codebeispiel unten zeigt nun wie diese ausgelesen werden kann.
PHP Code:
DWORD Address1 = *(DWORD*)( Offset + 11);
Um Andere Adressen wie z.B. 0041E1B0 auslesen zu können geht man ähnlich vor wie vorhin. Nur die Berechnung ändert sich da diese Adresse anders auf dem Speicher liegt. Man zählt wieder die Bytes vom Beginn unserer Maske (in diesem Fall C3) bis zur Adresse. Dieses Mal sind das 20 Bytes. Ausgelesen wird nun mit hilfe der Funktion unten.
Dies ist nötig weil die Adresse relativ zu dem Offset codiert ist. Man muss also erst den Wert auslesen der auf dem Offset liegt. In unserem Beispiel wäre das 994A, auf diesen Wert muss jetzt das Offset draufaddiert werden und dann noch die Länge der Adresse in Bytes also 4. Also 0x41E1B0 = 0x994A + (Offset +20) + 0x4.
Anschließend wird dieser Code in ein BYTE-Array kopiert, und die unbekannten (die gewünschten Adressen wie z.B. 61C028, 0041E1B0 usw.) doch 0-Bytes ersetzt.
Ich fände so etwas wie "Anschließend wird dieser Code in ein BYTE-Array kopiert und die Bytes, die sich von Version zu Version oder durch Relozierung des Moduls ändern könnten (also z.B. Adressen oder Offsets) und daher durch die noch folgende String Maske ignoriert werden, zur besseren Lesbarkeit durch 0-Bytes ersetzt." evtl. etwas leichter verständlich
Und sollte RealtiveToAbsolut nicht eher RelativeToAbsolute lauten? :)
Hier noch die FindPattern-Funktion in FASM, allerdings ohne einen fünften Parameter:
Ich fände so etwas wie "Anschließend wird dieser Code in ein BYTE-Array kopiert und die Bytes, die sich von Version zu Version oder durch Relozierung des Moduls ändern könnten (also z.B. Adressen oder Offsets) und daher durch die noch folgende String Maske ignoriert werden, zur besseren Lesbarkeit durch 0-Bytes ersetzt." evtl. etwas leichter verständlich
Und sollte RealtiveToAbsolut nicht eher RelativeToAbsolute lauten?
Hier noch die FindPattern-Funktion in FASM, allerdings ohne einen fünften Parameter:
Btw. fehlen nicht Credits? z.B. für die Funktion, welche du als Basis genommen hast?
Hatte auch mal in Assembler eine eigene SearchPattern funktion geschreiben aber in Nasm. Mit den Befehlen SCASB und CMPSB im zusammenhang mit Wiederholungspräfixen. Hab ein paar stunden dran gehangen um die funktion zu schreiben hab aber am ende dann vergessen die Quellcode datei abzuspeichern und hatte nach dem Ausschalten alles verloren .
Das hat mich so angegotzt das ich bis heute keine lust hatte die funktion neuzuschreiben .
@Akorn: hehe
jo, normalerweise bin ich voll der Fan von z.B. or ecx,-1
repnz scasb
Ich wollt's aber dann doch nicht so kompliziert haben..
Meine Funktion von oben ist ja eher straight-forward, also auf 'nem ziemlich niedrigen Niveau
(gab für mich ja sowieso nichts anderes zur Auswahl, eh? :P)
Naja, hab mich gerade mal dran gesetzt und es mit scasb und cmpsb umgesetzt, ist allerdings ein wenig ugly geraten..
Code:
format PE GUI 4.0
entry start
include 'win32a.inc'
section '.code' code readable executable
start:
push 0
call [GetModuleHandle]
mov ecx,[eax+03Ch]
push ptrn_size ;4. lstrlen(ptrn_mask)
push ptrn_mask ;3. Patternmaske
push ptrn ;2. Pattern
push dword [eax+ecx+01Ch] ;SizeOfCode
add eax,[eax+ecx+02Ch] ;BaseOfCode
push eax ;1. Startadresse
call FindPattern
push eax
push fmt
push buffer
call [wsprintf]
add esp,0Ch
push MB_OK
push 0
push buffer
push 0
call [MessageBox]
push 0
call [ExitProcess]
;Erster Fund
mov eax,10h
mov ebx,20h
mov ecx,30h
nop
;Zweiter Fund <-
mov eax,10h
mov ebx,20h
mov ecx,30h
db 91h
FindPattern:
push ebx esi edi
cld
mov edi,[esp+10h]
mov ecx,[esp+14h]
mov edx,[esp+1Ch]
dec dword [esp+20h]
.loop:
mov esi,[esp+18h]
mov al,[esi]
repnz scasb
jnz .not_found
xchg ebx,ecx
push edi
inc esi
mov ecx,[esp+24h]
.compare:
repe cmpsb
jnz .check_wildcard
pop eax
dec eax
jmp .fin
.check_wildcard:
jecxz .no_match
mov eax,[esp]
sub eax,edi
neg eax
cmp byte [edx+eax],'?'
je .compare
.no_match:
pop edi
xchg ebx,ecx
jmp .loop
.not_found:
xor eax,eax
.fin:
pop edi esi ebx
retn 14h
section '.data' data readable writeable
ptrn db 0B8h,000h,000h,000h,000h,0BBh,000h,000h,000h,000h,0B9h,000h,000h,000h,000h,091h
ptrn_mask db 'x????x????x????x'
ptrn_size = $-ptrn_mask
fmt db 'found @%08Xh',0
buffer rb 256
section '.idata' import data readable
library kernel32,'KERNEL32.DLL',\
user32,'USER32.DLL'
import kernel32,\
GetModuleHandle,'GetModuleHandleA',\
ExitProcess,'ExitProcess'
import user32,\
MessageBox,'MessageBoxA',\
wsprintf,'wsprintfA'
WTF hab meine SearchPattern funktion doch wieder gefunden
@link Eigentlich sind diese Befehle ganz nützlich aber ich habe mittlerweile von ein paar i-net seiten, optimierungs Manauals, und nen Kumpel erfahren das diese Befehle wie SCASB CMPSB etc mit REP und .co eigentlich viel Langsamer sind als wen man dessen funktionen einfach mittels normalen spring Vergleichs und schiebe befehlenen schreibt
Dann zeig doch mal :P
Das hier habe ich vor 2 Monaten mal in einem anderen Forum zu dem Thema geschrieben:
Quote:
I guess by "useless" instructions you mean those, which actually consist of two operations and were implemented to provide some higher level of code to draw simplicity but ended up being much slower and are therefore negatively connotated
examples are loop, enter, lods, stos, movs
on most processors they are considerably slower than their two instruction-pendants
kind of compareable to inc eax vs add eax,1 as the latter is often faster on newer processors
while 'inc' is considered more an 'exotic' instruction like the high-level instructions mentioned above
however, there are some exceptions like leave or rep lods/stos/movs, which are pretty fast
Auf mark.masmcode.com steht dazu Folgendes:
Quote:
Avoid complex instructions ( lods, stos, movs, cmps, scas, loop, xadd, enter, leave). Complex instructions are instructions that do multiple things. For instance stosb writes a byte to memory and also increments EDI. They stopped making these fast with the original Pentium because they were trying to make it more RISC like. Using REP and the string instructions is still fast. That is the only exception to the case.
Hab gerade sogar zufällig eine Wikipedia-Seite genau zu diesem Thema gefunden
Die zweite Funktion von mir ist vom Ansatz her auf jeden Fall etwas komplizierter, würde ich sagen, ob sie auch wirklich langsamer ist, werde ich später einfach mal kurz testen..
Hängt ja von vielem ab, wie z.B. Stalls oder Speicherzugriffen (die erste Version müsste wahrscheinlich den Stack wegen des Calls häufiger verwenden, was auch ziemlich langsam ist)
Edit:
Habe auch mal irgendwo gelesen, dass der Pentium ein CISC-Prozessor mit einem RISC-Kern sei, der intern die Befehle erst einmal dekodiert und dann für diesen translatiert, was, wenn dem so ist, solchen komplexen Instruktionen, die im RISC nicht implementiert sind, einen zusätzlichen Overhead verpassen würde
FindPatternBench.asm:
Code:
format PE console 4.0
entry start
include 'win32a.inc'
section '.code' code readable executable
start:
mov [display_handle],STD_OUTPUT_HANDLE
mov [read_handle],STD_INPUT_HANDLE
push 0
call [GetModuleHandle]
mov ecx,[eax+03Ch]
mov edx,[eax+ecx+01Ch]
mov [SizeOfCode],edx
add eax,[eax+ecx+02Ch]
mov [BaseOfCode],eax
;FindPatternHL
xor eax,eax
cpuid
rdtsc
push eax edx
push ptrn_size
push ptrn_mask
push ptrn
push [SizeOfCode]
push [BaseOfCode]
call FindPatternHL
mov edi,eax
xor eax,eax
cpuid
rdtsc
mov ecx,eax
pop eax ebx
xchg ecx,edx
sub edx,ebx
sbb ecx,eax
mov esi,buffer
push ecx
push edx
push edi
push _FindPatternHL
push _fmt
push esi
call [wsprintf]
add esp,18h
call display_string
;FindPattern
xor eax,eax
cpuid
rdtsc
push eax edx
mov eax,[BaseOfCode]
mov ecx,[SizeOfCode]
mov esi,ptrn
mov edi,ptrn_mask
call FindPattern
mov edi,eax
xor eax,eax
cpuid
rdtsc
mov ecx,eax
pop eax ebx
xchg ecx,edx
sub edx,ebx
sbb ecx,eax
mov esi,buffer
push ecx
push edx
push edi
push _FindPattern
push _fmt
push esi
call [wsprintf]
add esp,18h
call display_string
mov edx,wait_for_enter
mov ecx,1
call read_string
push 0
call [ExitProcess]
;Erster Fund
mov eax,10h
mov ebx,20h
mov ecx,30h
nop
;Zweiter Fund <-
mov eax,10h
mov ebx,20h
mov ecx,30h
db 91h
FindPatternHL:
push ebx esi edi
cld
mov edi,[esp+10h]
mov ecx,[esp+14h]
mov edx,[esp+1Ch]
dec dword [esp+20h]
.loop:
mov esi,[esp+18h]
mov al,[esi]
repnz scasb
jnz .not_found
xchg ebx,ecx
push edi
inc esi
mov ecx,[esp+24h]
.compare:
repe cmpsb
jnz .check_wildcard
pop eax
dec eax
jmp .fin
.check_wildcard:
jecxz .no_match
mov eax,[esp]
sub eax,edi
neg eax
cmp byte [edx+eax],'?'
je .compare
.no_match:
pop edi
xchg ebx,ecx
jmp .loop
.not_found:
xor eax,eax
.fin:
pop edi esi ebx
retn 14h
FindPattern:
push ebx ebp
.loop:
mov ebp,eax
call DataCompare
xchg eax,ebp
jc .found
inc eax
loop .loop
xor eax,eax
.found:
pop ebp ebx
retn
DataCompare:
push ecx
mov edx,edi
mov ecx,esi
.loop:
cmp byte [edx],0
stc
je .fin
mov bl,[eax]
cmp byte [edx],'x'
jnz .match
cmp [ecx],bl
clc
jnz .fin
.match:
inc eax
inc ecx
inc edx
jmp .loop
.fin:
pop ecx
retn
display_string:
invoke GetStdHandle,[display_handle]
mov edx,eax
mov edi,esi
or ecx,-1
xor al,al
repne scasb
neg ecx
sub ecx,2
invoke WriteFile,edx,esi,ecx,bytes_count,0
retn
read_string:
push ecx edx
invoke GetStdHandle,[read_handle]
pop edx
mov ecx,[esp]
invoke ReadFile,eax,edx,ecx,bytes_count,0
pop edx
or eax,eax
jz file_error
cmp edx,[bytes_count]
jne file_error
clc
retn
file_error:
stc
retn
section '.data' data readable writeable
ptrn db 0B8h,000h,000h,000h,000h,0BBh,000h,000h,000h,000h,0B9h,000h,000h,000h,000h,091h
ptrn_mask db 'x????x????x????x',0
ptrn_size = $-ptrn_mask-1
_FindPatternHL db 'FindPatternHL',0
_FindPattern db 'FindPattern ',0
_fmt db '%s: eax = %08Xh @ %I64d ticks',10,10,0
BaseOfCode dd ?
SizeOfCode dd ?
buffer rb 256
wait_for_enter db ?
display_handle dd ?
read_handle dd ?
bytes_count dd ?
section '.idata' import data readable
library kernel32,'KERNEL32.DLL',\
user32,'USER32.DLL'
import kernel32,\
ExitProcess,'ExitProcess',\
GetModuleHandle,'GetModuleHandleA',\
GetStdHandle,'GetStdHandle',\
ReadFile,'ReadFile',\
WriteFile,'WriteFile'
import user32,\
MessageBox,'MessageBoxA',\
wsprintf,'wsprintfA'
Ja muss mal kucken ob mein code überhaupt geht ich hatte ihn ja nie ausprobiert. Allerdings ist die Funktion noch nicht ganz ausgereift da es zum suchen keine maske verwendete.
Aus den OpCode muss jetzt eine Suchmaske erstellt werden, dazu sucht man sich ein Codefragment aus welches die gewünschte Funktion beinhaltet, es kann ruhig etwas weiter nach oben bzw. unten gehen (wie im Bild oben rot markiert), damit es später besser gefunden werden kann.
Das ist so nicht ganz richtig.
Man sollte Suchmasken so klein wie möglich halten, damit sie nicht durch Patches, die Code in der Umgebung verändern, veralten.
Natürlich muss man sie groß genug machen, damit es eindeutige Suchergebnisse sind, aber dafür ist gar nicht mal so viel nötig.
Wenn es keine Masken wie der Prolog einer __stdcall Funktion sind, dann reichen ein paar Bytes schon für eine eindeutige Suche.
Quote:
Dies ist nötig weil die Adresse relativ zu dem Offset codiert ist. Man muss also erst den Wert auslesen der auf dem Offset liegt. In unserem Beispiel wäre das 994A, auf diesen Wert muss jetzt das Offset draufaddiert werden und dann noch die Länge der Adresse in Bytes also 4. Also 0x41E1B0 = 0x994A + (Offset +20) + 0x4.
Kleiner Tipp:
Ich würde den Parameter OffsetAddress nennen und in der Rechnung im letzten Satz 0x994A + *(Offset +20) ... schreiben.
Das ganze hat mich erst total verwirrt, wollte erst schon schreiben, dass es doch nichts bringt, das Offset zu sich selbst zu addieren, bis ich gemerkt habe, dass es einmal dereferenziert wird und so das Offset genommen wird und einmal die Adresse davon.
Der Name Offset ist einfach total irreführend^^ Und in der Rechnung ists dann genau das gleiche: man erkennt nicht direkt, dass es einmal als Pointer verwendet wird.
Eine Deklaration als void* würde vielleicht auch nicht schaden.
Das ist so nicht ganz richtig.
Man sollte Suchmasken so klein wie möglich halten, damit sie nicht durch Patches, die Code in der Umgebung verändern, veralten.
Natürlich muss man sie groß genug machen, damit es eindeutige Suchergebnisse sind, aber dafür ist gar nicht mal so viel nötig.
Wenn es keine Masken wie der Prolog einer __stdcall Funktion sind, dann reichen ein paar Bytes schon für eine eindeutige Suche.
Kleiner Tipp:
Ich würde den Parameter OffsetAddress nennen und in der Rechnung im letzten Satz 0x994A + *(Offset +20) ... schreiben.
Das ganze hat mich erst total verwirrt, wollte erst schon schreiben, dass es doch nichts bringt, das Offset zu sich selbst zu addieren, bis ich gemerkt habe, dass es einmal dereferenziert wird und so das Offset genommen wird und einmal die Adresse davon.
Der Name Offset ist einfach total irreführend^^ Und in der Rechnung ists dann genau das gleiche: man erkennt nicht direkt, dass es einmal als Pointer verwendet wird.
Eine Deklaration als void* würde vielleicht auch nicht schaden.
aufgrund Ihrer Akademischen ausdrucksweise muss ich annehmen, dass es sich bei Ihrer Person um einen von mir nicht sehr gemochten bob namens Steven handelt (dessen Gehirn von einem NULL Pointer dereferenziert worden ist), ich bitte dies zu entschuldigen !
Find Pattern ASM [C++] 07/30/2014 - C/C++ - 2 Replies Hallo, ich versuche momentan einen ASM Code mit Find Pattern auszulesen.
Bisher konnte ich nur die Adresse zum z.B. call oder mov auslesen, was aber nicht mein Ziel ist.
Ich will entweder den kompletten ASM Code mit Find Pattern auslesen oder mit einer anderen Methode mir den ASM Code der mit Find Pattern ausgelesenen Adresse ausgeben lassen.
Hier nochmal der C++ Source Code wie ich das ganze momentan für die Adresse, die zum asm code führt regle:
#include <iostream>
#include...
Find Pattern 10/10/2013 - C/C++ - 6 Replies Hey, komm nach Tagen langen herum googlen immer noch nicht mit Find Pattern zurecht.
1. Frage
Was auf dem Screen ist alles wichtig dafür, bzw. fehlt da noch etwas relevantes?
http://i.epvpimg.com/RJFTb.jpg
2. Frage
Mein Code sieht bisher so aus, was fehlt dort damit ich den Wert meiner HP auslesen kann, bzw. kann ich überhaupt den Code mit den Infos ausn Screen vollenden?
#include "stdafx.h"
Addylogger Pattern... How i can find it? 08/11/2012 - WarRock - 0 Replies Hey dudes,
How i can find Patterns in OllyDBG? can someone explain that to me, please?
I already have a basic knowledge in OllyDBG.
Thank u :)
€: Or someone can give me a tut about that? I can´t find any tuts...
find pattern funktion.. hilfe 03/15/2009 - General Coding - 4 Replies ich brauch mal etwas hilfe. ich hab irgendwo ein denkfehler oder der code is total fürn arsch. vlt hab ihr ja bessere funktionen, würd mich freuen wenn ihr eine hier posten könntet.
DWORD dwFindPattern(DWORD dwStartAddress, DWORD dwEndAddress, BYTE *bMask, BYTE *bMask_O){
BYTE ScanCode_A = {};
/*
while(dwStartAddress < dwEndAddress){
ReadProcessMemory(hGW, (LPCVOID)dwStartAddress, &ScanCode_A, 1, 0);
if(ScanCode_A == bMask){
ReadProcessMemory(hGW, (LPCVOID)(dwStartAddress +...