Linux / Nasm / Beispielprogramme

Pascal, Basic und andere nicht aufgelistete
osculumobscenum
Beiträge: 25
Registriert: Do Okt 31, 2013 9:03 pm

Linux / Nasm / Beispielprogramme

Beitrag von osculumobscenum » Do Okt 31, 2013 9:14 pm

Hallo zusammen,

ich bin derzeit dabei, meine kläglichen Assemblerkenntnisse (Linux) etwas auf zu polieren. Und zwar dergestalt, dass ich vor einigen Jahren ein paar Testprogramme unter Fasm erstellt habe, und diese soweit möglich jetzt nach Nasm "konvertieren" will.

Mein Testsystem ist dabei Debian Wheezy, 64bit, und Nasm 2.10.01

Die Progrämmchen sind alles andere als perfekt, ("quick and dirty" wäre wohl teilweise der richtige Ausdruck), aber wenn auch nur ein Mensch auf der Welt etwas davon lernen kann, hat es sich meines Erachtens schon gelohnt.

Also, mal sehn, was kommt...

osculumobscenum
Beiträge: 25
Registriert: Do Okt 31, 2013 9:03 pm

Re: Linux / Nasm / Beispielprogramme

Beitrag von osculumobscenum » Do Okt 31, 2013 9:16 pm

Das obligatorische "Hello World"-Programm (wohlgemerkt ein Netzfund, also nicht von mir selbst)

Code: Alles auswählen

; 64-bit "Hello World!" in Linux NASM
; nasm -f elf64 hello_world.asm
; ld -s -o hello-world hello-world.o


[bits 64]

global _start			; global entry point export for ld

section .text	
_start:
	
	mov	rax, 1		; sys_write
	mov	rdi, 1			; stdout
	mov	rsi, message	; message address
	mov	rdx, length		; message string length
	syscall

					; sys_exit(return_code)
	
	mov	rax, 60		; sys_exit
	mov	rdi, 0			; return 0 (success)
	syscall

section .data
	message: db 'Hello, world!',0x0A	; message and newline
	length:	equ	$-message				; NASM definition pseudo-instruction
Auffallend dabei ist der "syscall"-Aufruf, weshalb auch mit ax=1 geschrieben wird, anstatt mit ax=4, wie man es bei Benutzung von int 80h machen würde.

osculumobscenum
Beiträge: 25
Registriert: Do Okt 31, 2013 9:03 pm

Re: Linux / Nasm / Beispielprogramme

Beitrag von osculumobscenum » Do Okt 31, 2013 9:24 pm

Folgendes Programm findet Primzahlen beginnend bei 12000 und abwärts bis 1 zählend.

Code: Alles auswählen

; 64-bit  ... gibt Primzahlen aus
; by myself
; nasm -f elf64 primzahlen.asm
; ld -s -o primzahlen primzahlen.o


[bits 64]

global _start			; global entry point export for ld

section .data
_start:
jmp start2

check_prim:
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
xor ax, ax
mov [primzahl], ax
mov si, [para_w]
mov di, si
nochmal:
mov ax,si
dec di
cmp di,1
je ende
xor dx,dx
div di
cmp dx,0
jnz nochmal 
mov ax,1
mov [primzahl], ax
ende:
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret



crlf:
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
mov eax, 4
mov ebx, 1
mov ecx, abc
mov edx, 2
int 128
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret



print_dez:
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
mov ax, [para_w]
xor cx,cx
mov bx,10

schl1:
xor dx,dx
div bx
push dx
inc cx
cmp ax,0

jnz schl1

schl2:
pop dx
add dl,30h
mov [ziffer],dl
push rcx
mov eax, 4
mov ebx, 1
mov ecx, ziffer
mov edx, 1
int 128
pop rcx
loop schl2

pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret


start2:   ; here starts the proggi

mov cx,12000

hano:
mov [para_w], cx
call check_prim

cmp word [primzahl], 1
je noehaa

mov [para_w], cx
call print_dez

call crlf
noehaa:
dec cx
cmp cx,1
jnz hano


mov eax,1
int 128




