From 0062a58ba9c53b44b80947f802289beecd3598f0 Mon Sep 17 00:00:00 2001 From: jdh8d <jdh8d@git.zephyr-software.com> Date: Thu, 18 Sep 2014 19:16:56 +0000 Subject: [PATCH] --- .gitattributes | 2 + include/range.h | 20 ++ include/unresolved.h | 72 +++++ include/zipr.h | 61 +++- include/zipr_all.h | 7 +- src/zipr.cpp | 717 ++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 875 insertions(+), 4 deletions(-) create mode 100644 include/range.h create mode 100644 include/unresolved.h diff --git a/.gitattributes b/.gitattributes index 52023e9..811b571 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,6 @@ * text=auto !eol +include/range.h -text +include/unresolved.h -text include/zipr.h -text include/zipr_all.h -text include/zipr_options.h -text diff --git a/include/range.h b/include/range.h new file mode 100644 index 0000000..94ac2b7 --- /dev/null +++ b/include/range.h @@ -0,0 +1,20 @@ +#ifndef range_h +#define range_h + + +typedef uintptr_t RangeAddress_t; + +class Range_t +{ + public: + Range_t(RangeAddress_t p_s, RangeAddress_t p_e) : m_start(p_s), m_end(p_e) { } + + RangeAddress_t GetStart() { return m_start; } + RangeAddress_t GetEnd() { return m_end; } + + protected: + + RangeAddress_t m_start, m_end; +}; + +#endif diff --git a/include/unresolved.h b/include/unresolved.h new file mode 100644 index 0000000..1a1a20a --- /dev/null +++ b/include/unresolved.h @@ -0,0 +1,72 @@ +#ifndef unresolved_h +#define unresolved_h + + + +enum UnresolvedType_t +{ + CondJump, + UncondJump_rel8, + UncondJump_rel32, + Call, + Data32, + Data64 +}; + +class UnresolvedInfo_t +{ + public: + virtual libIRDB::Instruction_t* GetInstruction() const =0 ; + private: +}; + + + +// instructions that can float to any address, but don't yet have one +class UnresolvedUnpinned_t : public UnresolvedInfo_t +{ + public: + UnresolvedUnpinned_t(libIRDB::Instruction_t* p_from) : from_instruction(p_from) {} + libIRDB::Instruction_t* GetInstruction() const { return from_instruction; } + private: + libIRDB::Instruction_t *from_instruction; + + friend bool operator< (const UnresolvedUnpinned_t& lhs, const UnresolvedUnpinned_t& rhs); +}; + +inline bool operator< (const UnresolvedUnpinned_t& lhs, const UnresolvedUnpinned_t& rhs) +{ return lhs.from_instruction->GetBaseID() < rhs.from_instruction->GetBaseID(); } + +// instructions that need a pin, but don't yet have one +class UnresolvedPinned_t : public UnresolvedInfo_t +{ + public: + UnresolvedPinned_t(libIRDB::Instruction_t* p_from) : from_instruction(p_from) {} + libIRDB::Instruction_t* GetInstruction() const { return from_instruction; } + private: + libIRDB::Instruction_t* from_instruction; + + friend bool operator< (const UnresolvedPinned_t& lhs, const UnresolvedPinned_t& rhs); + +}; + +inline bool operator< (const UnresolvedPinned_t& lhs, const UnresolvedPinned_t& rhs) +{ return lhs.from_instruction->GetBaseID() < rhs.from_instruction->GetBaseID(); } + + +// an ELF location that needs patching when an Unresolved instrcution +class Patch_t +{ + public: + Patch_t(RangeAddress_t p_from_addr, UnresolvedType_t p_t) : from_addr(p_from_addr), type(p_t) {} + + RangeAddress_t GetAddress() { return from_addr; } + UnresolvedType_t GetType() { return type; } + + private: + RangeAddress_t from_addr; + UnresolvedType_t type; +}; + + +#endif diff --git a/include/zipr.h b/include/zipr.h index c33b9ca..71650fe 100644 --- a/include/zipr.h +++ b/include/zipr.h @@ -8,12 +8,71 @@ class Zipr_t Zipr_t(libIRDB::FileIR_t* p_firp, const Options_t &p_opts) : m_firp(p_firp), m_opts(p_opts) { }; - void CreateBinaryFile(std::string name); + void CreateBinaryFile(const std::string &name); protected: + // data for the stuff we're rewriting. libIRDB::FileIR_t* m_firp; const Options_t& m_opts; + + // phases of rewriting. + void FindFreeRanges(const std::string &name); + void AddPinnedInstructions(); + void ResolvePinnedInstructions(); + void ReservePinnedInstructions(); + void ExpandPinnedInstructions(); + void Fix2BytePinnedInstructions(); + void OptimizePinnedInstructions(); + void PlopTheUnpinnedInstructions(); + + // range operatations + void SplitFreeRange(RangeAddress_t addr); + std::list<Range_t>::iterator FindFreeRange(RangeAddress_t addr); + Range_t GetFreeRange(int size); + + // queries about free areas. + bool AreBytesFree(RangeAddress_t addr, int num_bytes); + bool IsByteFree(RangeAddress_t addr); + + // emitting bytes. + void PlopByte(RangeAddress_t addr, char the_byte); + void PlopBytes(RangeAddress_t addr, const char the_byte[], int num); + void PlopJump(RangeAddress_t addr); + + // emiting instructions + RangeAddress_t PlopInstruction(libIRDB::Instruction_t* insn,RangeAddress_t addr); + RangeAddress_t PlopWithTarget(libIRDB::Instruction_t* insn, RangeAddress_t at); + + + // patching + void PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr); + void ApplyPatches(libIRDB::Instruction_t* insn, RangeAddress_t addr); + void PatchInstruction(RangeAddress_t addr, libIRDB::Instruction_t* insn); + + + + // helpers. + void ProcessUnpinnedInstruction(const UnresolvedUnpinned_t &uu, const Patch_t &p); + + + private: + // structures necessary for ZIPR algorithm. + std::set<UnresolvedUnpinned_t> unresolved_unpinned_addrs; + std::list<Range_t> free_ranges; // keep ordered + std::set<UnresolvedPinned_t> unresolved_pinned_addrs; + std::list<Range_t> pinned_ranges; // keep ordered + std::multimap<UnresolvedUnpinned_t,Patch_t> patch_list; + + // map of where bytes will actually go. + std::map<RangeAddress_t,char> byte_map; + + std::set<UnresolvedPinned_t> two_byte_pins; + std::map<UnresolvedPinned_t,RangeAddress_t> five_byte_pins; + + std::map<libIRDB::Instruction_t*,RangeAddress_t> final_insn_locations; + + }; #endif diff --git a/include/zipr_all.h b/include/zipr_all.h index 9fb648d..3ac0b4c 100644 --- a/include/zipr_all.h +++ b/include/zipr_all.h @@ -2,13 +2,18 @@ #define zipr_all_h #include <stdlib.h> -#include <string> +#include <stdint.h> +#include <set> +#include <list> +#include <map> #include <libIRDB-core.hpp> namespace zipr { #include <zipr_options.h> +#include <range.h> +#include <unresolved.h> #include <zipr.h> }; diff --git a/src/zipr.cpp b/src/zipr.cpp index 40dc3db..42b18c6 100644 --- a/src/zipr.cpp +++ b/src/zipr.cpp @@ -1,14 +1,727 @@ #include <zipr_all.h> +#include <libIRDB-core.hpp> +#include <iostream> +#include <stdlib.h> +#include <string.h> +#include <map> +#include <assert.h> +#include <sys/mman.h> +#include <ctype.h> +#include "elfio/elfio.hpp" +#include "elfio/elfio_dump.hpp" +#include "targ-config.h" +#include "beaengine/BeaEngine.h" + + +using namespace libIRDB; +using namespace std; using namespace zipr; +using namespace ELFIO; + -void Zipr_t::CreateBinaryFile(std::string name) +void Zipr_t::CreateBinaryFile(const std::string &name) { // create ranges, including extra range that's def. big enough. + FindFreeRanges(name); + // add pinned instructions - // resolve pinned instructions + AddPinnedInstructions(); + + // reserve space for pins + ReservePinnedInstructions(); + + // expand 2-byte pins into 4-byte pins + ExpandPinnedInstructions(); + + // Allocate space near 2-byte pins for a 5-byte pin + Fix2BytePinnedInstructions(); + + // Convert all 5-byte pins into full fragments + OptimizePinnedInstructions(); + + // now that pinning is done, start emitting unpinnned instructions, and patching where needed. + PlopTheUnpinnedInstructions(); + // resolve unpinned, otherwise unresolved instructions // truncate final range. // write binary file to disk } + +void Zipr_t::FindFreeRanges(const std::string &name) +{ + /* use ELFIO to load the sections */ + ELFIO::elfio* elfiop=new ELFIO::elfio; + assert(elfiop); + elfiop->load(name); +// ELFIO::dump::header(cout,*elfiop); + ELFIO::dump::section_headers(cout,*elfiop); + + RangeAddress_t last_end=0; + + // For all sections + Elf_Half n = elfiop->sections.size(); + for ( Elf_Half i = 0; i < n; ++i ) + { + section* sec = elfiop->sections[i]; + assert(sec); + + RangeAddress_t start=sec->get_address(); + RangeAddress_t end=sec->get_size()+start-1; + + if( (sec->get_flags() & SHF_ALLOC) ==0 ) + continue; + + assert(start>last_end); + last_end=end; + + if((sec->get_flags() & SHF_EXECINSTR)) + { + printf("Adding free range 0x%p to 0x%p\n", (void*)start,(void*)end); + free_ranges.push_back(Range_t(start,end)); + } + } + +#define PAGE_SIZE 4096 +#define PAGE_ROUND_UP(x) ( (((uintptr_t)(x)) + PAGE_SIZE-1) & (~(PAGE_SIZE-1)) ) + + // now that we've looked at the sections, add a (mysterious) extra section in case we need to overflow + // the sections existing in the ELF. + RangeAddress_t new_free_page=PAGE_ROUND_UP(last_end); + free_ranges.push_back( Range_t(new_free_page,(RangeAddress_t)-1)); + printf("Adding (mysterious) free range 0x%p to EOF\n", (void*)new_free_page); +} + +void Zipr_t::AddPinnedInstructions() +{ + + for( + set<Instruction_t*>::const_iterator it=m_firp->GetInstructions().begin(); + it!=m_firp->GetInstructions().end(); + ++it + ) + { + Instruction_t* insn=*it; + assert(insn); + + if(!insn->GetIndirectBranchTargetAddress()) + continue; + + unresolved_pinned_addrs.insert(UnresolvedPinned_t(insn)); + } + +} + + +Range_t Zipr_t::GetFreeRange(int size) +{ + for( list<Range_t>::iterator it=free_ranges.begin(); + it!=free_ranges.end(); + ++it) + { + Range_t r=*it; + if(r.GetEnd() - r.GetStart() > size) + return r; + } + assert(0);// assume we find a big enough range. +} + +list<Range_t>::iterator Zipr_t::FindFreeRange(RangeAddress_t addr) +{ + for( list<Range_t>::iterator it=free_ranges.begin(); + it!=free_ranges.end(); + ++it) + { + Range_t r=*it; + if(r.GetStart() <= addr && addr <=r.GetEnd()) + return it; + } + return free_ranges.end(); +} + +void Zipr_t::SplitFreeRange(RangeAddress_t addr) +{ + list<Range_t>::iterator it=FindFreeRange(addr); + assert(it!=free_ranges.end()); + + Range_t r=*it; + + if(r.GetStart()==r.GetEnd()) + { + assert(addr==r.GetEnd()); + free_ranges.erase(it); + } + else if(addr==r.GetStart()) + { + free_ranges.insert(it, Range_t(r.GetStart()+1, r.GetEnd())); + free_ranges.erase(it); + } + else if(addr==r.GetEnd()) + { + free_ranges.insert(it, Range_t(r.GetStart(), r.GetEnd()-1)); + free_ranges.erase(it); + } + else // split range + { + free_ranges.insert(it, Range_t(r.GetStart(), addr-1)); + free_ranges.insert(it, Range_t(addr+1, r.GetEnd())); + free_ranges.erase(it); + } +} + + +static bool should_pin_immediately(Instruction_t *upinsn) +{ + DISASM d; + upinsn->Disassemble(d); + + if(d.Instruction.BranchType==RetType) + return true; + + AddressID_t* upinsn_ibta=upinsn->GetIndirectBranchTargetAddress(); + assert(upinsn_ibta!=NULL); + + /* careful with 1 byte instructions that have a pinned fallthrough */ + if(upinsn->GetDataBits().length()==1) + { + if(upinsn->GetFallthrough()==NULL) + return true; + AddressID_t* ft_ibta=upinsn->GetFallthrough()->GetIndirectBranchTargetAddress(); + if(ft_ibta && (upinsn_ibta->GetVirtualOffset()+1) == ft_ibta->GetVirtualOffset()) + return true; + } + + return false; +} + +void Zipr_t::ReservePinnedInstructions() +{ + set<UnresolvedPinned_t> reserved_pins; + + + /* first, for each pinned instruction, try to put down a jump for the pinned instruction + */ + for( + set<UnresolvedPinned_t>::const_iterator it=unresolved_pinned_addrs.begin(); + it!=unresolved_pinned_addrs.end(); + ++it + ) + { + UnresolvedPinned_t up=*it; + + Instruction_t* upinsn=up.GetInstruction(); + RangeAddress_t addr=upinsn->GetIndirectBranchTargetAddress()->GetVirtualOffset(); + + if(upinsn->GetAddress()->GetFileID()==BaseObj_t::NOT_IN_DATABASE) + continue; + + /* sometimes, we need can't just put down a 2-byte jump into the old slot + * we may need to do alter our technique if there are two consecutive pinned addresses (e.g. 800 and 801). + * That case is tricky, as we can't put even a 2-byte jump instruction down. + * so, we attempt to pin any 1-byte instructions with no fallthrough (returns are most common) immediately. + * we also attempt to pin any 1-byte insn that falls through to the next pinned address (nops are common). + */ + if(should_pin_immediately(upinsn)) + { + printf("Final pinning %p-%p. fid=%d\n", (void*)addr, (void*)(addr+upinsn->GetDataBits().size()-1), + upinsn->GetAddress()->GetFileID()); + for(int i=0;i<upinsn->GetDataBits().size();i++) + { + byte_map[addr+i]=upinsn->GetDataBits()[i]; + SplitFreeRange(addr+i); + } + continue; + } + + char bytes[]={0xeb,0}; // jmp rel8 + printf("Two-byte Pinning %p-%p. fid=%d\n", (void*)addr, (void*)(addr+sizeof(bytes)-1), + upinsn->GetAddress()->GetFileID()); + + two_byte_pins.insert(up); + for(int i=0;i<sizeof(bytes);i++) + { + assert(byte_map.find(addr+i) == byte_map.end() ); + byte_map[addr+i]=bytes[i]; + SplitFreeRange(addr+i); + } + } + +} + + +void Zipr_t::ExpandPinnedInstructions() +{ + /* now, all insns have 2-byte pins. See which ones we can make 5-byte pins */ + + for( + set<UnresolvedPinned_t>::const_iterator it=two_byte_pins.begin(); + it!=two_byte_pins.end(); + ) + { + UnresolvedPinned_t up=*it; + Instruction_t* upinsn=up.GetInstruction(); + RangeAddress_t addr=upinsn->GetIndirectBranchTargetAddress()->GetVirtualOffset(); + + + char bytes[]={0xe9,0,0,0,0}; // jmp rel8 + bool can_update=AreBytesFree(addr+2,sizeof(bytes)-2); + if(can_update) + { + printf("Found %p can be updated to 5-byte jmp\n", (void*)addr); + PlopJump(addr); + five_byte_pins[up]=addr; + two_byte_pins.erase(it++); + } + else + { + ++it; + printf("Found %p can NOT be updated to 5-byte jmp\n", (void*)addr); + } + } + + printf("Totals: 2-byters=%d, 5-byters=%d\n", (int)two_byte_pins.size(), (int)five_byte_pins.size()); +} + + +void Zipr_t::Fix2BytePinnedInstructions() +{ + for( + set<UnresolvedPinned_t>::const_iterator it=two_byte_pins.begin(); + it!=two_byte_pins.end(); + ) + { + UnresolvedPinned_t up=*it; + Instruction_t* upinsn=up.GetInstruction(); + RangeAddress_t addr=upinsn->GetIndirectBranchTargetAddress()->GetVirtualOffset(); + + // check for near branch instructions + for(int i=0;i<120;i++) // two byte jump range is 127 bytes, but that's from the pc after it's been inc, etc. complicated goo. 120 is a safe estimate of range. + { + if(AreBytesFree(addr-i,5)) + { + printf("Found location for 2-byte->5-byte conversion (%p-%p)->(%p-%p)\n", + (void*)addr,(void*)(addr+1), (void*)(addr-i),(void*)(addr-i+4)); + + PatchJump(addr, addr-i); + five_byte_pins[up]=addr-i; + PlopJump(addr-i); + break; + } + else if(AreBytesFree(addr+i,5)) + { + printf("Found location for 2-byte->5-byte conversion (%p-%p)->(%p-%p)\n", + (void*)addr,(void*)(addr+1), (void*)(addr+i),(void*)(addr+i+5)); + five_byte_pins[up]=addr+i; + PlopJump(addr+i); + break; + } + else + { +// printf("Not free at %p or %p\n", (void*)(addr-i),(void*)(addr+i)); + } + } + two_byte_pins.erase(it++); + } + +} + + +bool Zipr_t::AreBytesFree(RangeAddress_t addr, int num_bytes) +{ + for(int i=0;i<num_bytes;i++) + if(!IsByteFree(addr+i)) + return false; + return true; +} + +bool Zipr_t::IsByteFree(RangeAddress_t addr) +{ + if(FindFreeRange(addr)!=free_ranges.end()) + return true; + return false; +} + + +void Zipr_t::OptimizePinnedInstructions() +{ + + // should only be 5-byte pins by now. + + assert(two_byte_pins.size()==0); + + + for( + std::map<UnresolvedPinned_t,RangeAddress_t>::iterator it=five_byte_pins.begin(); + it!=five_byte_pins.end(); + ) + { + RangeAddress_t addr=(*it).second; + UnresolvedPinned_t up=(*it).first; + + // ideally, we'll try to fill out the pinned 5-byte jump instructions with actual instructions + // from the program. That's an optimization. At the moment, let's just create a patch for each one. + + UnresolvedUnpinned_t uu(up.GetInstruction()); + Patch_t thepatch(addr,UncondJump_rel32); + + patch_list.insert(pair<UnresolvedUnpinned_t,Patch_t>(uu,thepatch)); + + printf("Converting 5-byte pinned jump to patch for %p-%p\n", (void*)addr,(void*)(addr+4)); + + // remove and move to next pin + five_byte_pins.erase(it++); + } + +#if 0 +// some useful bits about how we might do real optimization for pinned instructions. + printf("Found %p can be updated to use better than 2-byte jmp\n", (void*)addr); + + list<Range_t>::iterator it=FindFreeRange(addr+2); + assert(it!=free_ranges.end()); + Range_t r=*it; + RangeAddress_t fr_end=r->GetEnd(); + RangeAddress_t cur_addr=addr; + Instruction_t* cur_insn=up->GetInstruction(); + + /* while we still have an instruction, and we can fit that instruction, + * plus a jump into the current free section, plop down instructions sequentially. + */ + while(cur_insn && fr_end>(cur_addr+cur_insn->GetDataBits().length()+5)) + { + cur_addr+=PlopInstruction(cur_insn,addr); + cur_insn=cur_insn->GetFallthrough(); + } + + if(cur_insn) + PlopJump(addr, cur_insn); +#endif +} + + +void Zipr_t::PlopBytes(RangeAddress_t addr, const char the_byte[], int num) +{ + for(int i=0;i<num;i++) + { + PlopByte(addr+i,the_byte[i]); + } +} + +void Zipr_t::PlopByte(RangeAddress_t addr, char the_byte) +{ + if(byte_map.find(addr) == byte_map.end() ) + SplitFreeRange(addr); + byte_map[addr]=the_byte; +} + +void Zipr_t::PlopJump(RangeAddress_t addr) +{ + char bytes[]={0xe9,0,0,0,0}; // jmp rel8 + for(int i=0;i<sizeof(bytes);i++) // don't check bytes 0-1, because they've got the short byte jmp + { + PlopByte(addr+i,bytes[i]); + } +} + + +void Zipr_t::PatchJump(RangeAddress_t at_addr, RangeAddress_t to_addr) +{ + uintptr_t off=to_addr-at_addr-2; + + assert(!IsByteFree(at_addr)); + + switch(byte_map[at_addr]) + { + case (char)0xe9: /* 5byte jump */ + { + assert(0); + } + case (char)0xeb: /* 2byte jump */ + { + assert(off==(uintptr_t)(char)off); + + assert(!IsByteFree(at_addr+1)); + byte_map[at_addr+1]=(char)off; + } + } +} + + +static int DetermineWorseCaseInsnSize(Instruction_t* insn) +{ + + int required_size=0; + + switch(insn->GetDataBits()[0]) + { + case (char)0x72: + case (char)0x73: + case (char)0x74: + case (char)0x75: + case (char)0x76: + case (char)0x77: + case (char)0x78: + case (char)0x79: + case (char)0x7a: + case (char)0x7b: + case (char)0x7c: + case (char)0x7d: + case (char)0x7e: + case (char)0x7f: + { + // two byte JCC -> 6byte JCC + required_size=6; + break; + } + + case (char)0xeb: + { + // two byte JMP -> 5byte JMP + required_size=5; + break; + } + + case (char)0xe0: + case (char)0xe1: + case (char)0xe2: + case (char)0xe3: + { + // loop, loopne, loopeq, jecxz + // convert to: + // <op> +5: + // jmp fallthrough + // +5: jmp target + // 2+5+5; + required_size=10; + break; + } + + + default: + { + required_size=insn->GetDataBits().size()+5; + break; + } + } + return required_size; +} + +void Zipr_t::ProcessUnpinnedInstruction(const UnresolvedUnpinned_t &uu, const Patch_t &p) +{ + int req_size=DetermineWorseCaseInsnSize(uu.GetInstruction()); + Range_t r=GetFreeRange(req_size); + + + + + RangeAddress_t fr_end=r.GetEnd(); + RangeAddress_t cur_addr=r.GetStart(); + Instruction_t* cur_insn=uu.GetInstruction(); + + + + /* while we still have an instruction, and we can fit that instruction, + * plus a jump into the current free section, plop down instructions sequentially. + */ + while(cur_insn && fr_end>(cur_addr+DetermineWorseCaseInsnSize(cur_insn))) + { + // some useful bits about how we might do real optimization for pinned instructions. + DISASM d; + cur_insn->Disassemble(d); + printf("Emitting %s at %p\n", d.CompleteInstr, (void*)cur_addr); + + + cur_addr=PlopInstruction(cur_insn,cur_addr); + cur_insn=cur_insn->GetFallthrough(); + } + if(cur_insn) + { + // Mark this insn as needing a patch since we couldn't completely empty + // the 'fragment' we are translating into the elf section. + UnresolvedUnpinned_t uu(cur_insn); + Patch_t thepatch(cur_addr,UncondJump_rel32); + patch_list.insert(pair<UnresolvedUnpinned_t,Patch_t>(uu,thepatch)); + PlopJump(cur_addr); + } +} + +void Zipr_t::PlopTheUnpinnedInstructions() +{ + + // note that processing a patch may result in adding a new patch + while(!patch_list.empty()) + { + // grab first item + UnresolvedUnpinned_t uu=(*patch_list.begin()).first; + Patch_t p=(*patch_list.begin()).second; + + // and erase it. + patch_list.erase(patch_list.begin()); + + // process the instruction. + ProcessUnpinnedInstruction(uu,p); + + + + } +} + +void Zipr_t::ApplyPatches(Instruction_t* insn, RangeAddress_t addr) +{ + // find any patches for instruction insn, and apply them such that they go to addr. + + UnresolvedUnpinned_t uu(insn); + + pair< + multimap<UnresolvedUnpinned_t,Patch_t>::iterator, + multimap<UnresolvedUnpinned_t,Patch_t>::iterator + > p=patch_list.equal_range(uu); + + for( multimap<UnresolvedUnpinned_t,Patch_t>::iterator mit=p.first; + mit!=p.second; + ++mit + ) + { + DISASM d; + insn->Disassemble(d); + printf("Found (but not yet applying) a patch for %s at %p to %p\n", + d.CompleteInstr, (void*)final_insn_locations[insn], (void*)addr); + // Patch instruction + // + // ApplyPatch( ... ); + } + + // removing resolved patches + patch_list.erase(p.first, p.second); +} + +void Zipr_t::PatchInstruction(RangeAddress_t addr, Instruction_t* insn) +{ + // patch the instruction at address addr to go to insn. if insn does not yet have a concrete address, + // register that it's patch needs to be applied later. + + UnresolvedUnpinned_t uu(insn); + Patch_t thepatch(addr,UncondJump_rel32); + DISASM d; + insn->Disassemble(d); + + std::map<Instruction_t*,RangeAddress_t>::iterator it=final_insn_locations.find(insn); + if(it==final_insn_locations.end()) + { + printf("Instruction %s cannot be patch yet, as target is unknown.\n", d.CompleteInstr); + + patch_list.insert(pair<UnresolvedUnpinned_t,Patch_t>(uu,thepatch)); + } + else + { + printf("Found (and should be, but not yet applying) a patch for %s to %p\n", d.CompleteInstr, (void*)addr); + // Apply Patch + // ApplyPatch(uu,thepatch); + } +} + +RangeAddress_t Zipr_t::PlopInstruction(Instruction_t* insn,RangeAddress_t addr) +{ + DISASM d; + insn->Disassemble(d); + RangeAddress_t ret=addr; + + + final_insn_locations[insn]=addr; + + if(insn->GetTarget()) + { + ret=PlopWithTarget(insn,addr); + } + else + { + PlopBytes(addr,insn->GetDataBits().c_str(), insn->GetDataBits().length()); + ret+=insn->GetDataBits().length(); + ApplyPatches(insn,addr); + } + + return ret; +} + + +RangeAddress_t Zipr_t::PlopWithTarget(Instruction_t* insn, RangeAddress_t at) +{ + RangeAddress_t ret=at; + if(insn->GetDataBits().length() >2) + { + PlopBytes(ret,insn->GetDataBits().c_str(), insn->GetDataBits().length()); + ret+=insn->GetDataBits().length(); + PatchInstruction(ret, insn->GetTarget()); + return ret; + } + + // call, jmp, jcc of length 2. + switch(insn->GetDataBits()[0]) + { + case (char)0x72: + case (char)0x73: + case (char)0x74: + case (char)0x75: + case (char)0x76: + case (char)0x77: + case (char)0x78: + case (char)0x79: + case (char)0x7a: + case (char)0x7b: + case (char)0x7c: + case (char)0x7d: + case (char)0x7e: + case (char)0x7f: + { + // two byte JCC + char bytes[]={0x0f,0xc0,0x0,0x0,0x0,0x0 }; // 0xc0 is a placeholder, overwritten next statement + bytes[1]=insn->GetDataBits()[0]+0x10; // convert to jcc with 4-byte offset. + PlopBytes(ret,bytes, sizeof(bytes)); + PatchInstruction(ret, insn->GetTarget()); + ret+=sizeof(bytes); + return ret; + } + + case (char)0xeb: + { + // two byte JMP + char bytes[]={0xe9,0x0,0x0,0x0,0x0 }; + bytes[1]=insn->GetDataBits()[0]+0x10; // convert to jcc with 4-byte offset. + PlopBytes(ret,bytes, sizeof(bytes)); + PatchInstruction(ret, insn->GetTarget()); + ret+=sizeof(bytes); + return ret; + } + + case (char)0xe0: + case (char)0xe1: + case (char)0xe2: + case (char)0xe3: + { + // loop, loopne, loopeq, jecxz + // convert to: + // <op> +5: + // jmp fallthrough + // +5: jmp target + char bytes[]={0,0x5}; + bytes[0]=insn->GetDataBits()[0]; + PlopBytes(ret,bytes, sizeof(bytes)); + ret+=sizeof(bytes); + + PlopJump(ret); + PatchInstruction(ret, insn->GetFallthrough()); + ret+=5; + + PlopJump(ret); + PatchInstruction(ret, insn->GetTarget()); + ret+=5; + + return ret; + + } + + + default: + assert(0); + } +} -- GitLab