cea-sec / miasm

Reverse engineering framework in Python
https://miasm.re/
GNU General Public License v2.0
3.44k stars 471 forks source link

Analysis of linux shellcode #183

Closed Summus-31c04089c3cd80 closed 9 years ago

Summus-31c04089c3cd80 commented 9 years ago

Hi,

I'm trying to use Miasm2 to analyse Linux shellcodes and saw your conference on Sstic 2014 but i'm missing something ! When the shellcode is executed in Miasm, int 0x80 are not computed : no errors, no modifications on registers. So i tried to use Linux libs like you used Windows dlls on your example, using functions in _elf.py instead of ones in _pe.py. I tried to add stdlib.h with absolute path and i had an "KeyError" :

Traceback (most recent call last): File "process.py", line 91, in e_lib = vm_load_elf(myjit.vm, lib_name) File "/usr/local/lib/python2.7/dist-packages/miasm2/jitter/loader/elf.py", line 56, in vm_load_elf e = elf_init.ELF(fdata, kargs) File "/usr/local/lib/python2.7/dist-packages/elfesteem/elf_init.py", line 658, in init self.parse_content() File "/usr/local/lib/python2.7/dist-packages/elfesteem/elf_init.py", line 671, in parse_content self.Ehdr = WEhdr(self, self.sex, self.size, self.content) File "/usr/local/lib/python2.7/dist-packages/elfesteem/elf_init.py", line 32, in init self.cstr = self.wrapped(sex, size, args, *kargs) File "/usr/local/lib/python2.7/dist-packages/elfesteem/cstruct.py", line 56, in init pstr = fix_size(self._fields, wsize) File "/usr/local/lib/python2.7/dist-packages/elfesteem/cstruct.py", line 23, in fix_size v = size2type[wsize] KeyError: 1504

That is the code i used to add libs :

libs = libimp()
all_imp_lib = ["stdlib.h"]
all_e = []

for lib in all_imp_lib:
    lib_name = os.path.join('/usr/include/', lib)
    e_lib = vm_load_elf(myjit.vm, lib_name)
    libs.add_export_lib(e_lib, lib)
    all_e.append(e_lib)

for ee in all_e:
    preload_elf(myjit.vm, ee, libs)

e = mv_load_elf(myjit.vm, "shellcode.bin")
preload_elf(myjit.vm, e, libs)

If you could help me understanding how to correctly use Miasm :)

P.S. : In the way this is not possible, how can i execute a given system call in Miasm VM ?

commial commented 9 years ago

Can you supply the whole shellcode, or at least a minimalist example with the associated Python code ? That way, we will be able to reproduce and fix this issue.

In addition, please move your second question in a separate issue (and add the aforementioned code snippet).

Summus-31c04089c3cd80 commented 9 years ago

This was the used shellcode : "\x31\xc0\x83\xc0\x29\x6a\x02\x5b\xcd\x80\x83\xe8\x04\xcd\x80" I've since modified the python code , but it was a "basic" code, similar to this one : https://github.com/cea-sec/miasm/blob/master/example/jitter/x86_32.py (with the above given code part)

serpilliere commented 9 years ago

For the moment, put aside the libs. To have the same conditions, I first generate the following shellcode:

main:
    XOR EAX, EAX
    MOV AL, 1
    XOR EBX,EBX
    INT 0x80
    RET

So I assemble it:

python example/asm/shellcode.py x86_32 example/samples/x86_32_int80.S out.bin

And I emulate it:

$ python example/jitter/x86_32.py out.bin
RAX 0000000000000000 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000000 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000000
40000000 XOR        EAX, EAX
RAX 0000000000000000 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000001 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000000
40000002 MOV        AL, 0x1
RAX 0000000000000001 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000001 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000002
40000004 XOR        EBX, EBX
RAX 0000000000000001 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000001 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000004
40000006 INT        0x80
Traceback (most recent call last):
  File "example/jitter/x86_32.py", line 40, in <module>
    myjit.continue_run()
  File "/usr/local/lib/python2.7/dist-packages/miasm2/jitter/jitload.py", line 339, in continue_run
    return self.run_iterator.next()
  File "/usr/local/lib/python2.7/dist-packages/miasm2/jitter/jitload.py", line 311, in runiter_once
    assert(self.get_exception() == 0)
