PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Access Violation unterdrücken



SleiZer
15.07.2009, 13:05
Hallo,
ich bin grad dabei einen Hex Dupe zuschreiben mit Ansi-C
Mein Problem ist es wenn ich versuche nicht nur Datein auszulesen, sondern z.B. den Arbeitsspeicher.

Ich hab schon Programme gefunden die sowas können z.B. Winhex, aber ich würde gerne wissen wie man sowas umgeht, sodass man keinen Access Violation erhält.

Hoffe auf Antwort
grüße!

blackberry
15.07.2009, 15:35
Ich gehe jetzt mal davon aus, dass du einen virtuellen Speicherbereich auslesen möchtest und nicht den physikalischen Speicher (für diesen würdest du erhöhte Rechte benötigen - dh. einen eigenen Treiber oÄ.).

Um sicherzustellen, dass ein Fehler abgefangen wird und nicht das Programm beendet sollte try-catch ausreichen.

Bei mir hat das jedoch nicht funktioniert (Bug in MinGW??), also habe ich kurzerhand meinen eigenen Exception-Handler geschrieben (deshalb hat meine Antwort auch so lange gedauert, sorry):

#include <iostream>

#define _TRY \
asm( \
"pushl $_catch;" \
"pushl %fs:(0);" \
"movl %esp, %fs:(0);" \
"_try:" \
"pusha;" \
);
#define _CATCH \
asm( \
"jmp _endtry;" \
"_catch:;" \
"pusha;" \
);
#define _ENDTRY \
asm( \
"popa;" \
"movl 12(%esp), %eax;" \
"movl $_endtry, 0xB8(%eax);" \
"xorl %eax, %eax;" \
"ret;" \
"_endtry:;" \
"popl %fs:(0);" \
"addl $4, %esp;" \
);


int main(void)
{
int *i;
int val;

std::cout << "Ich bin ein C++ Programm mit eigenem Exception Handler!\n\n";

_TRY // TRY BLOCK
{
std::cout << " _TRY: Let's go!\n";
i = 0;
val = *i; // auskommentieren um den Fehler zu beheben!
std::cout << "Addr[" << i << "] ist: " << val << "\n";
}
_CATCH // CATCH BLOCK
{
std::cout << " _CATCH: Es ist ein Fehler aufgetreten... ich frag mich nur warum? xD\n";
}
_ENDTRY
std::cout << "_ENDTRY: Try beendet... Druecken Sie ENTER um fortzufahren!\n";

std::cin.get();
return 0;
}

In diesem Beispiel wird versucht Daten von der Speicheradresse 0 auszulesen (was verständlicherweise zu einem Fehler führt).
Dieser wird von dem _CATCH-Block abgefangen - hier könnte z.B. die Variable die du einlesen wolltest auf 0 gesetzt werden um zu indizieren, dass ihr Wert unbestimmt ist!
Nach _ENDTRY kommt der Code, der in beiden Fällen ausgeführt werden soll. (also egal ob ein Fehler auftrat, oder nicht)

Ich hoffe das hilft dir weiter :)

PS: die { }-Klammern sind nicht nötig, ich habe sie jedoch eingefügt um den Code übersichtlich zu halten!


mfG. BlackBerry

SleiZer
15.07.2009, 19:02
Blick ich nicht ganz durch der Wille zu Kommentieren war da :)

Leider bin ich nur richtig in Ansi-C geschult und verstehe nur die Hälfte von dem was du mir geschrieben hast.

Ich hatte mir das so vorgestellt, dass ich damit den Speicher anderer Programme einlesen kann. Ich hab mir das so gedacht, dass wenn ich über den Access Bereich des Programmes rüber komme ich im nächsten Programm lande. Seh ich das falsch?

danke für deine Antwort

blackberry
15.07.2009, 19:23
#include <stdio.h>

#define ADDR_START 0x400000
#define ADDR_END 0x401000

