Exemple #1
0
bool
instr_is_mov_constant(instr_t *instr, ptr_int_t *value)
{
    int opc = instr_get_opcode(instr);
    if (opc == OP_eor) {
        /* We include OP_eor for symmetry w/ x86, but on ARM "mov reg, #0" is
         * just as compact and there's no reason to use an xor.
         */
        if (opnd_same(instr_get_src(instr, 0), instr_get_dst(instr, 0)) &&
            opnd_same(instr_get_src(instr, 0), instr_get_src(instr, 1)) &&
            /* Must be the form with "sh2, i5_7" and no shift */
            instr_num_srcs(instr) == 4 &&
            opnd_get_immed_int(instr_get_src(instr, 2)) == DR_SHIFT_NONE &&
            opnd_get_immed_int(instr_get_src(instr, 3)) == 0) {
            *value = 0;
            return true;
        } else
            return false;
    } else if (opc == OP_mvn || opc == OP_mvns) {
        opnd_t op = instr_get_src(instr, 0);
        if (opnd_is_immed_int(op)) {
            *value = -opnd_get_immed_int(op);
            return true;
        } else
            return false;
    } else if (opc == OP_mov || opc == OP_movs || opc == OP_movw) {
        opnd_t op = instr_get_src(instr, 0);
        if (opnd_is_immed_int(op)) {
            *value = opnd_get_immed_int(op);
            return true;
        } else
            return false;
    }
    return false;
}
Exemple #2
0
/* First tried something like this, but we hit too many issues in decode and encode */
bool
compare_pages(void *drcontext, byte *start1, byte *start2)
{
    byte *p1 = start1, *p2 = start2;
    int skipped_bytes = 0, identical_skipped_bytes = 0;

    while (p1 < start1 + PAGE_SIZE) {
        int instr_size = decode_sizeof(drcontext, p1, NULL _IF_X64(NULL));
        if (p1 + instr_size > start1 + PAGE_SIZE) {
            /* We're overlapping the end of the page, skip these. */
            int end_skip = start1 + PAGE_SIZE - p1;
            VVERBOSE_PRINT("Passing PAGE_END %d bytes", end_skip);
            skipped_bytes += end_skip;
            if (memcmp(p1, p2, end_skip) == 0)
                identical_skipped_bytes += end_skip;
            break;
        }
        if (decode_sizeof(drcontext, p2, NULL _IF_X64(NULL)) != instr_size) {
            VVERBOSE_PRINT("Instruction alignment mismatch\n");
            return false;
        }
        /* assumption - instructions <= 4 bytes in size won't have relocations */
        if (instr_size < 5) {
            if (memcmp(p1, p2, instr_size) != 0) {
                VVERBOSE_PRINT("Difference found in small instr\n");
                return false;
            }
            p1 += size;
            p2 += size;
        } else {
            /* guess if there could be a relocation */
            instr_t *instr1 = instr_create(drcontext);
            instr_t *instr2 = instr_create(drcontext);
            p1 = decode(drcontext, p1, instr1);
            p2 = decode(drcontext, p2, instr2);
            if (p1 - start1 != p2 - start2) {
                VVERBOSE_PRINT("Instruction alignment mismatch on full decode\n");
                /* Fixme - free instr, don't expect this to happen */
                return false;
            }
            if (instr_get_num_srcs(instr1) != instr_get_num_srcs(instr2) ||
                instr_get_num_dsts(instr1) != instr_get_num_dsts(instr2)) {
                VVERBOSE_PRINT("Full decode operand mismatch");
                return false;
            }
            for (i = instr_get_num_srcs(instr1); i > 0; i--) {
                opnd_t opnd = instr_get_src(instr1, i);
                if (opnd_is_immed_int(opnd) && opnd_get_immed_int(opnd) > 0x10000) {
                    instr_set_src(instr1, i, opnd_create_immed_int(opnd_get_immed_int(opnd), opnd_get_size(opnd)));
                }
            }
        }
    }
}
Exemple #3
0
/* XXX: exporting this so drwrap can use it but I might prefer to have
 * this in drutil or the upcoming drsys
 */
