Miasm est un framework d'ingénierie inverse gratuit et open source (GPLv2). Miasm a pour objectif d'analyser/modifier/générer des programmes binaires. Voici une liste non exhaustive des fonctionnalités :
Consultez le blog officiel pour plus d’exemples et de démos.
Importer l'architecture Miasm x86 :
>>> from miasm.arch.x86.arch import mn_x86
>>> from miasm.core.locationdb import LocationDB
Obtenez une base de données de localisation :
>>> loc_db = LocationDB()
Assemblez une ligne :
>>> l = mn_x86.fromstring( ' XOR ECX, ECX ' , loc_db, 32 )
>>> print (l)
XOR ECX, ECX
>>> mn_x86.asm(l)
['1xc9', '3xc9', 'g1xc9', 'g3xc9']
Modifier un opérande :
>>> l.args[ 0 ] = mn_x86.regs. EAX
>>> print (l)
XOR EAX, ECX
>>> a = mn_x86.asm(l)
>>> print (a)
['1xc8', '3xc1', 'g1xc8', 'g3xc1']
Démontez le résultat :
>>> print (mn_x86.dis(a[ 0 ], 32 ))
XOR EAX, ECX
Utilisation de l'abstraction Machine
:
>>> from miasm.analysis.machine import Machine
>>> mn = Machine( ' x86_32 ' ).mn
>>> print (mn.dis( ' x33x30 ' , 32 ))
XOR ESI, DWORD PTR [EAX]
Pour les MIPS :
>>> mn = Machine( ' mips32b ' ).mn
>>> print (mn.dis( b ' x97xa3x00 ' , " b " ))
LHU V1, 0x20(SP)
Créez une instruction :
>>> machine = Machine( ' arml ' )
>>> instr = machine.mn.dis( ' x00 x88xe0 ' , ' l ' )
>>> print (instr)
ADD R2, R8, R0
Créez un objet de représentation intermédiaire :
>>> lifter = machine.lifter_model_call(loc_db)
Créez un ircfg vide :
>>> ircfg = lifter.new_ircfg()
Ajouter une instruction au pool :
>>> lifter.add_instr_to_ircfg(instr, ircfg)
Imprimer le pool actuel :
>>> for lbl, irblock in ircfg.blocks.items():
... print (irblock)
loc_0:
R2 = R8 + R0
IRDst = loc_4
Travailler avec l'IR, par exemple en obtenant des effets secondaires :
>>> for lbl, irblock in ircfg.blocks.items():
... for assignblk in irblock:
... rw = assignblk.get_rw()
... for dst, reads in rw.items():
... print ( ' read: ' , [ str (x) for x in reads])
... print ( ' written: ' , dst)
... print ()
...
read: ['R8', 'R0']
written: R2
read: []
written: IRDst
Plus d’informations sur Miasm IR se trouvent dans le bloc-notes Jupyter correspondant.
Donner un shellcode :
00000000 8d4904 lea ecx, [ecx+0x4]
00000003 8d5b01 lea ebx, [ebx+0x1]
00000006 80f901 cmp cl, 0x1
00000009 7405 jz 0x10
0000000b 8d5bff lea ebx, [ebx-1]
0000000e eb03 jmp 0x13
00000010 8d5b01 lea ebx, [ebx+0x1]
00000013 89d8 mov eax, ebx
00000015 c3 ret
>>> s = b ' x8d I x04x8d [ x01x80xf9x01 t x05x8d [ xffxebx03x8d [ x01x89xd8xc3 '
Importez le shellcode grâce à l'abstraction Container
:
>>> from miasm.analysis.binary import Container
>>> c = Container.from_string(s, loc_db)
>>> c
Démontage du shellcode à l'adresse 0
:
>>> from miasm.analysis.machine import Machine
>>> machine = Machine( ' x86_32 ' )
>>> mdis = machine.dis_engine(c.bin_stream, loc_db = loc_db)
>>> asmcfg = mdis.dis_multiblock( 0 )
>>> for block in asmcfg.blocks:
... print (block)
...
loc_0
LEA ECX, DWORD PTR [ECX + 0x4]
LEA EBX, DWORD PTR [EBX + 0x1]
CMP CL, 0x1
JZ loc_10
-> c_next:loc_b c_to:loc_10
loc_10
LEA EBX, DWORD PTR [EBX + 0x1]
-> c_next:loc_13
loc_b
LEA EBX, DWORD PTR [EBX + 0xFFFFFFFF]
JMP loc_13
-> c_to:loc_13
loc_13
MOV EAX, EBX
RET
Initialisation du moteur JIT avec une stack :
>>> jitter = machine.jitter(loc_db, jit_type = ' python ' )
>>> jitter.init_stack()
Ajoutez le shellcode dans un emplacement mémoire arbitraire :
>>> run_addr = 0x 40000000
>>> from miasm.jitter.csts import PAGE_READ , PAGE_WRITE
>>> jitter.vm.add_memory_page(run_addr, PAGE_READ | PAGE_WRITE , s)
Créez une sentinelle pour récupérer le retour du shellcode :
def code_sentinelle ( jitter ):
jitter . running = False
jitter . pc = 0
return True
> >> jitter . add_breakpoint ( 0x1337beef , code_sentinelle )
> >> jitter . push_uint32_t ( 0x1337beef )
Journaux actifs :
>>> jitter.set_trace_log()
Exécuter à une adresse arbitraire :
>>> jitter.init_run(run_addr)
>>> jitter.continue_run()
RAX 0000000000000000 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFF8 RBP 0000000000000000
zf 0000000000000000 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000000
40000000 LEA ECX, DWORD PTR [ECX+0x4]
RAX 0000000000000000 RBX 0000000000000000 RCX 0000000000000004 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFF8 RBP 0000000000000000
zf 0000000000000000 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
....
4000000e JMP loc_0000000040000013:0x40000013
RAX 0000000000000000 RBX 0000000000000000 RCX 0000000000000004 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFF8 RBP 0000000000000000
zf 0000000000000000 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000013
40000013 MOV EAX, EBX
RAX 0000000000000000 RBX 0000000000000000 RCX 0000000000000004 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFF8 RBP 0000000000000000
zf 0000000000000000 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000013
40000015 RET
>>>
Interagir avec la gigue :
>>> jitter.vm
ad 1230000 size 10000 RW_ hpad 0x2854b40
ad 40000000 size 16 RW_ hpad 0x25e0ed0
>>> hex (jitter.cpu. EAX )
'0x0L'
>>> jitter.cpu. ESI = 12
Initialisation du pool IR :
>>> lifter = machine.lifter_model_call(loc_db)
>>> ircfg = lifter.new_ircfg_from_asmcfg(asmcfg)
Initialisation du moteur avec les valeurs symboliques par défaut :
>>> from miasm.ir.symbexec import SymbolicExecutionEngine
>>> sb = SymbolicExecutionEngine(lifter)
Lancement de l'exécution :
>>> symbolic_pc = sb.run_at(ircfg, 0 )
>>> print (symbolic_pc)
((ECX + 0x4)[0:8] + 0xFF)?(0xB,0x10)
Idem, avec les journaux d'étapes (seules les modifications sont affichées) :
>>> sb = SymbolicExecutionEngine(lifter, machine.mn.regs.regs_init)
>>> symbolic_pc = sb.run_at(ircfg, 0 , step = True )
Instr LEA ECX, DWORD PTR [ECX + 0x4]
Assignblk:
ECX = ECX + 0x4
________________________________________________________________________________
ECX = ECX + 0x4
________________________________________________________________________________
Instr LEA EBX, DWORD PTR [EBX + 0x1]
Assignblk:
EBX = EBX + 0x1
________________________________________________________________________________
EBX = EBX + 0x1
ECX = ECX + 0x4
________________________________________________________________________________
Instr CMP CL, 0x1
Assignblk:
zf = (ECX[0:8] + -0x1)?(0x0,0x1)
nf = (ECX[0:8] + -0x1)[7:8]
pf = parity((ECX[0:8] + -0x1) & 0xFF)
of = ((ECX[0:8] ^ (ECX[0:8] + -0x1)) & (ECX[0:8] ^ 0x1))[7:8]
cf = (((ECX[0:8] ^ 0x1) ^ (ECX[0:8] + -0x1)) ^ ((ECX[0:8] ^ (ECX[0:8] + -0x1)) & (ECX[0:8] ^ 0x1)))[7:8]
af = ((ECX[0:8] ^ 0x1) ^ (ECX[0:8] + -0x1))[4:5]
________________________________________________________________________________
af = (((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8] ^ 0x1)[4:5]
pf = parity((ECX + 0x4)[0:8] + 0xFF)
zf = ((ECX + 0x4)[0:8] + 0xFF)?(0x0,0x1)
ECX = ECX + 0x4
of = ((((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8]) & ((ECX + 0x4)[0:8] ^ 0x1))[7:8]
nf = ((ECX + 0x4)[0:8] + 0xFF)[7:8]
cf = (((((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8]) & ((ECX + 0x4)[0:8] ^ 0x1)) ^ ((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8] ^ 0x1)[7:8]
EBX = EBX + 0x1
________________________________________________________________________________
Instr JZ loc_key_1
Assignblk:
IRDst = zf?(loc_key_1,loc_key_2)
EIP = zf?(loc_key_1,loc_key_2)
________________________________________________________________________________
af = (((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8] ^ 0x1)[4:5]
EIP = ((ECX + 0x4)[0:8] + 0xFF)?(0xB,0x10)
pf = parity((ECX + 0x4)[0:8] + 0xFF)
IRDst = ((ECX + 0x4)[0:8] + 0xFF)?(0xB,0x10)
zf = ((ECX + 0x4)[0:8] + 0xFF)?(0x0,0x1)
ECX = ECX + 0x4
of = ((((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8]) & ((ECX + 0x4)[0:8] ^ 0x1))[7:8]
nf = ((ECX + 0x4)[0:8] + 0xFF)[7:8]
cf = (((((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8]) & ((ECX + 0x4)[0:8] ^ 0x1)) ^ ((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8] ^ 0x1)[7:8]
EBX = EBX + 0x1
________________________________________________________________________________
>>>
Réessayez l'exécution avec un ECX concret. Ici, l'exécution symbolique/concolique arrive à la fin du shellcode :
>>> from miasm.expression.expression import ExprInt
>>> sb.symbols[machine.mn.regs. ECX ] = ExprInt( - 3 , 32 )
>>> symbolic_pc = sb.run_at(ircfg, 0 , step = True )
Instr LEA ECX, DWORD PTR [ECX + 0x4]
Assignblk:
ECX = ECX + 0x4
________________________________________________________________________________
af = (((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8] ^ 0x1)[4:5]
EIP = ((ECX + 0x4)[0:8] + 0xFF)?(0xB,0x10)
pf = parity((ECX + 0x4)[0:8] + 0xFF)
IRDst = ((ECX + 0x4)[0:8] + 0xFF)?(0xB,0x10)
zf = ((ECX + 0x4)[0:8] + 0xFF)?(0x0,0x1)
ECX = 0x1
of = ((((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8]) & ((ECX + 0x4)[0:8] ^ 0x1))[7:8]
nf = ((ECX + 0x4)[0:8] + 0xFF)[7:8]
cf = (((((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8]) & ((ECX + 0x4)[0:8] ^ 0x1)) ^ ((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8] ^ 0x1)[7:8]
EBX = EBX + 0x1
________________________________________________________________________________
Instr LEA EBX, DWORD PTR [EBX + 0x1]
Assignblk:
EBX = EBX + 0x1
________________________________________________________________________________
af = (((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8] ^ 0x1)[4:5]
EIP = ((ECX + 0x4)[0:8] + 0xFF)?(0xB,0x10)
pf = parity((ECX + 0x4)[0:8] + 0xFF)
IRDst = ((ECX + 0x4)[0:8] + 0xFF)?(0xB,0x10)
zf = ((ECX + 0x4)[0:8] + 0xFF)?(0x0,0x1)
ECX = 0x1
of = ((((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8]) & ((ECX + 0x4)[0:8] ^ 0x1))[7:8]
nf = ((ECX + 0x4)[0:8] + 0xFF)[7:8]
cf = (((((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8]) & ((ECX + 0x4)[0:8] ^ 0x1)) ^ ((ECX + 0x4)[0:8] + 0xFF) ^ (ECX + 0x4)[0:8] ^ 0x1)[7:8]
EBX = EBX + 0x2
________________________________________________________________________________
Instr CMP CL, 0x1
Assignblk:
zf = (ECX[0:8] + -0x1)?(0x0,0x1)
nf = (ECX[0:8] + -0x1)[7:8]
pf = parity((ECX[0:8] + -0x1) & 0xFF)
of = ((ECX[0:8] ^ (ECX[0:8] + -0x1)) & (ECX[0:8] ^ 0x1))[7:8]
cf = (((ECX[0:8] ^ 0x1) ^ (ECX[0:8] + -0x1)) ^ ((ECX[0:8] ^ (ECX[0:8] + -0x1)) & (ECX[0:8] ^ 0x1)))[7:8]
af = ((ECX[0:8] ^ 0x1) ^ (ECX[0:8] + -0x1))[4:5]
________________________________________________________________________________
af = 0x0
EIP = ((ECX + 0x4)[0:8] + 0xFF)?(0xB,0x10)
pf = 0x1
IRDst = ((ECX + 0x4)[0:8] + 0xFF)?(0xB,0x10)
zf = 0x1
ECX = 0x1
of = 0x0
nf = 0x0
cf = 0x0
EBX = EBX + 0x2
________________________________________________________________________________
Instr JZ loc_key_1
Assignblk:
IRDst = zf?(loc_key_1,loc_key_2)
EIP = zf?(loc_key_1,loc_key_2)
________________________________________________________________________________
af = 0x0
EIP = 0x10
pf = 0x1
IRDst = 0x10
zf = 0x1
ECX = 0x1
of = 0x0
nf = 0x0
cf = 0x0
EBX = EBX + 0x2
________________________________________________________________________________
Instr LEA EBX, DWORD PTR [EBX + 0x1]
Assignblk:
EBX = EBX + 0x1
________________________________________________________________________________
af = 0x0
EIP = 0x10
pf = 0x1
IRDst = 0x10
zf = 0x1
ECX = 0x1
of = 0x0
nf = 0x0
cf = 0x0
EBX = EBX + 0x3
________________________________________________________________________________
Instr LEA EBX, DWORD PTR [EBX + 0x1]
Assignblk:
IRDst = loc_key_3
________________________________________________________________________________
af = 0x0
EIP = 0x10
pf = 0x1
IRDst = 0x13
zf = 0x1
ECX = 0x1
of = 0x0
nf = 0x0
cf = 0x0
EBX = EBX + 0x3
________________________________________________________________________________
Instr MOV EAX, EBX
Assignblk:
EAX = EBX
________________________________________________________________________________
af = 0x0
EIP = 0x10
pf = 0x1
IRDst = 0x13
zf = 0x1
ECX = 0x1
of = 0x0
nf = 0x0
cf = 0x0
EBX = EBX + 0x3
EAX = EBX + 0x3
________________________________________________________________________________
Instr RET
Assignblk:
IRDst = @32[ESP[0:32]]
ESP = {ESP[0:32] + 0x4 0 32}
EIP = @32[ESP[0:32]]
________________________________________________________________________________
af = 0x0
EIP = @32[ESP]
pf = 0x1
IRDst = @32[ESP]
zf = 0x1
ECX = 0x1
of = 0x0
nf = 0x0
cf = 0x0
EBX = EBX + 0x3
ESP = ESP + 0x4
EAX = EBX + 0x3
________________________________________________________________________________
>>>
Miasm intègre son propre désassembleur, son propre langage intermédiaire et sa propre sémantique d'instruction. Il est écrit en Python.
Pour émuler le code, il utilise LLVM, GCC, Clang ou Python pour JIT la représentation intermédiaire. Il peut émuler des shellcodes et tout ou partie des binaires. Les rappels Python peuvent être exécutés pour interagir avec l'exécution, par exemple pour émuler les effets des fonctions de bibliothèque.
Certaines ressources de documentation sont disponibles dans le dossier doc.
Une documentation générée automatiquement est disponible :
Miasme utilise :
Pour activer le code JIT, l'un des modules suivants est obligatoire :
Miasm 'facultatif' peut également utiliser :
Pour utiliser le jitter, GCC ou LLVM est recommandé
pip install llvmlite
ou installe depuis llvmlite$ cd miasm_directory
$ python setup.py build
$ sudo python setup.py install
Si quelque chose ne va pas lors de la compilation de l'un des modules de jitter, Miasm ignorera l'erreur et désactivera le module correspondant (voir la sortie de la compilation).
La plupart des plugins IDA de Miasm utilisent un sous-ensemble des fonctionnalités de Miasm. Un moyen rapide de les faire fonctionner est d'ajouter :
pyparsing.py
vers C:...IDApython
ou pip install pyparsing
miasm/miasm
vers C:...IDApython
Toutes les fonctionnalités, à l'exception de celles liées à JITter, seront disponibles. Pour une installation plus complète, veuillez vous référer aux paragraphes ci-dessus.
Miasm est livré avec un ensemble de tests de régression. Pour les exécuter tous :
cd miasm_directory/test
# Run tests using our own test runner
python test_all.py
# Run tests using standard frameworks (slower, require 'parameterized')
python -m unittest test_all.py # sequential, requires 'unittest'
python -m pytest test_all.py # sequential, requires 'pytest'
python -m pytest -n auto test_all.py # parallel, requires 'pytest' and 'pytest-xdist'
Certaines options peuvent être spécifiées :
-m
-c
-t long
(exclut les tests longs)