#define _TRY \
asm( \
"pushl $_catch;" \
"pushl %fs:(0);" \
"movl %esp, %fs:(0);" \
"_try:" \
"pusha;" \
);
#define _CATCH \
asm( \
"jmp _endtry;" \
"_catch:;" \
"pusha;" \
);
#define _ENDTRY \
asm( \
"popa;" \
"movl 12(%esp), %eax;" \
"movl $_endtry, 0xB8(%eax);" \
"xorl %eax, %eax;" \
"ret;" \
"_endtry:;" \
"popl %fs:(0);" \
"addl $4, %esp;" \
);

int main(void)
{
unsigned char chr;
int addr;

for(addr = ADDR_START; addr < ADDR_END; addr++)
{
_TRY
{
chr = *((unsigned char *) addr);
printf("%.02X ", chr);
}
_CATCH
{
printf("-- ");
}
_ENDTRY
}

getc(stdin);
return 0;
}Klappt doch.
Syntaktisch: Ansi-C.
Inhaltlich: W32 gebunden.

SleiZer
15.07.2009, 19:32
gibt mir 3 Errors...

Assembler: Label expected in function 'main';
Duplicate label 'asm' in function 'main';
Duplicate label 'asm' in function 'main';

welchen compiler benutzt du, stellt sich meiner einfach nur an?

EBFE
15.07.2009, 19:51
Ich hatte mir das so vorgestellt, dass ich damit den Speicher anderer Programme einlesen kann. Ich hab mir das so gedacht, dass wenn ich über den Access Bereich des Programmes rüber komme ich im nächsten Programm lande. Seh ich das falsch?

Das wäre das Prinzip von DOS ;).

In modernen Betriebssystemen hat jeder Prozess einen eigenen virtuellen Speicherbereich : http://de.wikipedia.org/wiki/Virtuelle_Speicherverwaltung
kurz gesagt: das Programm kann auf den gesamten Speicher (theoretisch auf 32-Bit Rechnern auf 4GB, praktisch ist es durch OS interna auf 2-3 GB begrenzt) zugreifen, ohne einem anderen in die Quere zu kommen - die Umrechnung zwischen virtuellem und physikalischen wird durch MMU (memory managment unit) und OS bewerkstelligt und davon bekommt das Programm nichts mit.

Um auf den Speicher anderer Programme zuzugreifen muss man entwerder selbst in den "niedrigsten" Modus Ring0 (http://de.wikipedia.org/wiki/Ring_%28CPU%29) wechseln oder sich mit Betriebssystemfunktionen behelfen. Unter Windows wären das OpenProcess und ReadProcessMemory.

Um gezielt den kompletten Speicher lesen und darstellen zu können, muss man natürlich vorher auch ein paar Details über den Process herausfinden:
GetSystemInfo - damit bekommt man
lpMinimumApplicationAddress und lpMaximumApplicationAddress heraus und dann VirtualQueryEx auf die niedrigste Adresse - liefert einen

typedef struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress;
PVOID AllocationBase;
DWORD AllocationProtect;
SIZE_T RegionSize;
DWORD State;
DWORD Protect;
DWORD Type;}Es gibt da 3 Typen eines Speicherbereichs: commited, reserviert und frei. Die freien Bereiche kann man nicht mit ReadProcessMemory einlesen ;).
RegionSize sagt dann aus, wie groß der Block ist und damit lässt sich auch berechnen, wo der nächste Block liegt: BaseAddress + REgionSize. Bei dem nächsten Block führt man wieder VirtualQueryEx aus und kann sich so durch den ganzen Programmspeichern hangeln und diesen Anzeigen.

blackberry
15.07.2009, 20:03
welchen compiler benutzt du, stellt sich meiner einfach nur an?

Bei mir compiliert das unter MinGW32.

SleiZer
15.07.2009, 23:03
OpenProcess und ReadProcessMemory

