zppp project - un semplice esercizio di asm coding sotto Linux
Pubblicato da Ritz il 19/11/2000
Livello intermedio
Introduzione
La programmazione in Assembly sotto Linux non sembrava essere, fino a poco tempo fa, un argomento seguito da molte persone, almeno qui in Italia. Proprio per questo e' nato il RACL, Reversing & asm coding for Linux, che ha lo scopo di diffondere l'"arte" del reverse enginnering e asm coding riferiti a questo so. Per le varie info al riguardo rimando al sito http://racl.oltrelinux.com.
Cosa sia il reverse engineering o la programmazione in Assembly non lo spieghero' certo in questo articolo, qui infatti cerchero' di introdurre brevemente a *come* in Linux si puo' programmare in asm, portando anche un semplice esempio pratico.
Scarica i sorgenti.
Iniziamo
Naturalmente, Linux e' un sistema operativo a 32-bit, il quale opera in protected mode [piccola nota: quando dico asm coding intendo sempre in architetture ia32, ovvero da 386 in su =) ].
Quando scriviamo un programma in asm per Linux dobbiamo prima di tutto scegliere la sintassi che desideriamo adottare (a chi proviene dalla programmazione in asm sotto win32 o dos questo aspetto risultera' probabilmente strano). Nello scrivere un listato asm da compilare e linkare proprio sotto Linux, infatti, abbiamo a disposizione 2 scelte differenti proprio nella sintassi a nostra disposizione. La prima, la classica sintassi Intel, e' quella normalmente utilizzata in win o dos, mentre la seconda, la sintassi AT&T (quella utilizzata originariamente in sistemi UNIX), presenta alcune caratteristiche che la differenzia da quest'ultima e la rende, secondo molti, meno ambigua, ad esempio:
-----------------------------
global main
extern printf
extern scanf
extern strlen
NULL equ 0
-----------------------------
Prima di tutto dichiariamo l'entrypoint "main" (la funzione main() necessaria per il linker gcc) che indica in punto di inizio esecuzione del nostro codice. Fatto cio' dichiariamo le funzioni esterne delle libc che ci serviranno, nella fattispecie printf, scanf e strlen (non chiedetemi a cosa servono per favore:) ).
-----------------------------
section .data
graphic1 db 0Ah,0Dh,'\
+----------------------------+',0Dh,0Ah,NULL
head db '\
| zppp v1.0 by Ritz for RACL |',0Dh,0Ah,NULL
graphic2 db '\
+----------------------------+',0Ah,0Dh,0Ah,0Dh,NULL
copyright db 'This program is totally FREE and comes with NO WARRANTY. Run from root
account. ',0xA,0xD,NULL
explain db 'By using this simple program you will now configure a new Internet
connection.',0Dh,0Ah,'Please answer to the following questions [^C to
terminate]...',0Dh,0Ah,0Dh,0Ah,NULL
file1 db 0xA,'Writing /etc/ppp/options... ',NULL
file2 db 'Writing /etc/ppp/pppscript... ',NULL
file3 db 'Writing /etc/ppp/pap-secrets... ',NULL
file4 db 'Writing /etc/resolv.conf... ',NULL
script db 'Writing connection script... ',NULL
done db 'done.',0xD,0xA,NULL
errormex db 'error!! Leaving... ',0xD,0xA,NULL
phone db '. Write here your ISP phone number: ',NULL
phonebuffer TIMES 0x15 db NULL
lenphone dd NULL
device db '. Write here your modem device path or its symbolic link: ',NULL
devicebuffer TIMES 0x15 db NULL
lendev dd NULL
user db '. Write here the username for your account: ',NULL
userbuffer TIMES 0x15 db NULL
lenuser dd NULL
pw db '. Write here the password for your account: ',NULL
pwbuffer TIMES 0x15 db NULL
lenpw dd NULL
domain db '. Write here the domain name of your ISP: ',NULL
domainbuffer TIMES 0x15 db NULL
lendomain dd NULL
dns1 db '. Write here the first DNS IP for your account (needed): ',NULL
dns1buffer TIMES 0x15 db NULL
lendns1buffer dd NULL
dns2 db '. Write here the second DNS IP for your account (n = none): ',NULL
dns2buffer TIMES 0x15 db NULL
lendns2buffer dd NULL
perfect db 0xD,0xA,'\
The configuration has been completed successfully!',0xD,0xA,NULL
advice db '\
To connect to the Internet, simply execute the script /usr/sbin/zppp.',0xD,0xA,NULL
advice2 db '\
To let all users be able to connect, change the mode of /usr/sbin/pppd to
4755.',0xD,0xA,0xD,0xA,NULL
enjoy db 'Enjoy!',0xD,0xA,'@2000 by Ritz for RACL',0xD,0xA,0xD,0xA,NULL
format db '%s',NULL
handle dd NULL
; ---------------------------------------------------------
optionspath db '/etc/ppp/options',NULL
optionshandle dd NULL
optionsbuffer db 'lock',0xA
db 'defaultroute',0xA
db 'noipdefault',0xA
db 'modem',0xA
optionsins TIMES 0x15 db NULL
optionsbuffer2 db '115200',0xA
db 'crtscts',0xA
db 'passive',0xA
db 'asyncmap 0',0xA
db 'name "'
optionsins2 TIMES 0x15 db NULL
; ---------------------------------------------------------
pppscriptpath db '/etc/ppp/pppscript',NULL
pppscripthandle dd NULL
pppscriptbuffer db 'TIMEOUT 60',0xA
db 'ABORT ERROR',0xA
db 'ABORT BUSY',0xA
db 'ABORT "NO CARRIER"',0xA
db 'ABORT "NO DIALTONE"',0xA
db '"" "AT&FH0"',0xA
db 'OK "atdt'
pppscriptins TIMES 0x15 db NULL
pppscript2 db 'TIMEOUT 75',0xA
db 'CONNECT',NULL
; ---------------------------------------------------------
papsecretspath db '/etc/ppp/pap-secrets',NULL
ppsecretshandle dd NULL
papsecrets1 db '"'
papsecrets TIMES 0x40 db NULL
; ---------------------------------------------------------
scriptpath db '/usr/sbin/zppp',NULL
scriptbuffer db '/usr/sbin/pppd -detach connect "/usr/sbin/chat -v -f
/etc/ppp/pppscript"',NULL
; ---------------------------------------------------------
resolvpath db '/etc/resolv.conf',NULL
resolvhandle dd NULL
resolv db 'search '
resolvins TIMES 0x15 db NULL
resolv2 db 'nameserver '
resolvins2 TIMES 0x15 db NULL
resolv3 db 'nameserver '
resolvins3 TIMES 0x15 db NULL
-----------------------------
Come vedete dalla sezione .data il prg dovra' creare 5 file: /etc/ppp/options, /etc/ppp/pppscript, /etc/ppp/pap-secrets, /etc/resolv.conf e lo script di connessione, che potremo benissimo mettere in /usr/sbin/zppp. I "TIMES 0xN db NULL" equivalgono semplicemente alle dichiarazioni "db N dup(NULL)" del TASM, ovvero riempiono di NULL una quantita' N di byte. Il resto dovrebbe essere tutto chiaro.
-----------------------------
section .text
main:
push dword graphic1
call printf
add esp, 4
push dword head
call printf
add esp, 4
push dword graphic2
call printf
add esp, 4
push dword copyright
call printf
add esp, 4
push dword explain
call printf
add esp, 4
push dword phone
call printf
add esp, 4
push dword phonebuffer
push dword format
call scanf
add esp, 8
push dword device
call printf
add esp, 4
push dword devicebuffer
push dword format
call scanf
add esp, 8
push dword user
call printf
add esp, 4
push dword userbuffer
push dword format
call scanf
add esp, 8
push dword pw
call printf
add esp, 4
push dword pwbuffer
push dword format
call scanf
add esp, 8
push dword domain
call printf
add esp, 4
push dword domainbuffer
push dword format
call scanf
add esp, 8
push dword dns1
call printf
add esp, 4
push dword dns1buffer
push dword format
call scanf
add esp, 8
push dword dns2
call printf
add esp, 4
push dword dns2buffer
push dword format
call scanf
add esp, 8
-----------------------------
Anche qui tutto e' molto semplice: il prg infatti fa tutte le domande necessarie e aspetta la risposta da parte dell'utente. Notate SEMPRE che lo stack viene pulito con l'istro add esp. Potete farlo anche pushando lo stack in dei registri che non vi servono, ma qui oh preferito il primo metodo.
-----------------------------
push dword devicebuffer
call strlen
add esp, 4
mov dword [lendev], eax
mov ecx, eax
inc ecx
mov byte [devicebuffer+eax], 0xA
mov esi, dword devicebuffer
mov edi, dword optionsins
repz movsb
mov ecx, 0x28
mov esi, dword optionsbuffer2
mov edi, dword optionsins
add edi, dword [lendev]
inc edi
repz movsb
push dword userbuffer
call strlen
mov dword [lenuser], eax
mov byte [userbuffer+eax], '"'
mov ecx, eax
inc ecx
mov esi, dword userbuffer
mov edi, dword optionsins
add edi, 0x29
add edi, dword [lendev]
repz movsb
mov eax, dword optionsbuffer
add eax, 0x4E
add eax, dword [lendev]
add eax, dword [lenuser]
mov dword [eax], NULL
-----------------------------
Per fare tutte queste operazioni come vedete bisogna fare un po' di taglio e cucito;) nel senso che i byte dei buffer non si trovano tutti al loro posto, anzi, quindi repz movsb rulez. Come avrete gia' notato, nel NASM per muovere un offset in un registro si scrive "mov reg, dword buffer", mentre per muovere il contenuto del buffer in questione nello stesso registro si scrive "mov reg, dword [buffer]". Un po' diverso anche qui da TASM o MASM. Inoltre, viene accettato il prefisso 0x per indicare i valori hex.
-----------------------------
push dword phonebuffer
call strlen
mov dword [lenphone], eax
add esp, 4
mov byte [phonebuffer+eax], '"'
mov byte [phonebuffer+eax+1], 0xA
mov ecx, eax
add ecx, 2
mov esi, dword phonebuffer
mov edi, dword pppscriptins
repz movsb
mov ecx, 0x13
mov esi, dword pppscript2
mov edi, dword pppscriptins
add edi, eax
add edi, 2
repz movsb
mov eax, dword pppscriptbuffer
add eax, 0x71
add eax, dword [lenphone]
mov dword [eax], NULL
; ---------------------------------------------------------
push dword userbuffer
call strlen
mov dword [lenuser], eax
mov ecx, eax
inc ecx
mov esi, dword userbuffer
mov edi, dword papsecrets
repz movsb
mov dword [papsecrets+eax],0x22092A09
push dword pwbuffer
call strlen
add esp, 4
mov dword [lenpw], eax
mov byte [pwbuffer+eax],'"'
mov ecx, eax
inc ecx
mov esi, dword pwbuffer
mov edi, dword papsecrets
add edi, dword [lenuser]
add edi, 4
repz movsb
mov eax, dword papsecrets
add eax, 0x6
add eax, dword [lenuser]
add eax, dword [lenpw]
mov dword [eax], NULL
; ---------------------------------------------------------
push dword domainbuffer
call strlen
add esp, 4
mov dword [lendomain], eax
mov byte [domainbuffer+eax], 0xA
mov ecx, eax
inc ecx
mov esi, dword domainbuffer
mov edi, dword resolvins
repz movsb
push dword dns1buffer
call strlen
add esp, 4
mov dword [lendns1buffer], eax
mov byte [dns1buffer+eax], 0xA
mov ecx, eax
inc ecx
mov esi, dword dns1buffer
mov edi, dword resolvins2
repz movsb
cmp dword [dns2buffer], 0x0000006E
jz otherdns
; inizio sezione inutine se non c'e' il secondo dns ---------------
push dword dns2buffer
call strlen
add esp, 4
mov dword [lendns2buffer], eax
mov byte [dns2buffer+eax], 0xA
mov ecx, eax
inc ecx
mov esi, dword dns2buffer
mov edi, dword resolvins3
repz movsb
mov ecx, dword [lendns2buffer]
add ecx, 0xB
mov esi, dword resolv3
mov edi, dword resolvins2
add edi, dword [lendns1buffer]
inc edi
repz movsb
; fine sezione inutine se non c'e' il secondo dns -----------------
otherdns:
mov ecx, dword [lendns1buffer]
add ecx, dword [lendns2buffer]
add ecx, 0x16
mov esi, dword resolv2
mov edi, dword resolvins
add edi, dword [lendomain]
inc edi
repz movsb
mov eax, dword resolv
add eax, 0x1F
add eax, dword [lendomain]
add eax, dword [lendns1buffer]
add eax, dword [lendns2buffer]
mov dword [eax], NULL
-----------------------------
Soliti lavori di allineamento, con l'accortezza di aver inserito l'opzione di poter utilizzare anche un solo server dns.
-----------------------------
mov eax, 0x8
mov ebx, dword optionspath
mov ecx, 0x1A4
int 0x80
cmp eax, -1
jz near error
mov dword [handle], eax
-----------------------------
Il numero della chiamata e' 8, cioe' sys_create. in ebx va l'offset di optionspath, in ecx i permessi.
-----------------------------
push dword optionsbuffer
call strlen
add esp, 4
mov ebx, dword [handle]
mov ecx, dword optionsbuffer
mov edx, eax
mov eax, 0x4
int 0x80
-----------------------------
Con il pezzo sopra di codice andiamo a scrivere nel file che abbiam oappena creato tramine sys_write, numero 0x4. Il numero di byte da scrivere lo ricaviamo con un strlen del buffer che ci interessa.
-----------------------------
mov eax, 0x6
mov ebx, dword [handle]
int 0x80
-----------------------------
Fatte queste operazioni per i vari file, non dovremo fare altro che compilare il tutto con
$ nasm -f elf zppp.asme quindi andare a linkare il file .o con
$ gcc zppp.oper avere come risultato l'eseguibile a.out.
Conclusioni
Il nostro piccolo tool e' terminato e funziona a dovere.
Per i soliti suggerimenti, critiche, appunti, bug e cosi' via mandatemi pure una mail.
Byz,
Ritz
----- [email protected] -----
---- [email protected] ---
Attenzione
Le informazioni contenute in questo documento sono a puro scopo didattico; l'autore non incoraggia chi volesse servirsene per scopi illegali.