static unsigned patch_internal(int call, unsigned len, void *insnbuf, unsigned long ip) { u64 reloc; struct vmi_relocation_info *const rel = (struct vmi_relocation_info *)&reloc; reloc = call_vrom_long_func(vmi_rom, get_reloc, call); switch(rel->type) { case VMI_RELOCATION_CALL_REL: BUG_ON(len < 5); *(char *)insnbuf = MNEM_CALL; patch_offset(insnbuf, ip, (unsigned long)rel->eip); return 5; case VMI_RELOCATION_JUMP_REL: BUG_ON(len < 5); *(char *)insnbuf = MNEM_JMP; patch_offset(insnbuf, ip, (unsigned long)rel->eip); return 5; case VMI_RELOCATION_NOP: /* obliterate the whole thing */ return 0; case VMI_RELOCATION_NONE: /* leave native code in place */ break; default: BUG(); } return len; }
void patch_cmd(int argc, char **argv) { r_binfmt_s bin; patch_options_parse(argc, argv); r_binfmt_load(&bin, patch_options_filename, patch_options_arch); if(patch_options_offset != R_BINFMT_BAD_ADDR) { patch_offset(&bin, patch_options_offset, patch_options_bytes->bytes, patch_options_bytes->len); } else { patch_address(&bin, patch_options_address, patch_options_bytes->bytes, patch_options_bytes->len); } if(patch_options_output == NULL) patch_options_output = patch_options_filename; r_binfmt_write(&bin, patch_options_output); printf("[+] Patched %" PRIu64 " bytes (result saved in %s)\n", patch_options_bytes->len, patch_options_output); r_binfmt_free(&bin); }
/* * Apply patch if appropriate, return length of new instruction * sequence. The callee does nop padding for us. */ static unsigned vmi_patch(u8 type, u16 clobbers, void *insns, unsigned len) { switch (type) { case PARAVIRT_IRQ_DISABLE: return patch_internal(VMI_CALL_DisableInterrupts, len, insns); case PARAVIRT_IRQ_ENABLE: return patch_internal(VMI_CALL_EnableInterrupts, len, insns); case PARAVIRT_RESTORE_FLAGS: return patch_internal(VMI_CALL_SetInterruptMask, len, insns); case PARAVIRT_SAVE_FLAGS: return patch_internal(VMI_CALL_GetInterruptMask, len, insns); case PARAVIRT_SAVE_FLAGS_IRQ_DISABLE: if (len >= 10) { patch_internal(VMI_CALL_GetInterruptMask, len, insns); patch_internal(VMI_CALL_DisableInterrupts, len-5, insns+5); return 10; } else { /* * You bastards didn't leave enough room to * patch save_flags_irq_disable inline. Patch * to a helper */ BUG_ON(len < 5); *(char *)insns = MNEM_CALL; patch_offset(insns, irq_save_disable_callout); return 5; } case PARAVIRT_INTERRUPT_RETURN: return patch_internal(VMI_CALL_IRET, len, insns); case PARAVIRT_STI_SYSEXIT: return patch_internal(VMI_CALL_SYSEXIT, len, insns); default: break; } return len; }