Erstes Assembler-Programm

Assembler

Hallöle!

Supi! Danke für den Link!

Also ich habe mir nun MASM runtergeladen (von CDWs Linkseite) und installiert.
Zuerst habe ich mich 10 Minuten damit beschäftigt, wie ich bei der Installation einen Pfad auswählen kann...habe es dann aber aufgegeben und alles auf C:\ installiert :evil:

Schön! - Dachte ich als ich den mitinstallierten Editor sah...der übernimmt mir schon die lästige Compile-Link-Sache.

Nun habe ich meinen Code (siehe erstes Posting) in den Editor geschrieben und nach dem Compilebefehl gesucht.

Frage1:
Was ist nun der Unterschied zwischen:

-Assemble Hallo.asm
-Compile Hallo.asm
-Link OBJ File
-Assemble & Link

Da mir nur der Compilebefehl bekannt war, speicherte ich mein Programm erst mal unter Hallo.asm und wollte es dann compilieren...
mit folgender Fehlermeldung brach der Editor das Compilen ab:
http://www.autohotkey.net/~Gapa/Images/Compile-Fehler.GIF

und wenn ich statt compilieren auf assemble ASM File ging, dann kam folgender Fehler:
http://www.autohotkey.net/~Gapa/Images/Assemble-Fehler.GIF

Kann mir einer sagen was da falsch ist??

Und warum erkennt mein DOS immer noch nicht folgenden Befehl:

MASM Hallo.asm

??

Hoffe auf Antwort
Gapa
 
1. Du hast kein DOS. Windows kennt nur Programme direkt, die (ua.?) unter C:\Windows und \system32 gespeichert sind. ;)

2. Speichere dein hallo.asm unter C:\hallo.asm und versuche dann mal mit "Console Build all" das Programm zu kompilieren. Sollte dann C:\hallo.exe erstellen.

3. Linke deine Bilder mal so, dass sie 3te anschauen können :rolleyes: :D
 
Bild

Hm...komisch dass die Links bei dir nicht funktionieren!

Bei mir lassen sie sich prima anzeigen!

Nunja, sei es drumm...
Wenn ich statt Compile den Befehl Console Build All verwende, kommt exakt die selbe Fehlermeldung, ich zitiere:
Assembling: C:\Hallo.asm
C:Hallo.asm<21> : errorA2004: symbol type conflict
C:Hallo.asm<33> : warningA4023: with /coff switch leading underscore required for start adress : START
_
Assembly Error
Drücken sie eine beliebige Taste...


Diese Meldung erscheint in der Konsole...

Was bedeutet das?
Hat das irgendetwas mit dem START - Befehl in meinem Syntax (siehe erster Beitrag) zu tun?

Grüße
Gapa
 
Um masm in der Konsole verfügbar zu machen:
http://de.wikipedia.org/wiki/Umgebungsvariable#Grafische_Oberfl.C3.A4che
unter PATH den MASM Ordner (C:\MASM32\bin\) hinzufügen.
dann: TASM ist nicht gleich MASM, so schaut ein MASM Hello world aus:
Code:
DATA SEGMENT              ;- Beginn des Datensegments
Meldung db "Hello World"   ;- Die Zeichenkette "Hello World"
        db "$"             ;- Endzeichen der Zeichenkette
DATA ENDS                 ;- Ende des Datensegment
CODE SEGMENT              ;- Beginn des Codesegements
ASSUME CS:CODE,DS:DATA     ;- Dem Assembler die Segmente mitteilen
Anfang:                    ;- Label für den Anfang des Programms
mov ax, DATA               ;- das Daten...
mov ds, ax                 ;  ...segment laden
mov dx, offset Meldung     ;- den Text in das Datenregister laden
mov ah, 09h                ;- dem Betriebssystem mitteilen, was wir wollen
int 21h                    ;- diesen Befehl ausführen (hier Ausgabe des Texts)
mov ax, 4C00h              ;- dem Betriebssystem wieder mitteilen, was wir wollen
int 21h                    ;- diesen Befehl wieder ausführen (Programm beenden)
CODE ENDS                 ;- Ende des Codesegments
END Anfang                 ;- dem Assembler das Ende des Labels Anfang mitteilen
dann:
masm ist auf 32-bit ausgelegt. Möchte man damit 16-Bit DOS Programmierung betreiben, braucht man den alten Linker (und am besten auch den Assembler).
ML.exe
http://download.microsoft.com/download/vb60ent/Update/6/W9X2KXP/EN-US/vcpp.exe
(ist ein selbstextrahierendes Archiv, kann man auch mit 7Zip oder ähnlichem Öffnen)
Link.exe
http://download.microsoft.com/download/vc15/Update/1/WIN98/EN-US/Lnk563.exe
(ist auch ein Archiv - gebraucht wird nur die Link.exe).
am besten in einen extra ordner wie "DOSenMASM" platzieren:

