!42 - Das Tutorial

Hilfreiche Erklärungen und Tipps zum Lernen von Autohotkey

Moderator: jNizM

User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

!42 - Das Tutorial

14 Dec 2014, 01:59

!42 - Das Tutorial
Dieses Tutorial behandelt DllCalls nicht nur wie man sie benutzt, sondern auch wie sie funktionieren.
Du solltest alles vorsichtig und langsam lesen wenn du alles verstehen willst.
Dieses Tutorial behandelt nicht nur die Software Ebene, sondern verknüpft auch Informationen der Hardware Ebene damit.
Es wird außerdem nicht alle Fragen beantworten, sondern wird dir es dir ermöglichen neue Fragen zu stellen und wird dir hoffentlich helfen ein vollwertiger Programmierer zu werden. :)
Du kannst es als einen Einstieg in ein anderes Niveau von Programmierung sehen.
Das alles ist der Grund für diese Namensgebung !42 "Nicht die Antwort auf alle Fragen und alles."

Inhalt
Zuerst werde ich euch einen sehr groben Einblick geben wie ein Computer sehr grob funktioniert.
Danach werden wir uns ein bisschen mit Assembler beschäftigen.
Darauf behandeln wir die für die DllCall Funktion wichtigen Ausrufs Konventionen.
Wir schließen das ganze mit ein paar wichtigen Informationen ab.

Aber bevor ihr weiterlest lasst mich euch warnen. Ihr werdet euch wünschen es nicht gelesen zu haben und immer noch in einer Welt zu leben wo Computer einfach funktioniert haben. :rainbow: :wave:
Recommends AHK Studio
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Also wie funktioniert ein Computer dann?

15 Dec 2014, 03:53

Also wie funktioniert ein Computer dann?
Lasst mich ein paar Dinge vorab sagen.
Ich kann euch nicht erklären wie ein Computer im Detail funktioniert.
Aber ich kann euch ein gewisses Grundprinzip erläutern.

Die Computerbestandteile
Ein Computer kann aus sehr vielen unterschiedlichen Teilen bestehen und diese müssen nicht immer alle gleich sein.
Die meisten sind auch unwichtig für dieses Tutorial, ich werde deshalb der Einfachheit halber nur die für das Tutorial wichtigen Teile nennen.
Das sind der Zwischenspeicher (RAM), die Festplatte (HDD) und die Hauptkontrolleinheit (CPU).
Alle diese Bestandteile sind grundsätzlich erstmal Chips die über Kontakte auf dem Mainboard miteinander verbunden sind.
Wenn eine elektrische Spannung an diese Kontakte angelegt wird legt der Chip wieder seinerseits Spannung an andere Kontakte an. Wenn dies öfters in Wiederholung geschieht kann man von einer Kommunikation sprechen.
Wenn diese Chips nun richtig Kommunizieren, kann jeder Chip die Aufgabe erledigen für die er gebaut wurde:
Der RAM wird benutzt um Daten kurz zu speichern.
Die HDD wird benutzt um Daten lang zu speichern.
Die CPU führt Berechnungen aus und verknüpft alle Informationen.
Wenn man ein menschliches Gehirn mit einem PC vergleicht wäre die HDD unser Langzeitgedächtnis, der RAM unser Kurzzeitgedächtnis und die CPU der Rest.
Zuerst kümmern wir uns erstmal um die CPU und den RAM. Die HDD wird erst später wichtig werden.
Innerhalb des RAMs ist der Code gespeichert der ausgeführt werden soll, zudem werden dort alle Variablen gespeichert.
Innerhalb der CPU wird dein Code ausgeführt. Die CPU greift auf den RAM zu um neuen Code zum ausführen zu erhalten oder Variablen einzulesen.

Ein Blick auf Variablen
Um einen genaueren Einblick darin zu erhalten wie Variablen funktionieren muss man sich die Kommunikation zwischen CPU und RAM genauer anschauen.
Die CPU greift auf den RAM über Nummern zu. In der x86 Prozessorarchitektur gibt es bis zu 2**32 (2 hoch 32) sogenannter Speicheradressen die direkt angesprochen werden können.
Der RAM antwortet seinerseits dann mit Nummern. Jede Adresse steht für ein Byte. Mit der x86 Prozessorarchitektur können bis zu 4 GB direkt angesprochen werden. Wenn nun ein Compiler eine Variable im Code hat weist er ihr einfach eine Speicheradresse zu und greift dann über diese Speicheradresse auf den Inhalt der variable zu.Kleine Notiz am Rande: Adressen werden häufig im Hexadezimal Format geschrieben, da sie so einfacher zu merken sind : 0x1000


