Frameworky Capstone a Keystone: základ pro tvorbu assemblerů a disassemblerů

3. 1. 2023
Doba čtení: 32 minut

Sdílet

 Autor: Depositphotos
Seznámíme se s možnostmi nabízenými frameworkem Keystone, který se velmi často používá společně s frameworkem Capstone. Keystone je implementován formou knihovny a proto ho lze použít z různých programovacích jazyků.

Obsah

1. Frameworky Capstone a Keystone: základ pro tvorbu assemblerů a disassemblerů

2. Proč je vlastně zapotřebí „nový“ assembler a disassembler?

3. Instalace rozhraní mezi Pythonem a frameworky Capstone a Keystone

4. Ruční překlad knihovny Capstone

5. První kroky s knihovnou Keystone – zavolání assembleru s výpisem přeloženého kódu

6. 32bitový režim mikroprocesorů x86

7. 64bitový režim mikroprocesorů x86–64

8. Zápis instrukcí a operandů s využitím AT&T syntaxe

9. Návěští (labels), skoky a programové smyčky

10. Vnořené programové smyčky

11. Překlad do souboru s binárním kódem

12. Program typu „Hello world“ napsaný v assembleru procesorů x86 určený pro Linux

13. Vygenerování binárního souboru se strojovým kódem aplikace „Hello world“

14. Umístění řetězce (dat) před programový kód

15. Složitější kód zapsaný v assembleru založený na použití podprogramů (subrutin)

16. Překlad kódu zapsaného v assembleru se zpětným překladem

17. Seznam podporovaných architektur a režimů

18. Obsah navazujícího článku – framework Capstone

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Frameworky Capstone a Keystone: základ pro tvorbu assemblerů a disassemblerů

V dnešním článku se ve stručnosti seznámíme s frameworky nazvanými Capstone a Keystone. Jedná se o „univerzální“ assembler a disassembler, které však nejsou přímo volatelné z příkazového řádku tak, jako klasické assemblery (jmenujme například GAS nebo NASM). Namísto toho je jak assembler, tak i disassembler dostupný ve formě nativní knihovny, kterou je možné přímo volat z C i C++ (nebo z jiného jazyka přes FFI). Navíc ovšem existují i rozhraní pro další programovací jazyky, abecedně pro D, Clojure, F#, Common Lisp, Visual Basic, PHP, PowerShell, Haskell, Perl, Python, Ruby, C#, NodeJS, Java, Go, C++, OCaml, Lua, Rust, Delphi, Free Pascal a pro Valu.

Z jakého důvodu se však jedná o „univerzální“ assembler a disassembler? Cílem autorů těchto dvou frameworků je nabídnout uživatelům podporu pro mnoho procesorových architektur. Samozřejmě se v první řadě jedná o x86 (s podporou 16bitového, 32bitového i 64bitového režimu) a o AArch64. Podporovány jsou ovšem i další procesorové architektury, například 32bitový ARM, MIPS, PowerPC, SPARC a SystemZ, přičemž další architektury je možné díky „pluginovatelnosti“ Capstone i Keystone relativně snadno přidat (takto byl například disassembler rozšířen o podporu historického osmibitového mikroprocesoru MOS 6502 atd.).

Poznámka: dnes se sice budeme soustředit právě na Keystone, ale přitom se seznámíme i s některými dalšími (standardními) nástroji, zejména s od, strings a objdump.

2. Proč je vlastně zapotřebí „nový“ assembler a disassembler?

Na tomto místě si možná čtenáři tohoto článku kladou otázku, proč je vlastně zapotřebí vytvářet nový assembler a disassembler. Autoři frameworků Capstone a Keystone pracují mj. na nástrojích z oblasti kyberbezpečnosti, analýzy kódů atd. A v těchto oblastech je někdy zapotřebí provést zpětný překlad krátké sekvence bajtů (disassembling), úpravu takto získaného kódu v assembleru následovaného překladem do nativního kódu (assembling). Pro tyto účely se klasické assemblery a disassemblery ovládané z příkazového řádku příliš nehodí, takže může být vhodnější použít k tomuto účelu vytvořené nativní knihovny. Navíc v současnosti vlastně ani neexistuje skutečně univerzální assembler a disassembler s podporou různých způsobů zápisu instrukcí (GAS s Intel syntaxí, GAS s AT&T syntaxí, NASM, MASM, atd.). Keystone a Capstone se snaží (i když prozatím jen s částečným úspěchem) o dosažení plné kompatibility s těmito syntaxemi a současně o podporu co největšího množství procesorových architektur.

Poznámka: navíc je možné takto pojaté assemblery a disassemblery velmi jednoduše integrovat do různých IDE či jiných (specializovaných) nástrojů.

3. Instalace rozhraní mezi Pythonem a frameworky Capstone a Keystone

V této kapitole si ukážeme instalaci balíčků pro Python, které zajišťují rozhraní mezi skripty napsanými v Pythonu na jedné straně s frameworky Capstone a Keystone na straně druhé.