Assemblieren:
ml.exe /c hello.asm
Linken:
link.exe hello.obj

oder auch
ml.exe hello.asm (sofern der alte Linker im selben Ordner ist, wird dieser automatisch genutzt).
 
Masm

Hallo CDW,

ok das ist schonmal interessant, dass MASM nen andren Syntax akzeptiert als TASM.

Aber dennoch! Ich habe einen relativ neuen PC -> daraus schließe ich, dass ich wohl mit MASM (32-Bit) arbeiten kann.
Was jedoch nach wie vor (auch nach Ändern der Umgebungsvariablen) nicht funktioniert ist der MASM Hallo.asm - Befehl!!!
Mein Computer erkennt den Befehl MASM nicht!!!!!!!!
Gebe ich stattdessen ML Hallo.asm ein, erkennt er den Befehl!
Aber wieder treten neue Fehler auf.
Ich würde gerne die Bilder posten, jedoch lassen sich meine Links aber ANSCHEINEND nicht öffnen.

Schau dir mal die von mir in meinem letzten Beitrag rot markierte Fehlermeldung an...

Mann ich will doch einfach nur so ein besch******* HalloWelt Programm zum Laufen bringen!!
Wieso ist sowas immer so schei** kompliziert! Das erinnert mich geradezu an die ersten Versuche, ein QT4-Programm zu compilieren!!!

AARRGHHHH :baby:
 
1. erstelle die datei hello.asm unter C:\ mit foglendem Inhalt: (IceWeasel Tut)
Code:
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib

.data
MsgBoxCaption  db "Window title",0
MsgBoxText       db "Assembly is cool!",0

.code
start:
invoke MessageBox, NULL, addr MsgBoxText, addr MsgBoxCaption, NULL
invoke ExitProcess, NULL
end start

Nun öffnest du die asm Datei mit dem Quickeditor von MASM und kompilierst das Programm mit "Console Compile all"

lg
 
Aber dennoch! Ich habe einen relativ neuen PC -> daraus schließe ich, dass ich wohl mit MASM (32-Bit) arbeiten kann.
Du hast den Quellcode eines 16-Bit Programms gepostet. Damit kann MASM nicht mehr umgehen. Hat also nichts mit dem PC zu tun, sondern mit der Zielplattform - möchte man 16-Bit Programme assemblieren, nutzt man eben einen 16-Bit Assembler.
Oder Du fängst direkt mit 32-Bit Programmierung an.

Was jedoch nach wie vor (auch nach Ändern der Umgebungsvariablen) nicht funktioniert ist der MASM Hallo.asm - Befehl!!!
MASM ist kein Befehl, sondern ein Packet mit dem Assembler ML. ML=Assembler=MASM
 
16 - 32

Hallöle again,

@90nop:
Danke! Es hat funktioniert! Daraus schließe ich, dass das nun ein 32-bit-Programm war.

@CDW:
Ok, danke! Dann steht das in tem Tut, aus dem ich lerne, wohl falsch beschrieben wenn da steht, man solle doch in der DOS-Box "MASM Hallo.asm" eingeben.

Aber zwei Fragen habe ich jetzt noch:

1. Wie erkenne ich nun den Unterschied zwischen 16-bit Code und 32-bit Code?
Am besten wäre eine Erläuterung anhand der zwei hier aufgezeigten Beispiele (mein 16-bit Code und 90nops 32-bit Code)