scheinen mir Funktionen von c++ zusein, eigentlich hatte ich nicht vor
in c++ einzusteigen aber mir scheint es als hätte ich keine andere Wahl, oder
gibt es vergleichbares in Ansi-C?

Wenn ich es richtig verstehe, kann man von einem Prozess die Startaddresse des reservierten Speichers auslesen und die Größe viel Speicher Reserviert wurde, und dahinter fängt der nächste Prozess an?



Bei mir compiliert das unter MinGW32.

Ich schau mir den auch mal an, ich gammel hier noch mit einem einfachen C Compiler rum.

blackberry
15.07.2009, 23:25
Die WinAPI ist nicht notwendigerweise C++.
Versuch mal windows.h zu inkludieren.

Wenn der Header nicht vorhanden sein sollte musst du dir deine Prototypen eben selbst schreiben.

Für ReadProcessMemory lautet dieser wie folgt: ( http://msdn.microsoft.com/en-us/library/ms680553%28VS.85%29.aspx )

BOOL WINAPI ReadProcessMemory(
__in HANDLE hProcess,
__in LPCVOID lpBaseAddress,
__out LPVOID lpBuffer,
__in SIZE_T nSize,
__out SIZE_T *lpNumberOfBytesRead
);

Mit nativen Datentypen sollte das wie folgt lauten: (aus dem Gedächtnis übersetzt - muss nicht stimmen)

int __stdcall ReadProcessMemory(
void *hProcess,
void *lpBaseAddress,
void *lpBuffer,
unsigned int nSize,
unsigned int *lpNumberOfBytesRead
);

EBFE
16.07.2009, 09:11
scheinen mir Funktionen von c++ zusein, eigentlich
siehe BlackBerry - das sind Betriebssystemfunktionen (WinAPIs). Denn Prozessmanipulation ist Betriebssystemabhängig, man muss also die Schnittstellen/Funktionen nutzen, die das OS bietet - da bringt dich ANSI leider nicht weiter ;)


Wenn ich es richtig verstehe, kann man von einem Prozess die Startaddresse des reservierten Speichers auslesen und die Größe viel Speicher Reserviert wurde, und dahinter fängt der nächste Prozess an?
Nope. Eventuell bringt dich das weiter: http://msdn.microsoft.com/en-us/library/ms810616.aspx
Jeder Prozess hat 4 GB linear adressiertbaren eigenen Speicher, den er quasi nach belieben verändern kann,ohne dass er dabei System oder andere Programme gefährdet (nur sich selbst ;) ). Mit OpenProcess bekommst du ein Handle zu einem Prozess und alles was du ausließt, gehört nur zu diesem. Wenn du alle Prozesse auslesen möchtest (oder dem user eine Liste anbieten) musst du erstmal vom Betriebssystem eine solche Liste anfordern:
zuerst ein CreateToolhelp32Snapshot mit dem Parameter TH32CS_SNAPPROCESS. Dann kannst du die Rückgabe mit Process32First und Process32Next durchgehen und bekommst alle Infos zu dem Prozess geliefert:
http://msdn.microsoft.com/en-us/library/ms682489(VS.85).aspx (createToolhelp)
http://msdn.microsoft.com/en-us/library/ms684834(VS.85).aspx (Prozess32First)
http://msdn.microsoft.com/en-us/library/ms684839(VS.85).aspx (Processentry Struktur)


typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
DWORD th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
char szExeFile[MAX_PATH];
} PROCESSENTRY32;
typedef PROCESSENTRY32 * PPROCESSENTRY32;
typedef PROCESSENTRY32 * LPPROCESSENTRY32;


Hier ist ein Beispiel in C
http://www.google.com/codesearch/p?hl=en&sa=N&cd=2&ct=rc#1vVlSpePVl0/~hollingd/opsys-fall2006/code/win32/listprocs.c&q=lang:c%20Process32First%20process%20list

