Beispiel #1
0
static void
event_post_syscall(void *drcontext, int sysnum)
{
    process_id_t child_pid = 0;
    per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, cls_idx);
    /* XXX i#752: should DR provide a child creation event that gives us the pid? */
#ifdef LINUX
    if (sysnum == SYS_fork ||
        (sysnum == SYS_clone && !TEST(CLONE_VM, data->saved_param))) {
        child_pid = dr_syscall_get_result(drcontext);
        if (child_pid > 0)
            nudge_child(child_pid);
    }
#else
    if (sysnum == sysnum_CreateProcess || sysnum == sysnum_CreateProcessEx ||
        sysnum == sysnum_CreateUserProcess) {
        if (dr_syscall_get_result(drcontext) >= 0) { /* success */
            HANDLE *hproc = (HANDLE) data->saved_param;
            HANDLE h;
            size_t read;
            if (dr_safe_read(hproc, sizeof(h), &h, &read) && read == sizeof(h))
                data->child_pid = dr_convert_handle_to_pid(h);
            /* we can't nudge now b/c the child's initial thread is suspended */
        }
    } else if (sysnum == sysnum_ResumeThread) {
        /* child should be alive now */
        if (data->child_pid != INVALID_PROCESS_ID)
            nudge_child(data->child_pid);
    }