2. Gibt es im 32-bit Assemblercode etwa keine MOV, ADD, INT CMP etc mehr??
90nops Code sieht ja schon fast aus wie ne Hochsprache... ?(
Oder liegt das einfach nur daran, dass er nur Funktionen von den includierten Libs benutzt und dafür keine "richtigen" Assemblerbefehle braucht?

Grüße
Gapa
 
Natürlich ist meines 32 Bit Windows.

1. Wie erkenne ich nun den Unterschied zwischen 16-bit Code und 32-bit Code?
An den verwendeten Befehlen/Befehlsparametern, würde ich mal behaupten

Code:
mov ah, 09h
09h ist eine integrierte DOS Funktion zur Textausgabe. (INT21h )



2. Gibt es im 32-bit Assemblercode etwa keine MOV, ADD, INT CMP etc mehr??
Doch gibt es.

90nops Code sieht ja schon fast aus wie ne Hochsprache...
Der Assembler automatisiert immer wiederkehrende Prozeduren. Z.B. mit "Invoke" generiert er einen Call mit Parametern.
So must du nicht 10 mal PUSH verwenden umd die Parameter auf den Stack zu laden, sondern kannst sie wie in Hochsprachen in eine Linie schreiben. Wenn du das Programm dann kompilierst und in Olly den Disasm ansiehst dann siest du dass er automatisch die PUSH Befehle daraus generiert hat.

Aus
Code:
invoke myfunc, par1,par2,par3,par4
.. wird ...
Code:
        push par4 
        push par3 
        push par2 
        push par1 
        call myfunc


So ist es auch mit cmp: du kannst in masm if/else Konstrukte verwenden -> ergeneriert dann die cmp/jnz Konstrukte.

;)
 
Arbeitsspeicher / Stack / Heap

Ah! Hey supi danke!
Wow langsam kommen wir der Sache ja näher :D

Also fassen wir zusammen:

Der Arbeitsspeicher lässt sich also aufteilen in 3 oder mehr Teile:

1. (Name kenne ich nicht): Hier wird das Programm reingeladen
2. Stack: Hier werden Variablen je nach Programmbefehl reingeladen
3. Heap: Ähnlich wie Stack...

Stimmt das soweit?
Oder aus wie vielen Teilen besteht der Arbeitsspeicher genau?
Und was wird alles (und vor allem wann) in den Stack / Heap geladen? Nur Variablen / Werte oder noch anderes? Und stimmt es, dass zu Beginn des Programms der Stack noch leer ist? Denn es werden ja nur auf Befehl des Programms Dinge in den Stack geladen, oder?
Und warum lädt man etwas in den Stack? Wer ließt das dann wieder aus?

Grüße
Gapa
 
Stack ist sowas wie ne Zwischenablage -> auf den Stack schreibst du eben mit PUSH. Die Funktion liest dann die Werte wieder vom Stack. Das findest du nach 1 Min googeln...

Du solltest wirklich mal ein paar Bücher, /E.books lesen. Du fragst hier jedes Detail - und das kommt nicht gut. Wie CDW bereits gesagt hat.


Ich beschäftige mich seit ca 1Woche mit Assembler. 1h MASM. Du siehst, es kommt darauf an, wie man sich damit beschäftigt. Denn mein Wissen scheint bei weitem schon ausgeprägter zu sein als deines. :P
Man muss dazu sagen, dass ich durchs lösen vom Crackmes schon vorher einige Dinge in asm mitbekommen habe.
 
Assembler

Hallo!

@90nop:
Ja du hast ja Recht! Ich werde mir (noch) mehr Mühe geben mir selbst zu helfen =)

Ich habe zu der OP-Code - Daten-Code Trennungssache etwas im I-net gefunden.
CDW hat ja gesagt, dass der Programmierer selbst dafür sorgen muss, dass der Prozessor Daten (Variablen etc.) nicht als Befehle interpretiert.
Im Internet habe ich nun eine Quelle gefunden, welche sagt, man müsse zum Beispiel durch jmp-Befehle den Daten-teil überspringen. Stimmt das nun, oder gibt es da andere Möglichkeiten, wie man dem Prozessor sagt was OP-Code ist und was nicht.

Und noch ein paar Fragen:
Wenn mein Programm in den Arbeitsspeicher geladen wird, haben dann die einzelnen Befehle die selbe Adresse wie die Adressen ganz links wenn man Olly öffnet??
Und wie nennt man den Teil des Arbeitsspeichers, in den das Programm geladen wird?
Ich verstehe einfach den Zusammenhang zwischen Prozessor (CPU) Arbeitsspeicher, Stack und Register noch nicht...
Werden nur Daten oder auch OP-Code in den Stack geladen? Und wann genau kommen nun die Register ins Spiel?

Sorry wenn ich euch mit Fragen zuposte, aber eine präzise Antwort auf meine Fragen finde ich nicht...zumindest nicht so, dass ich den Zusammenhang verstehe.