Vzhledem k tomu, že rozhraní mezi Pythonem a frameworky Capstone a Keystone je dostupné ve formě klasických Pythonovských balíčků na PyPi (https://pypi.org/project/capstone/, https://pypi.org/project/keystone-engine/), je samotná instalace zcela bezproblémová. Nejdříve nainstalujeme jak vlastní Capstone (tedy disassembler), tak i příslušné rozhraní pro Python:

$ pip3 install --user capstone
 
Collecting capstone
  Downloading capstone-4.0.2-py2.py3-none-manylinux1_x86_64.whl (2.1 MB)
     |████████████████████████████████| 2.1 MB 1.4 MB/s
Installing collected packages: capstone
Successfully installed capstone-4.0.2

Následně nainstalujeme i Keystone (tedy assembler), pochopitelně opět s rozhraním pro Python:

$ pip3 install --user keystone-engine
 
Collecting keystone-engine
  Downloading keystone_engine-0.9.2-py2.py3-none-manylinux1_x86_64.whl (1.8 MB)
     |████████████████████████████████| 1.8 MB 1.6 MB/s
Installing collected packages: keystone-engine
Successfully installed keystone-engine-0.9.2
Poznámka: lokální instalace pro aktuálně přihlášeného uživatele typicky probíhá do adresáře ~/.local/lib/Python{verze}/site-packages, jehož minimální struktura by po instalaci měla vypadat přibližně takto:
.
├── capstone
│   ├── include
│   │   └── capstone
│   ├── lib
│   └── __pycache__
├── capstone-4.0.2.dist-info
├── keystone
│   └── __pycache__
├── keystone_engine-0.9.2.dist-info
├── pep517
│   ├── in_process
│   │   └── __pycache__
│   └── __pycache__
└── pep517-0.12.0.dist-info

4. Ruční překlad knihovny Capstone

V případě, že budete chtít využít nástroje Capstone a Keystone z programovacího jazyka C (popř. pochopitelně z C++) a nikoli z Pythonu, je možné si nechat tyto knihovny přeložit přímo překladačem jazyka (Capstone) nebo C++ (Keystone), a to ze zdrojových kódů dostupných na GitHubu. Ukažme si pro ilustraci postup při překladu knihovny Capstone, která vyžaduje pouze překladač jazyka C.

Naklonujeme repositář se zdrojovými kódy této knihovny:

$ git clone git@github.com:capstone-engine/capstone.git
 
Cloning into 'capstone'...
remote: Enumerating objects: 30861, done.
remote: Counting objects: 100% (1604/1604), done.
remote: Compressing objects: 100% (1045/1045), done.
remote: Total 30861 (delta 631), reused 1367 (delta 535), pack-reused 29257
Receiving objects: 100% (30861/30861), 48.27 MiB | 2.03 MiB/s, done.
Resolving deltas: 100% (21692/21692), done.

Ve druhém kroku se přepneme do větve next. To je velmi důležité, protože výchozí větev master se v tomto projektu nepoužívá:

$ cd capstone/
 
✔ /tmp/ramdisk/capstone [master|✔]
 
$ git checkout next
 
Branch 'next' set up to track remote branch 'next' from 'origin'.
Switched to a new branch 'next'

Vlastní překlad je založen na klasickém Makefile:

$ make
 
  CC      cs.o
  CC      utils.o
  CC      SStream.o
  CC      MCInstrDesc.o
  CC      MCRegisterInfo.o
  CC      arch/ARM/ARMModule.o
  CC      arch/ARM/ARMMapping.o
  CC      arch/ARM/ARMInstPrinter.o
  CC      arch/ARM/ARMDisassembler.o
  CC      arch/AArch64/AArch64Disassembler.o
  CC      arch/AArch64/AArch64Module.o
  CC      arch/AArch64/AArch64Mapping.o
  CC      arch/AArch64/AArch64InstPrinter.o
  CC      arch/AArch64/AArch64BaseInfo.o
  CC      arch/M68K/M68KInstPrinter.o
  ...
  ...
  ...
  LINK    test_m680x.static
  LINK    test_evm
  LINK    test_evm.static
  LINK    test_riscv
  LINK    test_riscv.static
  LINK    test_wasm
  LINK    test_wasm.static
  LINK    test_mos65xx
  LINK    test_mos65xx.static
  LINK    test_bpf
  LINK    test_bpf.static
make[1]: Leaving directory '/tmp/ramdisk/capstone/tests'
install -m0755 ./libcapstone.so.5 ./tests/
cd ./tests/ && rm -f libcapstone.so && ln -s libcapstone.so.5 libcapstone.so

Výsledkem bude především nativní knihovna libcapstone.so, kterou bude možné využít způsobem popsaným v navazujícím článku.

5. První kroky s knihovnou Keystone – zavolání assembleru s výpisem přeloženého kódu

Nyní se můžeme podívat na základní způsob použití knihovny Keystone, která implementuje assembler (jenž je do značné míry nezávislý na architektuře procesorů). Pokusíme se z Pythonu přeložit tři jednoduché instrukce ve starobylém šestnáctibitovém režimu mikroprocesorů s architekturou x86. Instrukce jsou zapsány v bytovém poli a oddělené jsou středníkem. Před překladem je nutné zvolit jak architekturu (KS_ARCH_X86), tak i režim (zde konkrétně KS_MODE16). Výsledkem překladu je buď výjimka (která nese základní informaci o tom, k jaké chybě došlo) nebo sekvence bajtů představujících binární strojový kód:

# import všech symbolů použitých ve skriptu
from keystone import Ks, KS_ARCH_X86, KS_MODE_16, KsError
 
# instrukce, které se mají assemblerem přeložit
CODE = b"MOV AX, 100; INC AX; MOV BX, AX"
 
try:
    # inicializace assembleru se specifikací architektury a popř. i režimu
    ks = Ks(KS_ARCH_X86, KS_MODE_16)
 
    # vlastní překlad (assembling)
    encoding, count = ks.asm(CODE)

    # tisk výsledku činnosti assembleru
    print("%s = %s (number of statements: %u)" % (CODE, encoding, count))
except KsError as e:
    print("ERROR: %s" % e)

Výsledek získaný po spuštění tohoto skriptu by měl vypadat následovně:

b'MOV AX, 100; INC AX; MOV BX, AX' = [184, 100, 0, 64, 137, 195] (number of statements: 3)

Alternativně je možné každou instrukci zapsat na samostatném řádku, tj. namísto středníků se použije znak pro konec řádku. Současně namísto pole bajtů použijeme klasický řetězec. V Pythonu může upravený skript vypadat takto:

# import všech symbolů použitých ve skriptu
from keystone import Ks, KS_ARCH_X86, KS_MODE_16, KsError
 
# instrukce, které se mají assemblerem přeložit
CODE = """
    MOV AX, 100
    INC AX
    MOV BX, AX
"""
 
try:
    # inicializace assembleru se specifikací architektury a popř. i režimu
    ks = Ks(KS_ARCH_X86, KS_MODE_16)
 
    # vlastní překlad (assembling)
    encoding, count = ks.asm(CODE)
 
    # tisk výsledku činnosti assembleru
    print("%s = %s (number of statements: %u)" % (CODE, encoding, count))
except KsError as e:
    print("ERROR: %s" % e)

Výsledek činnosti skriptu by měl být totožný se skriptem předchozím:

    MOV AX, 100
    INC AX
    MOV BX, AX
 = [184, 100, 0, 64, 137, 195] (number of statements: 4)
Poznámka: položka „number of statements“ zahrnuje i první prázdný řádek zdrojového kódu.

Konkrétně nyní překlad vypadá takto:

Offset Decimal Hexa Instrukce
0 184 100 0 b8 64 00 mov ax,0×64
3 64 40 inc ax
4 137 195 89 c3 mov bx,ax

6. 32bitový režim mikroprocesorů x86

V případě, že se pokusíme o překlad tří výše zmíněných instrukcí v 32bitovém režimu mikroprocesorů, překlad se podaří, ovšem výsledný binární kód bude odlišný, a to z toho důvodu, že odlišné je i samotné kódování instrukcí:

# import všech symbolů použitých ve skriptu
from keystone import Ks, KS_ARCH_X86, KS_MODE_32, KsError
 
# instrukce, které se mají assemblerem přeložit
CODE = """
    MOV AX, 100
    INC AX
    MOV BX, AX
"""
 
try:
    # inicializace assembleru se specifikací architektury a popř. i režimu
    ks = Ks(KS_ARCH_X86, KS_MODE_32)
 
    # vlastní překlad (assembling)
    encoding, count = ks.asm(CODE)
 
    # tisk výsledku činnosti assembleru
    print("%s = %s (number of statements: %u)" % (CODE, encoding, count))
except KsError as e:
    print("ERROR: %s" % e)

Povšimněte si, jak je výsledný binární kód dlouhý. Je tomu tak kvůli odlišným prefixům (102=0×66) u prakticky všech šestnáctibitových instrukcí:

    MOV AX, 100
    INC AX
    MOV BX, AX
 = [102, 184, 100, 0, 102, 64, 102, 137, 195] (number of statements: 4)

Konkrétně nyní překlad vypadá takto:

Offset Decimal Hexa Instrukce
0 102 184 100 0 66 b8 64 00 mov ax,0×64
4 102 64 66 40 inc ax
6 102 137 195 66 89 c3 mov bx,ax

7. 64bitový režim mikroprocesorů x86–64

Pro úplnost přeložíme ten samý kód, ovšem v 64bitovém režimu mikroprocesorů s architekturou x86–64. I v tomto režimu je totiž možné využít původní šestnáctibitové registry, když je to skutečně zapotřebí (většinou však není):

# import všech symbolů použitých ve skriptu
from keystone import Ks, KS_ARCH_X86, KS_MODE_64, KsError
 
# instrukce, které se mají assemblerem přeložit
CODE = """
    MOV AX, 100
    INC AX
    MOV BX, AX
"""
 
try:
    # inicializace assembleru se specifikací architektury a popř. i režimu
    ks = Ks(KS_ARCH_X86, KS_MODE_64)
 
    # vlastní překlad (assembling)
    encoding, count = ks.asm(CODE)
 
    # tisk výsledku činnosti assembleru
    print("%s = %s (number of statements: %u)" % (CODE, encoding, count))
except KsError as e:
    print("ERROR: %s" % e)

S výsledkem:

    MOV AX, 100
    INC AX
    MOV BX, AX
 = [102, 184, 100, 0, 102, 255, 192, 102, 137, 195] (number of statements: 4)

Konkrétně nyní překlad vypadá takto (opět odlišně – viz prostřední instrukci!):

Offset Decimal Hexa Instrukce
0 102 184 100 0 66 b8 64 00 mov ax,0×64
4 102 255 192 66 ff c0 inc ax
7 102 137 195 66 89 c3 mov bx,ax
Poznámka: v šestnáctibitovém režimu byla instrukce INC AX zakódována v jediném bajtu. V 32bitovém režimu bylo nutné přidat jednobajtový prefix, zatímco v režimu 64bitovém se kvůli většímu množství instrukcí, registrů a adresovacích režimů kódování zcela změnilo a původně velmi krátké instrukce se kódují delší sekvencí bajtů.

8. Zápis instrukcí a operandů s využitím AT&T syntaxe

Při použití různých assemblerů (zejména GNU Assembleru) na mikroprocesorech s architekturou i386 či x86–64 je možné použít dvě různé syntaxe zápisu programů. Proč ale vlastně k tomuto stavu došlo? Původní verze GNU Assembleru z historických důvodů používala zápis používaný v AT&T (resp. přesněji řečeno v Bell Labs při vývoji Unixu). Tento zápis je sice (samozřejmě jen do určité míry) konzistentní mezi různými platformami, ovšem pro mnoho programátorů pracujících na platformách s procesory s architekturou i386 je AT&T syntaxe velmi nezvyklá a taktéž nekompatibilní s dalšími typy assemblerů (dnes již historický Turbo Assembler – TASM, Microsoft Macro Assembler – MASM atd.). Proto mj. vznikl i projekt Netwide Assembler (NASM), který i na Linux (resp. přesněji řečeno do jeho toolchainu) přidal podporu pro zápis programů v assembleru podle zvyklostí z jiných systémů. Změny později nastaly i v GNU Assembleru, což mj. znamená, že od verze 2.10 je možné se jedinou direktivou přepnout do režimu částečně kompatibilního s TASM/MASM.

AT&T syntaxe je podporována i v Keystone, jen musíme explicitně specifikovat její použití (viz též zvýrazněnou část skriptu):

# import všech symbolů použitých ve skriptu
from keystone import Ks, KS_ARCH_X86, KS_MODE_16, KS_OPT_SYNTAX_ATT, KsError
 
# instrukce, které se mají assemblerem přeložit
CODE = """
    MOV %AX, 100
    INC %AX
    MOV %BX, %AX
"""
 
try:
    # inicializace assembleru se specifikací architektury a popř. i režimu
    ks = Ks(KS_ARCH_X86, KS_MODE_16)
 
    # exlicitní specifikace použité syntaxe
    ks.syntax = KS_OPT_SYNTAX_ATT
 
    # vlastní překlad (assembling)
    encoding, count = ks.asm(CODE)
 
    # tisk výsledku činnosti assembleru
    print("%s = %s (number of statements: %u)" % (CODE, encoding, count))
except KsError as e:
    print("ERROR: %s" % e)

Ve skutečnosti ovšem tento skript neobsahuje korektní sekvenci instrukcí, takže i výsledek nebude korektní:

    MOV %AX, 100
    INC %AX
    MOV %BX, %AX
 = [163, 0, 1, 64, 137, 216] (number of statements: 4)

V AT&T syntaxi se totiž operandy instrukcí uvádí v opačném pořadí (zdroj, cíl). Navíc se odlišně zapisují i konstanty. Provedeme tedy malé úpravy do podoby:

# import všech symbolů použitých ve skriptu
from keystone import Ks, KS_ARCH_X86, KS_MODE_16, KS_OPT_SYNTAX_ATT, KsError
 
# instrukce, které se mají assemblerem přeložit
CODE = """
    MOV $64, %AX
    INC %AX
    MOV %AX, %BX
"""
 
try:
    # inicializace assembleru se specifikací architektury a popř. i režimu
    ks = Ks(KS_ARCH_X86, KS_MODE_16)
 
    # exlicitní specifikace použité syntaxe
    ks.syntax = KS_OPT_SYNTAX_ATT
 
    # vlastní překlad (assembling)
    encoding, count = ks.asm(CODE)
 
    # tisk výsledku činnosti assembleru
    print("%s = %s (number of statements: %u)" % (CODE, encoding, count))
except KsError as e:
    print("ERROR: %s" % e)

Nyní by již mělo dojít k překladu, a to do stejné sekvence bajtů, jako tomu bylo u příkladů uvedených v páté kapitole:

    MOV $64, %AX
    INC %AX
    MOV %AX, %BX
 = [184, 100, 0, 64, 137, 195] (number of statements: 4)

9. Návěští (labels), skoky a programové smyčky

V prakticky všech programech zapsaných v assembleru se setkáme se skoky a programovými smyčkami (realizovanými opět skoky). Cíle skoků jsou přitom označeny pojmenovaným návěštím (label), přičemž za jméno návěští se zapisuje dvojtečka a samotné návěští typicky začíná na začátku assemblerovského řádku. Podívejme se nyní na realizaci velmi jednoduché programové smyčky, v níž je ve funkci počitadla použit 32bitový registr EAX, jehož hodnota se postupně snižuje od 100 k nule. Jakmile se dosáhne nulové hodnoty, skok JNZ (Jump if Not Zero) se již neprovede:

    MOV EAX, 100
LOOP:
    DEC EAX
    JNZ LOOP

Tuto smyčku lze přeložit do sekvence strojových instrukcí takto:

# import všech symbolů použitých ve skriptu
from keystone import Ks, KS_ARCH_X86, KS_MODE_64, KsError
 
# instrukce, které se mají assemblerem přeložit
CODE = """
    MOV EAX, 100
LOOP:
    DEC EAX
    JNZ LOOP
"""
 
try:
    # inicializace assembleru se specifikací architektury a popř. i režimu
    ks = Ks(KS_ARCH_X86, KS_MODE_64)
 
    # vlastní překlad (assembling)
    encoding, count = ks.asm(CODE)
 
    # tisk výsledku činnosti assembleru
    print("%s = %s (number of statements: %u)" % (CODE, encoding, count))
except KsError as e:
    print("ERROR: %s" % e)

Podívejme se nyní na způsob překladu výše uvedené programové smyčky do assembleru:

    MOV EAX, 100
LOOP:
    DEC EAX
    JNZ LOOP
 = [184, 100, 0, 0, 0, 255, 200, 117, 252] (number of statements: 5)

10. Vnořené programové smyčky

Ve strojovém kódu (a tedy i v assembleru) je možné realizovat skoky na prakticky libovolnou adresu v kódovém segmentu (i když některé architektury omezují cílové adresy na hodnoty dělitelné 4, 8 atd.). To mj. znamená, že je možné realizovat vnořené programové smyčky; ovšem fakt, že se jedná o strukturované vnořené smyčky a nikoli o „špagetový kód“ záleží jen na vývojáři.

Podívejme se nyní na triviální příklad s dvojicí vnořených smyček. Jako počitadlo vnější smyčky slouží pracovní registr EBX, pro vnitřní smyčku je jako počitadlo použit pracovní registr EAX. Počitadla jsou vždy snižována o jedničku a skok na začátek smyčky je proveden v případě, že hodnota počitadla ještě nedosáhla nuly:

    MOV EBX, 10
OUTER_LOOP:
    MOV EAX, 100
INNER_LOOP:
    DEC EAX
    JNZ INNER_LOOP
    DEC EBX
    JNZ OUTER_LOOP

Výše uvedenou dvojici programových smyček je možné přeložit do sekvence strojových instrukcí následovně:

# import všech symbolů použitých ve skriptu
from keystone import Ks, KS_ARCH_X86, KS_MODE_64, KsError
 
# instrukce, které se mají assemblerem přeložit
CODE = """
    MOV EBX, 10
OUTER_LOOP:
    MOV EAX, 100
INNER_LOOP:
    DEC EAX
    JNZ INNER_LOOP
    DEC EBX
    JNZ OUTER_LOOP
"""
 
try:
    # inicializace assembleru se specifikací architektury a popř. i režimu
    ks = Ks(KS_ARCH_X86, KS_MODE_64)
 
    # vlastní překlad (assembling)
    encoding, count = ks.asm(CODE)
 
    # tisk výsledku činnosti assembleru
    print("%s = %s (number of statements: %u)" % (CODE, encoding, count))
except KsError as e:
    print("ERROR: %s" % e)

Opět si ukažme, jakým způsobem se předchozí kód přeloží do assembleru:

    MOV EBX, 10
OUTER_LOOP:
    MOV EAX, 100
INNER_LOOP:
    DEC EAX
    JNZ INNER_LOOP
    DEC EBX
    JNZ OUTER_LOOP
 = [187, 10, 0, 0, 0, 184, 100, 0, 0, 0, 255, 200, 117, 252, 255, 203, 117, 243] (number of statements: 9)
Poznámka: v případě, že vám vygenerovaný strojový kód připadá příliš dlouhý, je vhodné si připomenout, že obě počitadla smyčky jsou realizována 32bitovými registry a tudíž načtení konstanty do takového registru je realizováno pětibajtovou instrukcí (první bajt je operačním kódem, další čtyři bajty obsahují hodnotu).

11. Překlad do souboru s binárním kódem

Vzhledem k tomu, že výsledkem překladu je v případě frameworku Keystone sekvence bajtů, je možné velmi snadno realizovat překlad do souboru obsahujícího binární kód. Pozor ovšem na to, že se nebude jednat o spustitelný soubor ani o nativní knihovnu, ale skutečně „pouze“ o sekvenci bajtů se zakódovanými strojovými instrukcemi.

Kód s vnořenými smyčkami přeložíme do binárního souboru nazvaného „loops.bin“ takto:

# import všech symbolů použitých ve skriptu
from keystone import Ks, KS_ARCH_X86, KS_MODE_64, KsError
 
# instrukce, které se mají assemblerem přeložit
CODE = """
    MOV EBX, 10
OUTER_LOOP:
    MOV EAX, 100
INNER_LOOP:
    DEC EAX
    JNZ INNER_LOOP
    DEC EBX
    JNZ OUTER_LOOP
"""
 
try:
    # inicializace assembleru se specifikací architektury a popř. i režimu
    ks = Ks(KS_ARCH_X86, KS_MODE_64)
 
    # vlastní překlad (assembling)
    encoding, count = ks.asm(CODE)
 
    # uložení výsledného nativního kódu do souboru
    with open("loops.bin", "wb") as fout:
        fout.write(bytes(encoding))
    print("Binary file written")
except KsError as e:
    print("ERROR: %s" % e)

Vygenerovaný soubor „loops.bin“ bude mít délku osmnácti bajtů, jejichž obsah si můžeme snadno prohlédnout v hexadecimálním prohlížeči (nenechte se zmýlit tím, že od původně znamenalo octal dump):

$ od -tx1 loops.bin
 
0000000 bb 0a 00 00 00 b8 64 00 00 00 ff c8 75 fc ff cb
0000020 75 f3
0000022

Nástrojem objdump se můžeme pokusit o disassembling binárního souboru, tedy vlastně o obnovu původního kódu napsaného v assembleru a doplněného o konkrétní adresy:

$ objdump -b binary -D -m i386:x86-64 -M intel loops.bin > loops_dump.asm

S tímto výsledkem:

loops.bin:     file format binary
 
 
Disassembly of section .data:
 
0000000000000000 <.data>:
   0:   bb 0a 00 00 00          mov    ebx,0xa
   5:   b8 64 00 00 00          mov    eax,0x64
   a:   ff c8                   dec    eax
   c:   75 fc                   jne    0xa
   e:   ff cb                   dec    ebx
  10:   75 f3                   jne    0x5
Poznámka: právě na tomto výpisu je patrné, proč je šest instrukcí uloženo v osmnácti bajtech: první dvě instrukce mají délku pěti bajtů.

12. Program typu „Hello world“ napsaný v assembleru procesorů x86 určený pro Linux

Podívejme se nyní, jak může vypadat zápis programu typu „Hello world!“ napsaný v GNU Assembleru a určený pro spuštění na architektuře i386 s Linuxem. Celý program vlastně volá jen dvě služby jádra: sys_write a sys_exit. V případě sys_exit je nutné nastavit tuto dvojici pracovních registrů:

Registr Význam Obsah
eax číslo syscallu sys_write=1
ebx návratová hodnota exit code = 0

U volání sys_write se naproti tomu nastaví pracovní registry takto:

Registr Význam Obsah
eax číslo syscallu sys_write=4
ebx číslo deskriptoru stdout=1
ecx adresa řetězce/bajtů nastaví se do .data segmentu
edx počet bajtů pro zápis strlen(„Hello world!\n“)=13

Zajímavý je obsah pracovního registru ecx, protože ten musí obsahovat adresu řetězce (resp. bloku bajtů). V AT&T syntaxi to vypadá následovně:

mov   $hello_lbl,%ecx

kdežto v syntaxi Intelu se namísto toho použije:

mov   ecx, hello_lbl

Přičemž hello_lbl je návěští (label) neboli pojmenovaná adresa. Povšimněte si, že řetězec není ukončen znakem s ASCII kódem 0. To není nutné, protože systémová služba přesně zná délku řetězce (bloku bajtů):

# asmsyntax=as
 
# Jednoducha aplikace typu "Hello world!" naprogramovana
# v assembleru GNU as - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky
 
.intel_syntax noprefix
 
 
# Linux kernel system call table
sys_exit=1
sys_write=4
 
 
 
_start:
        mov   eax, sys_write         # cislo syscallu pro funkci "write"
        mov   ebx, 1                 # standardni vystup
        mov   ecx, hello_lbl         # adresa retezce, ktery se ma vytisknout
        mov   edx, 13                # pocet znaku, ktere se maji vytisknout
        int   0x80                   # volani Linuxoveho kernelu
 
        mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
        mov   ebx, 0                 # exit code = 0
        int   0x80                   # volani Linuxoveho kernelu
 
 
hello_lbl:
        .string "Hello World!\n"     # string, ktery JE ukoncen nulou
Poznámka: tento prográmek napsaný v assembleru naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/he­llo_world.asm.

13. Vygenerování binárního souboru se strojovým kódem aplikace „Hello world“

Zdrojový kód napsaný v assembleru, který byl uveden v předchozí kapitole, můžeme přímo přeložit frameworkem Keystone. Postup je v tomto případě velmi jednoduchý: nejprve obsah souboru načteme do řetězce (s oddělovači řádků atd.), provedeme překlad (assembling) a výsledný binární sekvenci instrukcí uložíme do binárního souboru:

# import všech symbolů použitých ve skriptu
from keystone import Ks, KS_ARCH_X86, KS_MODE_64, KsError
 
 
try:
    # načtení kódu v assembleru ze souboru
    with open("hello_world.asm", "r") as fin:
        code = fin.read()
 
    # kontrolní výpis, jaký kód budeme překládat
    print(code)
 
    # inicializace assembleru se specifikací architektury a popř. i režimu
    ks = Ks(KS_ARCH_X86, KS_MODE_64)
 
    # vlastní překlad (assembling)
    encoding, count = ks.asm(code)
 
    # uložení výsledného nativního kódu do souboru
    with open("hello.bin", "wb") as fout:
        fout.write(bytes(encoding))
    print("Binary file written")
except KsError as e:
    print("ERROR: %s" % e)

Po spuštění výše uvedeného skriptu by měl vzniknout soubor nazvaný „hello.bin“ s délkou přesně 48 bajtů. Můžeme se samozřejmě podívat na obsah tohoto souboru. Nejdříve použijeme standardní osmičkový/hexadecimální prohlížeč od neboli octal dump:

$ od -tx1 hello.bin
 
0000000 b8 04 00 00 00 bb 01 00 00 00 b9 22 00 00 00 ba
0000020 0d 00 00 00 cd 80 b8 01 00 00 00 bb 00 00 00 00
0000040 cd 80 48 65 6c 6c 6f 20 57 6f 72 6c 64 21 0a 00
0000060

Zajímavější je ovšem použít nástroj objdump, a to následujícím způsobem:

$ objdump -b binary -D -m i386:x86-64 -M intel hello.bin > hello_dump.asm

Výsledek bude vypadat takto:

hello.bin:     file format binary
 
 
Disassembly of section .data:
 
0000000000000000 <.data>:
   0:   b8 04 00 00 00          mov    eax,0x4
   5:   bb 01 00 00 00          mov    ebx,0x1
   a:   b9 22 00 00 00          mov    ecx,0x22
   f:   ba 0d 00 00 00          mov    edx,0xd
  14:   cd 80                   int    0x80
  16:   b8 01 00 00 00          mov    eax,0x1
  1b:   bb 00 00 00 00          mov    ebx,0x0
  20:   cd 80                   int    0x80
  22:   48                      rex.W
  23:   65 6c                   gs ins BYTE PTR es:[rdi],dx
  25:   6c                      ins    BYTE PTR es:[rdi],dx
  26:   6f                      outs   dx,DWORD PTR ds:[rsi]
  27:   20 57 6f                and    BYTE PTR [rdi+0x6f],dl
  2a:   72 6c                   jb     0x98
  2c:   64 21 0a                and    DWORD PTR fs:[rdx],ecx
        ...
Poznámka: prvních devět instrukcí dává smysl; je to obraz původního kódu v assembleru. Další „instrukce“ vlastně žádné instrukce nejsou, protože se jedná o data – řetězec „Hello World\n“:
$ strings hello.bin 
 
Hello World!

14. Umístění řetězce (dat) před programový kód

Pokusme se nyní zdrojový kód napsaný v assembleru a uvedený ve dvanácté kapitole upravit tak, že definici řetězce vložíme před vlastní sekvenci instrukcí. Vzhledem k tomu, že v kódu není uvedena definice sekcí (segmentů) a vlastně ani nepoužíváme linker, bude se výsledný nativní binární kód lišit:

# asmsyntax=as
 
# Jednoducha aplikace typu "Hello world!" naprogramovana
# v assembleru GNU as - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky
 
.intel_syntax noprefix
 
 
# Linux kernel system call table
sys_exit=1
sys_write=4
 
 
hello_lbl:
        .string "Hello World!\n"     # string, ktery JE ukoncen nulou
 
 
_start:
        mov   eax, sys_write         # cislo syscallu pro funkci "write"
        mov   ebx, 1                 # standardni vystup
        mov   ecx, hello_lbl         # adresa retezce, ktery se ma vytisknout
        mov   edx, 13                # pocet znaku, ktere se maji vytisknout
        int   0x80                   # volani Linuxoveho kernelu
 
        mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
        mov   ebx, 0                 # exit code = 0
        int   0x80                   # volani Linuxoveho kernelu
Poznámka: tento prográmek napsaný v assembleru naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/he­llo_world2.asm.

Pro překlad použijeme skript:

# import všech symbolů použitých ve skriptu
from keystone import Ks, KS_ARCH_X86, KS_MODE_64, KsError
 
 
try:
    # načtení kódu v assembleru ze souboru
    with open("hello_world_2.asm", "r") as fin:
        code = fin.read()
 
    # kontrolní výpis, jaký kód budeme překládat
    print(code)
 
    # inicializace assembleru se specifikací architektury a popř. i režimu
    ks = Ks(KS_ARCH_X86, KS_MODE_64)
 
    # vlastní překlad (assembling)
    encoding, count = ks.asm(code)
 
    # uložení výsledného nativního kódu do souboru
    with open("hello_2.bin", "wb") as fout:
        fout.write(bytes(encoding))
    print("Binary file written")
except KsError as e:
    print("ERROR: %s" % e)

Výsledný binární soubor bude mít opět délku 48 bajtů, ovšem odlišný obsah:

$ od -tx1 hello_2.bin
 
0000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 21 0a 00 b8 04
0000020 00 00 00 bb 01 00 00 00 b9 00 00 00 00 ba 0d 00
0000040 00 00 cd 80 b8 01 00 00 00 bb 00 00 00 00 cd 80
0000060

I z výstupu získaného nástrojem objdump je patrné, že v souboru se nejdříve nachází samotný řetězec a teprve od offsetu 0×13 sekvence instrukcí:

hello_2.bin:     file format binary
 
 
Disassembly of section .data:
 
0000000000000000 <.data>:
   0:   48                      rex.W
   1:   65 6c                   gs ins BYTE PTR es:[rdi],dx
   3:   6c                      ins    BYTE PTR es:[rdi],dx
   4:   6f                      outs   dx,DWORD PTR ds:[rsi]
   5:   20 57 6f                and    BYTE PTR [rdi+0x6f],dl
   8:   72 6c                   jb     0x76
   a:   64 21 0a                and    DWORD PTR fs:[rdx],ecx
   d:   00 b8 04 00 00 00       add    BYTE PTR [rax+0x4],bh
  13:   bb 01 00 00 00          mov    ebx,0x1
  18:   b9 00 00 00 00          mov    ecx,0x0
  1d:   ba 0d 00 00 00          mov    edx,0xd
  22:   cd 80                   int    0x80
  24:   b8 01 00 00 00          mov    eax,0x1
  29:   bb 00 00 00 00          mov    ebx,0x0
  2e:   cd 80                   int    0x80
Poznámka: samozřejmě je možné pracovat i se sekcemi/segmenty a použít linker pro vytvoření výsledného binárního souboru. Tomuto tématu se budeme věnovat v samostatném článku.

15. Složitější kód zapsaný v assembleru založený na použití podprogramů (subrutin)

Nástroj Keystone dokáže přeložit i složitější programové kódy zapsané v assembleru. Může se jednat například o programy, v nichž se používají podprogramy neboli subrutiny. Povšimněte si, že subrutiny voláme instrukcí call ještě před jejich definicí, takže assembler nemůže při prvním průchodu znát jejich adresy (navíc ze subrutin voláme další subrutiny). Ty bude znát až na začátku druhého průchodu:

# asmsyntax=as
 
# Program pro otestovani chovani instrukci CALL a RET
# - pouzita je "Intel" syntaxe.
#
# Autor: Pavel Tisnovsky
 
.intel_syntax noprefix
 
 
# Linux kernel system call table
sys_exit   = 1
sys_write  = 4
 
# Dalsi konstanty pouzite v programu - standardni streamy
std_input  = 0
std_output = 1
 
 
 
message1:                            # adresa prvni zpravy
        .string "Hello World\n"
message1len = 13                     # delka prvni zpravy
 
message2:                            # adresa druhe zpravy
        .string "Assembler je fajn\n"
message2len = 18                     # delka druhe zpravy
 
 
_start:
        call  writeFirstMessage      # zavolani podprogramu pro vytisteni prvni zpravy
        call  writeSecondMessage     # zavolani podprogramu pro vytisteni druhe zpravy
        call  exit                   # zavolani podprogramu pro ukonceni procesu
 
 
 
# Podprogram pro vytisteni prvni zpravy
writeFirstMessage:
        mov   ecx, message1          # adresa retezce, ktery se ma vytisknout
        mov   edx, message1len       # pocet znaku, ktere se maji vytisknout
        call  writeMessage           # zavolani podprogramu pro vytisteni zpravy
        ret                          # navrat z podprogramu
 
 
 
# Podprogram pro vytisteni druhe zpravy
writeSecondMessage:
        mov   ecx, message2          # adresa retezce, ktery se ma vytisknout
        mov   edx, message2len       # pocet znaku, ktere se maji vytisknout
        call  writeMessage           # zavolani podprogramu pro vytisteni zpravy
        ret                          # navrat z podprogramu
 
 
 
# Podprogram pro vytisteni zpravy na standardni vystup
# Ocekava se, ze v ecx bude adresa zpravy a v edx jeji delka
writeMessage:
        mov   eax, sys_write         # cislo syscallu pro funkci "write"
        mov   ebx, std_output        # standardni vystup
        int   0x80                   # volani Linuxoveho kernelu
        ret                          # navrat z podprogramu
 
 
 
# Podprogram pro ukonceni procesu zavolanim syscallu
exit:
        mov   eax, sys_exit          # cislo sycallu pro funkci "exit"
        mov   ebx, 0                 # exit code = 0
        int   0x80                   # volani Linuxoveho kernelu
 
# finito
Poznámka: tento prográmek napsaný v assembleru naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/su­broutines.asm.

16. Překlad kódu zapsaného v assembleru se zpětným překladem

Výše uvedený kód zapsaný v assembleru přeložíme následujícím skriptem:

from keystone import *
 
try:
    with open("subroutines.asm", "r") as fin:
        code = fin.read()
 
    print(code)
 
    ks = Ks(KS_ARCH_X86, KS_MODE_32)
    encoding, count = ks.asm(code)
    with open("subroutines.bin", "wb") as fout:
        fout.write(bytes(encoding))
except KsError as e:
    print("ERROR: %s" %e)

Výsledkem překladu bude binární soubor o délce 104 bajtů, jehož obsah si můžeme prohlédnout nástrojem objdump. Povšimněte si, že vlastní instrukce začínají až od offsetu 0×20, protože v první části binárního souboru jsou uloženy řetězce:

subroutines.bin:     file format binary
 
 
Disassembly of section .data:
 
0000000000000000 <.data>:
   0:   48                      rex.W
   1:   65 6c                   gs ins BYTE PTR es:[rdi],dx
   3:   6c                      ins    BYTE PTR es:[rdi],dx
   4:   6f                      outs   dx,DWORD PTR ds:[rsi]
   5:   20 57 6f                and    BYTE PTR [rdi+0x6f],dl
   8:   72 6c                   jb     0x76
   a:   64 0a 00                or     al,BYTE PTR fs:[rax]
   d:   41 73 73                rex.B jae 0x83
  10:   65 6d                   gs ins DWORD PTR es:[rdi],dx
  12:   62                      (bad)
  13:   6c                      ins    BYTE PTR es:[rdi],dx
  14:   65 72 20                gs jb  0x37
  17:   6a 65                   push   0x65
  19:   20 66 61                and    BYTE PTR [rsi+0x61],ah
  1c:   6a 6e                   push   0x6e
  1e:   0a 00                   or     al,BYTE PTR [rax]
  20:   e8 0a 00 00 00          call   0x2f
  25:   e8 15 00 00 00          call   0x3f
  2a:   e8 2d 00 00 00          call   0x5c
  2f:   b9 00 00 00 00          mov    ecx,0x0
  34:   ba 0d 00 00 00          mov    edx,0xd
  39:   e8 11 00 00 00          call   0x4f
  3e:   c3                      ret
  3f:   b9 0d 00 00 00          mov    ecx,0xd
  44:   ba 12 00 00 00          mov    edx,0x12
  49:   e8 01 00 00 00          call   0x4f
  4e:   c3                      ret
  4f:   b8 04 00 00 00          mov    eax,0x4
  54:   bb 01 00 00 00          mov    ebx,0x1
  59:   cd 80                   int    0x80
  5b:   c3                      ret
  5c:   b8 01 00 00 00          mov    eax,0x1
  61:   bb 00 00 00 00          mov    ebx,0x0
  66:   cd 80                   int    0x80

17. Seznam podporovaných architektur a režimů

Na závěr si ještě pro úplnost uveďme seznam podporovaných architektur a jejich režimů, a to při použití kombinace Keystone + rozhraní pro Python. Může se totiž stát, že samotný Keystone bude v budoucnu podporovat i další architektury, které se do rozhraní pro Python zařadí s určitým zpožděním.

Seznam podporovaných architektur:

Konstanta Význam
KS_ARCH_ARM 32bitový ARM
KS_ARCH_ARM64 64bitový ARM (AArch64)
KS_ARCH_MIPS MIPS
KS_ARCH_X86 x86(64)
KS_ARCH_PPC PowerPC
KS_ARCH_SPARC SPARC
KS_ARCH_SYSTEMZ SystemZ
KS_ARCH_HEXAGON Qualcomm Hexagon
KS_ARCH_EVM EVM (Texas Instruments)

Režimy:

Konstanta Význam
KS_MODE_LITTLE_ENDIAN LE režim pro některé architektury
KS_MODE_BIG_ENDIAN BE režim pro některé architektury
KS_MODE_ARM klasický 32bitový ARM
KS_MODE_THUMB 32bitový ARM s instrukcemi Thumb a Thumb2
KS_MODE_V8 ARM v8
KS_MODE_V9 ARM v9
KS_MODE_MIPS3 režimy pro MIPS
KS_MODE_MIPS32R6 režimy pro MIPS
KS_MODE_MIPS32 režimy pro MIPS
KS_MODE_MIPS64 režimy pro MIPS
KS_MODE16 režimy pro x86(64)
KS_MODE32 režimy pro x86(64)
KS_MODE64 režimy pro x86(64)
KS_MODE_PPC32 režimy pro PowerPC
KS_MODE_PPC64 režimy pro PowerPC
KS_MODE_SPARC32 režimy pro SPARC
KS_MODE_SPARC64 režimy pro SPARC

18. Obsah navazujícího článku – framework Capstone

V navazujícím článku se seznámíme s frameworkem Capstone, jenž se velmi často používá společně s dnes popisovaným frameworkem Keystone. Zatímco výše zmíněný framework Keystone je univerzálním assemblerem, jedná se v případě frameworku Capstone o univerzální disassembler, který podporuje všechny v současnosti mainstreamové architektury (x86, x86–64, 32bitový ARM, AArch64 atd.) a je implementován formou nativní knihovny, kterou je možné volat z různých programovacích jazyků. Oba zmíněné frameworky, tedy jak Capstone, tak i Keystone, lze přitom používat společně. Příkladem mohou být systémy, které nejprve provedou disassembling vybraného binárního bloku dat s úpravou kódu v assembleru a s jeho následným překladem zpět do nativního binárního kódu.

Ukažme si tedy alespoň základní způsob zavolání disassembleru Capstone. Použijeme ho pro zpětný překlad nativních instrukcí uložených v souboru „loops.bin“, jenž vznikl překladem provedeným v rámci předchozích kapitol:

ict ve školství 24

from capstone import *
 
 
with open("loops.bin", "rb") as fin:
    code = fin.read()
 
md = Cs(CS_ARCH_X86, CS_MODE_64)
for i in md.disasm(code, 0x0000):
    print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))

Výsledek se do určité míry podobá výsledku získaného nástrojem objdump:

0x0:    mov     ebx, 0xa
0x5:    mov     eax, 0x64
0xa:    dec     eax
0xc:    jne     5
0xe:    dec     ebx
0x10:   jne     0

19. Repositář s demonstračními příklady

Zdrojové kódy všech prozatím popsaných demonstračních příkladů určených pro programovací jazyk Python 3 (nikoli ovšem pro starší verze Pythonu 2!) byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má velikost zhruba několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:

# Demonstrační příklad Stručný popis příkladu Cesta
1 first_steps_16bit_A.py překlad 16bitového kódu (zapsaného na jediném řádku) mikroprocesorů řady x86 https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/fir­st_steps_16bit_A.py
2 first_steps_16bit_B.py překlad 16bitového kódu (zapsaného na více řádcích) mikroprocesorů řady x86 https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/fir­st_steps_16bit_B.py
3 first_steps_32bit.py překlad 32bitového kódu mikroprocesorů řady x86 https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/fir­st_steps_32bit.py
4 first_steps_64bit.py překlad 32bitového kódu mikroprocesorů řady x86–64 https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/fir­st_steps_64bit.py
       
5 att_syntax_A.py kód zapsaný v assembleru, který využívá syntaxi AT&T (nekorektní varianta) https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/at­t_syntax_A.py
6 att_syntax_B.py kód zapsaný v assembleru, který využívá syntaxi AT&T (korektní varianta) https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/at­t_syntax_B.py
       
7 loop_A.py kód zapsaný v assembleru, v němž je použita programová smyčka https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/loop_A.py
8 loop_B.py kód zapsaný v assembleru, v němž jsou použity vnořené programové smyčky https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/loop_B.py
9 loops_dump.asm výsledek získaný skriptem loop_A.py a nástrojem objdump https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/lo­ops_dump.asm
       
10 into_binary_A.py překlad assembleru do binární formy (smyčky) https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/in­to_binary_A.py
11 into_binary_B.py překlad assembleru do binární formy („Hello world“) https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/in­to_binary_B.py
12 into_binary_C.py překlad assembleru do binární formy („Hello world“) https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/in­to_binary_C.py
13 into_binary_D.py překlad assembleru do binární formy (využití AT&T syntaxe) https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/in­to_binary_D.py
       
14 hello_world.asm program typu „Hello world“ napsaný v assembleru https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/he­llo_world.asm
15 hello_world2.asm upravený program typu „Hello world“ napsaný v assembleru https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/he­llo_world2.asm
16 hello_dump.asm zpětný překlad nástrojem objdump https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/he­llo_dump.asm
17 hello2_dump.asm zpětný překlad nástrojem objdump https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/he­llo2_dump.asm
       
18 subroutines.py překlad programu v assembleru, jenž používá podprogramy (subrutiny) https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/su­broutines.py
19 subroutines.asm program zapsaný v assembleru, jenž používá podprogramy (subrutiny) https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/su­broutines.asm
20 subroutines_dump.asm zpětný překlad nástrojem objdump https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/su­broutines_dump.asm
       