SleiZer
16.07.2009, 09:35
Irgendwie fehlt mir da gerade was.
Wenn openprocess und readprocessmemory funktionen sind,
dann muss ich denen ja die Werte geben und bekomme sie nicht von zurück was ich doch eingentlich wollte, oder verstehe ich da etwas falsch?

EBFE
16.07.2009, 10:00
OpenProcess möchte von dir

HANDLE WINAPI OpenProcess(
__in DWORD dwDesiredAccess,
__in BOOL bInheritHandle,
__in DWORD dwProcessId
); haben. Also für dwDesiredAccess z.B welche Zugriffsrechte du auf diesen Prozess haben möchtest.
Zum lesen des Speichers wären das PROCESS_VM_READ (==0x0010) und PROCESS_QUERY_INFORMATION (==0x0400). Beides sind Konstanten, die in windows.h oder ähnlichem definiert werden sollten (falls nicht, trägt man manuell eben die Werte ein).
Das mit dem Handle ist erstmal uninteressant. ProzessID wäre die Identifikationsnummer des Prozess. Wird z.B im Taskmanager angezeigt (muss man eventuell in der Ansicht -> PID Spalte aktivieren). Du bekommst die PID auch, wenn du wie im letzen Posting erwähnt, die Prozessliste durchgehst.
Wenn du z.B nach der PID von explorer.exe suchst, dann gehst du die Liste durch, bekommst eine PROCESSENTRY32 Struktur zurück und vergleichst den szExeFile String mit dem gewünschen (in dem fall "explorer.exe").
Du kannst natürlich auch erstmal die PID selber nachschauen und deinem Programm als Parameter übergeben.

ReadProccess möchte halt den Handle zum Prozess haben (den man mit OpenProcess bekommt), die gewünschte Adresse, ab der du den Speicher auslesen möchtest, wieviele Bytes du lesen willst und natürlich einen Buffer, in den überhaupt eingelesen werden kann (du kopierst damit den "fremden" Speicherbreich in deinen eigenen). Der letze Paramter ist die Adresse der Variablen, in die dann geschrieben wird, wieviele Bytes gelesen wurden. Hier kann man auch 0 übergeben.

SleiZer
16.07.2009, 11:43
#include <windows.h>
#include <stdio.h>
#define PROCESS_ID_MANUAL 4888

void main()
{
void *puffer;
void *pressid;
void *startip;
unsigned int *bytes;

pressid= OpenProcess(PROCESS_VM_READ,1,PROCESS_ID_MANUAL);
ReadProcessMemory(pressid,startip,puffer,100,bytes );

printf("%s", (char)*puffer);
}
Dies gibt mir nun ein Access Violation zurück. Aber sonst müsste alles richtig sein oder?

EBFE
16.07.2009, 12:46
Damit sind wir schon mal weiter ;)
Allerdings: puffer musst du schon mal selbst initialisieren. Deine startip ist auch nicht initialisiert. Um die Funktion zu testen, setze sie erstmal auf 0x400000 oder 0x1000000 (die üblichen Imagebasen der Exen - da sollte sich immer was zum auslesen finden). Außerdem solltest du entweder den Compiler nennen ("einfacher C Compiler" ist keine Bezeichnung ;) ) oder halt auf GCC (MingW32) für Windows umsteigen. Mein GCC wirft bei deinem Code schon ziemlich viele Warnungen ;)

Btw: mit printf %s den Speicherdump auszugeben ist keine gute Idee - es wird nur bis zur ersten 00 ein "string" ausgegeben.

Das ist dein Code etwas angepasst, getestet mit gcc:


#include <windows.h>
#include <stdio.h>
#define PROCESS_ID_MANUAL 1168