Grüße
Gapa
 
RE: Assembler

Original von Gapa
Und noch ein paar Fragen:
Wenn mein Programm in den Arbeitsspeicher geladen wird, haben dann die einzelnen Befehle die selbe Adresse wie die Adressen ganz links wenn man Olly öffnet??
Ja
Original von Gapa
Und wie nennt man den Teil des Arbeitsspeichers, in den das Programm geladen wird?
RAM ;) Ich wuesste nicht, dass der einen bestimmten Namen hat.
Original von Gapa
Ich verstehe einfach den Zusammenhang zwischen Prozessor (CPU) Arbeitsspeicher, Stack und Register noch nicht...
Ein Prozessor hat verschiedene Register, auf die man besonders schnell zugreifen kann.
Stack ist ein bisschen Speicher, welcher im Prozess fuers kurze zwischenspeichern von Werten gedacht ist (zB. Parameteruebergaber an eine Funktion). Dabei ist im esp Register ein Pointer auf die aktuelle Wert des Stacks zeigt gespeichert, der Pointer im esp Registers aendert sich je nach dem wie man push'ed und pop't. Generell ist noch zu sagen, dsas der Stack nach dem LIFO (last-in-first-out) Prinzip funktioniert.
Und der Arbeitsspeicher ist halt der Speicher wo die Daten, die vom Prozess benoetigt werden hineinkopiert werden, dies muss nicht nur ausfuehrbahrer Code sein.
Original von Gapa
Werden nur Daten oder auch OP-Code in den Stack geladen? Und wann genau kommen nun die Register ins Spiel?
Wie oben schon erwaehnt werden normalerweise nur Parameter auf dem Stack gespeichert, aber du kannst auch Opcodes pushen und dann sowas wie "call esp" machen. Kurz: der Stack ist auch nur Speicher und solang es nicht die Groeße des Stacks ueberschreitet kannste draufladen was du willst.
Nach einem Funktionsaufruf werden normal der Rueckgabewert im eax Register gespeichert. Ansonsten verwendest du die Register wie Variablen die du nicht extra deklarieren musst.
 
RE: Assembler

Original von Gapa

Ich habe zu der OP-Code - Daten-Code Trennungssache etwas im I-net gefunden.
CDW hat ja gesagt, dass der Programmierer selbst dafür sorgen muss, dass der Prozessor Daten (Variablen etc.) nicht als Befehle interpretiert.
Im Internet habe ich nun eine Quelle gefunden, welche sagt, man müsse zum Beispiel durch jmp-Befehle den Daten-teil überspringen. Stimmt das nun, oder gibt es da andere Möglichkeiten, wie man dem Prozessor sagt was OP-Code ist und was nicht.
Als "Weg" kann man es nicht nennen - aber i.R sorgt der Compiler/Assembler für die Trennung. Im Asm Code helfen dabei die Direktive ".code/.data" oder CODE SEGMENT/DATA SEGMENT
dann trennt der Assembler beide Bereiche.
Ich verstehe einfach den Zusammenhang zwischen Prozessor (CPU) Arbeitsspeicher, Stack und Register noch nicht...
Register sind streng genommen besonders schnnell ansprechbare Speicherbreiche, auf die man verschiedene Bitopreation anwenden kann. Stichwort dazu wäre ALU:
http://de.wikipedia.org/wiki/Arithmetisch-logische_Einheit
Aus sogenannten Gattern werden dabei Halbaddierer/Volladdierer
zusamengebaut, die dann Berechnungen anstellen können.
siehe auch weiterführende Links bei http://de.wikipedia.org/wiki/Digitaltechnik
Stack ist nichts anderes als Hilfsspeicher.
http://de.wikipedia.org/wiki/Stapelspeicher#Anwendungen
im Prinzip könnte man auch ohne auskommen - nur ist es sehr hilfreich, einen Speicherbreich zu haben, auf dem man beliebige Daten zwischenlagern kann.
z.B wird bei Funkitonsaufrufen die Rücksprungadresse (brechnet sich einfach aus der Adresse der aktuellen Anweisung+größe der Anweisung=Adresse nach dieser Anweisung) auf dem Stack abgelegt und wenn die Funktion abgearbeitet wurde, wird mit hilfe dieser Adresse zurück zu dem Aufruf gesprungen.
Da der Stack also sehr hilfreich ist und oft gebraucht wird, haben viele CPU Entwickler/Hersteller ihren CPUs besondere Register und Anweisungen spendiert, um besonders einfach mit dem Stack umgehen zu können. Bei x86 CPUs sind es ESP,EBP Register sowie Anweisungen wie PUSH, POP,ENTER,LEAVE.

