Variable wird immer überschrieben
-
Hallo
.486 .model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib include \masm32\include\masm32.inc includelib \masm32\lib\masm32.lib include \masm32\include\user32.inc includelib \masm32\lib\user32.lib .data var dw 0 dup(4) ,0 nr db 123 ,0 .code start: invoke StdIn,addr var, 3 mov eax, dword ptr [nr] ; eax sollte hier 7b sein aber die Variable nr wird immer überschrieben mit dem Wert der in var landet invoke ExitProcess,0 end start
Was mache ich hier falsch?
-
var_var schrieb:
var dw 0 dup(4)
Andersrum: dw 4 dup (0). In Deinem \masm32\help-Verzeichnis befinden sich ein Bündel CHM-Dateien - Draufklicken und sich helfen lassen! MASM32.CHM teilt dann mit: 'DUP: Syntax: count DUP (initialvalue [,initialvalue]...)'.
mov eax, dword ptr [nr]
nr ist aber als Byte definiert (DB = Data Byte, DW = Data Word, DD = Data Doubleword). Du lädtst aber ein Doubleword. Entweder Du machst nr zu einem Doubleword (am besten): 'nr dd 123 (die Null kannst Du Dir sparen)' oder Du lädtst das Byte trickreich: 'movzx eax, byte ptr [nr]'.
viele grüße
ralph
-
Irgendwie Blicke ich da bei den Variablen nicht durch.
.486 .model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib include \masm32\include\masm32.inc includelib \masm32\lib\masm32.lib include \masm32\include\user32.inc includelib \masm32\lib\user32.lib .data nr dw 123 ; nr2 db 489 Wenn nr2 hier deklariert ist dann stürtzt das Programm ab .code start: mov eax , dword ptr [nr] mov esi,offset nr+2 mov ebx,10 i: xor edx,edx div ebx add dl,30h mov [esi],dl dec esi cmp eax,0 jne i invoke StdOut, addr nr invoke Sleep,1000 invoke ExitProcess,0 end start
Wenn die Variable nr2 nicht deklariert ist dann funktioniert das Programm ohne Probleme.
Und nochmal zu der Sache mit dem define byte.
Hier in meinem Programm kann ich ja auch schreiben: nr db 123
Und mov eax , dword ptr [nr] verwenden es funktioniert, macht der Assembler da dann eine Typkonvertierung?
-
Du lädst eine DWORD obwohl nur ein WORD deklariert ist. Die Bytes über nr sind mit 0 installiert (gehören eigentlich nicht zu deinem Prog.), so dass du 123 lädst. Wenn du nun aber ein weiteres Byte deklarierst, wird der Wert in eax entsprechend größer (489 passt nicht in ein Byte!). Da dein 'Buffer', den du durch <offset nr+2> referenzierst, aber nur 4 Byte groß ist, schreibet deine Schleife schon fleißig an Adressen unterhalt von nr, so dass ein Zugriffsverletzung auftritt.
include \masm32\include\masm32rt.inc .data nr dd 123 ; dd = declare double word = 32 bit sz db 11 dup (?) .code start: mov eax , dword ptr [nr] mov esi,OFFSET sz[9] ; = OFFSET sz + 9 mov ebx,10 i: xor edx,edx div ebx add dl,30h mov [esi],dl dec esi cmp eax,0 jne i inc esi invoke StdOut, esi invoke StdOut,chr$(13,10) ; Zeilenumbruch inkey invoke ExitProcess,0 end start
var_var schrieb:
dword ptr [nr] verwenden [...] macht der Assembler da dann eine Typkonvertierung?
ja, durch das "DWORD ptr" überschreibst du den Typen des labels nr.
-
Hallo danke für eure Antworten, jetzt wird mir klar wieso das nicht funktionierte was ich da gebaut habe.
Hier ist meine korrektur:
.486 .model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib include \masm32\include\masm32.inc includelib \masm32\lib\masm32.lib include \masm32\include\user32.inc includelib \masm32\lib\user32.lib .data nr dd 489 ; int nr2 db 255 ; Die größte Dezimalzahl die mit db dargestellt werden kann ist 255 .code start: mov eax , dword ptr [nr] mov esi,offset nr+2 mov ebx,10 i: xor edx,edx div ebx add dl,30h mov [esi],dl dec esi cmp eax,0 jne i invoke StdOut, addr nr invoke Sleep,1000 invoke ExitProcess,0 end start
Ich habe mir mal diese Tabelle gemacht ist das so richtig?
db = byte 8bit z.b. al char
dw = word 16bit z.b. ax short
dd = dword 32bit z.b. eax intZahlen brauchen in Assembler keinen Nullterminator nur ein String brauch das Beispiel: var db "hallo",0 das ist doch auch richtig oder?
-
var_var schrieb:
Hier ist meine korrektur:...
Das ist ziemlich abenteuerlich und funktioniert in diesem Fall eigentlich nur zufällig. Weißt Du denn genau, was das Programm macht?
db = byte 8bit z.b. al char
dw = word 16bit z.b. ax short
dd = dword 32bit z.b. eax intNoch überall ein "unsigned" vor die C-Bezeichnungen (char, short, int), dann ist es richtig. C-Puristen werden einwenden, dass die C-Datentypen keine bestimmte Bitbreite haben.
Zahlen brauchen in Assembler keinen Nullterminator nur ein String brauch das Beispiel: var db "hallo",0 das ist doch auch richtig oder?
Jein. Assembler selbst kennt keine Strings, sondern nur eine Ansammlung von Bytes. Mit 'invoke' rufst Du ein Unterprogramm auf und übergibst Parameter. Welche Parameter das Unterprogramm verarbeiten kann, hat der Programmierer des Unterprogramms seinerzeit festgelegt. In diesem Fall rufst Du mit 'invoke StdOut, addr nr' das Unterprogramm StdOut der Masm32.lib auf und übergibst einen Zeiger ('addr') auf die Variable 'nr'. Was genau nun das Unterprogramm erwartet, erfährst Du in masmlib.chm: "StdOut proc lpszText:DWORD". 'lpsz' steht für 'long pointer string zero-terminated'. Du musst also einen 32-Bit-Zeiger ('long pointer') auf eine Ansammlung von Bytes, die als ASCII-Zeichen interpretiert werden können (string) und mit 0 abschließen (zero-terminated) übergeben. Das ist aber nicht immer so. Bei folgendem Programm wird kein solcher String (mit Null am Ende) übergeben, sondern nur die Zeichenfolge und deren Länge:
.386 .Model Flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib .DATA Hallo db 'Hallo Welt!' HalloLen EQU $-Hallo lpNrOfChars dd ? .CODE Start: invoke GetStdHandle, STD_OUTPUT_HANDLE invoke WriteFile, eax, ADDR Hallo, HalloLen, ADDR lpNrOfChars, 0 invoke ExitProcess, 0 END Start
viele grüße
ralph
-
rkhb schrieb:
Das ist ziemlich abenteuerlich und funktioniert in diesem Fall eigentlich nur zufällig. Weißt Du denn genau, was das Programm macht?
Ja ich weiss was das Programm macht. Der Wert der im EAX Register steht wird so oft durch 10 geteilt bis er 0 ist. Bei der Division von EAX = EAX / EBX ensteht ein Rest dieser Rest steht nach der Division im dl Register.
Und das was im dl Register ist schiebe ich nach ESI, ESI zeigt auf meine Variable nr dd wobei mir gerade auffällt das ich die Ergebnisse aus dl in einer Extra Variable speichern wollte.
Also so:
.486 .model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib include \masm32\include\masm32.inc includelib \masm32\lib\masm32.lib include \masm32\include\user32.inc includelib \masm32\lib\user32.lib .data nr dd 489 ; int var dd ? .code start: mov eax , dword ptr [nr] mov esi,offset var+2 mov ebx,10 i: xor edx,edx div ebx add dl,30h mov [esi],dl dec esi cmp eax,0 jne i invoke StdOut, addr var invoke Sleep,1000 invoke ExitProcess,0 end start
Aber warum ist das nicht gut wenn ich das so mache?
-
var_var schrieb:
Aber warum ist das nicht gut wenn ich das so mache?
-
nr ist als DWORD definiert und als INT kommentiert. Wenn Du in einem halben Jahr die mittlere Entfernung Erde-Sonne (149600000 km) ausgeben lassen willst, dann wird der Assembler nicht meckern. Du erntest aber einen grandiosen Absturz. Weißt Du warum?
-
Die kleinste Mondentfernung (363300 km) verursacht zwar keinen Absturz, gibt aber lediglich '300' aus. Weißt Du warum? Gleichzeitig wird der Inhalt von nr verändert. Weißt Du warum?
-
StdOut erwartet einen nullterminierten String. Dass hinter var im Augenblick eine Null folgt, ist reiner Zufall. Wenn Du in einem halben Jahr eine weitere Variable dahinter belegst, dann bekommst Du unerwartete Ausgaben.
EDIT: Und schon bin ich beim Lesen hereingefallen: DD sind 4 Bytes und das Programm ist so programmiert, dass das letzte Byte unberührt bleibt. Damit MUSS aber nr eine dreistellige Dezimalzahl sein (100 <= nr <=999). Wer soll denn das in einem halben Jahr noch wissen?
-
Weißt Du, welche drei inkludierten Dateien überflüssig sind?
-
Was soll das Sleep?
viele grüße
ralph
-
-
Die Größe des Puffers ist: Maximalwert(DWORD) = 4294967295 = 10 Ziffern -> 10+1(term. zero) = 11 Byte
Die Erste Ziffer wird an die Stelle <OFFSET Buffer+9> geschrieben.
Ist der Puffer zu klein, besteht die Gefahr, dass es zu einen "buffer overflow" kommt.PS: mehr zu inkludieren als man derzeit brauch, schadet nicht -> masm32rt.inc mach einem das Leben deutlich einfacher
-
mov esi,offset var+5 ; so kann 363300 auch ausgeben werden
mov esi,offset var+8 ; so kann 149600000 ausgeben werdenElemente : 01234567
Variable var: 00000000 363300 / 10 = 36330 in dl bleibt die 0 über (der Rest)
Und diese 0 schreibe ich dann in var[7] das geht dann halt so lange weiter bis die letzte Zahl in var steht.Btw. dword steht ja für define word. nr ist aber als double word definiert, und ich schreibe aber: mov eax , dword ptr [nr] dann ist das dword doch falsch oder?
Ich kenne bisher nur: byte ptr, word ptr und dword ptr
Das Sleep hab ich nur drin damit ich sehe was das Programm ausgibt, dann muss ich es nicht immer über Konsole starten, ich hab auf meinem Desktop eine Batch Datei worüber ich das Programm assembliere.
Ich hab nochmal etwas darüber nachgedacht was an meinem Programm schlecht ist:
Lösungs Ansatz 1
.486 .model flat, stdcall option casemap :none include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib include \masm32\include\masm32.inc includelib \masm32\lib\masm32.lib .data nr dd 489 ; int var dd ? .code start: mov eax , dword ptr [nr] mov esi,offset var + Anzahl der Zeichen die nr enthält mov ebx,10 i: xor edx,edx div ebx add dl,30h mov [esi],dl dec esi cmp eax,0 jne i invoke StdOut, addr var invoke Sleep,1000 invoke ExitProcess,0 end start
Ich könnte mir ja eine Funktion schreiben womit ich ermitteln kann wie viele Zeichen in nr stehen.
Z.b. so:
Ich teile nr so oft durch 10 bis eax 0 ist bei jedem Schleifendurchlauf könnte ich ja z.b. im ecx Register zählen lassen wie oft die Schleife durchlaufen wurde. Dann weiss ich ja hinterher wie viele Zeichen in der Variable nr stehen.
Aber ist das eine gute Idee das so zu machen?Mein zweiter Ansatz:
Ich schreibe den Rest aus dl einfach in das erste Element von nr.
Das Problem die Ausgabe erfolgt rückwärts, und dafür jetzt wieder eine Funktion zu schreiben die das ganze vorwärts ausgibt ist warscheinlich auch nicht optimal oder?.486 .model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib include \masm32\include\masm32.inc includelib \masm32\lib\masm32.lib include \masm32\include\user32.inc includelib \masm32\lib\user32.lib .data nr dd 363300 ; integer 32bit var dd ?,0 .code start: mov eax , dword ptr [nr] mov esi,offset var mov ebx,10 i: xor edx,edx div ebx add dl,30h mov [esi],dl inc esi cmp eax,0 jne i invoke StdOut, addr var invoke Sleep,1000 invoke ExitProcess,0 end start
Was wäre die beste Lösung?
-
Oops bei Lösungsansatz 1 habe ich bei var dd ? den Nullterminator vergessen und mein Kommentar bei nr nicht geändert in: ; integer 32bit
-
Da es bei dir wohl noch einige Verwirrung gibt:
db = decalre BYTE
dw = declare WORD
dd = declare DWORD
dq = declare QWORDBYTE = unsigned char = 8 Bit
WORD = unsigned short = 16 Bit
DWORD = Double WORD = unsigned long = 216Bit = 32Bit
QWORD = Quad WORD = unsigned long long = 416 Bit = 64 BitFür BYTE, WORD, DWORD und QWORD gibt es auch jeweils das Vorzeichenbehaftete Gegenstücke:
SBYTE = char = 8 Bit
SWORD = short = 16 Bit
SDWORD = long = 32 Bit
[[SQWORD = long long]](ml.exe v8+)Die Bedeutung von PTR ist Kontext abhängig: es wird zum überschreiben des Typen benutzt (XXX ptr YYY), zum deklarieren von typisierten Zeigern (typedef, proto/proc) oder es stellt den Datentypen eines Pointers dar (proto/proc, je nach segment word size 32Bit oder 16 Bit)
MASM hat eine Besonderheit gegenüber andern Assemblern: Wenn man auf eine Variable zugreifen will, braucht man nur das label hinzuschreiben:
mov eax,nr ; gewöhnlicher MASM-Syntax, der zu bevorzugen ist.
Die eckigen Klammern kann man, wie du es bereits getan hast, zusätzlich verwenden:
mov eax,[nr] mov eax,DWORD ptr [nr] ; ”DWORD ptr” würde den Datentypen überschreiben – ist hier aber nicht notwendig
Wenn man die Adresse laden will, muss man den OFFSET-Operator oder LEA benutzen:
mov eax,OFFSET nr lea eax,nr
Im Zusammenhang mit INVOKE taucht dann auch noch der ADDR-Operator auf, welcher ebenfalls eine Adresse zurückgibt.
Bezüglich Adressierung hat MASM einen extrem flexible Syntax – Obiges stellt nur ein kleine Ausschnitt der Möglichkeiten dar.
Zur Puffergröße: Vielleicht nicht effizient, aber korrekt ist es, zuerst die Anzahl der Ziffern zu ermitteln, und dann den Puffer entsprechend zu füllen. Ändert aber nichts daran, dass der statische angelegte Puffer für die Maximale Größe ausgelegt sein muss.
Die Konsole kannst du mit dem inkey-macro offenhalten (macros.asm oder masm32rt.inc).---> MASM Programmer's Guide <---
-
Danke, für die weitere Antwort und den MASM Programmer Link direkt der erste Google Eintrag: http://www.masm32.com/board/index.php?topic=5433.0
Dort findet man den Guide zum herunterladen, als chm Datei da ist alles übersichtlich dargestellt. Das Buch muss unbedint noch in die FAQ dort wird ja alles erklärt.