int main()
{
unsigned char puffer[256];
void *pressid;
void *startip;
unsigned int bytes;

pressid= OpenProcess(PROCESS_VM_READ,1,PROCESS_ID_MANUAL);
ReadProcessMemory(pressid,(void*)0x400000,puffer,1 00,&bytes);

printf("%s", puffer); /* gibt nur MZ aus, weil danach 00 kommen */
/*bisschen aufgeppept*/
printf("konnte 0x%X Bytes einlesen\n",bytes);
int i;
for (i=1;i<bytes-1;i++)
{
printf("%0.2X ",puffer[i-1]);
if ((i%16==0)) printf("\n"); /*Zeilenumbruch alle 16 Bytes*/
}
return 0;
}

Ausgabe (PID ist in dem fall von meinem Explorer)


MZÉkonnte 0x64 Bytes einlesen
4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00
B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 E8 00 00 00
0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68
69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F
74 20

Keksdose
16.07.2009, 13:02
cool läuft (Dev-C++) Mir stellen sich da 2 Fragen:
1. Warum stürzt das irgend wann ab? also wie bei einer Endlosschleife "has stopped working"
2. Was fang ich denn damit an? Kann ich auch meinen eigenen Code da reinschmuggeln oder so?

PS: kenn mich in C++ aus aber net mit Speicher ...

SleiZer
16.07.2009, 15:39
Also jeder Prozess kriegt einen Speicher von 4gb zugewiesen.
Das heißt man müsste von der Addresse 0 bis zur Addresse 4gb,
auslesen um den ganzen Prozess zusehen?

Wenn ich jetzt von der Addresse 0 start hört er aber nicht mehr auf zulaufen.
Er läuft bis zu einem Access Violation.

Kann man vor dem Access Violation auch anhalten oder muss ich den Access Violation unterdrücken, damit das Programm nicht abstürtzt?

EBFE
16.07.2009, 16:43
1. Warum stürzt das irgend wann ab? also wie bei einer Endlosschleife "has stopped working"
2. Was fang ich denn damit an? Kann ich auch meinen eigenen Code da reinschmuggeln oder so?
1)keine Kristallkugel anwesend ;). Ist nur rudimentärer Beispielcode, ohne Fehlerprüfungen.
2)Threadstarter wollte speicher anderer Programme auslesen. Und anfangen kann man damit schon einiges. Unter anderem auch eigenen Code/Daten schreiben ;) (Stichwort:Codeinjection, memory patching).



Wenn ich jetzt von der Addresse 0 start hört er aber nicht mehr auf zulaufen.
Er läuft bis zu einem Access Violation.

Kann man vor dem Access Violation auch anhalten oder muss ich den Access Violation unterdrücken, damit das Programm nicht abstürtzt?

Du kannst nicht ALLES auslesen. Wie schon gesagt, es werden nicht die geamten 4 GB belegt. Viele Bereiche sind erstmal frei und werden erst auf eine Anforderung des Programms zugewiesen (z.B über VirtualAlloc bzw über C-Runtime DLL mit malloc - kommt allerdings auf die konkrete C-Runtime Bibliothek an)[1]. Vor dem Auslesen solltest du also erstmal mit VirtualQueryEx prüfen, wie groß der Bereich ist und welche Flags er hat.

Wenn der Speicher eine MEM_FREE Markierung besitzt, hat es keinen Sinn, diesen auszulesen ;). Dann geht man zum nächsten Speicherblock.

Btw: Ne AccessViolation kommt in dem Fall sowieso nicht vom auslesen (da liefert ReadProcessMemory eine 0 zurück), sondern weil ich im Beispielcode die
"bytes" als unsigned deklariert hab. Wenn 0 Bytes eingelesen wurde, stand in der FOR-schleife praktisch for(i=0;i<0xFFFFFFFE;i++). Das ist der Nachteil, wenn man mal schnell eine hübsche Ausgabe basteln will. Mit einem Debugger allerdings sehr schnell zu finden - und mit dem sollte man sich sowieso anfreunden, wenn man sich mit Low-Level Sachen (Speicherauslesen, Manipulieren usw) beschäfitgen möchte ;)

