Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue implementing the ptrace syscall #1427

Open
ArthurVermot opened this issue May 10, 2022 · 4 comments
Open

Issue implementing the ptrace syscall #1427

ArthurVermot opened this issue May 10, 2022 · 4 comments

Comments

@ArthurVermot
Copy link

I made a post here, but maybe I should have tried here before.

TL;DR: I need to implement the ptrace syscall (not implemented by Miasm) for my DSE, so I wrote something using syscall.py as reference, but it results in two issues that I still can't fix:

  • either a "DriftException: Drift of exception_flags: 16 instead of 0"
  • or the fact that the execution doesn't seem to resume correctly and a syscall 0 is invoked right after the ptrace one (because it returns 0).

How should I proceed here?

@serpilliere
Copy link
Contributor

Hi @ArthurVermot
You are correct for the analyse of driftexception: a register differ between the emulated and concrete analysis.
the register in question here is exception flag which store the cpu exceptions flags of miasm (interrupt, segfault, div 0, ...)
If you look at the example of syscall use in miasm, in example/jitter/x86_64.py the handler is like this:

def log_syscalls(jitter):
    # For parameters, see
    # https://en.wikibooks.org/wiki/X86_Assembly/Interfacing_with_Linux
    # Example of how to implement some syscalls
    if jitter.cpu.EAX == 1:
        # Write
        size_t = jitter.cpu.RDX
        print("write(fd: {}, buf: {}, size_t: {})".format(
            jitter.cpu.RDI,
            jitter.vm.get_mem(jitter.cpu.RSI, size_t),
            size_t
        ))
        # Return value is the size written
        jitter.cpu.EAX = size_t
    elif jitter.cpu.EAX == 0x3c:
        # exit
        print("Exit syscall - stopping the machine")
        return False
    else:
        # Most syscalls are not implemented, it may create issues
        if jitter.cpu.EAX in SYSCALL:
            print("syscall {} - {} : Not Implemented".format(jitter.cpu.EAX, SYSCALL[jitter.cpu.EAX]))
        else:
            print("Unknown syscall {} : NotImplemented".format(jitter.cpu.EAX))
    jitter.cpu.set_exception(0)
    jitter.cpu.EAX = 0
    return True

As you enter in the syscall using syscal or int 0x80, those instruction will generate exceptions.
So that's why you have to reset the exception flags in the handler, like it's done here with:

    jitter.cpu.set_exception(0)

Dont forget to do some syscall stuff as well, for example setting eax for ret values and so on.
Tell me if it's ok for you.

In miasm, there is no complete documentation, but there are plenty of examples which are stored by 'category' which show how to use APIs

@ArthurVermot
Copy link
Author

ArthurVermot commented May 11, 2022

Thank you for the answer, it is much appreciated.
I was aware of this set_exception function, but I figured I shouldn't use it myself as Miasm call it at the end of the syscall exception handler.
Calling it does resolve the DriftException, but I am back to the second issue which is that a second syscall (read, as ptrace returns 0) is invoked right after the first:

[DEBUG   ]: sys_ptrace(0, 0, 1, 0)
[DEBUG   ]: sys_ptrace(0, 0, 1, 0)
[DEBUG   ]: -> 0
[DEBUG   ]: -> 0
[DEBUG   ]: sys_read(0, 0, 1)
[DEBUG   ]: sys_read(0, 0, 1)

It happens everywhere there is a syscall : further in my binary, there is a sys_write that outputs 8 characters, so RAX is 8, and it try to invokes a syscall 8 right after. I don't know why it behaves like this, I have a feeling the answer is basic, but it is my first time using Miasm more advanced tools.

@serpilliere
Copy link
Contributor

Hum. Maybe we are missing something.
Lets fo on a simple example.
Let's take the following asm code:

main:
    MOV RAX, 0x1   ; use the `write` [fast] syscall
    MOV RDI, 0x1   ; write to stdout
    LEA RSI, QWORD PTR  [RIP - _ + msg] ; use string "Hello World"
    MOV RDX, 4     ; write 12 characters
    SYSCALL        ; make syscall

    MOV RAX, 60    ; use the `_exit` [fast] syscall
    MOV RDI, 0x0   ; error code 0
    SYSCALL        ; make syscall
    RET

msg:
.string	"toto"

You can assemble it with:

python miasm/example/asm/shellcode.py  x86_64 syscall_x86_64.S syscall_x86_64.bin

And now, you can run the example code:

python example/jitter/x86_64.py -v   syscall_x86_64.bin 
40000000 MOV        RAX, 0x1
RAX 0000000000000001 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000 RSI 0000000000000000 RDI 0000000000000000 RSP 000000000123FFF8 RBP 0000000000000000 RIP 0000000040000007
R8  0000000000000000 R9  0000000000000000 R10 0000000000000000 R11 0000000000000000 R12 0000000000000000 R13 0000000000000000 R14 0000000000000000 R15 0000000000000000 zf 0 nf 0 of 0 cf 0
40000007 MOV        RDI, 0x1
RAX 0000000000000001 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000 RSI 0000000000000000 RDI 0000000000000001 RSP 000000000123FFF8 RBP 0000000000000000 RIP 000000004000000E
R8  0000000000000000 R9  0000000000000000 R10 0000000000000000 R11 0000000000000000 R12 0000000000000000 R13 0000000000000000 R14 0000000000000000 R15 0000000000000000 zf 0 nf 0 of 0 cf 0
4000000E LEA        RSI, QWORD PTR [RIP + 0x1A]
RAX 0000000000000001 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000000 RSI 000000004000002F RDI 0000000000000001 RSP 000000000123FFF8 RBP 0000000000000000 RIP 0000000040000015
R8  0000000000000000 R9  0000000000000000 R10 0000000000000000 R11 0000000000000000 R12 0000000000000000 R13 0000000000000000 R14 0000000000000000 R15 0000000000000000 zf 0 nf 0 of 0 cf 0
40000015 MOV        RDX, 0x4
RAX 0000000000000001 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000004 RSI 000000004000002F RDI 0000000000000001 RSP 000000000123FFF8 RBP 0000000000000000 RIP 000000004000001C
R8  0000000000000000 R9  0000000000000000 R10 0000000000000000 R11 0000000000000000 R12 0000000000000000 R13 0000000000000000 R14 0000000000000000 R15 0000000000000000 zf 0 nf 0 of 0 cf 0
4000001C SYSCALL    
RAX 0000000000000001 RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000004 RSI 000000004000002F RDI 0000000000000001 RSP 000000000123FFF8 RBP 0000000000000000 RIP 000000004000001E
R8  0000000000000000 R9  0000000000000000 R10 0000000000000000 R11 0000000000000000 R12 0000000000000000 R13 0000000000000000 R14 0000000000000000 R15 0000000000000000 zf 0 nf 0 of 0 cf 0
write(fd: 1, buf: b'toto', size_t: 4)
4000001E MOV        RAX, 0x3C
RAX 000000000000003C RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000004 RSI 000000004000002F RDI 0000000000000001 RSP 000000000123FFF8 RBP 0000000000000000 RIP 0000000040000025
R8  0000000000000000 R9  0000000000000000 R10 0000000000000000 R11 0000000000000000 R12 0000000000000000 R13 0000000000000000 R14 0000000000000000 R15 0000000000000000 zf 0 nf 0 of 0 cf 0
40000025 MOV        RDI, 0x0
RAX 000000000000003C RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000004 RSI 000000004000002F RDI 0000000000000000 RSP 000000000123FFF8 RBP 0000000000000000 RIP 000000004000002C
R8  0000000000000000 R9  0000000000000000 R10 0000000000000000 R11 0000000000000000 R12 0000000000000000 R13 0000000000000000 R14 0000000000000000 R15 0000000000000000 zf 0 nf 0 of 0 cf 0
4000002C SYSCALL    
RAX 000000000000003C RBX 0000000000000000 RCX 0000000000000000 RDX 0000000000000004 RSI 000000004000002F RDI 0000000000000000 RSP 000000000123FFF8 RBP 0000000000000000 RIP 000000004000002E
R8  0000000000000000 R9  0000000000000000 R10 0000000000000000 R11 0000000000000000 R12 0000000000000000 R13 0000000000000000 R14 0000000000000000 R15 0000000000000000 zf 0 nf 0 of 0 cf 0
Exit syscall - stopping the machine

It seems the example runs correctly both syscalls.

@ArthurVermot
Copy link
Author

ArthurVermot commented May 22, 2022

No problem on my end, I get the same result as you on this example.
I guess the issue lie in the initializations of the objects used in my script. If I manage to pinpoint exactly the error, I'll edit this post to explain it, in case someone else encounter this.

EDIT: (not a solution unfortunately) I tried to use example/symbol_exec/dse_crackme.py, which run fine on the dse_crackme.c provided. But if I try to use it on my binary, just adding:

def EXIT_syscall(jitter, linux_env):
    status, = jitter.syscall_args_systemv(1)
    print(f"Exit syscall, status {hex(status)}.")
    jitter.running = False


is_ptrace_first_call = True
def PTRACE_syscall(jitter, linux_env):
    request, pid, addr, data = jitter.syscall_args_systemv(4)
    log.debug("sys_ptrace(%x, %x, %x, %x)", request, pid, addr, data)

    global is_ptrace_first_call
    ptrace_return_value = -1

    if is_ptrace_first_call:
        ptrace_return_value = 0
        is_ptrace_first_call = False

    jitter.cpu.set_exception(0)
    jitter.syscall_ret_systemv(ptrace_return_value)
    return True


syscall_callbacks_x86_64[0x3c] = EXIT_syscall
syscall_callbacks_x86_64[0x65] = PTRACE_syscall
enable_syscall_handling(sb.jitter, LinuxEnvironment_x86_64(), syscall_callbacks_x86_64)

then the exact same issue arise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants