/* * 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); }
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; }