AssertionError

We have an error on the int 0x80. This instruction through an exception. The basic emulation code doesn't handle exceptions. To deal with it, you have to add an exception handler::

def exception_int(jitter):
    print 'interrupt!'
    return False
myjit.add_exception_handler(EXCEPT_INT_XX, exception_int)

(You have to import EXCEPT_INT_XX something like: from miasm2.jitter.csts import PAGE_READ, PAGE_WRITE, EXCEPT_INT_XX)

Result:

$ python xxx.py out.bin 
RAX 0000000000000000 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000000 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000000
40000000 XOR        EAX, EAX
RAX 0000000000000000 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000001 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000000
40000002 MOV        AL, 0x1
RAX 0000000000000001 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000001 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000002
40000004 XOR        EBX, EBX
RAX 0000000000000001 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000001 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000004
40000006 INT        0x80
interrupt!

And the shellcode stops (the return false makes the execution stop)

Now you have to implement your own system call for the int 0x80!

For example:

def exception_int(jitter):
    print 'interrupt!'
    print 'syscall num:', hex(jitter.cpu.EAX)
    jitter.cpu.EAX = 0x1337
    jitter.cpu.set_exception(0)
    return True

(Here, jitter.cpu.set_exception(0) resets the exception generated by the int 0x80)

And the result:

$ python xxx.py out.bin 
RAX 0000000000000000 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000000 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000000
40000000 XOR        EAX, EAX
RAX 0000000000000000 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000001 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000000
40000002 MOV        AL, 0x1
RAX 0000000000000001 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000001 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000002
40000004 XOR        EBX, EBX
RAX 0000000000000001 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000001 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000004
40000006 INT        0x80
interrupt!
syscall num: 0x1L
RAX 0000000000001337 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000
RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFFC RBP 0000000000000000
zf 0000000000000001 nf 0000000000000000 of 0000000000000000 cf 0000000000000000
RIP 0000000040000008
40000008 RET        

See EAX is 1337 after the system call.

serpilliere commented 9 years ago

It seems your posted shellcode doesn't have any call so it doesn't match your issue.

As @commial said, move this sub issue in another topic.

Summus-31c04089c3cd80 commented 9 years ago

Ok thank you ! I moved the question about call in another topic, this shellcode was about this issue :)

So is it possible to execute an external function in the miasm VM ? If i implement a syscall in python and execute it in the exception_handler, the environment should be different from the one where is executed the shellcode, right ?

serpilliere commented 9 years ago

No, if you modify jitter.cpu.EAX, it modify the EAX in the current VM. I may not have understand your request: What do you mean by "external" function?

Summus-31c04089c3cd80 commented 9 years ago

For example : getuid. I'm asking if the return will be the same if it is called by the python code or if it is called directly by the shellcode (in the VM) ?

serpilliere commented 9 years ago

The Miasm VM is there to simulate the behavior of a real machine. So you can set EAX to any uid you want here.

commial commented 9 years ago

Note: the discussion on the second issue continues on #184.

Summus-31c04089c3cd80 commented 9 years ago

Yeah that wasn't a good example, and i can not find another one. The problem comes when the shellcode is expecting a specific value, but even in the VM this can not work since it is different from the target machine ...

serpilliere commented 9 years ago

Have you got a "good" example or the original shell code? (if you can share it with us)

Summus-31c04089c3cd80 commented 9 years ago

I have not yet found a shellcode which generates a such problem. So I'll close this issue, thank you for all the help you have given me !