Wenn ein Programm gestartet wird, weist das Betriebssystem diesem nicht nur den normalen Speicher zu - es zwackt auch einen Bereich vom Speicher ab, der als Stack dienen soll. Die Stack-Register ESP/EBP werden dann mit diesen Werten (Stackanfang) initialisiert, so dass ein Programm nach dem Start direkt einen Speicherbereich namens Stack zur Verfügung hat.
 
Stack

Hallo!

Ah! Das ist interessant!

Wenn ein Programm gestartet wird, weist das Betriebssystem diesem nicht nur den normalen Speicher zu - es zwackt auch einen Bereich vom Speicher ab, der als Stack dienen soll.
Das bedeutet also, dass es nicht einen bestimmten Bereich im Arbeitsspeicher gibt, welcher als Stack gekennzeichnet ist, sondern dass jedes Programm seinen eigenen Stack bekommt beim Starten, oder?
Das heißt, ohne laufende Programme gäbe es auch keinen Stack, oder?
Und wie läuft das dann mit der Adressierung?
Sieht der Arbeitsspeicher dann etwa so aus? :
Arbeitsspeicher

Wie werden aber nun die Adressen bestimmt. Jedes Programm bekommt ja 4 GB Speicher zur Verfügung, d.h., dass ein Programm immer 4000000000 Adressen zur Verfügung hat, richtig? (denn eine Adresse hat ein Byte Speicher, oder?).
Wie ist da nun die Adressengebung?
Bekommt beim Starten des PCs das erste Programm, welches in den Arbeitsspeicher geladen wird, die ersten 4000000000 Adressen zur Verfügung, das 2. die nächsten 4000000000...??
Eigentlich wäre das ja logisch. Denn nicht jedes Programm hat die Startadresse 0001!
Wenn ich ein beliebiges Programm in OllyDbg öffne, dann ist die erste Adresse 00401000.
Nur das komische ist, dass sich diese Adresse nie ändert...Wenn ich zwischendurch ein anderes Programm starte (also in den Arbeitsspeicher rufe) und dann nochmals das Programm in Olly öffne, ist die Startadresse wieder 00401000...wieso ist diesmal die Startadresse nicht 00401000 + 4000000000??

Und nohcmal zum Stack:
Der Stack heftet sich einfach an den Programmspeicher? Wie ist das Verhältnis Programmspeicher - Stack? Also was ich meine ist, wie viele GBs von den 4 GB sind nun Stack?

Die Stack-Register ESP/EBP werden dann mit diesen Werten (Stackanfang) initialisiert, so dass ein Programm nach dem Start direkt einen Speicherbereich namens Stack zur Verfügung hat.
Diese Register werden mit der Startadresse des Stacks initialisiert, dass das Programm weiß, ab wo der Stack beginnt, oder?
Und man kann nur mittels den 2 Registern (ESP/EBP) mit dem Stack arbeiten? Oder wozu brauche ich diese Register überhaupt?
Ich kann doch auch einfach beispielsweise "PUSH 80" schreiben. Dann wird der Wert 80 auf den Stack gelegt, oder? (Habe aber keine Ahnung, was das bringen soll). Wenn ich diese 80 nun wieder runterholen will, dann schreibe ich POP (und dann die Adresse des Wertes 80 im Stack, oder?).

Ich hoffe ich habe mich so ausgedrückt, dass man halbwegs versteht was ich meine...

Viele Grüße
Gapa
 
Warum ist ImageBase bei PE-Dateien immer 0x400000 Das sollte die erste Haelfte deines Postings erklaeren. Das Bild am Ende von mir ist stark vereinfacht!

Zum Stack: dieser ist idR 3000 Bytes groß. Oeffne mal ein beliebiges Programm mit Ollydbg drueck [alt]+[m] und dir wird die Memorymap deines Prozesses angezeigt. Bei 12D000 (bei mir) steht dann auch schon, dass sich dort der Stack vom Hauptthread befindet (es gibt pro Thread ein Stack).