Ein Blick auf Code:
Der Code der im RAM gespeichert ist ist auch nichts anderes als eine Aneinanderreihung von Bytes.
Diese große Aneinanderreihung kann aufgeteilt werden in kleinere Teilabschnitte, sogenannte Instruktionen.
Jede Instruktion ist eine kleinere Aktion die eine CPU ausführen soll.
Ein paar Beispiele wären, dass laden eines oder mehrerer Bytes von einer Speicheradresse, speichern von etwas an einer Speicheradresse, aufrufen einer Funktion, subtrahieren einer Zahl von einer anderen , addieren, vergleichen...
Die CPU speichert Nummern die sie von dem RAM erhält in sogenannten Registern. Hier nimmt die CPU dann Veränderungen an den Nummern vor.
Abhängig von der CPU Architektur haben diese Register unterschiedliche Größen.
Bei x86 sind es 32 bit. Bei x86_64 sind es 64 bit....
Eine CPU hat viele Register. Ich werde nun ein paar der wichtigeren aufzählen(wieder für die x86 Architektur):
  • EAX
  • EBX
  • ECX
  • EDX
  • ESP
  • EIP
Recommends AHK Studio
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Eine kleine Einführung in x86 Assembler

21 Dec 2014, 02:55

Eine kleine Einführung in Assembler
Assembler ist eine der am einfachsten aufgebauten Sprachen die mit bekannt sind.
Denn es ist nichts anderes als die oben genannten Instruktionen für einen Menschen lesbar aufgeschrieben.
Es kann dir helfen den Code noch besser zu verstehen, da sie ja die kleinsten Bestandteile des Codes sind.
Wir werden x86 Assembler behandeln. Ich werde vielleicht ein Tutorial machen was an diesen Teil des Tutorials anschließt.
Falls irgendjemand so etwas interessiert kann er ja einen Kommentar da lassen.

Syntax:
Assembler hat eine sehr einfach Syntax
Es hat viele Ähnlichkeiten mit AutoHotkey Befehlen.
Es besteht aus der Instruktion auf die dann ihre Parameter folgen:
Beispiel:

Code: Select all

MOV EAX,EBX
Parameter?
Parameter sind Informationen welche Informationen die Instruktion erhalten soll und wo sie ihren output hin loswerden soll. Der erste Parameter erhält immer das Ergebnis, gibt aber oft auch gleichzeitig Input.
Als Input können viele Register, Memory Adressen oder ganz normale Zahlen verwendet werden.
Als Output kommen nur Memory Adressen oder Register in Frage.
Der erste Parameter ist meistens der Ziel Parameter der den Output der Instruktion erhalten soll, falls es denn welchen gibt:

Code: Select all

MOV EAX,EBX ;kopiert den Inhalt von EBX in EAX
ADD EAX,EBX ;addiert EAX zu EBX und peichert das Ergebnis in EAX
MOV EAX,[0x1000] ;Lade den Wert der bei der Memory Adresse 0x1000 gespeichert 
ADD EAX,1000 ;addiert 1000 zu EAX und speichert das Ergebnis in EAX
MOV [0x1000],EAX ;speichert EAX in der Speicher Adresse 0x1000


Code: Select all

Addtoavar:
ADD [0x1000],1000 ; Macht das selbe wie die letzten 3 Instruktionen aus dem letzten Beispiel-


Die CALL Instruktion kommt mir einem Helfer der RET Instruktion.
Wenn die CALL Instruktion ausgeführt wird springt die CPU zu der Ziel Adresse und führt den Code der sich dort befindet aus. Die RET Instruktion springt wieder zu der Position zurück wo die letzte CALL Instruktion ausgeführt wurde (return).


