Linux assembler - En kort introduktion
|
02-10-2013, 10:17
(Denne besked var sidst ændret: 12-04-2015, 16:32 af iTick.)
|
|||
|
|||
Linux assembler - En kort introduktion
Assembler er det sprog der kommer nærmest maskinkode, uden at være det. Og jeg vil lige starte med en advarsel om,
man er nød til at læse lidt, før man kan begynde at skrive kode. Det er sjældent man bruger assembler til at lave et helt program med, med mindre det har meget begrænset funktionalitet. F.eks. bruges det til bootloaderen som initieres af BIOS, da man kun har 512 bytes tilgængelig. Oftest vil man lave små moduler i assembler, hvor hastigheden er meget vigtig, og så kalde disse moduler fra et 3. generationssprog. Det er mest set i grafik drivere og sikkert spil engines osv. Selve assembler koden, som egentlig bare er tekst, vil først blive compilet til OP koder, eller rigtige maskinkode instruktioner. Her efter linkes det sammen med eventuelle andre compilede assembler filer, som f.eks. libraries. Når det linkes, vil de faktiske adresser blive fastlagt. Når forskellige stykker assembler kode bliver compilet seperat, ved compileren ikke, hvilke adresser, funktioner, variabler osv. vil få, før det hele er linket sammen, til een fil. Denne lille teaser vil ikke være dybdegående, men forhåbentlig give nogen af jer lidt blod på tanden, og komme i gang. Der er ikke andre måder at lære assembler på, end at læse og kode. Tro det eller ej, men i dagens danmark, og resten af verden, er assembler faktisk blevet en del nemmere. Man skal ikke længere holde øje med, hvilket segment man er i, med mindre du vil kode dit eget operativsystem. Og man kan slippe afsted med at lave systemkald. Altså kald til funktioner i kernen. Det er også den primære årsag til, der er forskel på assembler til Linux og Windows. Det vil jeg dog ikke gå i detaljer med. Der er primært to assembler stilarter til CPUer, som understøtter IA32 (Intel Architecture) og amd64, og disse er Intel-style og AT&T style. Instruktionerne er ikke alle ens og AT&T-style flytter fra venstre til højre og Intel-style, flytter fra højre til venstre. For mig, som har kodet en del forskellige sprog, er det underligt at "flytte mod højre", så jeg bruger altid Intel-style, og det er det, vi vil bruge i dette eksempel. Uden jeg vil forklare selve koden, er her et eksempel på forskellen: Kode: Intel AT&T Jeg kan ikke forene mig selv med tanken om at skulle flytte mod højre, så denne guide dækker kun Intel-style assembler. Inde i CPUen er der nogle registre, som vi ofte fylder værdier i, når vi skal regne på dem. Man bruger ikke helt så meget variabler, som man kender det fra andre sprog. Disse registre har nogle faste formål, men kan i dag, også bruges mere generelt. Et af disse registre er det nye 64 bit RAX, som så selvfølgelig indeholder 64 bits. Det er ikke unormalt at tænke på disse som binære registre. Et tomt RAX register vil se således ud: RAX: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Hvis man flytter et tal til RAX, gøres det med "mov(e)" kommandoen, som naturligt nok flytter data. mov eax, 8 Her efter vil RAX se således ud: RAX: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001000 Selv om der med 64 bit CPUen, blev introduceret nye "General Purpose" registre, er det ikke fordi, der er så mange, at man kan fråse med dem. Man kan derfor arbejde i de 32 bits til højre i RAX, ved at bruge EAX. Så EAX og RAX er det samme register, og hvis du flytter noget til det ene, vil det overskrive indholdet i det andet. Her er et overblik: Kode: RAX: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001000 Kode: RAX: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001000 AX registret kan deles op i to, og man kan flytte data til de 8 bits til højre og de 8 bits til venstre, seperat. Kode: RAX: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00001000 Kode: RAX: EAX |AH |AL | Dette kommer sig af, at man i gamle dage, kun havde de små registre, og som tingene udviklede sig, blev registrene større og større. Men for at holde bagud kompabiliteten, har man udvidet de eksisterende registre, men stadig beholdt de gamle referencer. Andre Interessante registre som i gamle dage havde mere bestemte formål, men nu også kan bruges til andre ting: EAX 32 GP register (General Purpose/Accumulator register) EBX 32 GP register (General Purpose/Base register) ECX 32 GP register (General Purpose/Counter register) EDX 32 GP register (General Purpose/Data) ESP 32 Stack Pointer (Pejer på det element i stacken, som vil blive fjernet næste gang der fjernes. Toppen af stacken) EBP 32 Base Pointer (Bruges til at bygge en stack frame med) EIP 32 Instruction Pointer (Offset) ESI 32 Source Index EDI 32 Destination Index Ovenstående er de mest interessante 32 bit registre, og de findes også med forskellige bit længder. Her under deres 64 bit versioner som f.eks. RAX, RBX osv. Før vi ser på et par instruktioner, vil jeg præsentere et lille skelet jeg bruger, når jeg starter på noget nyt: Kode: BITS 32 ; Dette er et 32 bit eksempel. IA32 er Intel Architecture 32 bit. Det mest interessante i skellettet, er de tre SECTION dele. .data bruger vi til vores initialiserede data. Tilsvarende pseudo kode kunne være: string besked = "Hello World!" Altså variabler som får en værdi fra starten. Modsat har vi .bss sektionen, hvor vi har vores data som ikke er initialiseret. F.eks. en buffer til at læse data ind i. .text sektionen er den del som indeholder vores kode. Dette kommer også fra de gamle dage, da man havde meget begrænset ram og bagudkompabilitet. F.eks. kunne man i de gamle 80386 CPUer ikke have et segment som var større end 64K. Hvis man kunne presse både data, stack og kode, ind i eet segment, havde man et .com program. Ofte var det ikke nok, så stacken og heapen havde deres egne segmenter, og man havde måske flere kode segmenter. Og man havde så en .exe fil. Det slipper vi heldigvis for i dag, og man slipper for at holde øje med hvilket segment man er i, og hvor man skal hen. Bemærk at heapen er ram, allokeret af operativ systemet, og ikke en del af de tre sektioner. Som i kan se, bruger man ; til kommentarer. De tre kode linier i skabelonen gør faktisk ikke andet, end at fortælle kernen at programmet er færdig med at køre. At eksekveringen ikke længere skal fortsætte. Forunderligt nok, kræver det tre instruktioner, bare at afslutte. Jeg starter med at forklare den sidste instruktion først, for så giver de andre to god mening. int 0x80 Int(errupt) kommandoen prikker til CPUen og siger, den har noget, kernen skal udføre. Eller, det gjorde den i gamle dage, i realiteten, er det bare en form for hop (jump) ind i kernel space. Under 0x80 findes en stak system kald og dette bruges af alt software der kører i userspace. For at kernen ved hvad, INT instruktionen skal udføre, kigger den i eax registrret. Det er i rax registret i 64 bit systemer mov eax,1 Vi har med mov(e) kommandoen, lagt et 1-tal over i EAX og det betyder vi ønsker at udføre en exit(). Jeg kunne sådan set godt have udført en mov al,1 kommando. Det ville have givet samme resultat, men det er god skik at sørge for, hele registret er tomt, og der ikke er nogen gamle data, højere oppe i EAX. Eller længere til venstre om man vil. Hvis EAX ser således ud inden mov kommandoen: 00010100 00001000 01010010 00001000 vil mov al,1 ikke rense ud og eax vil se således ud: 00010100 00001000 01010010 00000001 Hvis jeg bruger eax i stedet for al, vil jeg i hvert fald få tømt alle 32 bits i registret, så det kun indeholder 1. Eller jeg kunne bruge RAX i stedet. Men jeg vil gerne have, i skal kunne rette eksemplet til at kunne køre 32 bit, uden de store ændringer. Den midterste kommando som flytter 0 over i ebx registret, fortæller bare INT, at returkoden til operativ systemet er 0. For hver af disse int 0x80 instruktioner, er der krav til, og beskrivelser af, hvilke data der skal være i de andre registre, før man kalder int 0x80. Man kan se alle 32 bit system kald i filen unistd_32.h, og alle 64 bit system kald i unistd_64.h Disse findes normalt i /usr/include/x86_64-linux-gnu/asm i Kali Linux. Og sikkert noget nær samme sted på f.ek.s Debian og Ububtu. Kode: mov eax,1 ; Systemkaldet til exit() Man kan også udføre systemkald i både 32 bit og 64 bit Linux med sysenter kommanden, men det er ret besværligt. int 0x80 findes både i 32 bit og 43 bit instruktionssættet (ISA), men findes kun i 64 bit, på grund af bagud kompabilitet. I 64 bit programmer, anbefales det at bruge syscall. Den eksekverer også hurtigere. Nu til hello world eksemplet: Kode: ;title "hello" Som det fremgår af eksemplet, bruger man mange instruktioner på at forberede et kald til kernen. Vi bruger i alt 7 linier på at skrive en besked ud på konsollen. Nedenstende variabel msg, indeholder teksten Hello world!. Det efterfølgende 0xa er line feed, for at få et linieskift. msg: db 'Hello world!', 0xa db står for define byte, og det er det mest logiske at bruge bytes her. Et alternetiv kunne være define word, som er to bytes. Kode: Collective terms of memory: Nedenstående udfører sådan set ikke noget i sig selv, men gør det lidt nemmere for os selv. len equ $-msg Man kunne godt hardkode længden af strengen til edx registret direkte, men hvis man så ændrer tekst længden, passer dette ikke længere. Det der sker er, at jeg sætte len til forskellen mellem msg starte adresse og len start adresse. Da len kommer lige efter msg, kan vi trygt bruge det til at beregne længden af msg strengen. Kode: mov ebx,1 ; File descriptor til stdout Ovenstående kode, samler de informationer som int skal bruge, for at udføre et write() systemkald. Vi flytter værdien 1 over i ebx registret, da det fortæller int, hvilken filedescriptor, vi ønsker at skrive beskeden til. mov ebx,1 I følge POSIX standarden, skal et operativ system have følgende file descriptors: 0 = standard input (stdin) 1 = standard output (stdout) 2 = standard error (stderr) Dem kender i nok fra jeres arbejde i konsollen, når i bruger sådan noget som "echo 1 > /proc/...", som defaulter til file descriptor 1. Vi flytter så værdien 4 over i eax, da det er system kaldet write(). mov eax,4 Så flytter vi start adressen på msg, over i ecx, så kernen ved, hvad der skal skrives ud på konsollen. Det sidste vi udfører, inden vi kalder int, er at fortælle hvor lang strengen som skal skrives ud, er. Ellers får vi måske skrevet for lidt ud, eller endnu værre skrevet for meget ud. Tilfældige data i ram. mov edx,len Så er vi klar til at udføre selve kaldet med int kommandoen. int 0x80 Så meget for så lidt. Hvis vi har givet int, de rigtige informationer og kaldet går godt, returnerer int en returkode i eax. Vi læser dog ikke i dette tilfælde, denne værdi. Hvad gør vi så nu?! Vi gemmer koden i en fil, som vi gør når vi programmerer i andre sprog. Gem koden i en fil: hello.asm Så skal vi compile filen, og det gør vi med nedenstående kommando: nasm -f elf32 ./hello.asm -o ./hello.o -g -Z ./errors.log Der er selvfølgelig flere måder at gøre det på, men ovenstående er, som jeg gør det. nasm er compileren og vi giver den argumentet ".f elf32", som fortæller at det er 32 bit Excutable and Linkable Format. elf og elf32 er det samme og begge argumenter kan bruges. Argumentet "-o ./hello.o" fortæller at vores binære output fil skal hedde hello.o. Argumentet "-g" gør at nasm genererer debug informationer, hvilket kan være rart, når først man begynder at kore, teste og fejlfinde sine programmer i en debugger. Argumentet "-Z ./errors.log" får nasm til at sende fejlbeskederne til filen errors.log. Det er en smagssag, om man vil det, men prøv lidt forskelligt, og se hvad der passer til dig. Selv om vi kun har en enkelt fil, i dette eksempel, skal den stadig linkes, og det gør vi med følgende: ld -o ./hello ./hello.o -melf_i386 Vi fortæller linkeren ld, at vi skal linke en binære output fil ./hello.o over i en eksekverbar fil, som skal hedde ./hello. Argumentet "-o" står bare for output. Argumentet "-melf_i386" fortæller linkeren, den binære objekt fil, bruger 32 bit adresser osv. Man vil stadig kunne køre programmet på en 64 bit linux box. Både compileren og linkeren har mange features og options, som er værd at kigge på. Begge kan formegentlig hentes med et standard pakkesystem på den distro du nu bruger. Så er der kun tilbage at køre programmet med ./hello og se, om der kommer det på skærmen, vi forventer. Der er meget meget mere til assembler og maskinkode, end jeg har skrevet. Det er som i ved, et kæmpe område, så jeg vil efterlade jer, med lidt ekstra informationer. Når man programmerer assembler, er der meget at se til. En ting man kan tænke over, er hvad der sker, hvis man har værdien 250 i et 8 bit register, værdien 200 i et andet 8 bit register, og lægger dem sammen. Så vil resultatet bliver større end 8 bit. Her til, har vi nogle flag, som bliver sat. dvs. en bit bliver sat til 1, så programmet kan tage forbehold for disse situationer. I ovenstående eksempel, vil et Overflow flag blive sat, så programmet kan vide at resultatet af beregningen blev større en resultetet. Under 32 bit har vi et register med alle disse flag i, der hedder eflags, og 64 bit versionen hedder rflags. Hvordan skælner man mellem positive og negative tal!? Der er mange små ting, som nok også er en af årsagerne til, assembler kun bruges når optimal hurtig kode kræves eller begrænset plads er et problem. Conditional branching: CMP (Compare) sammen ligner to værdier, og bruges til forgreninger: cmp eax,ebx ; Sammenligner eax med ebx registret. I virkeligheden trækker den eax fra ebx og ser, om resultatet bliver 0. Hvis resultatet bliver 0, er de ens. En anden ting, der sker er, at Zero flaget bliver sat. Det er bl.a. det, nedenstående Jcc kommandoer undersøger. Zero flager bliver sat, hver gang en matematisk beregning gør, at et register bliver sat til 0. Vi kan så bruge disse flag til at forgrene med. Det er ofte en af følgende, men der findes mange: JA Jump Above JAE Jump Above or Equal JB Jump Below JBE Jump Belov or Equal JE Jump Equal JNE Jump Not Equal JG Jump Greater JGE Jump Greater or Equal JL Jump Lower JLE Jump Lower or Equal Nogle er til signed værdier og nogle er til unsigned værdier. Altså om værdien kan være positiv eller negativ, eller det ikke har nogen betydning. Hvis et 8 bit register indeholder 10000000 binært, vil det svare til værdien -1 eller 128 decimalt. Der er jo en del forskel. Loops: Nedenstående eksempel viser en simpel loop. Der er flere måder at gøre det på. Kode: ... Der er tilsvarende en inc(rement) instruks som tæller opad. Man kan også bruge loop instruktionen til at lave en løkke med: Kode: ... Kald til en subrutine: Kode: ... Ud over at have sine variabler i .data og i .bss, er det muligt at overføre værdier ved at smide (pushe) værdierne på stacken inden man bruger call instruksen. De kan så poppes igen tilbage, når man er i subrutinen. Man skal dog være lidt varsom, for når man bruger call, bliver sådan noget som IP, Instruktionspointeren også pushed på stacken, og det sker efter, de værdier vi selv har lagt på. Så det er noget med at poppe af stacken, tage vores værdier, og pushe adressen, til ip tilbage på stacken. Stacken er en LIFO. Der findes flere forskellige push og pop instrukser til dette. Man kan også læse data, ved at bruge esp registret, som er vores stack pointer. esp pejer på toppen af stacken. Overblik: Kode: General purpose registers: R8-R15 Der er også 8 nye 128 bit hurtige matematik SSE registre. EIP: 32 bit instruction pointer. RIP: 64 bit instruction pointer. 32 bit Flags register, EFLAGS: Se bl.a. CF, DF, ZF og OF flagene. Bonus: De gamle 32 bit CPUer havde en begrænsning i, hvor meget ram den kunne addresere med 32 bit. Nu, vi er gået over til 64 bit, er vi ude i overkill 16 exabytes, så man har valgt, ikke at bruge alle 64 bits som tidligere, men at dele CPUens allokering op i Virtuel og Fysisk ram. Historik frem til nu, hvor nummer 4 er standarden: Memory models: 1. Real Mode Flat Model. 2. Real Mode Segmented Model. (DOS) 3. Protected Mode Flat Model. (Linux & modern Windows) 80386+ 4. Long Mode Flat Model. (64 bit) Jeg kan kun anbefale at surfe lidt rundt efter noget godt materiale og se at få kodet noget assembler. Det er på godt og ondt, normalt at bøger om assembler, først præsenterer kode og kommandoer, når man har passeret de første 100-200 sider om den interne arkitektur. Det er desværre nødvendigt. Man er også nød til at vide hvilke flag, der bliver sat hvornår. EDIT: Jeg vil lige tilføje at Intel godt nok har deres eget 64 bit instruktionssæt, og de faktisk var ude med en 64 bit processor før AMD, men Intel understøtter og bruger nu primært AMDs instruktionssæt. Problemet var at Intels 64 bit ISA ikke var sønderligt bagudkompatibelt. Somme tider kunne IA32 kode køre på en Intel 64 bit processor, ofte skulle det dog recompiles og i værste fald, skulle koden rettes til. EDIT2: Jeg fik lige lyst til at lave et filter: Kode: ;title "cho" Som i kan se, bruger dette program uinitialiserede data. Det læser et tegn fra stdin, kigger om det er mellemrum, 0-9, a-z eller A-Z. Hvis det er det, bliver det sendt til stdout. I kan her se jeg bruger følgende til at reservere en byte med. buffer resb 1 Her bliver brugt lidt forskellige jumps. Man kan også se forskellen på hvordan følgende to kommandoer skælner mellem adressen og indholdet i buffer: mov ecx, buffer cmp byte [buffer], 30h Programmet "cho" eller CharOnly filteret, kan bruges som følger: "cat EnFilMedTekstOgJunkTegn.txt | ./cho > NyFil.txt" Alt andet end mellemrum og de almindelige alfanumeriske tegn bliver fjernet. Hvor anvendeligt det er i den virkelige verden, ved jeg ikke, men jeg synes det er et godt, men simpelt eksempel. EDIT: Bonuskode - Et ARM helloworld.s Assembly program: Kode: .data // Start af datasektion. $ as helloworld.s -o helloworld.o $ gcc helloworld.o -o helloworld Links: Linux System Call Quick Reference - http://www.scribd.com/doc/50665838/LINUX...-Reference Lidt windows system kald - http://en.wikipedia.org/wiki/INT_21H Instruktionssæt - http://www.intel.com/content/www/us/en/p...nuals.html ASCII tabel - http://www.asciitable.com/ NASM doc - http://www.nasm.us/doc/ Linux LD Linker - http://man7.org/linux/man-pages/man1/ld.1.html
---
Writing a shellcode decoder stub in assembly is like talking gibberish in such a way that it is still perfectly intelligible. - iTick |
|||
02-10-2013, 10:41
|
|||
|
|||
RE: Linux assembler - En kort introduktion
Super fed introduktion! Selvom det er Linux, er det stort set det samme i Windows :)
|
|||
02-10-2013, 11:03
(Denne besked var sidst ændret: 02-10-2013, 11:15 af iTick.)
|
|||
|
|||
RE: Linux assembler - En kort introduktion
(02-10-2013, 10:41)Crypt Skrev: Super fed introduktion! Selvom det er Linux, er det stort set det samme i Windows :) Nej, der er ikke den store forskel. Jeg tænkte bare at nu der er så mange guides, som viser brugen af debuggers, er det nok på sin plads, at få lidt fundamentalt på plads. :) EDIT: Og så glæder vi os til din C eller C++ intro. :)
---
Writing a shellcode decoder stub in assembly is like talking gibberish in such a way that it is still perfectly intelligible. - iTick |
|||
03-10-2013, 11:17
|
|||
|
|||
RE: Linux assembler - En kort introduktion
Det var noget af en smørre, men spændende læsning. Kanon arbejde!
Don't learn to hack, hack to learn
|
|||
03-10-2013, 14:47
|
|||
|
|||
RE: Linux assembler - En kort introduktion
(03-10-2013, 11:17)Spagnum Skrev: Det var noget af en smørre, men spændende læsning. Kanon arbejde! Hehe. Det har du ret i. Der skal bare så meget til, før man skriver den første linie kode. :( Vi må se om der er nogen som gider assembler, af dem som ikke allerede kan det der står i guiden. :)
---
Writing a shellcode decoder stub in assembly is like talking gibberish in such a way that it is still perfectly intelligible. - iTick |
|||
09-10-2013, 11:15
(Denne besked var sidst ændret: 03-11-2024, 15:15 af BlimBlamBlar.)
|
|||
|
|||
RE: Linux assembler - En kort introduktion
slettet slettet slettet slettet
|
|||
09-10-2013, 13:32
|
|||
|
|||
RE: Linux assembler - En kort introduktion
Rigtig god introduktion. Meget præcist og let forståeligt.
Kvalitets ting fra dig gang på gang :) Dog har jeg lige et spørgsmål. Ved dit hello world eksempel benytter du DL registret til at opbevare længden af din tekststreng. Men eftersom DL kun er et 8 bit register så har du vel en maksimal størrelse på 255 chars. Hvad ville der ske hvis man smed en streng ind som var 256 lang ? Nogen speciel grund til at du ikke blot benytter EDX når det nu alligevel ikke bruges ?
Følg mig på twitter: https://twitter.com/Morph3s
|
|||
09-10-2013, 18:15
(Denne besked var sidst ændret: 09-10-2013, 18:25 af iTick.)
|
|||
|
|||
RE: Linux assembler - En kort introduktion
(09-10-2013, 11:15)BlimBlamBlar Skrev: Mht. systemkald så er der da kæmpe forskel på Windows og Linux. Linux systemkald er faste og ændrer sig ikke mellem opgraderinger af kernen, derfor kan du lave en 'int 0x80' eller 'sysenter'. Det kan du ikke i Windows, der er du nødt til at bruge et 'call ...' til en funktion, som så implementerer det korrekte systemkald for den givne kerneversion...også samme grund til at Windows shellcode er en hel del større end Linux shellcode. At kode assembler giver ikke den store forkel. Du håndterer ikke selv adresserne i dine kernel services call gate til dit interrupt vector table. Og det er fordi, de adresser ændrer sig, hver gang der rettes noget i koden. Hvis der fjernes eller tilføjes kode når der patches i kernens service rutiner. Og heldigvis for det. :) Vi er ikke længere der hvor vi skal holde øje med, hvilket segment vi er i. :D Men jeg har ikke kodet assember til windows siden da. Instruktionssætter er dog magen til, da det er CPU afhængigt og ikke OS afhængigt. Så om man bruger call i windows nu, tør jeg ikke sige. Om man bruger nogle wrappers til at kalde kernen, aner jeg ikke. Det virker så mest som om, assembler på windows er ved at blive et 3g sprog. :) Det er interrupt nummeret, der ikke ændrer sig, hvis det er det du mener? (09-10-2013, 13:32)Morph3s Skrev: Rigtig god introduktion. Meget præcist og let forståeligt. Hvis du smider en længere streng ind, sker der ikke noget. For den pejer bare på starten af strengen og skriver skriv 255 tegn ud. Så vil det sidste bare ikke komme med. Jeg har mest skrevet DL i stedet for EDX, for at vise brugen af det. :) Der er beskrivelser af hvilke kald som skal have hvad i hvilket register, og hvor i registret de skal være. :) Jeg kunne i farten ikke lige finde en komplet liste, men her er et udsnit: Kode: %eax Name %ebx %ecx %edx %esx %edi
---
Writing a shellcode decoder stub in assembly is like talking gibberish in such a way that it is still perfectly intelligible. - iTick |
|||
09-10-2013, 18:34
(Denne besked var sidst ændret: 03-11-2024, 15:15 af BlimBlamBlar.)
|
|||
|
|||
RE: Linux assembler - En kort introduktion
slettet slettet slettet slettet
|
|||
09-10-2013, 18:40
(Denne besked var sidst ændret: 09-10-2013, 18:40 af iTick.)
|
|||
|
|||
RE: Linux assembler - En kort introduktion
(09-10-2013, 18:34)BlimBlamBlar Skrev: Komplet syscall table for 32 bit Linux: http://docs.cs.up.ac.za/programming/asm/...calls.html Gode links. :) Ok. Jeg var ikke klar over, at de ændrer på de forskellige inerrupts i windows. Weird. De har godt nok ikke gjort det nemmere for sig selv. På den måde er windows god. Du kan lave indirekte jumps til din shellcode, ved at jumpe til et sted i user32 eller kernel32, hvor der er et jump til starten af din nopsled i din sandwich. :)
---
Writing a shellcode decoder stub in assembly is like talking gibberish in such a way that it is still perfectly intelligible. - iTick |
|||
|
User(s) browsing this thread: 1 Gæst(er)