VC++ Zugriffsverweigerung(Dereferenzierung)



  • Zunächst möchte ich feststellen, dass diese Frage nichts mit der MFC oder MSVS zu tun hat, sondern im Code. 🙂

    Folgendes: in deiner EXE, die von Windows ausgeführt wird, befindet sich Binärcode. Dieser Binärcode kann wiederum unterteilt werden in Daten und Maschinencode. Der Maschinencode greift auf die Daten zu, und die Daten bieten dem Code eine Möglichkeit, sich auszutoben, und das nennt man dann Ausführung.

    Wenn ein Prozess gestartet wird, bekommt dieser erst einmal 4 GB an RAM zugewiesen (auf 32-Bit-Systemen). Diese sind allerdings rein virtuell und müssen erst über Einträge in den Pagefiles mit reelem, physikalisch existieren Speicher verknüpft werden (über malloc / calloc , dem new -Operator oder einer HeapXXXX -Funktion), sonst haut der Kernel/Betriebssystem eine Fehlermeldung raus. Diese 4 GB werden dann der Reihe nach mit dem Binärcode gefüllt. Maschinencode befindet sich für gewöhnlich im Codesegment (oder mehreren), Daten im Datensegment. Früher hatte das einen wichtigen Grund, aber ich glaube, heute macht man das nur der Abwärtskompatibilität wegen.

    Weiter im Text: in der EXE steht ziemlich genau, wo das Programm eingeladen sein will. Im virtuellen Speicher (übrigens hast du immer nur auf diesen Zugriff, außer bei der Treiberprogrammierung, habe ich gehört, aber nimmer damit beschäftigt) wird der Rotz dann eingeladen und angelegt, und der Code verlässt sich darauf, dass die Adressen des Speichers für die Daten im Datensegment richtig sind (auch hier gibt es Ausnahmen, wenn ein DLL-Modul oder eine Bibliothek meint, ausgerechnet in den von deiner Anwendung ausgewählten Lieblingsbereich geladen zu werden, dann muss das Programm an eine andere Stelle geladen und die Adressen hochgerechnet werden. Das sollte allerdings kaum passieren, da die Windows-DLL standardmäßig am Arsch des virtuellen Speichers geladen werden und daher mit deinem Programm fast nie in Berührung kommen sollten). Auf diese wird tatsächlich statisch zugegriffen:

    1. Funktion muss aufgerufen werden, übernimmt einen Parameter. Die Adresse des zu übergebenden Wertes ist 0x12345678.
    2. An Stelle 0x12345678 wird der Wert abgefragt und dann auf den Stack als Parameterübergabe gelegt (wobei es hier Unterschiede in der Reihenfolge und in dem Objekt, welches die Daten auf den Stack legt, gibt, aber das braucht dich nicht zu kümmern).
    3. Fertig.

    Diese Art der Referenzierung ist ziemlich einfach. Man ruft einfach den Wert an einer Adresse ab und verwendet ihn.

    Allerdings hat das auch einen Nachteil, welche einem umso bewusster wird, wenn man C oder C++ als LowLevel-Sprache definiert, weil Sie nativ keine Strings unterstützt. Es gibt zwar gute Klassen, die diese emulieren, aber nativ sind diese nicht in der Sprache vorhanden - und zwar aus dem Grund, weil zur Kompilierzeit nicht ersichtlich ist, wie groß das Objekt später ist. Ähnlich in den Segmenten und im Stack, hier muss auch bekannt sein, wie groß das Objekt ist, welches man verwenden will. 😞

    Deshalb hat ein weiser und liebender Gott Zeiger erfunden. Zeiger sind nichts anderes als ganz normale Variablen, in denen Werte stehen. Der Unterschied zu normalen Variablen ist jedoch, dass man bei Zeigern den Wert als Adresse interpretiert. In dieser Adresse können dann Daten stehen, und zwar sehr viel dynamischere, als es uns die Datensegmente oder der Stack erlauben. Der Zeiger an sich ist immer noch auf dem Stack oder im Segment, aber wenn man den Wert in ihm als Adresse interpretiert, kommt man zu freiem Speicher - wenn man ihn dem Betriebssystem so verkauft hat, dafür gibt es die bereits oben genannten Wege a'la new . Denn das OS bestimmt, was frei ist und was nicht.

    1. Funktion muss aufgerufen werden, übernimmt einen Parameter. Dieser ist unbekannt, aber im Code steht, dass, wenn man den Wert an Adresse 0x12345678 wiederum als Adresse interpretiert, dieser Wert erscheint.
    2. An Stelle 0x12345678 wird der Wert abgefragt und als Adresse interpretiert.
    3. An der interpretierten Adresse findet sich der gesuchte Wert.
    4. Der gefundene Wert wird auf den Stack gelegt.
    5. Fertig.

    Langer Rede kurzer Sinn: ich habe keine Ahnung, was du genau versuchst, aber eventuell solltest du nicht nach der Adresse, sondern nach dem Zeiger suchen. 😉

    EDIT: Es kann auch sein, dass ein Zeiger nicht im Stack oder in den Segmenten liegt. In diesem Fall braucht es einen Zeiger auf einen Zeiger, um auf diese Adresse weisen zu können.

    int main()
    {
        char A;                //Wert auf dem Stack
        char*B=&A;             //Zeiger, welcher auf den zuvor angelegten Wert auf dem Stack zeigt.
        char*C=new char[10];   //Vom OS einen Speicherbereich von sizeof(char)*10, also 10 Bytes fordern.
        delete[] C;            //Diesen aber auch flugs wieder löschen.
        char**D=new char*[10]; //Sophisticated - man erstellt einen Zeiger auf dem Stack und fordert Speicherplatz für
                               //10 Zeiger auf einen character ein. Diese brauchen dann auch ihren Speicher.
    
        for(int i=0;i<10;i++)
            D[i]=new char[10];  //Jedem dieser Zeiger, auf den D zeigt, ebenfalls Platz für 10 characters einräumen.
    
        //Nun darf man D nicht einfach löschen. Der Speicher, auf den D zeigt, geht dadurch verloren, und damit auch die
        //Adressen, auf die die Unterzeiger von D gezeigt haben. Hier also 100 Bytes, die man bei OS wieder als
        //Arbeitslos melden muss. :)
        for(int i=;i<10;i++)
            delete[] D[i];
    
        //Nun kann man D löschen.
        delete[] D;
        return 0;
    }
    

    Ich hoffe, du hast das verstanden.



  • Infemo schrieb:

    Allerdings ist dies dcoh ein Pointer. Dieser bleibt immer gleich und zeigt auf die richtige Adresse.

    Der Pointer sollte sich keineswegs ändern.

    Wenn ja, wie kann ich dies verhindern?

    Der Pointer selbst hat ja auch eine Adresse. Der Zugriff darauf schlägt ja schon fehl und zwar wie gesagt aus dem Grund, dass Windows dein Programm bei jedem Start an eine andere Speicheradresse läd (die Startadresse/Ladeadresse/etc dies Moduls). Übrigens genau um das zu verhindern bzw zu erschweren was du vorhast (Malwareautoren können sich nicht mehr auf feste, immer gleiche Adressen in ihren Exploits verlassen).

    Intern im Programmcode sind Zugriffe meistens relativ zur Startadresse kodiert (also nicht "Zugriff auf absolut 0xABCD" sondern als "Zugriff auf $aktPos+0x1234"). Wenn sie absolut sind, dann trägt Windows beim Laden des Programms dafür Sorge, dass die entsprechenden Offsets aufaddiert werden.

    Du musst also nicht die absolute Adresse deines Pointers nehmen sondern die Pointeradresse relativ zur Startadresse deines Programms ermitteln. Jedesmal beim Laden von deiner DLL wiederrum die aktuelle Ladeadresse ermitteln und dir damit die richtige Adresse ausrechnen.

    Wenn Dir das alles zu hoch ist, nimm für den Anfang bzw zum Testen Windows XP, da bleiben die Adressen gleich und es funktioniert mit deiner aktuellen Methode (sofern Du sonst keine Fehler drin hast, natürlich).



  • @Der aus dem Westen...

    Vielen Dank für diese umfangreiche Antwort. Viele Dinge waren mir unklar bzw. ich habe noch nie von dem was Sie geschrieben haben gehört, allerdings war es sehr interessant und belehrend!
    Soweit ich das mitbekommen habe, haben wir den Wert des Zeigers auf dem Stack, warum genau ein Stack weis ich nicht, aber ich bin mir sicher, man kann auch andere Datenstruckturen verwenden 😉
    Allerdings möchte ich den Wert verändern und dies funktioniert nur durch Dereferenzierung, indem ich den Wert eines, auf ein Objekt zeigenden, Zeiges verändere.
    Das Objekt in diesem Fall wäre die Adresse.
    Hier missling jedoch die Berechnung, weswegen das Verwenden auch nicht funktioniert 😉

    Ich habe verstanden was Sie erklärt haben, leider bringt mich das momentan noch nicht weiter 😞
    Der Witz mit den Arbeitslosen war aber gut 😉

    Nochmal zur Erklärung:
    Ich möchte durch Dll Injection einen Wert in einem Programm (in diesem Fall Inkball) verändern. Da sich die Adresse jedoch bei jedem Programmstart änder brauche ich einen Pointer.
    Diesen habe ich mit Hilfe von Cheat Engine herausgefunen:

    DWORD Address_Base = 0x010AFFD4;// Baseadresse
    DWORD Offset = 0x26B4;
    DWORD Offset2 = 0x28;
    

    Diese drei Adressen sollten durch folgenden Berechnung die Adresse ergeben, von welche ich den Wert verändern möchte.

    DWORD Address1 = *(DWORD*)(Address_Base + Offset);
    DWORD Address_Outcome = *(DWORD*)(Address1 + Offset2);
    

    Um den Wert der Adresse verändern zu können, kommt die Dereferenzierung ins Spiel:

    //Zeiger
    int *Pointer; 
    
    //Adresse übergeben
    *Pointer = *(DWORD*)Address_Outcome;
    
    //Dereferenzieren
    *Pointer=5000;
    

    Allerdings funktioniert dies ja nicht, da Windows 7 die Ladeadressen randomisiert bei jedem Programmstart und diese sich so ändern.
    Allerdings sollte dies durch die Benutzung des Pointers egal sein.
    Es funktioniert trotzdem nicht^^

    Problem verstanden? 🙂

    @Morle
    Meinst du ich sollte die Moduladresse von dem Prozess in den die Dll injeziert wurde nehmen und die zu der Adresse addieren?
    Wie komme ich denn von der Absoluten auf die Adresse, welche relativ zur Programmstartadresse ist?

    Das Problem liegt darin, dass ich kein Windows XP zum testen zur Verfügung habe 😉
    Also bleibt mir nur die Hoffnung auf weitere hilfestellung von Ihnen!

    Mit freundlichen Grüßen
    Infemo



  • Infemo schrieb:

    @Der aus dem Westen...

    Vielen Dank für diese umfangreiche Antwort. Viele Dinge waren mir unklar bzw. ich habe noch nie von dem was Sie geschrieben haben gehört, allerdings war es sehr interessant und belehrend!

    Kannst mich übrigens ruhig duzen, habe die 21 nicht mal überschritten und will mich nicht so alt fühlen. 😃

    Infemo schrieb:

    Soweit ich das mitbekommen habe, haben wir den Wert des Zeigers auf dem Stack, warum genau ein Stack weis ich nicht, aber ich bin mir sicher, man kann auch andere Datenstruckturen verwenden 😉

    Jetzt sprichst du was an. Als ich mich in die Materie einlaß, habe ich folgende Arten von Speicher kennengelernt:

    - Datenspeicher (wo globale Variablen und statische Elemente definiert werden)
    - Codespeicher (nona, wo der Code nun mal geladen wird)
    - Register (Speicher direkt auf der CPU, sehr klein, sehr beschränkt - seit x64 nicht mehr so stark, aber für den kompiliert ja kaum einer für Windows - aber sehr schnell)
    - Stack (C++ ist wie C sehr LowLevel-basiert, weshalb einige Konzepte 1:1 in Maschinencode übertragen werden können. So das Prozedurenkonzept - wenn eine Funktion aufgerufen wird, die ihre eigenen Werte definiert und speichert, werden diese auf den Stack gelegt und bei Rückkehr wieder weggenommen. Last in, first out - dieser Speicher ist, verglichen mit den vorherigen drei Speichern dynamischer, aber nicht dynamisch genug)
    - Heap (der restliche Speicher, nicht vom Betriebssystem reserviert. Der freieste Speicher, aber auch der, für den man die größte Verantwortung übernimmt, denn diesen muss man auch wieder explizit freigeben)

    Mit diesem Modell kann man sich gut vorstellen, warum der Zeiger an sich im Datensegment oder auf dem Stack liegen sollte - man kennt die Adresse. Zugriff ist sehr wichtig, vor allem auf dem Heap. Wenn ich mit new Speicher reserviere, dessen Adresse aber in keinem Zeiger reserviere, den ich irgendwie ansprechen kann, dann kann ich diesen auch nicht wieder freigeben. Und der Basiszugriff läuft nur über diese Speicher ab - am Ende kommst du nur auf konstante Adressen, in denen Werte als dynamische Adressen gesichert werden.

    Infemo schrieb:

    Allerdings möchte ich den Wert verändern und dies funktioniert nur durch Dereferenzierung, indem ich den Wert eines, auf ein Objekt zeigenden, Zeiges verändere.
    Das Objekt in diesem Fall wäre die Adresse.
    Hier missling jedoch die Berechnung, weswegen das Verwenden auch nicht funktioniert 😉

    Der sagt denn, dass an dieser Stelle ein Zeiger auf dem Stack oder im Datensegment liegt? Es kann ja sehr gut sein, dass am Ende ein Zeiger auf dem Stack auf einen Zeiger im Heap zeigt, und dieser zeigt dann auf den von dir gewünschten Wert. Wenn du also die Kette verstehen willst, reicht es nicht, davon auszugehen, dass Zeiger immer eine konstante Adresse aufweisen, denn nicht du oder der Compiler bestimmen bei dynamischer Speicherverwaltung, wo der Zeiger am Ende hinzeigt, sondern das OS.

    Such also erst einmal die Adresse auf dem Stack, interpretiere dann den Wert als Adresse, hohl dir dann den Wert an dieser Adresse und interpretiere diese noch einmal (interpretieren==dereferenzieren).

    Infemo schrieb:

    DWORD Address_Base = 0x010AFFD4;// Baseadresse
    DWORD Offset = 0x26B4;
    DWORD Offset2 = 0x28;
    

    Diese drei Adressen sollten durch folgenden Berechnung die Adresse ergeben, von welche ich den Wert verändern möchte.

    DWORD Address1 = *(DWORD*)(Address_Base + Offset);
    DWORD Address_Outcome = *(DWORD*)(Address1 + Offset2);
    

    Ich kenne mich mit dem genannten Programm nicht aus, nur mit IDA. 😃 Eventuell ist dessen Analyse aufschlussreicher, Schaden kann es nicht.

    Infemo schrieb:

    Allerdings funktioniert dies ja nicht, da Windows 7 die Ladeadressen randomisiert bei jedem Programmstart und diese sich so ändern.
    Allerdings sollte dies durch die Benutzung des Pointers egal sein.
    Es funktioniert trotzdem nicht^^

    Das Windows 7 die Ladeadressen randomisiert ist mir relativ neu. Ich erinnere mich an eigene Experimente, wo ich aus einer einfachen Scriptsprache heraus Werte aus konstanten Adressen auslesen konnte. Ja, diese waren KONSTANT im Code definiert, im Datensegment der Anwendung - und diese Experimente fanden unter Windows 7 statt.

    Die Randomisierung würde auch eigentlich dem Sinn von PE-Modulen widersprechen. Weshalb sonst sollte man die DLL-Module in die hinteren Reihe des virtuellen Speichers schmeißen, wenn am Ende Anwendungen nicht an Basisadresse 0x04001000 (glaube zumindest, so oder so ähnlich war die Standardadresse für Anwendungen) geladen werden sollen? Das ganze würde mehr an das ELF-Format von Linux erinneren, in dem Anwendungen tatsächlich dynamisch in den Speicher geladen werden. Dies führt (wenn man Wikipedia vertrauen darf) dazu, dass die Adressen hochgerechnet werden müssen, bei jedem Adresszugriff, was zu langsameren Ausführungszeiten kann.

    Wenn du mir erklären kannst, wie das gehen soll, würde ich mich furchtbar gerne belehren lassen. 😉



  • Zunächst:
    http://en.wikipedia.org/wiki/Address_space_layout_randomization

    Wie dem auch sei, eigentlich hätte ich gedacht ich hätte genug geschrieben. Die richtige Adresse (bzw das richtige Offset) findet man, indem man:

    a) Sich die Moduladresse "m" notiert
    b) Sich dann die Adresse des Pointers "p" raussucht (ohne das man zwischendurch das Programm beeendet natürlich)

    c) sich den Offset "o" berechnet als "o=p-m"

    Im Folgenden kann man sich dann die richtige Adresse jedesmal ermitteln, indem man zunächst die aktuelle Moduladresse ermittelt und dann jeweils "o" draufaddiert.

    Und jetzt mache ich Fondue 🙂



  • Ein Nachtrag noch:

    Das was ich schrieb gilt für Pointer die im Datensegment liegen.



  • Der aus dem Westen... schrieb:

    Kannst mich übrigens ruhig duzen, habe die 21 nicht mal überschritten und will mich nicht so alt fühlen.

    Okay, konnte ich nicht wissen, nur eine Frage des Respektes 😉

    Der aus dem Westen... schrieb:

    - Datenspeicher (wo globale Variablen und statische Elemente definiert werden)
    - Codespeicher (nona, wo der Code nun mal geladen wird)
    - Register (Speicher direkt auf der CPU, sehr klein, sehr beschränkt - seit x64 nicht mehr so stark, aber für den kompiliert ja kaum einer für Windows - aber sehr schnell)
    - Stack (C++ ist wie C sehr LowLevel-basiert, weshalb einige Konzepte 1:1 in Maschinencode übertragen werden können. So das Prozedurenkonzept - wenn eine Funktion aufgerufen wird, die ihre eigenen Werte definiert und speichert, werden diese auf den Stack gelegt und bei Rückkehr wieder weggenommen. Last in, first out - dieser Speicher ist, verglichen mit den vorherigen drei Speichern dynamischer, aber nicht dynamisch genug)
    - Heap (der restliche Speicher, nicht vom Betriebssystem reserviert. Der freieste Speicher, aber auch der, für den man die größte Verantwortung übernimmt, denn diesen muss man auch wieder explizit freigeben)

    Und ich dachte ich hätte Ahnung 😃 Mit DAtenstruckturen kenn ich mich relativ gut aus, aber das zwei als "Hauptspeicher" dienen war mir nie bewusst

    Der aus dem Westen... schrieb:

    Das Windows 7 die Ladeadressen randomisiert ist mir relativ neu. Ich erinnere mich an eigene Experimente, wo ich aus einer einfachen Scriptsprache heraus Werte aus konstanten Adressen auslesen konnte. Ja, diese waren KONSTANT im Code definiert, im Datensegment der Anwendung - und diese Experimente fanden unter Windows 7 statt.

    Die Randomisierung würde auch eigentlich dem Sinn von PE-Modulen widersprechen. Weshalb sonst sollte man die DLL-Module in die hinteren Reihe des virtuellen Speichers schmeißen, wenn am Ende Anwendungen nicht an Basisadresse 0x04001000 (glaube zumindest, so oder so ähnlich war die Standardadresse für Anwendungen) geladen werden sollen? Das ganze würde mehr an das ELF-Format von Linux erinneren, in dem Anwendungen tatsächlich dynamisch in den Speicher geladen werden. Dies führt (wenn man Wikipedia vertrauen darf) dazu, dass die Adressen hochgerechnet werden müssen, bei jedem Adresszugriff, was zu langsameren Ausführungszeiten kann.

    Wenn du mir erklären kannst, wie das gehen soll, würde ich mich furchtbar gerne belehren lassen.

    Die Basisadresse sollte im Normalfall 0x400000, jedoch hat sich das Problem bereits gelößt
    Ich brauche keine Moduleadressen^^ Und eigentlich wiederspricht die dann auch dem randomisieren, da es so einwanfrei funktioniert. Habe das Programm bestimmt 20Mal neugestartet und es hat jedes Mal geklappt 😉

    void RewriteValues()
    {
    	DWORD Address1 = *(DWORD*)(Address_Base) + Offset;
    	DWORD Address_Outcome = *(DWORD*)(Address1) + Offset2; 
    
    	*(DWORD*)Address_Outcome = 5000;
    }
    

    Falsche Klammern und den neuen Zeiger brauche ich auch nicht

    Und ich glaube ich wäre nicht in der Lage Dich zu belehren!
    Aber ich wünsche Dir einen guten Rutsch(gehabt zu haben)ins neue Jahr und alle Gute für 2012

    Morle schrieb:

    Wie dem auch sei, eigentlich hätte ich gedacht ich hätte genug geschrieben. Die richtige Adresse (bzw das richtige Offset) findet man, indem man:

    a) Sich die Moduladresse "m" notiert
    b) Sich dann die Adresse des Pointers "p" raussucht (ohne das man zwischendurch das Programm beeendet natürlich)

    c) sich den Offset "o" berechnet als "o=p-m"

    Im Folgenden kann man sich dann die richtige Adresse jedesmal ermitteln, indem man zunächst die aktuelle Moduladresse ermittelt und dann jeweils "o" draufaddiert.

    So würde es auch funktionieren, allerdings wäre das nur ein normaler Pointer. In diesem Fall haben wir einen Level 2Pointer. Bei komplexeren Programmen geht es auch bis auf 5 hoch. Da würde das dann etwas komplizierter werden, glaube ich.
    Aber das Problem hat sich ja schon gelößt. Die Adresse ist somit auch eine Absolute 😉

    Morle schrieb:

    Und jetzt mache ich Fondue 🙂

    Hoffe Du hast es Dir schmecken lassen 😉
    Dir auch noch ein frohes neues Jahr 😉 Und alles Gute!

    Vielen Dank für Eure Hilfe! Auch wenn wir nicht direkt zur Lösunge gelant sind, hat denke ich jeder etwas neues dazu gelernt^^
    Also wenn Ihr Fragen zu Cheat Engine oder Dereferenzierung bezüglich einer absoluten Adresse eines Level 2 Pointers habt, dann fragt mich 😃

    Wüsche allen ALLES GUTE für das Jahr 2012!

    Mit freundlichen Grüßen
    Infemo

    Edit (Martin Richter): Quotes gefixed



  • "Maximal sind 10 Smielys erlaubt."
    Entschuldigt meine Ernsthaftigekeit in dem schreiben aber WTF?!

    Und mein mniese Zitieren ^^
    Ich habe die Codes nicht drauf 😕

    Bis dann 🙂



  • Infemo schrieb:

    Okay, konnte ich nicht wissen, nur eine Frage des Respektes 😉

    Rüschpeckt. Du bist gut. 😃

    Infemo schrieb:

    Und ich dachte ich hätte Ahnung 😃 Mit DAtenstruckturen kenn ich mich relativ gut aus, aber das zwei als "Hauptspeicher" dienen war mir nie bewusst

    Eigentlich ist der Heap ja auch der Hauptspeicher. Ah, das geht sehr weit in die Windowsprogrammierung rein.

    Wenn du deine Anwendung startest, wird erst einmal ein Prozess gestartet. Dieser Prozess bekommt seine 4 GB an vir. Speicher und einen eigenen Heap. Muss ich so nennen, obwohl es eigentlich nur einen Heap gibt, aber in Windows kann man nun mal mehrere Heaps verwalten. Heap ist englisch, bedeutet "Halde" oder "Haufen", und das Konzept dahinter ist auch recht logisch. Es gibt einen globalen Heap für den Prozess, auf den jeder Thread zugreifen kann (mit der Win-API-Funktion [url)http://msdn.microsoft.com/en-us/library/windows/desktop/aa366569%28v=vs.85%29.aspx]GetProcessHeap[/url] ), und du kannst auch noch weitere Heaps erstellen, in denen du dann deine Sachen speicherst. Allerdings geschleunigt dies die Fragmentierung im virtuellen Adressraum, sodass der Kernel irgendwann meldet, dass für dein Prozess kein Speicher mehr übrig ist, selbst wenn im physikalisch vorhanden Speicher noch Platz für mindestens fünf chinesische Großstädte ist.

    Der Stack ist da anders. Mit deinem Prozess bekommst du automatisch auch einen Thread ( main , WinMain , _tmain und wie sie nicht alle heißen - ich persönlich verwende nur main oder WinMain ), den dein Prozess abarbeitet. Du kannst weitere Thread erstellen lassen (vom OS, C++ definiert nativ keine Threads, oder zumindest noch nicht - ich meine, gehört zu haben, dass sich dies mit dem Standard C++11 ändert, aber sicher bin ich mir nicht), welche dann Code ausführen. Jeder Thread verfügt über einen eigenen Stack, und auf den Stack anderer Threads sollte man möglichst nicht zugreifen. Wenn man es schafft, das Programm so zu schreiben, dass die Threads weitestgehend unabhängig voneinander arbeiten können, ohne dass Thread A Thread B in irgendwelche Variablen reinfuscht (wie gesagt, da kein Teil des Standards, muss man sich hier auf die Funktionen des OS verlassen), ist die Anwendung Threadsicher. Bei jedem Prozess bekommst du also einen Heap und bei jedem Thread einen eigenen Stack zur Verfügung gestellt.

    Infemo schrieb:

    Die Basisadresse sollte im Normalfall 0x400000, jedoch hat sich das Problem bereits gelößt
    Ich brauche keine Moduleadressen^^ Und eigentlich wiederspricht die dann auch dem randomisieren, da es so einwanfrei funktioniert. Habe das Programm bestimmt 20Mal neugestartet und es hat jedes Mal geklappt 😉

    Ich habe etliche PE-Exen mit Hex-Editoren, die dafür ausgelegt waren, Maschinencode zu interpretieren (HIEW) und Disassemblern (IDA) geöffnet. Ich meine, mich zu erinnern, dass die ersten 0x1000 Bytes Headerdaten beinhalten (ist allerdings nicht fix - das PE-Format ist recht dynamisch, je nachdem, was du später alles im Speicher haben willst), dann kommt ein Adresssprung, und plötzlich bin ich bei 0x00401000 oder um den Dreh. Aber gut ... ich habe ja gesagt, dass die Sachen statisch im Stack oder in den Datensegmenten stehen. 😉

    Infemo schrieb:

    Und ich glaube ich wäre nicht in der Lage Dich zu belehren!
    Aber ich wünsche Dir einen guten Rutsch(gehabt zu haben)ins neue Jahr und alle Gute für 2012

    Ach, quatsch. Man lernt nicht aus. 😉

    Infemo schrieb:

    So würde es auch funktionieren, allerdings wäre das nur ein normaler Pointer. In diesem Fall haben wir einen Level 2Pointer. Bei komplexeren Programmen geht es auch bis auf 5 hoch. Da würde das dann etwas komplizierter werden, glaube ich.
    Aber das Problem hat sich ja schon gelößt. Die Adresse ist somit auch eine Absolute 😉

    Es war ein Adressbereich, der dynamisch erstellt wurde, richtig? 😃

    Dir auch ein interessantes Jahr 2012.



  • [qoute="Der aus dem Westen..."]Rüschpeckt. Du bist gut. 😃 [/qoute]
    Danke, ist einfach nur meine Meinung 🙂
    [qoute="Der aus dem Westen..."]
    Eigentlich ist der Heap ja auch der Hauptspeicher. Ah, das geht sehr weit in die Windowsprogrammierung rein.

    Wenn du deine Anwendung startest, wird erst einmal ein Prozess gestartet. Dieser Prozess bekommt seine 4 GB an vir. Speicher und einen eigenen Heap. Muss ich so nennen, obwohl es eigentlich nur einen Heap gibt, aber in Windows kann man nun mal mehrere Heaps verwalten. Heap ist englisch, bedeutet "Halde" oder "Haufen", und das Konzept dahinter ist auch recht logisch. Es gibt einen globalen Heap für den Prozess, auf den jeder Thread zugreifen kann (mit der Win-API-Funktion [url)http://msdn.microsoft.com/en-us/library/windows/desktop/aa366569%28v=vs.85%29.aspx]GetProcessHeap[/url] ), und du kannst auch noch weitere Heaps erstellen, in denen du dann deine Sachen speicherst. Allerdings geschleunigt dies die Fragmentierung im virtuellen Adressraum, sodass der Kernel irgendwann meldet, dass für dein Prozess kein Speicher mehr übrig ist, selbst wenn im physikalisch vorhanden Speicher noch Platz für mindestens fünf chinesische Großstädte ist.[/qoute]
    Achso. Warum aber verwendet man einen Heap?
    Deine Vergleiche sind episch: " selbst wenn im physikalisch vorhanden Speicher noch Platz für mindestens fünf chinesische Großstädte ist. " 😃
    [qoute="Der aus dem Westen..."]
    Der Stack ist da anders. Mit deinem Prozess bekommst du automatisch auch einen Thread (main, WinMain,_tmain und wie sie nicht alle heißen - ich persönlich verwende nur main oder WinMain), den dein Prozess abarbeitet. Du kannst weitere Thread erstellen lassen (vom OS, C++ definiert nativ keine Threads, oder zumindest noch nicht - ich meine, gehört zu haben, dass sich dies mit dem Standard C++11 ändert, aber sicher bin ich mir nicht), welche dann Code ausführen. Jeder Thread verfügt über einen eigenen Stack, und auf den Stack anderer Threads sollte man möglichst nicht zugreifen. Wenn man es schafft, das Programm so zu schreiben, dass die Threads weitestgehend unabhängig voneinander arbeiten können, ohne dass Thread A Thread B in irgendwelche Variablen reinfuscht (wie gesagt, da kein Teil des Standards, muss man sich hier auf die Funktionen des OS verlassen), ist die Anwendung Threadsicher. Bei jedem Prozess bekommst du also einen Heap und bei jedem Thread einen eigenen Stack zur Verfügung gestellt.[/qoute]
    Jetzt klärt sich das schon wieder mit dem letzten Satz 😉
    Man lernt nie aus 🙂
    [qoute="Der aus dem Westen..."]
    Ich habe etliche PE-Exen mit Hex-Editoren, die dafür ausgelegt waren, Maschinencode zu interpretieren (HIEW) und Disassemblern (IDA) geöffnet. Ich meine, mich zu erinnern, dass die ersten 0x1000 Bytes Headerdaten beinhalten (ist allerdings nicht fix - das PE-Format ist recht dynamisch, je nachdem, was du später alles im Speicher haben willst), dann kommt ein Adresssprung, und plötzlich bin ich bei 0x00401000 oder um den Dreh. Aber gut ... ich habe ja gesagt, dass die Sachen statisch im Stack oder in den Datensegmenten stehen. 😉 [/quote]
    Stimmt.
    [qoute="Der aus dem Westen..."]
    Es war ein Adressbereich, der dynamisch erstellt wurde, richtig? 😃 [/qoute]
    😉

    Auf jeden Fall danke ich Allen, die Geholfen haben mein Problem zu lösen, 🙂
    und sage auf Wiedersehen, bis zu meinem nächsten Problem 😃


Anmelden zum Antworten