Der Stack
Es gibt einen speziellen Ort im Speicher den Programme nutzen können und auf den sie über Instruktionen gesondert zugreifen können. Stack bedeutet übersetzt so viel wie Stapel. Und genau so beschreibt man diesen Ort am besten auch. Er stapelt binäre Informationen. Du kannst etwas oben auf den Stack legen und es dann lesen, auch wenn noch etwas oben auf den Stack gelegt wird. Entfernen kannst du aber nur von dem oberen Ende des Stacks.
Den Stack findet man am Ende der Adressen Reichwiete:
Bei 32 bit fängt er bei der höchst möglichen Zahl (Nach unseren bisherigen Informationen theoretisch 0xFFFFFFFF) an und zählt dann runter. Wenn man zum Beispiel 4 bytes auf den Stapel legen würde er den sogenannten Stackpointer um 4 reduzieren (dann theoretisch 0xFFFFFFFA).
Wenn nun die 4 bytes wieder entfernt werden, wird der Stackpointer um 4 erhöht.
Es gibt ein Register welches nur die Aufgabe hat den Stackpointer zu halten - das ESP Register.(Extended Stack Pointer Register)

Man kann neue Informationen über die Push Instruktion zu dem Stack hinzufügen. Und per Pop Informationen von dem Stack entfernen.

Code: Select all

PUSH a ;was auch immer ihr als a haben wollt (Speicheradresse oder ein Register)
POP EAX ;tut das auf den Stack geschobene a in EAX und entfernt es davon,entfernt.
Man kann auf den Stack auch über eine MOV Instruktion zugreifen:

Code: Select all

MOV EAX,[ESP] ;Greife auf den Wert zu der oben auf dem Stack gespeichert ist und lege ihn in EAX
MOV [ESP],EAX  ;Lege den Wert von EAX in den Wert der am Ende des Stacks gespeichert ist
Wenn man per Call eine Funktion aufruft, legt die Call Instruktion die Adresse der nächsten Instruktion die auf die Call Instruktion folgt auf den Stack. Ret hingegen nimmt die oberste auf dem Stack liegende Information und springt zu dieser Adresse zurück.
Recommends AHK Studio
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Aufrufkonventionen

30 Dec 2014, 03:47

Aufrufkonventionen
Jetzt kommen wir zu dem eigentlich wichtigen Teil dieses Tutorials. Hier verknüpfen wir das grade gelernte mit dem schon bekannten. In diesem Teil geht es um die für die DllCall wichtigen Aufrufs Konventionen.

Was ist das?
Wenn man eine Funktion aufruft kann man Parameter übergeben z.B. funktion(Parameter1,Parameter2).
Aufrufskonventionen legen fest wie eine Funktion Parameter entgegen nimmt.
Für uns sind erstmal nur die cdecl und die stdcall Konvention wichtig, da diese mit der DllCall Funktion verwendet werden können.

stdcall
Bei der stdcall Aufrufskonvention handelt es sich um den Standard der WinAPI.
In den meisten fällen wo wir auf Windows dlls zugreifen verwenden wir diese Konvention.
Bei ihr werden alle Parameter von rechts nach links auf den Stack gelegt(gepusht) und dann die Funktion aufgerufen.
Der Stack wird dann wieder durch die aufrufende Funktion (nicht durch die aufgerufene) frei gemacht (gepopt).
Der Rückgabe wert wird in das EAX Register gelegt.
Stelen wir uns doch einfach mal vor wir haben folgenden C++ code:

Code: Select all

long __stdcall add(long a,long b)
{
return a+b;
}
(Visual C++ Notation)
Die Parameter sind a und b zwei Integer mit 32 Bytes Länge, der Rückgabewert ist selber auch 32 Bytes lang.
Die Aufrufskonvention ist die stdcall Aufrufskonvention, also werden alle Parameter von rechts nach links auf den Stack gelegt und der Rückgabewert in EAX gelegt.
Der Code eines Aufrufes der Funktion inklusive der Funktion könnte also so in Assembler aussehen:

Code: Select all

add:
MOV EAX,[ESP+4] ;Leg a in EAX
;a ist nach dem Aufruf dieser Additions Funktion der zweit letzte Wert der oben auf dem Stack liegt
;darüber auf dem Stack liegt der Wert der durch die Call instruktion enstanden.
;unter a liegt b 
ADD EAX,[ESP+8] ;addiere b([ESP+8]) zu EAX und lege das Ergebnis in EAX
RET ;Springe wieder zurück zu der Adresse die auf dem Stack liegt und nehme sie von diesem runter

