panda-re / panda

Platform for Architecture-Neutral Dynamic Analysis
https://panda.re
Other
2.48k stars 479 forks source link

syscalls2 on_sys_pread64_return/on_sys_pread64_enter don't work #1042

Closed AnyKeyShik closed 3 years ago

AnyKeyShik commented 3 years ago

Seems like pread64 doesn't processed by syscalls2

Proof of concept:



from pandare import Panda

panda = Panda(generic="x86_64")
panda.require("osi")
panda.require("osi_linux")

@panda.queue_blocking
def prepare():
    panda.revert_sync("root")
    panda.copy_to_guest("helloworld")

    panda.run_serial_cmd("helloworld/hellworld")
    panda.end_analysis()

@panda.ppp("syscalls2", "on_sys_pread64_enter")
def pread_enter(*unused):
    print("Pread64 enter")

@panda.ppp("syscalls2", "on_sys_pread64_return")
def pread_return(*unused):
    print("Pread64 return")

@panda.ppp("syscalls2", "on_sys_read_return")
def read_retur(*unused):
    print("Read return")

panda.run()```

UPD. This script print only "Read return", not all of that prints
AnyKeyShik commented 3 years ago

Is it my wrong or it is a PANDA bug?

lacraig2 commented 3 years ago

Hi,

Apologies for the delay. It's been quite busy around here. Though I would say in regard to your second question that whether it is a PANDA bug or your bug you should still get a response (and ideally a more timely one).

I recreated your script and example program and have seen evidence that sys_pread64 is called on enter and return. I did the following:

C++ Example (copied and modified from here)

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <iostream>
using namespace std;

int main()
{
    int fd = open("/root/example/test.txt", O_RDONLY);
    if(fd < 0)
    {
        perror("open failed");
        return 1;
    }

    char buf[1024] = {0};
    int offset = 5;
    ssize_t ret = 0;
    int count = 10;
    if((ret = pread(fd, buf, count, offset)) == -1)
    {
        perror("pread failed");
        return 1;
    }

    std::cout << "read buf = " << buf << std::endl;
    return 0;
}

Compile with c++ helloworld.cpp -o helloworld

And for my python script I used:

#!/usr/bin/env python3
from sys import argv
from pandare import Panda

panda = Panda(generic="x86_64")

@panda.queue_blocking
def run_cmd():
    panda.revert_sync("root")
    panda.copy_to_guest("example",timeout=1000)
    print(panda.run_serial_cmd("/root/example/helloworld",timeout=60))
    panda.end_analysis()

@panda.ppp("syscalls2", "on_sys_pread64_enter")
def pread_enter(cpu, *unused):
    print(f"proc: {panda.get_process_name(cpu)} Pread64 enter")

@panda.ppp("syscalls2", "on_sys_pread64_return")
def pread_return(cpu, *unused):
    print(f"proc: {panda.get_process_name(cpu)} Pread64 return")

panda.run()

Which prints out:

proc: helloworld Pread64 enter
proc: helloworld Pread64 return
read buf = fghijklmno

I would check a few things to start:

If neither of these work you could provide the helloworld source and I could look at it again from there.

AnyKeyShik commented 3 years ago

Hi,

Apologies for the delay. It's been quite busy around here. Though I would say in regard to your second question that whether it is a PANDA bug or your bug you should still get a response (and ideally a more timely one).

I recreated your script and example program and have seen evidence that sys_pread64 is called on enter and return. I did the following:

C++ Example (copied and modified from here)

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <iostream>
using namespace std;

int main()
{
    int fd = open("/root/example/test.txt", O_RDONLY);
    if(fd < 0)
    {
        perror("open failed");
        return 1;
    }

    char buf[1024] = {0};
    int offset = 5;
    ssize_t ret = 0;
    int count = 10;
    if((ret = pread(fd, buf, count, offset)) == -1)
    {
        perror("pread failed");
        return 1;
    }

    std::cout << "read buf = " << buf << std::endl;
    return 0;
}

Compile with c++ helloworld.cpp -o helloworld

And for my python script I used:

#!/usr/bin/env python3
from sys import argv
from pandare import Panda

panda = Panda(generic="x86_64")

@panda.queue_blocking
def run_cmd():
    panda.revert_sync("root")
    panda.copy_to_guest("example",timeout=1000)
    print(panda.run_serial_cmd("/root/example/helloworld",timeout=60))
    panda.end_analysis()

@panda.ppp("syscalls2", "on_sys_pread64_enter")
def pread_enter(cpu, *unused):
    print(f"proc: {panda.get_process_name(cpu)} Pread64 enter")

@panda.ppp("syscalls2", "on_sys_pread64_return")
def pread_return(cpu, *unused):
    print(f"proc: {panda.get_process_name(cpu)} Pread64 return")

panda.run()

Which prints out:

proc: helloworld Pread64 enter
proc: helloworld Pread64 return
read buf = fghijklmno

I would check a few things to start:

* The output of your `panda.run_serial_cmd("helloworld/hellworld")` command. I would note that it looks like "helloworld" is misspelled in the binary so that could be an issue.

* The syscalls actually used by the program. Your program wasn't included so I couldn't check, but using `strace` to see if pread64 is called instead of some equivalent.

If neither of these work you could provide the helloworld source and I could look at it again from there.

Ty for your detailed answer. It really helped me, but I ran into another problem. How can I trace an x86 binary on an x86_64 system? When I try to run it PANDA just says me that file doesn't exist in system but I can grab info about it with ls, file and etc

My python script for tracing:

#! /usr/bin/env python3

from pandare import Panda
from cffi import FFI
from base64 import b64encode
import os
import json

ffi = FFI()

panda = Panda(generic="x86_64")
panda.require('osi')
panda.require('osi_linux')

# For files which opens by emulated program
opened_files = {}

# For store reads and writes
calls = {"read": [], "write": []}

# Binary file's name and its dir
filename = 'helloworld'
filepath = '/root/helloworld/helloworld'
dirname = 'helloworld'

@panda.queue_blocking
def drive_guest():
    global dirname
    global filepath

    panda.revert_sync("root")
    panda.copy_to_guest(dirname)

    print( panda.run_serial_cmd('ls -las /root/helloworld') )
    print( panda.run_serial_cmd('file /root/helloworld/helloworld') )
    print( panda.run_serial_cmd('/root/helloworld/helloworld') )
    panda.end_analysis()

@panda.ppp("syscalls2", "on_sys_openat_return")
def open(cpu, pc, flags, fname, *unused):
    name = panda.get_process_name(cpu)
    if name == filename:
        filedesc = panda.plugins['syscalls2'].get_syscall_retval(cpu)
        if filedesc < 0:
            return

        name = panda.read_str(cpu, fname)
        opened_files[filedesc] = name

@panda.ppp("syscalls2", "on_sys_close_return")
def close(cpu, pc, fd):
    name = panda.get_process_name(cpu)
    if name == filename:
        if fd in opened_files.keys():
            opened_files.pop(fd)

@panda.ppp("syscalls2", "on_sys_read_return")
def read(cpu, pc, fd, buf, *unused):
    name = panda.get_process_name(cpu)
    if name == filename:
        retval = panda.plugins['syscalls2'].get_syscall_retval(cpu)
        if retval < 0:
            print("FAIL READ :c")
            return

        data = b64encode(panda.virtual_memory_read(cpu, buf, retval)).decode('utf-8')
        if fd in opened_files.keys():
            fname = opened_files[fd]
        else:
            fname = "external file"

        print(f"Read {data} from {fname}")
        calls["read"].append({fname: data})

@panda.ppp("syscalls2", "on_sys_write_return")
def write(cpu, pc, fd, buf, *unused):
    name = panda.get_process_name(cpu)
    if name == filename:
        retval = panda.plugins['syscalls2'].get_syscall_retval(cpu)
        if retval < 0:
            print("FAIL WRITE :c")
            return

        data = b64encode(panda.virtual_memory_read(cpu, buf, retval)).decode('utf-8')
        if fd in opened_files.keys():
            fname = opened_files[fd]
        else:
            fname = "external file"

        print(f"Write {data} to {fname}")
        calls["write"].append({fname: data})

panda.run()

And source of binary what I try to trace:

#include <stdio.h>

int
main(void)
{
    FILE *out;
    char *str;

    out = fopen("./hello_out", "w");
    str = "Hello world!\n";

    if (!out) {
        fprintf(stderr, "Error while opening out file!\n");

        return -1;
    }

    fprintf(out, "%s", str);
    printf("All files have been written!\n");

    return 0;
}

How I can try to solve this problem?

UPD. Output of my script:

using generic x86_64
Loading libpanda from /usr/local/lib/python3.8/dist-packages/pandare/data
PANDA[core]:os_familyno=2 bits=64 os_details=ubuntu:4.15.0-72-generic-noaslr-nokaslr
[PYPANDA] Panda args: [/usr/local/lib/python3.8/dist-packages/pandare/data/x86_64-softmmu/libpanda-x86_64.so -L /usr/local/lib/python3.8/dist-packages/pandare/data/pc-bios /home/anykeyshik/.panda/bionic-server-cloudimg-amd64-noaslr-nokaslr.qcow2 -display none -m 1024 -serial unix:/tmp/pypanda_smxruf8t9,server,nowait -monitor unix:/tmp/pypanda_mmgcxrz7a,server,nowait]
PANDA[core]:loading required plugin osi
PANDA[core]:initializing osi
PANDA[core]:loading required plugin osi_linux
PANDA[core]:initializing osi_linux
PANDA[osi_linux]:W> kernelinfo bytes [20-23] not read
PANDA[core]:loading required plugin syscalls2
PANDA[core]:initializing syscalls2
PANDA[syscalls2]:using profile for linux x64 64-bit
PANDA[core]:loading required plugin hooks
PANDA[core]:initializing hooks
PANDA[core]:loading required plugin osi_linux
PANDA[core]:/usr/local/lib/python3.8/dist-packages/pandare/data//x86_64-softmmu/panda/plugins//panda_osi_linux.so already loaded
PPP automatically loaded plugin syscalls2
PANDA[core]:loading required plugin syscalls2
PANDA[core]:/usr/local/lib/python3.8/dist-packages/pandare/data//x86_64-softmmu/panda/plugins//panda_syscalls2.so already loaded
total 48
4 drwxrwxr-x 2 1000 1000  4096 Jul 27  2021 .
4 drwx------ 9 root root  4096 May  4 17:48 ..
20 -rwxr-xr-x 1 1000 1000 17488 Jul 27  2021 helloworld
20 -rwxr-xr-x 1 1000 1000 18608 Jul 27  2021 helloworld64
/root/helloworld/helloworld: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, BuildID[sha1]=fa1c062f8c124484d55af76e7c2e46c4fb64a24a, for GNU/Linux 4.4.0, with debug_info, not stripped
-bash: /root/helloworld/helloworld: No such file or directory
lacraig2 commented 3 years ago

That's a general ubuntu multiarch problem. See: https://askubuntu.com/a/454452

github-actions[bot] commented 3 years ago

Stale issue message