#endif
}
Beispiel #2
0
print_simple_value(drsys_arg_t *arg, bool leading_zeroes)
{
    bool pointer = !TEST(DRSYS_PARAM_INLINED, arg->mode);
    OUTPUT(pointer ? PFX : (leading_zeroes ? PFX : PIFX), arg->value);
    if (pointer && ((arg->pre && TEST(DRSYS_PARAM_IN, arg->mode)) ||
                    (!arg->pre && TEST(DRSYS_PARAM_OUT, arg->mode)))) {
        ptr_uint_t deref = 0;
        ASSERT(arg->size <= sizeof(deref), "too-big simple type");
        /* We assume little-endian */
        if (dr_safe_read((void *)arg->value, arg->size, &deref, NULL))
            OUTPUT((leading_zeroes ? " => "PFX : " => "PIFX), deref);
    }
}
Beispiel #3
0
/* NOTE: the routine returns up to 64 bit memory values */
static int64
safe_read_field(buf_info_t *buf, void *addr_to_resolve, size_t addr_size,
                bool print_value)
{
    int64 mem_value = 0;
    ASSERT(addr_size <= sizeof(mem_value), "too-big mem value to read");
    if (!dr_safe_read(addr_to_resolve, addr_size, &mem_value, NULL)) {
        OUTPUT(buf, "<field unreadable>");
        return 0;
    }
    if (print_value)
        OUTPUT(buf, "0x"HEX64_FORMAT_STRING, mem_value);
    return mem_value;
}
Beispiel #4
0
static void do_mem_dump(file_t file, uint base_pc,uint size){

	uint read;
	bool ok;
	byte * mem_values = dr_global_alloc(sizeof(byte) * size);
	ssize_t written;

	ok = dr_safe_read(base_pc, size, mem_values, &read);
	DR_ASSERT(ok);
	written = dr_write_file(file, mem_values, size);
	DEBUG_PRINT("read %d from %x of size %d and written %d\n", read, base_pc, size, written);

	dr_global_free(mem_values, sizeof(byte) * size);

}
Beispiel #5
0
static void
handle_pre_execve(void *drcontext)
{
#ifndef USE_DRSYMS
    /* PR 453867: tell postprocess.pl to watch for new logdir and
     * fork a new copy.
     * FIXME: what if syscall fails?  Punting on that for now.
     * Note that if it fails and then a later one succeeds, postprocess.pl
     * will replace the first with the last.
     */
    char logdir[MAXIMUM_PATH]; /* one reason we're not inside os_post_syscall() */
    size_t bytes_read = 0;
    /* Not using safe_read() since we want a partial read if hits page boundary */
    dr_safe_read((void *) dr_syscall_get_param(drcontext, 0),
                 BUFFER_SIZE_BYTES(logdir), logdir, &bytes_read);
    if (bytes_read < BUFFER_SIZE_BYTES(logdir))
        logdir[bytes_read] = '\0';
    NULL_TERMINATE_BUFFER(logdir);
    ELOGF(0, f_fork, "EXEC path=%s\n", logdir);
#endif
}
Beispiel #6
0
static bool
event_pre_syscall(void *drcontext, int sysnum)
{
    bool modify_write = (sysnum == write_sysnum);
    dr_atomic_add32_return_sum(&num_syscalls, 1);
#ifdef UNIX
    if (sysnum == SYS_execve) {
        /* our stats will be re-set post-execve so display now */
        show_results();
# ifdef SHOW_RESULTS
        dr_fprintf(STDERR, "<---- execve ---->\n");
# endif
    }
#endif
#ifndef SHOW_RESULTS
    /* for sanity tests that don't show results we don't change the app's output */
    modify_write = false;
#endif
    if (modify_write) {
        /* store params for access post-syscall */
        int i;
        per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, tcls_idx);
#ifdef WINDOWS
        /* stderr and stdout are identical in our cygwin rxvt shell so for
         * our example we suppress output starting with 'H' instead
         */
        byte *output = (byte *) dr_syscall_get_param(drcontext, 5);
        byte first;
        size_t read;
        bool ok = dr_safe_read(output, 1, &first, &read);
        if (!ok || read != 1)
            return true; /* data unreadable: execute normally */
        if (dr_is_wow64()) {
            /* store the xcx emulation parameter for wow64 */
            dr_mcontext_t mc = {sizeof(mc),DR_MC_INTEGER/*only need xcx*/};
            dr_get_mcontext(drcontext, &mc);
            data->xcx = mc.xcx;
        }
#endif
        for (i = 0; i < SYS_MAX_ARGS; i++)
            data->param[i] = dr_syscall_get_param(drcontext, i);
        /* suppress stderr */
        if (dr_syscall_get_param(drcontext, 0) == (reg_t) STDERR
#ifdef WINDOWS
                && first == 'H'
#endif
           ) {
            /* pretend it succeeded */
#ifdef UNIX
            /* return the #bytes == 3rd param */
            dr_syscall_result_info_t info = { sizeof(info), };
            info.succeeded = true;
            info.value = dr_syscall_get_param(drcontext, 2);
            dr_syscall_set_result_ex(drcontext, &info);
#else
            /* XXX: we should also set the IO_STATUS_BLOCK.Information field */
            dr_syscall_set_result(drcontext, 0);
#endif
#ifdef SHOW_RESULTS
            dr_fprintf(STDERR, "<---- skipping write to stderr ---->\n");
#endif
            return false; /* skip syscall */
        } else if (dr_syscall_get_param(drcontext, 0) == (reg_t) STDOUT) {
            if (!data->repeat) {
                /* redirect stdout to stderr (unless it's our repeat) */
#ifdef SHOW_RESULTS
                dr_fprintf(STDERR, "<---- changing stdout to stderr ---->\n");
#endif
                dr_syscall_set_param(drcontext, 0, (reg_t) STDERR);
            }
            /* we're going to repeat this syscall once */
            data->repeat = !data->repeat;
        }
    }
    return true; /* execute normally */
}
Beispiel #7
0
static bool
event_pre_syscall(void *drcontext, int sysnum)
{
    ATOMIC_INC(num_syscalls);
#ifdef LINUX
    if (sysnum == SYS_execve) {
        /* our stats will be re-set post-execve so display now */
        show_results();
# ifdef SHOW_RESULTS
        dr_fprintf(STDERR, "<---- execve ---->\n");
# endif
    }
#endif
#ifdef SHOW_RESULTS
    dr_fprintf(STDERR, "[%d] "PFX" "PFX" "PFX"\n",
               sysnum, 
               dr_syscall_get_param(drcontext, 0),
               dr_syscall_get_param(drcontext, 1),
               dr_syscall_get_param(drcontext, 2));
#endif
    if (sysnum == write_sysnum) {
        /* store params for access post-syscall */
        int i;
        per_thread_t *data = (per_thread_t *) drmgr_get_cls_field(drcontext, tcls_idx);
#ifdef WINDOWS
        /* stderr and stdout are identical in our cygwin rxvt shell so for
         * our example we suppress output starting with 'H' instead
         */
        byte *output = (byte *) dr_syscall_get_param(drcontext, 5);
        byte first;
        size_t read;
        bool ok = dr_safe_read(output, 1, &first, &read);
        if (!ok || read != 1)
            return true; /* data unreadable: execute normally */
        if (dr_is_wow64()) {
            /* store the xcx emulation parameter for wow64 */
            dr_mcontext_t mc = {sizeof(mc),DR_MC_INTEGER/*only need xcx*/};
            dr_get_mcontext(drcontext, &mc);
            data->xcx = mc.xcx;
        }
#endif
        for (i = 0; i < SYS_MAX_ARGS; i++)
            data->param[i] = dr_syscall_get_param(drcontext, i);
        /* suppress stderr */
        if (dr_syscall_get_param(drcontext, 0) == (reg_t) STDERR
#ifdef WINDOWS
            && first == 'H'
#endif
            ) {
            /* pretend it succeeded */
#ifdef LINUX
            /* return the #bytes == 3rd param */
            dr_syscall_set_result(drcontext, dr_syscall_get_param(drcontext, 2));
#else
            /* we should also set the IO_STATUS_BLOCK.Information field */
            dr_syscall_set_result(drcontext, 0);
#endif
#ifdef SHOW_RESULTS
            dr_fprintf(STDERR, "  [%d] => skipped\n", sysnum);
#endif
            return false; /* skip syscall */
        } else if (dr_syscall_get_param(drcontext, 0) == (reg_t) STDOUT) {
            if (!data->repeat) {
                /* redirect stdout to stderr (unless it's our repeat) */
#ifdef SHOW_RESULTS
                dr_fprintf(STDERR, "  [%d] STDOUT => STDERR\n", sysnum);
#endif
                dr_syscall_set_param(drcontext, 0, (reg_t) STDERR);
            }
            /* we're going to repeat this syscall once */
            data->repeat = !data->repeat;
        }
    }
    return true; /* execute normally */
}
Beispiel #8
0
DR_EXPORT
void dr_init(client_id_t id)
{
    char buf[MAXIMUM_PATH];
    int64 pos;
    int i;
    uint prot;
    byte *base_pc;
    size_t size;
    size_t bytes_read, bytes_written;
    byte *edge, *mbuf;
    bool ok;
    byte *f_map;

    /* The Makefile will pass a full absolute path (for Windows and Linux) as the client
     * option to a dummy file in the which we use to exercise the file api routines.
     * TODO - these tests should be a lot more thorough, but the basic functionality
     * is there (should add write tests, file_exists, directory etc. tests). */
    file = dr_open_file(dr_get_options(id), DR_FILE_READ);
    if (file == INVALID_FILE)
        dr_fprintf(STDERR, "Error opening file\n");
    memset(buf, 0, sizeof(buf));
    dr_read_file(file, buf, 10);
    pos = dr_file_tell(file);
    if (pos < 0)
        dr_fprintf(STDERR, "tell error\n");
    dr_fprintf(STDERR, "%s\n", buf);
    if (!dr_file_seek(file, 0, DR_SEEK_SET))
        dr_fprintf(STDERR, "seek error\n");
    memset(buf, 0, sizeof(buf));
    dr_read_file(file, buf, 5);
    dr_fprintf(STDERR, "%s\n", buf);
    for (i = 0; i < 100; i++) buf[i] = 0;
    if (!dr_file_seek(file, pos - 5, DR_SEEK_CUR))
        dr_fprintf(STDERR, "seek error\n");
    memset(buf, 0, sizeof(buf));
    dr_read_file(file, buf, 7);
    dr_fprintf(STDERR, "%s\n", buf);
    if (!dr_file_seek(file, -6, DR_SEEK_END))
        dr_fprintf(STDERR, "seek error\n");
    memset(buf, 0, sizeof(buf));
    /* read "x\nEOF\n" from the data file */
    dr_read_file(file, buf, 6);
    /* check for DOS line ending */
    if (buf[4] == '\r') {
        /* Account for two line endings: the snippet is "x\r\nEOF\r\n".
         * No conversion required--ctest will discard the '\r' when comparing results.
         */
        if (!dr_file_seek(file, -8, DR_SEEK_END))
            dr_fprintf(STDERR, "seek error\n");
            memset(buf, 0, sizeof(buf));
            dr_read_file(file, buf, 8);
    }
    dr_fprintf(STDERR, "%s\n", buf);
#define EXTRA_SIZE 0x60
    size  = PAGE_SIZE + EXTRA_SIZE;
    f_map = dr_map_file(file, &size, 0, NULL, DR_MEMPROT_READ, DR_MAP_PRIVATE);
    if (f_map == NULL || size < (PAGE_SIZE + EXTRA_SIZE))
        dr_fprintf(STDERR, "map error\n");
    /* test unaligned unmap */
    if (!dr_unmap_file(f_map + PAGE_SIZE, EXTRA_SIZE))
        dr_fprintf(STDERR, "unmap error\n");

    /* leave file open and check in exit event that it's still open after
     * app tries to close it
     */
    dr_register_exit_event(event_exit);

    /* Test dr_rename_file. */
    test_dr_rename_delete();

    /* Test the memory query routines */
    dummy_func();
    if (!dr_memory_is_readable((byte *)dummy_func, 1) ||
        !dr_memory_is_readable(read_only_buf+1000, 4000) ||
        !dr_memory_is_readable(writable_buf+1000, 4000)) {
        dr_fprintf(STDERR, "ERROR : dr_memory_is_readable() incorrect results\n");
    }

    if (!dr_query_memory((byte *)dummy_func, &base_pc, &size, &prot))
        dr_fprintf(STDERR, "ERROR : can't find dummy_func mem region\n");
    dr_fprintf(STDERR, "dummy_func is %s%s%s\n", TEST(DR_MEMPROT_READ, prot) ? "r" : "",
              TEST(DR_MEMPROT_WRITE, prot) ? "w" : "",
              TEST(DR_MEMPROT_EXEC, prot) ? "x" : "");
    if (base_pc > (byte *)dummy_func || base_pc + size < (byte *)dummy_func)
        dr_fprintf(STDERR, "dummy_func region mismatch");

    memset(writable_buf, 0, sizeof(writable_buf)); /* strip off write copy */
    if (!dr_query_memory(writable_buf+100, &base_pc, &size, &prot))
        dr_fprintf(STDERR, "ERROR : can't find dummy_func mem region\n");
    dr_fprintf(STDERR, "writable_buf is %s%s%s\n", TEST(DR_MEMPROT_READ, prot) ? "r" : "",
              TEST(DR_MEMPROT_WRITE, prot) ? "w" : "",
#ifdef UNIX
              /* Linux sometimes (probably depends on version and hardware NX
               * support) lists all readable regions as also exectuable in the
               * maps file.  We just skip checking here for Linux to make
               * matching the template file easier. */
              ""
#else
              TEST(DR_MEMPROT_EXEC, prot) ? "x" : ""
#endif
              );
    if (base_pc > writable_buf || base_pc + size < writable_buf)
        dr_fprintf(STDERR, "writable_buf region mismatch\n");
    if (base_pc + size < writable_buf + sizeof(writable_buf))
        dr_fprintf(STDERR, "writable_buf size mismatch "PFX" "PFX" "PFX" "PFX"\n",
                  base_pc, size, writable_buf, sizeof(writable_buf));

    if (!dr_query_memory(read_only_buf+100, &base_pc, &size, &prot))
        dr_fprintf(STDERR, "ERROR : can't find dummy_func mem region\n");
    dr_fprintf(STDERR, "read_only_buf is %s%s\n", TEST(DR_MEMPROT_READ, prot) ? "r" : "",
              TEST(DR_MEMPROT_WRITE, prot) ? "w" : "");
    if (base_pc > read_only_buf || base_pc + size < read_only_buf)
        dr_fprintf(STDERR, "read_only_buf region mismatch");
    if (base_pc + size < read_only_buf + sizeof(read_only_buf))
        dr_fprintf(STDERR, "read_only_buf size mismatch");

    /* test the safe_read functions */
    /* TODO - extend test to cover racy writes and reads (won't work on Linux yet). */
    memset(safe_buf, 0xcd, sizeof(safe_buf));
    if (!dr_safe_read(read_only_buf + 4000, 1000, safe_buf, &bytes_read) ||
        bytes_read != 1000 || !memchk(safe_buf, 0, 1000) || *(safe_buf+1000) != 0xcd) {
        dr_fprintf(STDERR, "ERROR in plain dr_safe_read()\n");
    }
    memset(safe_buf, 0xcd, sizeof(safe_buf));
    /* read_only_buf will be in .rodata on Linux, and can be followed by string
     * constants with the same page protections.  In order to be sure that we're
     * copying zeroes, we map our own memory.
     */
    mbuf = dr_nonheap_alloc(PAGE_SIZE*3, DR_MEMPROT_READ|DR_MEMPROT_WRITE);
    memset(mbuf, 0, PAGE_SIZE*3);
    dr_memory_protect(mbuf + PAGE_SIZE*2, PAGE_SIZE, DR_MEMPROT_NONE);
    edge = find_prot_edge(mbuf, DR_MEMPROT_READ);
    bytes_read = 0xcdcdcdcd;
    if (dr_safe_read(edge - (PAGE_SIZE + 10), PAGE_SIZE+20, safe_buf, &bytes_read) ||
        bytes_read == 0xcdcdcdcd || bytes_read > PAGE_SIZE+10 ||
        !memchk(safe_buf, 0, bytes_read)) {
        dr_fprintf(STDERR, "ERROR in overlap dr_safe_read()\n");
    }
    dr_nonheap_free(mbuf, PAGE_SIZE*3);
    dr_fprintf(STDERR, "dr_safe_read() check\n");

    /* test DR_TRY_EXCEPT */
    DR_TRY_EXCEPT(dr_get_current_drcontext(), {
        ok = false;
        *((int *)4) = 37;
    }, { /* EXCEPT */