Callingcode:
;etwas Code ist vor diesem Aufruf, dieser ist aber unbekannt.
PUSH b ;Was auch immer b sein soll
PUSH a ;Was auch immer a sein soll
CALL add ; die funktion aufrufen
POP ECX  ;An sich nicht weiter bedeutsam außer, dass der Stack bereinigt werden soll.
POP ECX  ;An sich nicht weiter bedeutsam außer, dass der Stack bereinigt werden soll.
;etwas kommt nach diesem Code
Zusätzlich bestimmt die Aufrufskonvention welche Register überschrieben werden dürfen und welche erhalten bleiben sollten. Im Falle der stdcall und cdecl Aufufskonvention dürfen die Register EAX,ECX und EDX überschrieben werden.

cdecl
Die cdecl ist die standard C Aufrufkonvention.
Sie ist eigentlich exakt wie die stdcall Aufrufskonvention, außer dass bei ihr die aufgerufene Funktion den Stack bereinigen muss.

Daher:

Code: Select all

long add(long a,long b)
{
return a+b;
}
Mit der cdecl Aufrufkonvention die Standard ist und damit nicht explizit genannt werden muss ensteht dann folgender Code.

Code: Select all

add:
MOV EAX,[ESP+4] ;Leg a in EAX
;a ist nach dem Aufruf dieser Additions Funktion der zweit letzte Wert der oben auf dem Stack liegt
;darüber auf dem Stack liegt der Wert der durch die Call instruktion enstanden.
;unter a liegt b 
ADD EAX,[ESP+8] ;addiere b([ESP+8]) zu EAX und lege das Ergebnis in EAX
RET 0x8 ;Springe wieder zurück zu der Adresse die auf dem Stack liegt und nehme sie von diesem runter 
;zusätzlich werden hier 8 byte vom Stack genommen (a und b)

Callingcode:
;etwas Code ist vor diesem Aufruf, dieser ist aber unbekannt.
PUSH b ;Was auch immer b sein soll
PUSH a ;Was auch immer a sein soll
CALL add ; die funktion aufrufen
;etwas kommt nach diesem Code
Wenn man diese Beispiele in AHK aufrufen wollte könnte man dies so machen :

Code: Select all

Result:=DllCall(add,"Int",a,"Int",b) ;für die stdcall Konvention
Result:=DllCall(add,"Int",a,"Int",b,"cdecl") ;für die cdecl Konvention
Recommends AHK Studio
Barbossa155
Posts: 30
Joined: 13 Feb 2016, 07:31

Re: !42 - Das Tutorial

19 Apr 2016, 13:20

Sehr interessant Danke!

Es ist doch möglich, per NumPut Register zu verwenden oder?

also zb wenn ich meinen ASM Code habe

Code: Select all


	DWORD FUNCADDR = 0x100000;
	__asm
	{
		push 0
		call FUNCADDR
		add esp, 4
	}
zb wäre ein PUSH in Hex 0x68 [size 4] oder

Code: Select all

NumPut(0x68, REGISTER1, 0, "UChar")
NumPut(Value1, REGISTER1, 1, "Int")
NumPut(0x68, REGISTER1, 5, "UChar")
NumPut(Value2,REGISTER1, 6, "Int")
 NumPut(0x68, REGISTER1, 10, "UChar")
Kennt jemand spezielle Tutorials dafür?
Ein bisschen kann ich schon ;)
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: !42 - Das Tutorial

22 Apr 2016, 07:11

Am einfachsten wäre es glaube ich einen Assembly compiler wie z.b. NASM mit einem Generator für die MCODE Funktion zu verbinden.
Das Problem mit der NumPut funktion ist, dass Funkionen die im Speicher liegen eventuell in einem gewissen Bereich liegen müssen.
Diesen Bereich erzeugt Bentschis MCODE Funktion automatisch und platziert die Werte die dem Maschinen Code entsprechen via einer Windows Funktion zum übersetzen von Hex >> Binär.
Jedoch könnte man da auch MCODE verwenden.
Wenn ich meinen alten Compiler finde kann ich dir den anbieten.
Recommends AHK Studio
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: !42 - Das Tutorial

22 Apr 2016, 07:31

Müsste auch anders gehen. Ich such mal die AutoIt links raus.

Code: Select all

# BinaryCall UDF - Write Subroutines In C, Call In AutoIt
https://www.autoitscript.com/forum/topic/162366-binarycall-udf-write-subroutines-in-c-call-in-autoit/

# The Embedded Flat Assembler (FASM) UDF
https://www.autoitscript.com/forum/topic/111613-the-embedded-flat-assembler-fasm-udf/

# AutoIt Inline Assembly UDF
https://www.autoitscript.com/forum/topic/86672-autoit-inline-assembly-udf/

# Embed DLLs in script and call functions from memory (MemoryDll UDF)
https://www.autoitscript.com/forum/topic/77463-embed-dlls-in-script-and-call-functions-from-memory-memorydll-udf/
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: !42 - Das Tutorial

22 Apr 2016, 08:12

Ja anders aber nicht grade einfach.
Theoretisch würde es sich dann anbieten direkt etwas vor den Compiler/Interpreter zu schalten. (So wie ich es bei mir Zuhause habe)
Ich werde mich mal ransetzten und etwas ähnliches für AHK erschaffen.
Recommends AHK Studio
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: !42 - Das Tutorial

23 Apr 2016, 07:02

OK ich habe mir gestern angeschaut was ich machen müsste:
Die DLLs von Memory zu laden ist eigentlich Grundvorausetzung für das Erfolgreiche erzeugen sinnvollen MCodes.
Die Alternative wäre eine temporäre Datei zu erstellen (Ich weiß nicht ob pipes Möglich sind).

DLLs enthalten neben den Eigenschaften über Code Abschnitte Linker Informationen und Informationen darüber wie gewisse Speicherbereiche angelegt und im Code aufgelöst werden müssen sowie externe Funktionen deren Pointer in den Code müssen.
Diese gesamten Informationen müsste ich vollständig einlesen und richtig übersetzen.

Das garantiert aber noch lange nicht, dass eine Person in der Lage sein wird vollständig richtigen Assembler/C Code in eine DLL zu verwandeln.
Der Assembler den ich benutze gibt eine Datei aus welche dann an einen Linker gesendet werden kann.
Jedoch müsste man dabei schon im Code richtig Segmentieren. Ich müsste mich hierfür selber nochmal informieren und weitere Tutorials schreiben.

Ich fühle mich alleine der Aufgabe nicht gewachsen und hätte gerne ein bisschen Unterstützung dabei :)
Recommends AHK Studio
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: !42 - Das Tutorial

25 Apr 2016, 01:28

Da wird es wohl hier mit rein deutscher Hilfe ein bisschen eng :D
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: !42 - Das Tutorial

25 Apr 2016, 07:29

Manchmal frage ich mich, wo das Problem ist sowas in AHK 1.1 zu integrieren, wenn es doch bei dir auch läuft.
Hat der gute Herr keine Lust es zu übernehmen oder will er es einfach nicht, weil es nicht aus seinem Hirn stammt?
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: !42 - Das Tutorial

25 Apr 2016, 07:32

Ich denke ws wird einfach nichts experimtelles übernommen was noch mehr Fragen und Probleme bringt.
Außerdem, wenn es der gute Herr nicht braucht, hat er auch keine Lust es zu integrieren :)
User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: !42 - Das Tutorial

25 Apr 2016, 07:39

Wenn es so weiter geht (auch mit v2) werde ich wohl den Großteil meiner Scripte mal nach und nach Richtung _H umziehen.
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: !42 - Das Tutorial

25 Apr 2016, 07:50

Alleine WinApi macht schon so ein riesigen Unterschied beim schreiben der Skripts.
Es ist auch super einfach eigene Mcode Functionen hinzuzufügen mithilfe #DllImport :)

Code: Select all

#DllImport,RGB_TO_BGR,8B4C24040FB6C18BD1C1E01081E200FF00000BC2C1E9100BC1C3:0FB6C18BD1C1E910C1E01081E200FF00000BC20BC1C3,UInt,,CDecl UInt
MsgBox % format("0x{1:X}",RGB_TO_BGR(0xAABBCC))
Aber es gibt auch viele andere sehr nützliche Funktionen die uns das Leben einfacher machen.
Das neue multi-threading ist auch echt spannend :)
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: !42 - Das Tutorial

26 Apr 2016, 02:06

Dann bleibt ja nur noch übrig einen Compiler damit zu verlinken.
Der Spassige Teil :)
Recommends AHK Studio

Return to “Tutorials”

Who is online

Users browsing this forum: No registered users and 9 guests