Und wenn du normal programmierst brauchst du weder ebp noch esp. Die brauchst du nur, wenn du iwas besonderes Richtung Obfuscator, Protector oder sonstige PE-Spielerein machen willst.
 
RE: Stack

Original von Gapa

Das bedeutet also, dass es nicht einen bestimmten Bereich im Arbeitsspeicher gibt, welcher als Stack gekennzeichnet ist, sondern dass jedes Programm seinen eigenen Stack bekommt beim Starten, oder?
korrekt
Das heißt, ohne laufende Programme gäbe es auch keinen Stack, oder?
Und wie läuft das dann mit der Adressierung?
Sieht der Arbeitsspeicher dann etwa so aus? :
Arbeitsspeicher
nein. Zur Vereinfachung erstmal: stellt Dir vor, es läuft nur ein Programm alleine.
dann wäre so eine Aufteilung vorhanden:
Code:
1:code
2:code
..
40: daten
41: daten
...
100: stackende
101: stack
200: stackanfang
Stack "wächst" von unten nach oben - von größeren Adressen zu kleineren. Das ist aber nur eine Konvention.
D.h der Stack hat nicht dieselbe Adresse wie der Code/Daten, sondern ist ein anderer Speicherbreich.

Wie werden aber nun die Adressen bestimmt. Jedes Programm bekommt ja 4 GB Speicher zur Verfügung, d.h., dass ein Programm immer 4000000000 Adressen zur Verfügung hat, richtig? (denn eine Adresse hat ein Byte Speicher, oder?).
jein. Theoretisch ist es zwar so, praktisch aber wird das meistens begrenzt - das Betriebssystem blendet z.B einen Teil seiner Bibliotheken in den Speicher ein (damit das Programm diese nutzen kann). Ein Teil des Betriesbsystem ist nicht auslagerbar (d.h dieser Speicher kann von der Anwendung nicht beschrieben/freigegeben werden).
Das sind aber Betriebssystemdetails.

Wie ist da nun die Adressengebung?
Bekommt beim Starten des PCs das erste Programm, welches in den Arbeitsspeicher geladen wird, die ersten 4000000000 Adressen zur Verfügung, das 2. die nächsten 4000000000...??
die "nächsten" 400000000 Adressen hätten eine Wert über 32-Bit und wären damit erstmal gar nicht ansprechbar ;)
gleich dazu mehr (ist ein großer Brocken)

Und nohcmal zum Stack:
Der Stack heftet sich einfach an den Programmspeicher? Wie ist das Verhältnis Programmspeicher - Stack? Also was ich meine ist, wie viele GBs von den 4 GB sind nun Stack?
Da gibt es Defaultwerte. Unter Win wird zum Beispiel im PE Header der benötigte Wert angegeben (i.r einfach ein Defaultwert seitens Linker):
Code:
NEW EXE
+0 PE
 4 WORD  Machine;
....
18 Optional Header
....
34 DWORD   ImageBase;    
38 DWORD   SectionAlignment;
3c DWORD   FileAlignment;    
....
5e WORD    DllCharacteristics;    
60 DWORD   SizeOfStackReserve;  <----
64 DWORD   SizeOfStackCommit;
Wenn man z.B viel mit Rekursionen arbeitet, wird man diesen Wert manchmal anpassen müssen ;) das perfekte Labyrinth

Die Stack-Register ESP/EBP werden dann mit diesen Werten (Stackanfang) initialisiert, so dass ein Programm nach dem Start direkt einen Speicherbereich namens Stack zur Verfügung hat.
Diese Register werden mit der Startadresse des Stacks initialisiert, dass das Programm weiß, ab wo der Stack beginnt, oder?
ja

Und man kann nur mittels den 2 Registern (ESP/EBP) mit dem Stack arbeiten? Oder wozu brauche ich diese Register überhaupt?
Ich kann doch auch einfach beispielsweise "PUSH 80" schreiben. Dann wird der Wert 80 auf den Stack gelegt, oder? (Habe aber keine Ahnung, was das bringen soll). Wenn ich diese 80 nun wieder runterholen will, dann schreibe ich POP (und dann die Adresse des Wertes 80 im Stack, oder?).
Da gibt es verschiedene Anwendungsmöglichkeiten. Zum einen liegen die lokalen Variablen (die nur innerhalb der Funktion gültig sind) auf dem Stack. Zum anderen werden i.R Parameter über den Stack übergeben.
Dabei kommt es sehr stark auf die Konvention an, mit der man Funktionen aufruft.
http://en.wikipedia.org/wiki/X86_calling_conventions#Caller_clean-up
jedesmal, wenn man PUSH macht, wird automatisch auch ESP=ESP-4 ausgeführt
bei POP ESP=ESP+4
ESP zeigt damit so zu sagen auf den obersten Eintrag im Stack.