; here just variables ------  end of code -------------------------------


para_b db 0
para_w dw 0
primzahl dw 0
ziffer db 0
zahl dd 0
abc db 13,10



Edit by Xin: Parameter für nasm -f hinzugefügt auf Wunsch von osculumobscenum

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8858
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Linux / Nasm / Beispielprogramme

Beitrag von Xin » Do Okt 31, 2013 9:31 pm

osculumobscenum hat geschrieben:Hallo zusammen,
Willkommen im Forum,

lass mich raten, Dein Pseudonym ist weltweit eindeutig, oder?
osculumobscenum hat geschrieben:ich bin derzeit dabei, meine kläglichen Assemblerkenntnisse (Linux) etwas auf zu polieren.
Immerhin hast Du welche :-D

Unter Linux habe ich mich noch gar nicht probiert.
osculumobscenum hat geschrieben:Die Progrämmchen sind alles andere als perfekt, ("quick and dirty" wäre wohl teilweise der richtige Ausdruck), aber wenn auch nur ein Mensch auf der Welt etwas davon lernen kann, hat es sich meines Erachtens schon gelohnt.
Ich werde 2014 oder 2015 Assembler können müssen, mich interessiert das also durchaus. Ich kann mir auch vorstellen, dass andere das interessiert, daher wäre es auch schön, wenn Du die Tools, die du benutzt (nasm, ld...?) vorstellst und vielleicht auch was zum Aufbau der Assembleralgorithmen schreiben kannst.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

osculumobscenum
Beiträge: 25
Registriert: Do Okt 31, 2013 9:03 pm

Re: Linux / Nasm / Beispielprogramme

Beitrag von osculumobscenum » Do Okt 31, 2013 9:33 pm

"Armstrong-Zahlen" (mir war der Begriff bis heute mittag auch neu...) sind Zahlen, deren einzelne Ziffern hoch drei wieder die Gesamtsumme ergeben. Als Beispiel

153 = (1 hoch 3) + (5 hoch 3) + (3 hoch 3) = 1 + 125 +27 = 153

Keine Ahnung, wer sich sowas überlegt, aber es ist eine interessante Übung zum programmieren :

(wie man wohl erkennt ist das Grundgerüst meiner Programme oft ähnlich, aber weshalb sollte man das Rad auch mehrfach neu erfinden?)

Code: Alles auswählen

; 64-bit  ... gibt Armstrong-Zahlen aus
; => alle Einzelziffern einer Zahl sollen im Kubik wieder aufsummiert die Ausgangszahl werden.
; (1 hoch 3) + (5 hoch 3) + (3 hoch 3) = 153 
; by myself
; nasm -f elf64 armstrong.asm
; ld -s -o armstrong armstrong.o


[bits 64]

global _start			; global entry point export for ld

section .data
_start:
jmp start2

check_arm:
push rax				; alle Register sichern 
push rbx				; ging früher mit pusja/popa,aber ab 64-bit
push rcx				; leider nicht mehr
push rdx
push rsi
push rdi
xor rax,rax
mov [armzahl], rax		; "armzahl" ist ein Boolean, im Moment 0
mov rax, [para_q]		; die zu testende Zahl ist jetzt im rax-Register
xor rcx, rcx
mov rbx, 10

schleife_1a:
xor rdx,rdx				; wir teilen die Zahl immer wieder durch 10, so dass die "Restwerte"
div bx					; auf den Stack gepusht werden, um dort die einzelnen Ziffern der Zahl zu haben.
push dx
inc cx
cmp ax,0				; alle Einzelziffern gepusht?

jnz schleife_1a				; wenn nicht, oben weitermachen

xor rdx, rdx
schleife_2a:
xor rax, rax
pop ax					; die einzelne Ziffer aus dem Stack holen
push ax					; ax zweimal pushen, damit 2 mal mit sich selber multipliziert werden kann.
push ax
pop bx					; als bl (wir vernachlässigen bh) wieder holen
mul bl					; und multiplizieren = > Quadrat in ax
pop bx					; nochmal die Ziffer nach bl poppen
mul bl					; und multiplizieren
						; Kubikzahl der Ziffer ist jetzt in ax
