/*
 * The NaCl five-byte safe indirect calling sequence looks like this:
 *   83 e0 f0                 and  $0xfffffff0,%eax
 *   ff d0                    call *%eax
 * The call may be replaced with a ff e0 jmp. Any register may
 * be used, not just eax. The validator requires exactly this
 * sequence.
 * TODO(brad): validate or write the masks.
 */
static void ValidateIndirect5(const struct NCDecoderState *mstate) {
  uint8_t               *jmpopcode;
  uint8_t               *andopcode;
  uint8_t               mrm;
  uint8_t               targetreg;
  const uint8_t         kReg_ESP = 4;

  struct NCDecoderState *andinst = PreviousInst(mstate, -1);
  assert(andinst != NULL);
  if (andinst->inst.length == 0) {
    BadInstructionError(mstate, "Unsafe indirect jump");
    Stats_UnsafeIndirect(mstate->vstate);
    return;
  }
  jmpopcode = mstate->inst.maddr;   /* note: no prefixbytes allowed */
  andopcode = andinst->inst.maddr;  /* note: no prefixbytes allowed */
  mrm = jmpopcode[1];
  targetreg = modrm_rm(mrm);  /* Note that the modrm_rm field holds the   */
                              /* target addr the modrm_reg is the opcode. */

  Stats_CheckTarget(mstate->vstate);
  Stats_TargetIndirect(mstate->vstate);
  do {
    /* no prefix bytes allowed */
    if (mstate->inst.prefixbytes != 0) break;
    if (andinst->inst.prefixbytes != 0) break;
    /* Check all the opcodes. */
    /* In GROUP5, 2 => call, 4 => jmp */
    if (jmpopcode[0] != 0xff) break;
    if ((modrm_reg(mrm) != 2) && (modrm_reg(mrm) != 4)) break;
    /* Issue 32: disallow unsafe call/jump indirection */
    /* example:    ff 12     call (*edx)               */
    /* Reported by defend.the.world on 11 Dec 2008     */
    if (modrm_mod(mrm) != 3) break;
    if (targetreg == kReg_ESP) break;
    if (andopcode[0] != 0x83) break;
    /* check modrm bytes of or and and instructions */
    if (andopcode[1] != (0xe0 | targetreg)) break;
    /* check mask */
    if (andopcode[2] != (0x0ff & ~mstate->vstate->alignmask)) break;
    /* All checks look good. Make the sequence 'atomic.' */
    ForgetIP(mstate->inst.vaddr, mstate->vstate);
    /* as a courtesy, check call alignment correctness */
    if (modrm_reg(mrm) == 2) ValidateCallAlignment(mstate);
    return;
  } while (0);
  BadInstructionError(mstate, "Unsafe indirect jump");
  Stats_UnsafeIndirect(mstate->vstate);
}
示例#2
0
size_t hook_generate(void* out, hook_function* f, void* write_address, hook_function* read_f) {
	if (!write_address) write_address = out;
	if (!read_f) read_f = f;

	class code_buf_t : public out_buf {
	public:
		uint8_t* code_c;
		uint8_t* write_c;
		code_buf_t(uint8_t* code_c, uint8_t* write_c) : code_c(code_c), write_c(write_c) {}
		virtual void puc(uint8_t x) { *write_c++ = x; ++code_c;  }
		virtual void pus(uint16_t x) { *(uint16_t*)write_c = (uint16_t)(x); write_c += 2; code_c += 2; }
		virtual void pui(uint32_t x) { *(uint32_t*)write_c = (uint32_t)(x); write_c += 4; code_c += 4; }
		virtual uint32_t addr() { return (uint32_t)code_c; }
	};

	code_buf_t code_buf((uint8_t*)out, (uint8_t*)write_address);
	codegen gen(&code_buf);

	//gen.int3();

	gen.add_rm_immx<32>(modrm_reg(badreg, esp), -(int)sizeof(hook_struct));

#define rm_h(r,o,...) modrm_dispx(r,-gen.esp_val - sizeof(hook_struct) + (offsetof(hook_struct,o) __VA_ARGS__),sib_nomul(esp))

	for (int i = 0; i < 8; i++) {
		if (read_f->flags&hookflag_regs[i]) gen.mov_rm_r<32>(rm_h((reg)i, _eax, +i * 4));
	}

	gen.mov_r_rm<32>(modrm_dispx(eax, -gen.esp_val, sib_nomul(esp)));
	gen.mov_rm_r<32>(rm_h(eax, retaddress));

	int r = 2;
	for (int i = 0; i < read_f->args; i++) {
		gen.mov_r_rm<32>(modrm_dispx((reg)r, -gen.esp_val + 4 + i * 4, sib_nomul(esp)));
		gen.mov_rm_r<32>(rm_h((reg)r, arg[i]));
		gen.mov_rm_r<32>(rm_h((reg)r, ref_arg[i]));
		if (--r == -1) r = 2;
	}
	if (read_f->pre) {
		gen.mov_rm_imm<8>(rm_h(badreg, calloriginal), 1);
		if (-gen.esp_val == sizeof(hook_struct)) gen.mov_r_rm<32>(modrm_reg(eax, esp));
		else gen.lea_r_rm<32>(rm_h(eax, ref_arg[0]));
		gen.push_imm32((uint32_t)f);
		gen.push_r32(eax);
		gen.call_rel32((uint32_t)read_f->pre);
		gen.add_rm_immx<32>(modrm_reg(badreg, esp), 8);
		gen.mov_r_rm<8>(rm_h(eax, calloriginal));
		gen.test_rm_r<8>(modrm_reg(eax, eax));
		gen.push_imm32(0);
		uint32_t* retaddr = (uint32_t*)(code_buf.write_c - 4);
		for (int i = 0; i < 8; i++) {
			if (read_f->flags&hookflag_regs[i]) gen.mov_r_rm<32>(rm_h((reg)i, _eax, +4 * i));
		}
		gen.jnz_relx((uint32_t)read_f->entry);
		gen.mov_r_rm<32>(rm_h(eax, retval));
		gen.add_rm_immx<32>(modrm_reg(badreg, esp), 4 + (read_f->flags&hookflag_callee_cleanup ? 4 * read_f->args : 0));
		while (gen.c->addr() % 4) gen.nop();
		*retaddr = (uint32_t)gen.c->addr();
	} else {
		for (int i = 0; i < 8; i++) {
			if (read_f->flags&hookflag_regs[i]) gen.mov_r_rm<32>(rm_h((reg)i, _eax, +4 * i));
		}
		gen.call_rel32((uint32_t)read_f->entry);
		if (read_f->flags&hookflag_callee_cleanup) gen.esp_val += 4 * read_f->args;
	}
	if (read_f->post) {
		gen.mov_rm_r<32>(rm_h(eax, retval));
		if (-gen.esp_val == sizeof(hook_struct)) gen.mov_r_rm<32>(modrm_reg(eax, esp));
		else gen.lea_r_rm<32>(rm_h(eax, ref_arg[0]));
		gen.push_imm32((uint32_t)f);
		if (-gen.esp_val == sizeof(hook_struct)) gen.push_r32(esp);
		else gen.push_r32(eax);
		gen.call_rel32((uint32_t)read_f->post);
		gen.mov_r_rm<32>(rm_h(eax, retval));
	}

#undef rm_h
	gen.add_rm_immx<32>(modrm_reg(badreg, esp), -gen.esp_val);
	if (read_f->flags&hookflag_callee_cleanup && read_f->args) gen.ret_imm16(read_f->args * 4);
	else gen.ret();
	return code_buf.code_c - (uint8_t*)out;
}