Das EBP wird in der Regel als Stack-Basis-Register genutzt.
Code:
function(int a)
{
   int x=1;
   int y=x+1;
  return y
}
wäre:

_function:
    push ebp     ;alten Wert sichern
    mov ebp, esp  ;das soll die neue Stackbasis sein, EBP zeigt im Moment auf den alten EBP Wert
    sub esp, 8     ; reserviere Speicher für lokale Variablen

    mov [ebp-4],1 ;initilisiere erste lokale Variable mit 1
    mov eax,[ebp-4]
    add eax,1
    mov [ebp-8],eax ;schreibe Ergebnis in die zweite lokale Variable
   ;return
   mov eax,[ebp-8]
   sub esp,8   ;reservierten Speicher freigeben
   mov esp, ebp 
   pop ebp    ; alten EBP Wert wiederherstellen
   ret
das ist nur ein Beispiel - es gibt mehrere Konventionen (siehe Wikilink).
Interessant sind diese vor allem für Compilerentwickler oder arme Programmierer, die mit mehreren Sprachen ihre Anwendungsteile schreieben und dann sich abmühen, aus einer Sprache heraus eine "Fremdfunktion" aufrufen zu müssen.




Eigentlich wäre das ja logisch. Denn nicht jedes Programm hat die Startadresse 0001!
Wenn ich ein beliebiges Programm in OllyDbg öffne, dann ist die erste Adresse 00401000.
Nur das komische ist, dass sich diese Adresse nie ändert...Wenn ich zwischendurch ein anderes Programm starte (also in den Arbeitsspeicher rufe) und dann nochmals das Programm in Olly öffne, ist die Startadresse wieder 00401000...wieso ist diesmal die Startadresse nicht 00401000 + 4000000000??
in dmesg Link wird ja schon ziemlich viel Erklärt.
Achtung: folgender Teil stark vereinfacht (und damit nicht immer ganz korrekt):