add rdx, rax			; und wird in rdx aufsummiert
loop schleife_2a

cmp rdx, [para_q]		; ist die Summe der Kubikzahlen so groß wie die Ausgangszahl?
jne noarm				; falls nicht, springe zu "noarm"

mov rax,1
mov [armzahl], rax		; setze Boolean "armzahl" (Es handelt sich in para_q um eine Armstrong-Zahl)

noarm:

pop rdi					; Register wieder retten
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret



crlf:
push rax				
push rbx
push rcx
push rdx
push rsi
push rdi
mov eax, 4				; "schreiben"
mov ebx, 1				; und zwar ein Zeichen
mov ecx, abc			; abc ist ein Zeilenumbruch, siehe Variablen
mov edx, 2
int 80h
pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret



print_dez:				; Routine um Zahlen zu schreiben
push rax
push rbx
push rcx
push rdx
push rsi
push rdi
mov rax, [para_q]		; die zu schreibende Zahl wird hier als qword übergeben
xor cx,cx
mov bx,10				; ...immer durch 10 geteilt

schleife1:
xor dx,dx
div bx
push dx					; ... der Rest auf den Stack gepusht
inc cx
cmp ax,0

jnz schleife1

schleife2:
pop dx					; ... dann wieder aus dem Stack gepoppt
add dl,30h				; 30h addiert, damit es eine Ascii-Ziffer wird
mov [ziffer],dl
push rcx
mov eax, 4				; und dann geschrieben.
mov ebx, 1
mov ecx, ziffer
mov edx, 1
int 80h
pop rcx
loop schleife2

pop rdi
pop rsi
pop rdx
pop rcx
pop rbx
pop rax
ret



start2:   				; here starts the proggi

mov rcx,1				; rcx immer hochzählen

nochmal:
mov [para_q], rcx		; ... als Parameter übergeben
call check_arm			; auf "armstrong getestet

cmp qword [armzahl], 1
jne noehaa

mov [para_q], rcx		; ... und falls armstrong, dann auch 
call print_dez			; auf der Console geschrieben

call crlf				; ...und den Zeilenumbruch nicht vergessen.
noehaa:
inc rcx
cmp rcx,1000			; alle Zahlen zwischen 1 und 1000 auf "Armstrong" testen
jne nochmal


mov rax,1
int 80h




; here just variables ------  end of code -------------------------------


para_q dq 0
para_w dw 0
armzahl dq 0
ziffer db 0
abc db 13,10

Edit by Xin: Parameter für nasm -f hinzugefügt auf Wunsch von osculumobscenum

osculumobscenum
Beiträge: 25
Registriert: Do Okt 31, 2013 9:03 pm

Re: Linux / Nasm / Beispielprogramme

Beitrag von osculumobscenum » Do Okt 31, 2013 9:41 pm

Xin hat geschrieben:
osculumobscenum hat geschrieben:Hallo zusammen,
Willkommen im Forum,

lass mich raten, Dein Pseudonym ist weltweit eindeutig, oder?
osculumobscenum hat geschrieben:ich bin derzeit dabei, meine kläglichen Assemblerkenntnisse (Linux) etwas auf zu polieren.
Immerhin hast Du welche :-D

Unter Linux habe ich mich noch gar nicht probiert.
osculumobscenum hat geschrieben:Die Progrämmchen sind alles andere als perfekt, ("quick and dirty" wäre wohl teilweise der richtige Ausdruck), aber wenn auch nur ein Mensch auf der Welt etwas davon lernen kann, hat es sich meines Erachtens schon gelohnt.
Ich werde 2014 oder 2015 Assembler können müssen, mich interessiert das also durchaus. Ich kann mir auch vorstellen, dass andere das interessiert, daher wäre es auch schön, wenn Du die Tools, die du benutzt (nasm, ld...?) vorstellst und vielleicht auch was zum Aufbau der Assembleralgorithmen schreiben kannst.
Mein Name ist nicht wirklich eindeutig, es gibt mehrere davon, weil es sich schlicht und ergreifend um ein Lied von Hypocrisy handelt ;)
"Die Tools vorstellen, die ich benutze" ist eine kurze Vorstellung: Wie gesagt, ich habe ein 64-bit-Linux, und verwende den Nasm 2.10.01 und zum linken halt das "ld"-Programm, das meinem Linux beigefügt ist.
Die Programme selbst könen in jedem beliebigen Editor geschrieben werden (in meinem Fall "Geany"), da sie ja ohnehin später unabhängig davon auf der Konsole assembliert und gestartet werden.
Die genauen Befehle zum assemblieren und linken sind jeweils in den Quellcode-Stücken im Kopfbereich mit angegeben.
Algorithmen, sofern überhaupt welche vorkommen, sind innerhalb des Quellcodes ein wenig beschrieben, sofern das möglich war.