korrektes Beispiel:

#include <windows.h>
#include <stdio.h>
#define PROCESS_ID_MANUAL 1168

int dump(void* hProcess,void *base,int size)
{
unsigned char* buff= malloc(size);
int bytes=0;

int result=ReadProcessMemory(hProcess,base,buff,size,&bytes);
if (result!=0)
printf("konnte 0x%X Bytes einlesen von 0x%0.8X\n",bytes,base);
else
{
printf("konnte 0x%0.8X NICHT auslesen\n",base);
free(buff);
return 0;
}

int i;
for (i=1;i<bytes-1 && i<size-1;i++)
{
printf("%0.2X ",buff[i-1]);
if ((i%16==0)) printf("\n"); /*Zeilenumbruch alle 16 Bytes*/
}
free(buff);
return 0;
}
int main()
{
unsigned char puffer[256];
void *pressid;
void *startip;
pressid= OpenProcess(PROCESS_VM_READ,1,PROCESS_ID_MANUAL);
dump(pressid,(void*)0x1000,50);
dump(pressid,(void*)0x400000,50);
dump (pressid,(void*)0x600000,50);
dump (pressid,(void*)0x80000000,50); /*sollte nix ausgeben,
da OS reserviert*/
return 0;
}

ausgabe (bei mir zumindest):

konnte 0x00001000 NICHT auslesen
konnte 0x32 Bytes einlesen von 0x00400000
4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF 00 00
B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
konnte 0x32 Bytes einlesen von 0x00600000
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
konnte 0x80000000 NICHT auslesen
Schöner wäre es allerdings wie erwähnt, mit VirtualQueryEx erstmal abzufragen, was Sache ist. Denn die meisten kleineren Programme belegen nur paar MB des RAMs. Man braucht also nicht stur alle 4GB durchzuklappern und zu dumpen (was dann auch recht langsam wäre).

PS/OT: GCC -Wall wird ein paar Warnungen ausspucken weil bei den APIs ich recht gnadenlos caste. Allerdings hatte ich keine Lust in den Defs/Headern zu wühlen und richtige typedefs zu suchen - das sollte schließlich nur ein Beispiel sein ;). Außerdem kann ich C nicht wirklich - erinnert mich zu sehr an C++, um sich damit rumzuplagen :)

SleiZer
25.07.2009, 22:35
Also mein Programm sieht jetzt so aus:


#include <windows.h>
#include <stdio.h>
#include<fcntl.h>
#define PROCESS_ID_MANUAL 1484

int main()
{
unsigned char puffer[1024];
void *pressid;
void *startip;
unsigned int bytes=1024;
char puffer2[2] ="H";
int stream_r;
char test[] = "c:/text.txt";
stream_r=open(test,O_WRONLY);
pressid= OpenProcess(PROCESS_VM_READ,1,PROCESS_ID_MANUAL);
ReadProcessMemory(pressid,(void*)0x0,puffer,100,&bytes);

printf("%s", puffer); /* gibt nur MZ aus, weil danach 00 kommen */
/*bisschen aufgeppept*/
printf("konnte 0x%X Bytes einlesen\n",bytes);
int i;
for (i=1;i<bytes-1;i++)
{
printf("%0.2X ",puffer[i-1]);
// if(isprint(puffer[i-1]) || puffer[i-1] == 20)
if(puffer[i-1] != 0)
{
write(stream_r,&puffer[i-1],1);
}
if ((i%16==0)) printf("\n"); /*Zeilenumbruch alle 16 Bytes*/
}
system("pause");
return 0;
} nun kriege ich aber Trotz abfrage von zwei verschiedenen Prozessen das selbe Ergebnis ... Wie kann das sein?

Ich glaube das weicht gerade vom Topic ab...
ich habe das Thema im Thread neugestartet
http://free-hack.com/showthread.php?p=360212#post360212
ansonsten kann dieser thread geschlossen werden ...