Stichtwort ist virtueller Speicher. Vorher musste man nämlich als Programmierer wirklich aufpassen, wo man seine Daten hinschreibt (um z.B keine Betriebssystemdaten zu überschreiben) - beim Multitasking müssten die gleichzeitig laufenden Programme also auch noch quasi von einander bescheidwissen und nicht gleichzeitig denselben Speicher nutzen. Auch mussten sie wissen, wieviel Speicher überhaupt im Computer steckt (sonst würden sie ja etwas Adressieren und ansprechen, was gar nicht da ist).
Da hat man sich eben eine Speichertrennung überlegt.
Vereinfacht gesagt gibt es den "wirklichen" physikalischen Speicher - den krallt sich das Betriebssystem. Nach "Konvention" hat ein Programm erstmal seine 4GB zur alleinigen Verfügung. D.h der Programmierer kann davon ausgehen, dass er (in etwa :rolleyes: ) 4 GB RAM ansprechen und beschrieben kann, ohne wissen zu müssen, wieviel RAM im Rechner nun wikrlich steckt.
Wenn das Betriebssystem nun einen Prozess startet, erstellt es einfach eine Tabelle für diesen - in der ersten Spalte stehen die virtuellen Adressen, in der zweiten entweder 0 oder die physikalischen. Weiterhind hat das OS eine eigene Verwaltungstabelle, in der vermerkt ist, welcher physikalischer Speicherbereich aktuell frei/oder (von welchem Prozess) belegt ist.
Wenn ein Prozess nun auf eine Adresse schreibt (z.B auf 400000) kommt das Betriebsystem dazwischen und schaut in der zum Prozess zugehörigen (bei dessen Start angelegten) Seitentabelle nach, welche physikalische Ensprechnung dazu gibt. Ist da z.B nichts vorhanden (noch keine Ensprechnung) schaut das Betriebsystem in der eigenen Verwaltungstabelle mit den Zuordnungen (wo vermerkt ist, welcher Speicherbereich des RAMs belegt ist und von wem). Ist da etwas frei, wird es dem Eintrag in der Seitentabelle zugewiesen und der Schreibzugriff also quasi auf diese physikalische Adresse umgelegt. Ist da nichts frei, wird ein Eintrag aus dem phys. Speicher "rausgeschmissen" und auf der Festplatte gespeichert (http://de.wikipedia.org/wiki/Auslagerungsdatei), wobei natürlich vermerkt wird, welcher Prozess vorher diese Daten nutzte (z.B in der prozesseigenen Seitentabelle als "Ausgelagert auf Festplattenbereich 1234" )

Möchte nun ein anderer Prozess auf die virtuelle Adresse 400000 schreiben, läuft das Spiel genauso ab - sein Zugriff wird auf den frei verfgübaren RAM-Bereich umgelenkt. (d.h beide Prozesse schrieben zwar auf dieselbe virtuelle Adresse, aber im Endeffekt auf eine andere physikalische).

Möchte ein Prozess nun Daten lesen, wird der Zugriff wieder "abgefangen" und erstmal in der Seitentabelle (wie gesagt, jeder Prozess hat eine eigene!) nachgeschaut, welche physikalische Entsprechung diese virtuelle Adresse hat. Steht in der Seitentabelle die physikalischer Adresse, wird der Zugriff einfach auf diese Adresse umgelenkt. Wurde der Eintrag zwischenzeitlich auf die Festplatte ausgelagert, steht da ja ein Vermerk "Ausgelagert auf Festplattenbreich 4567" - dann sucht sich das Betriebssystem erstmal wieder einen freien RAM Bereich (oder schmeißt andere Daten raus - macht also einen Bereich frei) und lädt die Daten aus der Auslagerungsdatei in den RAM - dann lenkt es den Zugriff des Prozess um (schreibt in die Seitentabelle die RAM Adresse, wo die Daten nun stehen).

Dabei gibt es "Hardwareunterstützung" mittels MMU http://de.wikipedia.org/wiki/Memory_Management_Unit und eventell noch paar Teilen, die mir nicht mehr einfallen.
 
Arbeitsspeicher

Hallo,

mann echt spitze diese detailierten Antworten!!

Stack "wächst" von unten nach oben - von größeren Adressen zu kleineren. Das ist aber nur eine Konvention. D.h der Stack hat nicht dieselbe Adresse wie der Code/Daten, sondern ist ein anderer Speicherbreich.
Wieso wächst der von unten nach oben?
Und was genau meinst du mit "der Stack hat nicht dieselbe Adresse wie der Code/Daten??Haben die etwa ne spezielle Adresse? Oder meintest du Adressbereich??
Denn so wie du das im Beispiel gezeigt hast, sind Code/Daten und Stack genauso hintereinander angeordnet.
Der Assembler trennt Code und Daten durch seperate Segmente voneinander, richtig?
Also sprich: Mein Programm besteht aus 2 Segmenten. Wo aber stehen infos zu diesen Segmenten (Anfangs/End - Adresse)?
Die Segmente sind ja wohl nicht einfach durch ein paar "leere Adressen" voneinander getrennt, oder?
Und das selbe ist dann mit dem Stack, oder? Der ist dann einfach ein drittes Segment, oder?
So stelle ich mir das vor:
Beispielprogramm:
Code:
0001:  code
0002:  code
0003:  code
0004:  code
0005:  leere Adresse     <---- hier ist der Zwischenraum der Segmente Code - Daten
0006:  daten
0007:  daten
0008:  daten
0009:  daten
000A:  leere Adresse     <---- hier ist der Zwischenraum der Segmente Daten - Stack
000B:  Stackplatz (leer)
000C:  Stackplatz (leer)
000D:  Stackplatz (leer)
000E:  Stackplatz (leer)
Stimmt das jetzt so mit dem Stack oder nicht? Wenn ja, wieso baut er sich dann von unten nach oben auf?
Steht die Stackanfangsadresse im ESP oder im EBP ?

Zu deinem Codebeispiel der Funktion habe ich auch noch eine Frage:
push ebp ;alten Wert sichern
Verstehe ich nicht...
Wenn EBP beim Programmstart eine Adresse (Anfangsadresse des Stacks) beinhaltet, dann bewirkt dieser Befehl doch nur, dass diese Adresse als Wert in eine neue Adresse auf den Stack gelegt wird, oder? Aber was bringt das??

Viele Grüße
Gapa
 
Zurück
Oben