DR_EXPORT
int
drmgr_decode_sysnum_from_wrapper(app_pc entry)
{
    void *drcontext = dr_get_current_drcontext();
    int num = -1;
    byte *pc = entry;
    uint opc;
    instr_t instr;
    instr_init(drcontext, &instr);
    do {
        instr_reset(drcontext, &instr);
        pc = decode(drcontext, pc, &instr);
        if (!instr_valid(&instr))
            break; /* unknown system call sequence */
        opc = instr_get_opcode(&instr);
        /* sanity check: wrapper should be short */
        if (pc - entry > 20)
            break; /* unknown system call sequence */
        if (opc == OP_mov_imm && opnd_is_reg(instr_get_dst(&instr, 0)) &&
            opnd_get_reg(instr_get_dst(&instr, 0)) == DR_REG_EAX &&
            opnd_is_immed_int(instr_get_src(&instr, 0))) {
            num = (int) opnd_get_immed_int(instr_get_src(&instr, 0));
            break; /* success */
        }
        /* stop at call to vsyscall (wow64) or at int itself */
    } while (opc != OP_call_ind && opc != OP_int &&
             opc != OP_sysenter && opc != OP_syscall);
    instr_free(drcontext, &instr);
    return num;
}
static
dr_emit_flags_t bb_event(void* drcontext, void *tag, instrlist_t* bb,
                         bool for_trace, bool translating)
{
    instr_t *instr;
    instr_t *next_instr;
    reg_t in_eax = -1;

    for (instr = instrlist_first(bb); instr != NULL; instr = next_instr) {
        next_instr = instr_get_next(instr);
        if (instr_get_opcode(instr) == OP_mov_imm &&
            opnd_get_reg(instr_get_dst(instr, 0)) == REG_EAX)
            in_eax = opnd_get_immed_int(instr_get_src(instr, 0));
        if (instr_is_syscall(instr) &&
            in_eax == SYS_getpid) {
            instr_t *myval = INSTR_CREATE_mov_imm
                (drcontext, opnd_create_reg(REG_EAX), OPND_CREATE_INT32(-7));
            instr_set_translation(myval, instr_get_app_pc(instr));
            instrlist_preinsert(bb, instr, myval);
            instrlist_remove(bb, instr);
            instr_destroy(drcontext, instr);
        }
    }
    return DR_EMIT_DEFAULT;
}
Exemple #5
0
static void
process_ret(instr_t *instr, syscall_info_t *info)
{
    assert(instr_is_return(instr));
    if (opnd_is_immed_int(instr_get_src(instr, 0)))
        info->num_args = (int) opnd_get_immed_int(instr_get_src(instr, 0));
    else
        info->num_args = 0;
}
Exemple #6
0
void
modify_instr_for_relocations(void *drcontext, instr_t *inst,
                             ptr_uint_t *immed, ptr_uint_t *disp)
{
    int i;
    ptr_uint_t limmed = 0, ldisp = 0;
    for (i = instr_num_srcs(inst) - 1; i >= 0; i--) {
        opnd_t opnd = instr_get_src(inst, i);
        if (opnd_is_immed_int(opnd) && opnd_get_immed_int(opnd) > 0x10000) {
            if (limmed != 0) {
                ASSERT(false);
            } else {
                limmed = opnd_get_immed_int(opnd);
            }
            instr_set_src(inst, i, opnd_create_immed_int(0, opnd_get_size(opnd)));
        }
        if (opnd_is_base_disp(opnd) && opnd_get_disp(opnd) > 0x10000) {
            if (ldisp != 0 && ldisp != opnd_get_disp(opnd)) {
                ASSERT(false);
            } else {
                ldisp = opnd_get_disp(opnd);
            }
            instr_set_src(inst, i, opnd_create_base_disp(opnd_get_base(opnd), opnd_get_index(opnd), opnd_get_scale(opnd), 0, opnd_get_size(opnd)));
        }
    }
    for (i = instr_num_dsts(inst) - 1; i >= 0; i--) {
        opnd_t opnd = instr_get_dst(inst, i);
        ASSERT(!opnd_is_immed(opnd));
        if (opnd_is_base_disp(opnd) && opnd_get_disp(opnd) > 0x10000) {
            if (ldisp != 0 && ldisp != opnd_get_disp(opnd)) {
                ASSERT(false);
            } else {
                ldisp = opnd_get_disp(opnd);
            }
            instr_set_dst(inst, i, opnd_create_base_disp(opnd_get_base(opnd), opnd_get_index(opnd), opnd_get_scale(opnd), 0, opnd_get_size(opnd)));
        }
    }
    if (limmed != 0)
        *immed = limmed;
    if (ldisp != 0)
        *disp = ldisp; 
}
Exemple #7
0
static dr_emit_flags_t
drmgr_event_bb_insert(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst,
                      bool for_trace, bool translating, void *user_data)
{
    if (instr_get_app_pc(inst) == addr_KiCallback) {
        dr_insert_clean_call(drcontext, bb, inst, (void *)drmgr_cls_stack_push,
                             false, 0);
    }
    if (instr_get_opcode(inst) == OP_int &&
        opnd_get_immed_int(instr_get_src(inst, 0)) == CBRET_INTERRUPT_NUM) {
        dr_insert_clean_call(drcontext, bb, inst, (void *)drmgr_cls_stack_pop,
                             false, 0);
    }
    return DR_EMIT_DEFAULT;
}
Exemple #8
0
static void
look_for_usercall(void *dcontext, byte *entry, const char *sym, LOADED_IMAGE *img,
                  const char *modpath)
{
    bool found_push_imm = false;
    int imm = 0;
    app_pc pc, pre_pc;
    instr_t *instr;
    if (entry == NULL)
        return;
    instr = instr_create(dcontext);
    pc = entry;
    while (true) {
        instr_reset(dcontext, instr);
        pre_pc = pc;
        pc = decode(dcontext, pc, instr);
        if (verbose) {
            instr_set_translation(instr, pre_pc);
            dr_print_instr(dcontext, STDOUT, instr, "");
        }
        if (pc == NULL || !instr_valid(instr))
            break;
        if (instr_get_opcode(instr) == OP_push_imm) {
            found_push_imm = true;
            imm = (int) opnd_get_immed_int(instr_get_src(instr, 0));
        } else if (instr_is_call_direct(instr) && found_push_imm) {
            app_pc tgt = opnd_get_pc(instr_get_target(instr));
            bool found = false;
            int i;
            for (i = 0; i < NUM_USERCALL; i++) {
                if (tgt == usercall_addr[i]) {
                    dr_printf("Call #0x%02x to %s at %s+0x%x\n", imm, usercall_names[i],
                              sym, pre_pc - entry);
                    found = true;
                    break;
                }
            }
            if (found)
                break;
        } else if (instr_is_return(instr))
            break;
        if (pc - entry > MAX_BYTES_BEFORE_USERCALL)
            break;
    }
    instr_destroy(dcontext, instr);
}
Exemple #9
0
static bool
reg_update_is_limited(instr_t *instr, reg_id_t reg)
{
    int opcode;
    int offset;

    opcode = instr_get_opcode(instr);
    if (opcode == OP_inc || opcode == OP_dec)
        return true;
    if (opcode == OP_and) 
        /* for 0xffffffd0 & reg => reg */
        return true;

    if ((opcode == OP_add || opcode == OP_sub || 
         opcode == OP_adc || opcode == OP_sbb) &&
        opnd_is_immed_int(instr_get_src(instr, 0))) {
        offset = opnd_get_immed_int(instr_get_src(instr, 0));
        if (offset > PAGE_SIZE || offset < -PAGE_SIZE)
            return false;
        return true;
    }
    if (reg != DR_REG_XSP)
        return false;

    if (opcode >= OP_push && opcode <= OP_popa) {
        if (opcode == OP_pop && opnd_same(instr_get_dst(instr, 0),
                                          opnd_create_reg(DR_REG_XSP)))
            return false;
        return true;
    }
    if (opcode >= OP_call && opcode <= OP_call_far_ind)
        return true;
    if (opcode == OP_ret || opcode == OP_ret_far ||
        opcode == OP_enter || opcode == OP_leave ||
        opcode == OP_pushf || opcode == OP_popf)
        return true;
    return false;
}
Exemple #10
0
/* prints out the operands / populates the operands in the instrace mode */
static void output_populator_printer(void * drcontext, opnd_t opnd, instr_t * instr, uint64 addr, uint mem_type, operand_t * output){


	int value;
	float float_value;
	uint width;
	int i;

	per_thread_t * data = drmgr_get_tls_field(drcontext,tls_index);


	if(opnd_is_reg(opnd)){
		
		value = opnd_get_reg(opnd);
		if (value != DR_REG_NULL){
			width = opnd_size_in_bytes(reg_get_size(value));
		}
		else{
			width = 0;
		}
		
#ifdef READABLE_TRACE
		dr_fprintf(data->outfile,",%u,%u,%u",REG_TYPE, width, value);
#else	
		output->type = REG_TYPE; 
		output->width = width;
		output->value = value;

#endif
	
	}
	else if(opnd_is_immed(opnd)){
		
		//DR_ASSERT(opnd_is_immed_float(opnd) == false);

		if(opnd_is_immed_float(opnd)){

			width = opnd_size_in_bytes(opnd_get_size(opnd));

			if (instr_get_opcode(instr) == OP_fld1){
				dr_fprintf(data->outfile, ",%u,%u,1", IMM_FLOAT_TYPE, width);
			}
			else if (instr_get_opcode(instr) == OP_fldz){
				dr_fprintf(data->outfile, ",%u,%u,0", IMM_FLOAT_TYPE, width);
			}
			else{
				dr_messagebox("immediate float unknown\n");
				dr_abort();
			}

			//float_value = opnd_get_immed_float(opnd);

#ifdef READABLE_TRACE
			//dr_fprintf(data->outfile,",%u,%u,%.4f",IMM_FLOAT_TYPE,width,float_value);
#else
			output->type = IMM_FLOAT_TYPE;
			output->width = width;
			output->float_value = float_value;
#endif

		}
		
		if(opnd_is_immed_int(opnd)){

			width = opnd_size_in_bytes(opnd_get_size(opnd));
			value = opnd_get_immed_int(opnd);
#ifdef READABLE_TRACE
			dr_fprintf(data->outfile,",%u,%u,%d",IMM_INT_TYPE,width,value);
#else
			output->type = IMM_INT_TYPE;
			output->width = width;
			output->value = value;
#endif
		}

	}
	else if(opnd_is_memory_reference(opnd)){

		width = drutil_opnd_mem_size_in_bytes(opnd,instr);
#ifdef READABLE_TRACE
		dr_fprintf(data->outfile, ",%u,%u,%llu",mem_type,width,addr);
#else
		output->type = mem_type;
		output->width = width;
		output->float_value = addr;
#endif

	}
	

}
Exemple #11
0
/* returns false on failure */
static bool
decode_syscall_num(void *dcontext, byte *entry, syscall_info_t *info, LOADED_IMAGE *img)
{
    /* FIXME: would like to fail gracefully rather than have a DR assertion
     * on non-code! => use DEBUG=0 INTERNAL=1 DR build!
     */
    bool found_syscall = false, found_eax = false, found_edx = false, found_ecx = false;
    bool found_ret = false;
    byte *pc, *pre_pc;
    int num_instr = 0;
    instr_t *instr;
    byte *preferred = get_preferred_base(img);
    if (entry == NULL)
        return false;
    info->num_args = -1; /* if find sysnum but not args */
    info->sysnum = -1;
    info->fixup_index = -1;
    instr = instr_create(dcontext);
    pc = entry;
    /* FIXME - we don't support decoding 64bit instructions in 32bit mode, but I want
     * this to work on 32bit machines.  Hack fix based on the wrapper pattern, we skip
     * the first instruction (mov r10, rcx) here, the rest should decode ok.
     * Xref PR 236203. */
    if (expect_x64 && *pc == 0x4c && *(pc+1) == 0x8b && *(pc+2) == 0xd1)
        pc += 3;
    while (true) {
        instr_reset(dcontext, instr);
        pre_pc = pc;
        pc = decode(dcontext, pc, instr);
        if (verbose) {
            instr_set_translation(instr, pre_pc);
            dr_print_instr(dcontext, STDOUT, instr, "");
        }
        if (pc == NULL || !instr_valid(instr))
            break;
        if (instr_is_syscall(instr) || instr_is_call_indirect(instr)) {
            /* If we see a syscall instr or an indirect call which is not syscall,
             * we assume this is not a syscall wrapper.
             */
            found_syscall = process_syscall_instr(dcontext, instr, found_eax, found_edx);
            if (!found_syscall)
                break; /* assume not a syscall wrapper, give up gracefully */
        } else if (instr_is_return(instr)) {
            /* we must break on return to avoid case like win8 x86
             * which has sysenter callee adjacent-"inlined"
             *     ntdll!NtYieldExecution:
             *     77d7422c b801000000  mov     eax,1
             *     77d74231 e801000000  call    ntdll!NtYieldExecution+0xb (77d74237)
             *     77d74236 c3          ret
             *     77d74237 8bd4        mov     edx,esp
             *     77d74239 0f34        sysenter
             *     77d7423b c3          ret
             */
            if (!found_ret) {
                process_ret(instr, info);
                found_ret = true;
            }
            break;
        } else if (instr_get_opcode(instr) == OP_call) {
            found_syscall = process_syscall_call(dcontext, pc, instr,
                                                 found_eax, found_edx);
            /* If we see a call and it is not a sysenter callee,
             * we assume this is not a syscall wrapper.
             */
            if (!found_syscall)
                break; /* assume not a syscall wrapper, give up gracefully */
        } else if (instr_is_cti(instr)) {
            /* We expect only ctis like ret or ret imm, syscall, and call, which are
             * handled above. Give up gracefully if we hit any other cti.
             * XXX: what about jmp to shared ret (seen in the past on some syscalls)?
             */
            /* Update: win10 TH2 1511 x64 has a cti:
             *   ntdll!NtContinue:
             *   00007ff9`13185630 4c8bd1          mov     r10,rcx
             *   00007ff9`13185633 b843000000      mov     eax,43h
             *   00007ff9`13185638 f604250803fe7f01 test    byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
             *   00007ff9`13185640 7503            jne     ntdll!NtContinue+0x15 (00007ff9`13185645)
             *   00007ff9`13185642 0f05            syscall
             *   00007ff9`13185644 c3              ret
             *   00007ff9`13185645 cd2e            int     2Eh
             *   00007ff9`13185647 c3              ret
             */
            if (expect_x64 && instr_is_cbr(instr) &&
                opnd_get_pc(instr_get_target(instr)) == pc + 3/*syscall;ret*/) {
                /* keep going */
            } else
                break;
        } else if ((!found_eax || !found_edx || !found_ecx) &&
                   instr_get_opcode(instr) == OP_mov_imm &&
                   opnd_is_reg(instr_get_dst(instr, 0))) {
            if (!found_eax && opnd_get_reg(instr_get_dst(instr, 0)) == REG_EAX) {
                info->sysnum = (int) opnd_get_immed_int(instr_get_src(instr, 0));
                found_eax = true;
            } else if (!found_edx && opnd_get_reg(instr_get_dst(instr, 0)) == REG_EDX) {
                uint imm = (uint) opnd_get_immed_int(instr_get_src(instr, 0));
                if (imm == 0x7ffe0300 ||
                    /* On Win10 the immed is ntdll!Wow64SystemServiceCall */
                    (expect_wow && imm > (ptr_uint_t)preferred &&
                     imm < (ptr_uint_t)preferred + img->SizeOfImage))
                    found_edx = true;
            } else if (!found_ecx && opnd_get_reg(instr_get_dst(instr, 0)) == REG_ECX) {
                found_ecx = true;
                info->fixup_index = (int) opnd_get_immed_int(instr_get_src(instr, 0));
            }
        } else if (instr_get_opcode(instr) == OP_xor &&
                   opnd_is_reg(instr_get_src(instr, 0)) &&
                   opnd_get_reg(instr_get_src(instr, 0)) == REG_ECX &&
                   opnd_is_reg(instr_get_dst(instr, 0)) &&
                   opnd_get_reg(instr_get_dst(instr, 0)) == REG_ECX) {
            /* xor to 0 */
            found_ecx = true;
            info->fixup_index = 0;
        }
        num_instr++;
        if (num_instr > MAX_INSTRS_BEFORE_SYSCALL) /* wrappers should be short! */
            break; /* avoid weird cases like NPXEMULATORTABLE */
    }
    instr_destroy(dcontext, instr);
    return found_syscall;
}
Exemple #12
0
/* returns false on failure */
static bool
decode_syscall_num(void *dcontext, byte *entry, syscall_info_t *info)
{
    /* FIXME: would like to fail gracefully rather than have a DR assertion
     * on non-code! => use DEBUG=0 INTERNAL=1 DR build!
     */
    bool found_syscall = false, found_eax = false, found_edx = false, found_ecx = false;
    bool found_ret = false;
    byte *pc;
    int num_instr = 0;
    instr_t *instr;
    if (entry == NULL)
        return false;
    info->num_args = -1; /* if find sysnum but not args */
    info->sysnum = -1;
    info->fixup_index = -1;
    instr = instr_create(dcontext);
    pc = entry;
    /* FIXME - we don't support decoding 64bit instructions in 32bit mode, but I want
     * this to work on 32bit machines.  Hack fix based on the wrapper pattern, we skip
     * the first instruction (mov r10, rcx) here, the rest should decode ok.
     * Xref PR 236203. */
    if (expect_x64 && *pc == 0x4c && *(pc+1) == 0x8b && *(pc+2) == 0xd1)
        pc += 3;
    while (true) {
        instr_reset(dcontext, instr);
        pc = decode(dcontext, pc, instr);
        if (verbose)
            dr_print_instr(dcontext, STDOUT, instr, "");
        if (pc == NULL || !instr_valid(instr))
            break;
        /* ASSUMPTION: a mov imm of 0x7ffe0300 into edx followed by an
         * indirect call via edx is a system call on XP and later
         * On XP SP1 it's call *edx, while on XP SP2 it's call *(edx)
         * For wow it's a call through fs.
         * FIXME - core exports various is_*_syscall routines (such as
         * instr_is_wow64_syscall()) which we could use here instead of
         * duplicating if they were more flexible about when they could
         * be called (instr_is_wow64_syscall() for ex. asserts if not
         * in a wow process).
         */
        if (/* int 2e or x64 or win8 sysenter */
            (instr_is_syscall(instr) && found_eax && (expect_int2e || expect_x64 || expect_sysenter)) ||
            /* sysenter case */
            (expect_sysenter && found_edx && found_eax &&
             instr_is_call_indirect(instr) &&
             /* XP SP{0,1}, 2003 SP0: call *edx */
             ((opnd_is_reg(instr_get_target(instr)) &&
               opnd_get_reg(instr_get_target(instr)) == REG_EDX) ||
              /* XP SP2, 2003 SP1: call *(edx) */
              (opnd_is_base_disp(instr_get_target(instr)) &&
               opnd_get_base(instr_get_target(instr)) == REG_EDX &&
               opnd_get_index(instr_get_target(instr)) == REG_NULL &&
               opnd_get_disp(instr_get_target(instr)) == 0))) ||
            /* wow case 
             * we don't require found_ecx b/c win8 does not use ecx
             */
            (expect_wow && found_eax &&
             instr_is_call_indirect(instr) &&
             opnd_is_far_base_disp(instr_get_target(instr)) &&
             opnd_get_base(instr_get_target(instr)) == REG_NULL &&
             opnd_get_index(instr_get_target(instr)) == REG_NULL &&
             opnd_get_segment(instr_get_target(instr)) == SEG_FS)) {
            found_syscall = true;
        } else if (instr_is_return(instr)) {
            if (!found_ret) {
                process_ret(instr, info);
                found_ret = true;
            }
            break;
        } else if (instr_is_cti(instr)) {
            if (instr_get_opcode(instr) == OP_call) {
                /* handle win8 x86 which has sysenter callee adjacent-"inlined"
                 *     ntdll!NtYieldExecution:
                 *     77d7422c b801000000      mov     eax,1
                 *     77d74231 e801000000      call    ntdll!NtYieldExecution+0xb (77d74237)
                 *     77d74236 c3              ret
                 *     77d74237 8bd4            mov     edx,esp
                 *     77d74239 0f34            sysenter
                 *     77d7423b c3              ret
                 */
                byte *tgt;
                assert(opnd_is_pc(instr_get_target(instr)));
                tgt = opnd_get_pc(instr_get_target(instr));
                /* we expect only ret or ret imm, and possibly some nops (in gdi32).
                 * XXX: what about jmp to shared ret (seen in the past on some syscalls)?
                 */
                if (tgt > pc && tgt <= pc + 16) {
                    bool ok = false;
                    do {
                        if (pc == tgt) {
                            ok = true;
                            break;
                        }
                        instr_reset(dcontext, instr);
                        pc = decode(dcontext, pc, instr);
                        if (verbose)
                            dr_print_instr(dcontext, STDOUT, instr, "");
                        if (instr_is_return(instr)) {
                            process_ret(instr, info);
                            found_ret = true;
                        } else if (!instr_is_nop(instr))
                            break;
                        num_instr++;
                    } while (num_instr <= MAX_INSTRS_BEFORE_SYSCALL);
                    if (ok)
                        continue;
                }
            }
            /* assume not a syscall wrapper if we hit a cti */
            break; /* give up gracefully */
        } else if ((!found_eax || !found_edx || !found_ecx) &&
            instr_get_opcode(instr) == OP_mov_imm &&
            opnd_is_reg(instr_get_dst(instr, 0))) {
            if (!found_eax && opnd_get_reg(instr_get_dst(instr, 0)) == REG_EAX) {
                info->sysnum = (int) opnd_get_immed_int(instr_get_src(instr, 0));
                found_eax = true;
            } else if (!found_edx && opnd_get_reg(instr_get_dst(instr, 0)) == REG_EDX) {
                int imm = (int) opnd_get_immed_int(instr_get_src(instr, 0));
                if (imm == 0x7ffe0300)
                    found_edx = true;
            } else if (!found_ecx && opnd_get_reg(instr_get_dst(instr, 0)) == REG_ECX) {
                found_ecx = true;
                info->fixup_index = (int) opnd_get_immed_int(instr_get_src(instr, 0));
            }
        } else if (instr_get_opcode(instr) == OP_xor &&
                   opnd_is_reg(instr_get_src(instr, 0)) &&
                   opnd_get_reg(instr_get_src(instr, 0)) == REG_ECX &&
                   opnd_is_reg(instr_get_dst(instr, 0)) &&
                   opnd_get_reg(instr_get_dst(instr, 0)) == REG_ECX) {
            /* xor to 0 */
            found_ecx = true;
            info->fixup_index = 0;
        }
        num_instr++;
        if (num_instr > MAX_INSTRS_BEFORE_SYSCALL) /* wrappers should be short! */
            break; /* avoid weird cases like NPXEMULATORTABLE */
    }
    instr_destroy(dcontext, instr);
    return found_syscall;
}