osculumobscenum
Beiträge: 25
Registriert: Do Okt 31, 2013 9:03 pm

Re: Linux / Nasm / Beispielprogramme

Beitrag von osculumobscenum » Do Okt 31, 2013 10:04 pm

Es wird an der Konsole aufgefordert, etwas ein zu geben, und danach wird der Text wieder daselbst ausgegeben.

Code: Alles auswählen

; 64-bit  input und output
; => Zeichen an der Tastatur eingeben, welche später wieder ausgegeben werden
; by myself
; nasm -f elf64 inp_outp.asm
; ld -s -o inp_outp inp_outp.o


[bits 64]

global _start			; global entry point export for ld

section .text
_start:

	mov	rax,4				; int 80h / ax, 4 => schreiben
	mov	rbx,1				; bx, 1 => an die Konsole
	mov	rcx,msg			; was wir schreiben
	mov	rdx,msg_size		; und wie lang der Text ist
	int	80h
	
	mov	rax,3				; einlesen
	mov	rbx,1				; von der Tastatur
	mov	rcx,inp_buf			; wo wir das Ganze speichern
	mov	rdx,100h			; und wieviel davon
	int	80h
	
	mov [inp_buf_size],rax	; rax hat wohl mitgezählt, wieviel Zeichen wir eingegeben haben
	
	
	mov	rax,4				; wieder Ausgabe
	mov	rbx,1				; an die Konsole
	mov	rcx,msg2			; aus Variable msg2 (siehe unten)
	mov	rdx,msg2_size		; und wie lange der Text ist
	int	80h
	
	mov	rax,4				; nochmal Ausgabe
	mov	rbx,1				; an die Konsole
	mov	rcx,inp_buf			; unseren Eingabepuffer von vorhin
	mov	rdx,[inp_buf_size]	; und die Länge des Puffers
	int	80h

	mov	rax,4				; Ausgabe
	mov	rbx,1				; an die Konsole
	mov	rcx,crlf			; Zeilenumbruch (unten definiert)
	mov	rdx,1				; ist in Linux(Unix nur ein Zeichen, im Gegensatz zu Windows
	int	80h

	mov	rax,1				; Programmende
	xor	rbx,rbx
	int	80h


section .data

crlf db 0ah

msg db 'Input some data: '
msg_size:	equ	$-msg

inp_buf: dq 100h
inp_buf_size dq 1

msg2 db 'You entered: '
msg2_size: equ	$-msg2
Edit by Xin: Parameter für nasm -f hinzugefügt auf Wunsch von osculumobscenum

osculumobscenum
Beiträge: 25
Registriert: Do Okt 31, 2013 9:03 pm

Re: Linux / Nasm / Beispielprogramme

Beitrag von osculumobscenum » Do Okt 31, 2013 10:32 pm

Nochmals ein Netzfund, Autor unbekannt.
Es wird mittels Escape-Sequenzen die Console gelöscht ("ClearScreen"), danach der Cursor an eine bestimmte Position platziert, und dann farbig und unterstrichen geschrieben. Escape-Sequenzen sind ein Thema für sich, aber das Internet hat bestimmt Informationen dazu. Wie man sie in Nasm unter Linux einsetzen kann, sieht man hier:

Code: Alles auswählen

; 64-bit  ... mit Escape-Sequenzen den Bildschrim säubern,
; den Cursor an eine bestimmte Position setzen und dort schreiben.
; by ???  (Netzfund)
; nasm -f elf64 consol.asm
; ld -s -o consol consol.o


[bits 64]

global _start			; global entry point export for ld

section .text
_start:
jmp start

        ESC             equ 0x1b  ; escape character

start:
; first clear the screen
        mov     eax, 4						; schreiben
        mov     ebx, 1						; Konsole
        mov     ecx, clear_screen
        mov     edx, clear_screen_size
        int       0x80
; move cursor to (x:25, y:12)
        mov     eax, 4						; schreiben
        mov     ebx, 1						; Konsole
        mov     ecx, move_cursor
        mov     edx, move_cursor_size
        int       0x80
; write the message
        mov     eax, 4						
        mov     ebx, 1
        mov     ecx, message
        mov     edx, message_size
        int       0x80
; exit from the program
        mov     eax, 1						; Programm beenden
        xor      ebx, ebx					        ; Fehlercode 0
        int       0x80

clear_screen: db ESC, "[2J"
clear_screen_size: equ $ - clear_screen
move_cursor:  db ESC, "[12;25H"
move_cursor_size: equ $ - move_cursor
message:      db ESC, "[31m", ESC, "[5m", ESC, "[4m" ; red, blink on, underline on
              db "Programming linux is easy", 0xa
              db ESC, "[25m", ESC, "[24m" ; blink off, underline off
message_size: equ $ - message
Edit by Xin: Parameter für nasm -f hinzugefügt auf Wunsch von osculumobscenum

osculumobscenum
Beiträge: 25
Registriert: Do Okt 31, 2013 9:03 pm

Re: Linux / Nasm / Beispielprogramme

Beitrag von osculumobscenum » Do Okt 31, 2013 10:47 pm

Ebenfalls Autor unbekannt, Netzfund...
Wie man externe Programme innerhalb eines Assemblerprogramms in Linux aufruft.

Seltsamerweise wird der Programmname einmal mit und einmal ohne Parameter angegeben...

Code: Alles auswählen

; 64-bit  ... andere Programme ausführen, hier "ls" mit Parameter
; by ???
; nasm -f elf64 lstest.asm
; ld -s -o lstest lstest.o


[bits 64]

global _start			; global entry point export for ld

section .data
_start:

mov rax,0xB			; Funktion "externes Programm ausführen" (vermute ich mal so...)
mov rcx,args			; Programmname mit Parameter
mov rdx,0
mov rbx,arg0			; Programmname ohne Parameter
int 80h

mov rax,1                          ; Programm beenden
int 80h

arg0: db '/bin/ls',0		; Programmangaben, mit abschließender Null
arg1: db '-l',0
args: dd arg0,arg1,0 
Edit by Xin: Parameter für nasm -f hinzugefügt

osculumobscenum
Beiträge: 25
Registriert: Do Okt 31, 2013 9:03 pm

Re: Linux / Nasm / Beispielprogramme

Beitrag von osculumobscenum » Fr Nov 01, 2013 11:21 am

Hier eine kleine Demonstration, wie man mit Assembler innerhalb eines Programms nicht nur Variablen, sondern auch den Programmcode verschlüsseln kann, und somit Programme hat, die sich erst zur Laufzeit decodieren.

Schritt 1:

Wir erstellen zunächst ein handelsübliches Programm, zum Beispiel das allseits beliebte "Hello World".

Code: Alles auswählen

; 64-bit "Hello World!" uncodiert... 1 von 3
; by myself
; nasm -f elf64 hello-cod01.asm
; ld -s -o hello-cod01 hello-cod01.o


[bits 64]

global _start			; global entry point export for ld

section .data	
_start:

jmp decode
	db 90h                     ; opcode für den nop-Befehl
	db 90h                     ; also "no operation"
	db 90h
	db 90h

sss:
	mov	rax,4               ; schreiben
	mov	rbx,1               ; an der Console
	mov	rcx,msg
	mov	rdx,msg_size
	int	0x80

	mov	rax,1
	xor	rbx,rbx
	int	0x80
	
msg: db 'Hello world!',0xA
msg_size:	equ	$-msg

decode:	
	jmp sss
	db 90h                              ; wieder 4 mal opcodes für den nop-Befehl,
	db 90h                              ; also "no operation"
	db 90h
	db 90h
Vor und nach dem eigentlichen Programm habe ich jeweils 4 mal den nop-Befehl verwendet, bzw. den zugehörigen opcode, um später beim debuggen, oder beim hexdump leichter die wichtigen Stellen dazwischen identifizieren zu können.
OK, dann wie im Quellcode beschrieben assemblieren und linken, fertig ist der erste Schritt.

Schritt 2:

Wir nehmen uns die ausführbare Version des obigen Programms und schauen es uns an.
Linux liefert normalerweise ein hexdump-Programm mit, ansonsten geht das auch mit jedem beliebigen Hexeditor.
Ich selbst habe mit "hexdump -C hello-cod01" folgende Ausgabe erhalten:

Code: Alles auswählen

00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  02 00 3e 00 01 00 00 00  78 00 60 00 00 00 00 00  |..>.....x.`.....|
00000020  40 00 00 00 00 00 00 00  c8 00 00 00 00 00 00 00  |@...............|
00000030  00 00 00 00 40 00 38 00  01 00 40 00 03 00 02 00  |....@.8...@.....|
00000040  01 00 00 00 06 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 60 00 00 00 00 00  00 00 60 00 00 00 00 00  |..`.......`.....|
00000060  b6 00 00 00 00 00 00 00  b6 00 00 00 00 00 00 00  |................|
00000070  00 00 20 00 00 00 00 00  eb 36 90 90 90 90 b8 04  |.. ......6......|
00000080  00 00 00 bb 01 00 00 00  48 b9 a3 00 60 00 00 00  |........H...`...|
00000090  00 00 ba 0d 00 00 00 cd  80 b8 01 00 00 00 48 31  |..............H1|
000000a0  db cd 80 48 65 6c 6c 6f  20 77 6f 72 6c 64 21 0a  |...Hello world!.|
000000b0  eb cc 90 90 90 90 00 2e  73 68 73 74 72 74 61 62  |........shstrtab|
000000c0  00 2e 64 61 74 61 00 00  00 00 00 00 00 00 00 00  |..data..........|
000000d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000100  00 00 00 00 00 00 00 00  0b 00 00 00 01 00 00 00  |................|
00000110  03 00 00 00 00 00 00 00  78 00 60 00 00 00 00 00  |........x.`.....|
00000120  78 00 00 00 00 00 00 00  3e 00 00 00 00 00 00 00  |x.......>.......|
00000130  00 00 00 00 00 00 00 00  04 00 00 00 00 00 00 00  |................|
00000140  00 00 00 00 00 00 00 00  01 00 00 00 03 00 00 00  |................|
00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000160  b6 00 00 00 00 00 00 00  11 00 00 00 00 00 00 00  |................|
00000170  00 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00  |................|
*
00000188

In der Zeile 00000070 finden wir nach kurzer Suche unsere opcodes mit "90" wieder, welche wir vorhin zur besseren Identifikation mit ein programmiert haben. Und in der Zeile 000000b0 finden wir die anderen "90"-er Opcodes, die wir am Ende unseres kleinen Programms mit eingebaut haben. Das heißt, alles was dazwischen ist, ist jeweils der opcode der Befehle des eigentlichen Programms, weches "Hello World!" schreibt, und sich dann selbt beendet.

Schritt 3:

Wir erstellen eine Kopie des ersten asm-Programms und ersetzen die Befehle für den Schreibbefehl und den Befehl für das Programmende durch ihre Opcodes. Opcods kann man wie Variablen ohne Namen eingeben.
Es geht also wie gesgat los nach den 4 mal 90h, die für die4 "nop"-Befehle stehen und beginnt somit mit "b8" "04" "00" "00" usw...
bis wir eben wieder bei den unteren 90h Opcodes angelangt sind.
Faktisch gehen wir also zu dem sss:-Label und beginnen dort mit
db b8h
db 04h
db 00h
db 00h
usw...
bis wir alle Befehle durch Opcodes ersetzt haben. Im Programm selbst habe lasse ich jene Opcodes, die mit Buchstaben beginnen noch mit einer Null anführen, sozusagen aus ästhetischen Gründen. :-)
Nach getaner Arbeit haben wir dann einen Assembler-Quellcode, der so aussieht:

Code: Alles auswählen

; 64-bit "Hello World!" uncodiert, aber als opcodes... 2 von 3
; by myself
; nasm -f elf64 hello-cod02.asm
; ld -s -o hello-cod02 hello-cod02.o


[bits 64]

global _start			; global entry point export for ld

section .data	
_start:


jmp decode
	db 90h
	db 90h
	db 90h
	db 90h

sss:
db 0b8h
db 04h
db 00h
db 00h
db 00h
db 0bbh
db 01h
db 00h
db 00h
db 00h
db 48h
db 0b9h
db 0a3h
db 00h
db 60h
db 00h
db 00h
db 00h
db 00h
db 00h
db 0bah
db 0dh
db 00h
db 00h
db 00h
db 0cdh
db 80h
db 0b8h
db 01h
db 00h
db 00h
db 00h
db 48h
db 31h
db 0dbh
db 0cdh
db 80h
db 48h
db 65h
db 6ch
db 6ch
db 6fh
db 20h
db 77h
db 6fh
db 72h
db 6ch
db 64h
db 21h
db 0ah



decode:	
	jmp sss
	db 90h
	db 90h
	db 90h
	db 90h
Wenn wir die Datei assemblieren und linken, sollte eine exakte Kopie des ersten Programms entstanden sein, die auch exakt die gleiche Ausgabe produziert.
Womit wir auch schon zum dritten Schritt kommen.

Schritt 3:

Wir erstellen uns vom zweiten Programm ebenfalls eine Kopie und bearbeiten diese im Editor wie folgt:
Zu allen vorhin eingegebenen Opcodes (die ja letztlich nur eine hexadezimale Zahl darstellen) addieren wir 18h, so dass wir beginnen mit

Code: Alles auswählen

sss:
zeugs db 0b8h + 18h
db 04h + 18h
db 00h + 18h
db 00h + 18h
db 00h + 18h
db 0bbh + 18h
...und das ganze fortführen bis zu den altbekannten "90h"-Opcodes. Den Variablen-Namen "Zeugs habe ich ebenfalls eingeführt, weil wir für die Decodierroutine einen Offset brauchen, um fest zu legen, wo mit der Decodierung angefangen werden soll.
Praktischerweise startet das Programm ja bereits mit einem Sprung zu "decode:" (als ob wir's geahnt hätten...) so dass wir genau dort sofort mit der Decodierung anfangen können.

Code: Alles auswählen

decode:	
	sub byte [zeugs],18h
	sub byte [zeugs+1],18h
	sub byte [zeugs+2],18h
	sub byte [zeugs+3],18h
	sub byte [zeugs+4],18h
	sub byte [zeugs+5],18h
usw...
Wir sagen also dem Programm, dass es ab dem Offset "zeugs" bei jedem folgenden byte 18h abziehen soll, nämlich genau jene, die wir vorhin händisch hinzu addiert haben. Und das machen wir bei allen einzelnen Opcodes, die wir vorhin ersetzt haben und dann 18h addiert haben.
Danach springen wir zu dem eigentlich aus zu führenden Programmteil, den wir bis dahin decodiert ahben, und alles sollte wieder funktionieren wie beim allerersten "Hello World!"-Programm.
Das fertige asm-Programm sollte also zum Schluss so aussehen:

Code: Alles auswählen

; 64-bit "Hello World!" codiert, als original opcodes + 18h... 3 von 3
; by myself
; nasm -f elf64 hello-cod03.asm
; ld -s -o hello-cod03 hello-cod03.o


[bits 64]

global _start			; global entry point export for ld

section .data
_start:


jmp decode
	db 90h
	db 90h
	db 90h
	db 90h

sss:
zeugs db 0b8h + 18h
db 04h + 18h
db 00h + 18h
db 00h + 18h
db 00h + 18h
db 0bbh + 18h
db 01h + 18h
db 00h + 18h
db 00h + 18h
db 00h + 18h
db 48h + 18h
db 0b9h + 18h
db 0a3h + 18h
db 00h + 18h
db 60h + 18h
db 00h + 18h
db 00h + 18h
db 00h + 18h
db 00h + 18h
db 00h + 18h
db 0bah + 18h
db 0dh + 18h
db 00h + 18h
db 00h + 18h
db 00h + 18h
db 0cdh + 18h
db 80h + 18h
db 0b8h + 18h
db 01h + 18h
db 00h + 18h
db 00h + 18h
db 00h + 18h
db 48h + 18h
db 31h + 18h
db 0dbh + 18h
db 0cdh + 18h
db 80h + 18h
db 48h + 18h
db 65h + 18h
db 6ch + 18h
db 6ch + 18h
db 6fh + 18h
db 20h + 18h
db 77h + 18h
db 6fh + 18h
db 72h + 18h
db 6ch + 18h
db 64h + 18h
db 21h + 18h
db 0ah + 18h


decode:	
	sub byte [zeugs],18h
	sub byte [zeugs+1],18h
	sub byte [zeugs+2],18h
	sub byte [zeugs+3],18h
	sub byte [zeugs+4],18h
	sub byte [zeugs+5],18h
	sub byte [zeugs+6],18h
	sub byte [zeugs+7],18h
	sub byte [zeugs+8],18h
	sub byte [zeugs+9],18h
	sub byte [zeugs+10],18h
	sub byte [zeugs+11],18h
	sub byte [zeugs+12],18h
	sub byte [zeugs+13],18h
	sub byte [zeugs+14],18h
	sub byte [zeugs+15],18h
	sub byte [zeugs+16],18h
	sub byte [zeugs+17],18h
	sub byte [zeugs+18],18h
	sub byte [zeugs+19],18h
	sub byte [zeugs+20],18h
	sub byte [zeugs+21],18h
	sub byte [zeugs+22],18h
	sub byte [zeugs+23],18h
	sub byte [zeugs+24],18h
	sub byte [zeugs+25],18h
	sub byte [zeugs+26],18h
	sub byte [zeugs+27],18h
	sub byte [zeugs+28],18h
	sub byte [zeugs+29],18h
	sub byte [zeugs+30],18h
	sub byte [zeugs+31],18h
	sub byte [zeugs+32],18h
	sub byte [zeugs+33],18h
	sub byte [zeugs+34],18h
	sub byte [zeugs+35],18h
	sub byte [zeugs+36],18h
	sub byte [zeugs+37],18h
	sub byte [zeugs+38],18h
	sub byte [zeugs+39],18h
	sub byte [zeugs+40],18h
	sub byte [zeugs+41],18h
	sub byte [zeugs+42],18h
	sub byte [zeugs+43],18h
	sub byte [zeugs+44],18h
	sub byte [zeugs+45],18h
	sub byte [zeugs+46],18h
	sub byte [zeugs+47],18h
	sub byte [zeugs+48],18h
	sub byte [zeugs+49],18h
	sub byte [zeugs+50],18h
	jmp sss
	db 90h
	db 90h
	db 90h

Man könnte an dieser Stelle natürlich noch einen 4. Schritt anhängen und die Decodierroutine als Schleife formulieren, aber das gehört nicht mehr zu der Sache, die ich erläutern wollte. Wichtig bei allem ist auf jeden Fall, dass die ganze section als "data" definiert wird, denn das standardmäßige "text" ließe eine Veränderung der Opcodes nicht zu, da schreibgeschützt.

Wenn wir die erstellten 3 Programme jetzt mit einem Hexeditor anschauen, können wir also sehen, dass die ersten beiden die "Hello-World"-Meldung im Klartext vorliegen haben, so dass sie von jedem Hinterhof-Hacker verändert werden können, während das im dritten Programm nicht mehr der Fall ist.

Antworten