21 disasm.py zpětný disassembling binárního kódu https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/disasm.py
       
22 att_syntax.asm kód v assembleru se syntaxí AT&T https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/at­t_syntax.asm
23 att_syntax_dump.asm zpětný překlad nástrojem objdump https://github.com/tisnik/most-popular-python-libs/blob/master/keystone/at­t_syntax_dump.asm

20. Odkazy na Internetu

  1. Keystone Engine na GitHubu
    https://github.com/keystone-engine/keystone
  2. Keystone: The Ultimate Assembler
    https://www.keystone-engine.org/
  3. The Ultimate Disassembler
    http://www.capstone-engine.org/
  4. Tutorial for Keystone
    https://www.keystone-engine.org/docs/tutorial.html
  5. Rozhraní pro Capstone na PyPi
    https://pypi.org/project/capstone/
  6. Rozhraní pro Keystone na PyPi
    https://pypi.org/project/keystone-engine/
  7. KEYSTONE: Next Generation Assembler Framework
    https://www.keystone-engine.org/docs/BHUSA2016-keystone.pdf
  8. AT&T Syntax versus Intel Syntax
    http://web.mit.edu/rhel-doc/3/rhel-as-en-3/i386-syntax.html
  9. AT&T assembly syntax and IA-32 instructions
    https://gist.github.com/mishu­rov/6bcf04df329973c15044
  10. ARM GCC Inline Assembler Cookbook
    http://www.ethernut.de/en/do­cuments/arm-inline-asm.html
  11. Extended Asm – Assembler Instructions with C Expression Operands
    https://gcc.gnu.org/online­docs/gcc/Extended-Asm.html
  12. ARM inline asm secrets
    http://hardwarebug.org/2010/07/06/arm-inline-asm-secrets/
  13. How to Use Inline Assembly Language in C Code
    https://gcc.gnu.org/online­docs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C
  14. GCC-Inline-Assembly-HOWTO
    http://ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
  15. A Brief Tutorial on GCC inline asm (x86 biased)
    http://www.osdever.net/tu­torials/view/a-brief-tutorial-on-gcc-inline-asm
  16. GCC Inline ASM
    http://locklessinc.com/ar­ticles/gcc_asm/
  17. GNU Assembler Examples
    http://cs.lmu.edu/~ray/no­tes/gasexamples/
  18. X86 Assembly/Arithmetic
    https://en.wikibooks.org/wi­ki/X86_Assembly/Arithmetic
  19. Art of Assembly – Arithmetic Instructions
    http://oopweb.com/Assembly/Do­cuments/ArtOfAssembly/Volu­me/Chapter6/CH06–2.html
  20. The GNU Assembler Tutorial
    http://tigcc.ticalc.org/doc/gnu­asm.html
  21. The GNU Assembler – macros
    http://tigcc.ticalc.org/doc/gnu­asm.html#SEC109
  22. ARM subroutines & program stack
    http://www.toves.org/books/armsub/
  23. Generating Mixed Source and Assembly List using GCC
    http://www.systutorials.com/240/ge­nerate-a-mixed-source-and-assembly-listing-using-gcc/
  24. Calling subroutines
    http://infocenter.arm.com/hel­p/index.jsp?topic=/com.ar­m.doc.kui0100a/armasm_cih­cfigg.htm
  25. Linux assemblers: A comparison of GAS and NASM
    http://www.ibm.com/develo­perworks/library/l-gas-nasm/index.html
  26. Programovani v assembleru na OS Linux
    http://www.cs.vsb.cz/gryga­rek/asm/asmlinux.html
  27. Is it worthwhile to learn x86 assembly language today?
    https://www.quora.com/Is-it-worthwhile-to-learn-x86-assembly-language-today?share=1
  28. Why Learn Assembly Language?
    http://www.codeproject.com/Ar­ticles/89460/Why-Learn-Assembly-Language
  29. Is Assembly still relevant?
    http://programmers.stackex­change.com/questions/95836/is-assembly-still-relevant
  30. Why Learning Assembly Language Is Still a Good Idea
    http://www.onlamp.com/pub/a/on­lamp/2004/05/06/writegreat­code.html
  31. Assembly language today
    http://beust.com/weblog/2004/06/23/as­sembly-language-today/
  32. Assembler: Význam assembleru dnes
    http://www.builder.cz/rubri­ky/assembler/vyznam-assembleru-dnes-155960cz
  33. Assembler pod Linuxem
    http://phoenix.inf.upol.cz/li­nux/prog/asm.html
  34. Online x86 / x64 Assembler and Disassembler
    https://defuse.ca/online-x86-assembler.htm#disassembly

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.