Hey, ich habe bei einer Aufgabe Schwierigkeiten zu verstehen, was folgender Assembler-Code macht.
Das zugehörige Programm akzeptiert als Übergabeparameter eine Seriennummer und prüft ob diese gültig oder ungültig ist. Nun geht es darum den Algorithmus rauszufinden bzw. das Schema nach welchem die Nummern akzeptiert werden.
Ich muss dazu sagen, dass ich mich vorher nie wirklich mit Assembler beschäftigt habe.
Deswegen ein paar Fragen:
Code:
mov %rdi,-0x18(%rbp)
Hier verschiebe ich also den Inhalt aus %rdi an die Stelle -0x18(%rbp) (also 24Bytes vor dem Register rbp.
Falls das nun ein 4Byte int ist. Steht der dann in positiver oder negativer Richtung praktisch? Also von: -0x18(%rbp) bis -0x14(%rbp) oder von -0x18(%rbp) bis -0x1C(%rbp).
Danach wird vermutlich mein char*-Übergabeparameter mit atoi in ein int umgewandelt. Woher weiß ich da in welches Register bzw an. welche Stelle der Wert geschrieben wird?
Ich würde jetzt mal raten, dass der int-Wert nach -0x4(%rbp) geschrieben wurde.
Und danach wird geprüft ob der Wert 0 ist?
Code:
cmpl $0x0,-0x4(%rbp)
Falls ja, dann wird der maximale int-Wert in das %eax Register geschrieben und die Methode verlassen?
Mir gehts prinzipiell erstmal darum den Code bis zum ersten "cmpl" zu verstehen. Habe da so etwas meine Schwierigkeiten. Evtl. kennt auch einer gute Literatur wo man sich einlesen kann.
Der Assembler Code ist für eine x86 Maschine (64Bit).
Hey, ich habe bei einer Aufgabe Schwierigkeiten zu verstehen, was folgender Assembler-Code macht.
Das zugehörige Programm akzeptiert als Übergabeparameter eine Seriennummer und prüft ob diese gültig oder ungültig ist. Nun geht es darum den Algorithmus rauszufinden bzw. das Schema nach welchem die Nummern akzeptiert werden.
Ich muss dazu sagen, dass ich mich vorher nie wirklich mit Assembler beschäftigt habe.
Deswegen ein paar Fragen:
Code:
mov %rdi,-0x18(%rbp)
Hier verschiebe ich also den Inhalt aus %rdi an die Stelle -0x18(%rbp) (also 24Bytes vor dem Register rbp.
Falls das nun ein 4Byte int ist. Steht der dann in positiver oder negativer Richtung praktisch? Also von: -0x18(%rbp) bis -0x14(%rbp) oder von -0x18(%rbp) bis -0x1C(%rbp).
Danach wird vermutlich mein char*-Übergabeparameter mit atoi in ein int umgewandelt. Woher weiß ich da in welches Register bzw an. welche Stelle der Wert geschrieben wird?
Ich würde jetzt mal raten, dass der int-Wert nach -0x4(%rbp) geschrieben wurde.
Und danach wird geprüft ob der Wert 0 ist?
Code:
cmpl $0x0,-0x4(%rbp)
Falls ja, dann wird der maximale int-Wert in das %eax Register geschrieben und die Methode verlassen?
Mir gehts prinzipiell erstmal darum den Code bis zum ersten "cmpl" zu verstehen. Habe da so etwas meine Schwierigkeiten. Evtl. kennt auch einer gute Literatur wo man sich einlesen kann.
Der Assembler Code ist für eine x86 Maschine (64Bit).
Hoffe mich kann da jemand etwas aufklären.
Grüße
Ich bin einmal ganz ehrlich:
Da ich die AT&T-Syntax wirklich verabscheue, habe ich mir den Code nur ganz grob angeschaut, aber was ich soweit sagen kann:
1. Bei x86-64 gibt es nur 2 verschiedene Arten eine Funktion zu callen einmal die "Microsoft"-Variante und die "System V AMD64"-Variante.
Ich vermute dein Code lief/läuft auf einem Linux-System, weil unter Windows meistens die Intel-Syntax benutzt wird.
Daher werden all Funktionen wie folgt gecalled:
Quote:
The calling convention of the System V AMD64 ABI is followed on Solaris, Linux, FreeBSD, Mac OS X, and other UNIX-like or POSIX-compliant operating systems. The first six integer or pointer arguments are passed in registers RDI, RSI, RDX, RCX, R8, and R9, while XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 and XMM7 are used for floating point arguments. For system calls, R10 is used instead of RCX.
Das heißt wenn ich die Syntax richtig lese, müsste in "rdi" der Pointer zu deinem String stehen.
2.
Das Ergebnis, also der Return-Wert, sollte ,wie immer und wie auch bei vielen 32Bit-Calls (korrigiert mich wenn ich falsch liege), in "eax" bzw. "rax" liegen.
3.
Ein "mov %rdi,-0x18(%rbp)" fängt an der Stelle "rbp-0x18" an zu schreiben und schreibt dann an die Stelle "rbp-0x17" das nächste Byte ...
In Intel-Syntax ist das etwas "intuitiver" dargestellt wie ich finde:
"mov [rbp-0x18], rdi" (man beachte die vertauschte Reihenfolge der Intel-Syntax).
4.
Eigentlich alle Register mit einem "r" vorne und der Zeichenlänge von 3 sind 8Byte = 64Bit groß. Register mit einem "e" an erster Stelle + Zeichenlänge 3 normalerweise 4Byte und Register mit Zeichenlänge 2, 2Byte.
Ich bin mir gerade unsicher, ob das wirklich immer in allen Fällen zutrifft, aber zumindest in 99% der Fälle passt das.
Hab an ein paar Stellen rangeschrieben von wo es kommt, aber auch dort, wo nichts steht, sollte sich dann recht leicht wiederfinden lassen. So groß ist die Funktion ja nicht
Wenn etwas unklar ist, frag einfach. Ich weiß nicht, wie detailliert erklärt du es brauchst
Ein paar Grundlagen hat Shadow ja schon erläutert.
Mit freundlichen Grüßen
Jeoni
Das war frei Hand. Bzw. mit Visual Studio als Editor, aber da hätte es auch jeder Andere getan. IDA nutze ich sehr gerne bei größeren Projekten. Da ist es denn auch eine Hilfe, wenn man die Funktionen / Variablen (lokal wie global) frei benennen kann und das dann im gesamten restlichen Code automatisch übernommen wird. Hier waren es ja nur 2 lokale Variablen (von denen nicht eine nötig gewesen wäre), die kann ich mir noch so merken.
Wäre hier jetzt komplizierter gewesen die Funktion in IDA reinzubekommen als das tatsächliche Analysieren
Also eine Variable wird in ein Register geladen? Danach wird die Variable um 8 inkrementiert. Und was ist der 3. Schritt? Wird die inkrementierte Variable wieder in "rax" gemovt? Sollte sie da nicht schon drinstehen? Oder passiert da evtl. doch was ganz anderes.
Bevor der Key geprüft wird der Schlüssel + 8 gerechnet?
Eine andere Sache wäre: Beim ersten cmp wird [rbp-0x14] mit 2 verglichen. Ich kann mir jetzt nur vorstellen dass damit die Länge des args-Arrays gemeint ist. Weil wenn die nicht passt wird auch eine Fehlermeldung ausgegeben. Vor dem print passieren aber dann noch etliche andere Sachen.
Gibt es irgendein gutes Buch a la "Assembler from scratch" oder eine gute Seite wo man Schritt für Schritt die Befehle nachvollziehen kann?
Dies dereferenziert rax und lädt das Ergebnis wieder nach rax. Man könnte es sich in C folgendermaßen vorstellen:
Code:
rax = *rax;
(Register sind natürlich untypisiert)
Das erste cmp vergleicht die lokale Variable an rbp-0x14 mit 2. Die lokale Variable wird zwei Zeilen darüber mit RDI, welches dort das erste Argument von main enthält, gefüllt. Du gehst also recht in der Annahme, dass es sich dort um argc handelt.
Das Argument für die check_key funktioniert kommt letztlich ja aus der lokalen Variable an rbp-0x20. Oben in main sieht man wie diese Variable mit RSI, laut der Aufrufkonvention also dem zweiten Argument von main (argv) gefüllt wird.
Wenn man sich nun ansieht, was damit gemacht wird, um zum argument für die check_key Funktion zu gelangen kommt man für den Aufruf erstmal auf sowas:
Code:
check_key(*(argv + 8))
(mal unter der Annahme, dass es keine Typen gäbe und Pointerarithmetik uns hier nicht aufhält)
argv ist allgemein bekannt als Array aus Pointern auf (konstante) Zeichenketten (const char*[]). 8 ist, da es x64 ist, genau einmal die Pointergröße. Was hier also passiert ist, dass auf den Eintrag von argv an Index 1 zugegriffen wird.
Code:
check_key(argv[1])
Damit macht der vorherige Check von argc natürlich auch Sinn.
Kann leider keine Empfehlungen für Bücher oder Tutorials machen, ich habe mir das eigentlich alles selbst beigebracht (und ein paar Mal bei Bekannten nachgefragt). Wenn ich eine Instruktion nicht kannte, habe ich sie bei Google gesucht. Als Referenz nutze ich häufig , aber gab auch schon Instruktionen, die dort nicht erläutert wurden. Nebenbei muss man dann natürlich sich auch Wissen zu den Registern und Aufrufkonventionen aneignen. Ist dann auch recht lehrreich, wenn man sich den asm Code ansieht, den der Compiler aus eigenem Source Code ausspuckt. Dann fallen einen noch eher die Zusammenhänge auf, was beim Rückübersetzen von asm dann natürlich auch nützlich ist.
Werde vor allem die Berechnungen in der check_key Methode morgen nochmal Schritt für Schritt durchgehen. Irgendein System muss da ja hintersteken, sodass man das vereinfachen kann.
Rein theoretisch müsste ich ja so alle gültigen Schlüssel ausgeben lassen können:
Code:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
int var0;
int i;
for (i = 0; i <= 9223372036854775807; i++) {
var0=i;
var0 = (((var0 << 2) + var0) * 2) + var0; // @ +45 to +62
var0 = (var0 + (var0 >> 31)) >> 1; // @ +62 to +77
var0 >>= 3; // @ +77
var0 += 0xf7f9; // @ +81
var0 -= 0x13c; // @ +88
if (var0 == 0x17){
printf("%d", i);
}
}
}
Und er findet tatsächlich passende Schlüssel.
Respekt ich war echt skeptsich ob das mit den ganzen Klammern und Shifts passt O.o
Falls es dich interessiert: Ich hatte "sar" für einen einfachen Shift gehalten, der es aber nicht ist. Andernfalls wäre 0x17 wohl nicht erreichbar, wenn ich mich jetzt auf die schnelle nicht vertan habe.
Mit freundlichen Grüßen
Jeoni
Nein, die Lösung des "korrigierten" Codes liegt lediglich im negativem Bereich, wie ich gerade durch Rückentwickeln festgestellt habe. Mit einer Eingabe von -91843 ist das Endergebnis nach den paar Operationen ebenso 23 (0x17). Ich bin mir recht sicher (aber das will nichts heißen), dass meine korrigierte Fassung die korrektere ist, da die benutzte sar-Instruktion eben kein normaler Shift ist, sondern sich wie ein n-faches vorzeichenbehaftetes Teilen durch 2 verhält, was meine erste Version außer Acht lässt. Dies wäre korrekt, wenn statt "sar" die "shr" Instruktion benutzt werden würde.
Referenz: .
Obige Zahl ist meines Wissens nach auch die einzige Lösung, so dass sich die check_key Methode auf ein simples
Interessant, du hast da mehr Ahnung als ich, deswegen vertraue ich dir mal.
Da deine erste Lösung aber nur passende Schlüssel generiert, muss es auch irgendwie passen. Eine Aufgabe ist auch einen Keygenerator zu schreiben, der nur passende Schlüssel generiert. Das spricht ja auch dafür, dass es mehr als einen passenden Schlüssel gibt.
Falls du dich selbst überzeugen möchtest, hab ich das Programm einfach mal angehängt.
-91843 ist allerdings auch eine korrekte Eingabe für das Programm. Ich kanns mir nicht wirklich erklären, aber deine "nicht-ganz-korrekte" Version produziert ausschließlich korrekte Schlüssel.
Ah, jetzt hab ich's. Bei positiver Eingabe ist ja egal, ob shr ("richtiger" Shift) oder sar (n-faches vorzeichenbehaftetes Dividieren durch 2). Das heißt, dass die zweite, korrigierte Version den positiven Lösungsraum der ersten Version, von der du ja schon Schlüssel gefunden hast, inkludiert, sich der negative Lösungsraum jedoch unterscheidet.
Die zweite Version ist in der Tat korrekter, aber meine Annahme, dass es nur eine einzige Lösung gäbe, war falsch, was wohl darauf zurückzuführen ist, dass ich die Divisionen nicht mehr als Shifts gesehen habe, was sie ja immernoch sind.
Vielleicht gehst du auch nochmal den negativen Zahlenraum durch, vielleicht befindet sich dort ja auch noch eine Lösung, wobei ich das irgendwie nicht glaube.
Mit freundlichen Grüßen
Jeoni
Assembler Code *YaY* 10/24/2013 - General Coding - 17 Replies Hallo, ich habe seit Neustem im Studium (Wirtschaftsinformatik) den Kurs Informatik I. Im Zuge der ersten Übungen sollen wir, bevor es mit Java los/weiter geht mit Assembler Code bzw. auch in Maschinensprache programmieren.
Eine unserer ersten Aufgaben lautet, die Formel x = (x-5) * (x+3) in Assembler Code zu übersetzen. Dafür haben wir eine kleine Tabelle mit Befehlen in der Assembler Sprache bekommen:
Define x y27 Load x18 Store x03 Add c07 AddMem x05 Mult c09 MultMem x
Die...
Assembler code 12/05/2011 - Off Topic - 2 Replies Hello
im studing some basic with assembler and I have got a task to make a code(on assembler) which would make my initials. I still dont know the right task,just that i have to make assembler to show me my initials(like a picture, teacher said somethign about pixels).So I decided to ask you guys for some help, maybe someone already did a similar task. I wish to see how that code looks, doenst matter what initials used. (would be nice M and P, this are my ones :D )