Skip to content
Snippets Groups Projects
unpin.cpp 9.64 KiB
Newer Older
jdh8d's avatar
jdh8d committed
/***************************************************************************
 * Copyright (c)  2014  Zephyr Software LLC. All rights reserved.
 *
 * This software is furnished under a license and/or other restrictive
 * terms and may be used and copied only in accordance with such terms
 * and the inclusion of the above copyright notice. This software or
 * any other copies thereof may not be provided or otherwise made
 * available to any other person without the express written consent
 * of an authorized representative of Zephyr Software LCC. Title to,
 * ownership of, and all rights in the software is retained by
 * Zephyr Software LCC.
 *
 * Zephyr Software LLC. Proprietary Information
 *
 * Unless otherwise specified, the information contained in this
 * directory, following this legend, and/or referenced herein is
 * Zephyr Software LLC. (Zephyr) Proprietary Information.
 *
 * CONTACT
 *
 * For technical assistance, contact Zephyr Software LCC. at:
 *
 *
 * Zephyr Software, LLC
 * 2040 Tremont Rd
 * Charlottesville, VA 22911
 *
 * E-mail: jwd@zephyr-software.com
 **************************************************************************/


#include <zipr_sdk.h>
#include <string>
#include <algorithm>
#include "utils.hpp"
#include "Rewrite_Utility.hpp"
#include "unpin.h"

using namespace libIRDB;
using namespace std;
using namespace Zipr_SDK;
using namespace ELFIO;

static bool has_cfi_reloc(Instruction_t* insn)
{
	for(
		RelocationSet_t::iterator rit2=insn->GetRelocations().begin(); 
		rit2!=insn->GetRelocations().end();
		rit2++
	   )
	{

		/* check for a nonce relocation */
		if ( (*rit2) -> GetType().find("cfi_nonce") != string::npos )
		{
			return true;
		}
	}
	return false;
}

static bool should_cfi_pin(Instruction_t* insn)
{
	// add command line option that:
	// 	1) return false if !has_cfi_reloc(insn)
	// 	2) return true if option is on.
	return false;
}


jdh8d's avatar
jdh8d committed
void Unpin_t::DoUnpin()
{
	DoUnpinForScoops();
	DoUnpinForFixedCalls();
}


// scan instructions and process instruction relocs that can be unpinned.
void Unpin_t::DoUnpinForFixedCalls()
{
	int unpins=0;
	int missed_unpins=0;

	for(
		InstructionSet_t::iterator it=zo->GetFileIR()->GetInstructions().begin();
		it!=zo->GetFileIR()->GetInstructions().end();
		++it
	   )
	{
		Instruction_t* from_insn=*it;

		for(
			RelocationSet_t::iterator rit=from_insn->GetRelocations().begin(); 
			rit!=from_insn->GetRelocations().end();
			rit++
		   )
		{
			Relocation_t* reloc=*rit;

			
			// this probably won't work on shared objects.
			// complicated with the push64-reloc plugin also rewriting these things?
			if(reloc->GetType()==string("32-bit") || reloc->GetType()==string("push64"))
			{
				// skip if there's no WRT, that means it's unpinned for something besides a fixed call.
				if(reloc->GetWRT()==NULL)
					continue;

				// getWRT returns an BaseObj, but this reloc type expects an instruction
				// safe cast and check.
				Instruction_t* wrt_insn=dynamic_cast<Instruction_t*>(reloc->GetWRT());
				assert(wrt_insn);
				if(should_cfi_pin(wrt_insn))
					continue;


				if(wrt_insn->GetIndirectBranchTargetAddress())
				{
					cout<<"Unpin::Found "<<reloc->GetType()<<" relocation for pinned insn at "<<hex<<
						wrt_insn->GetIndirectBranchTargetAddress()->GetVirtualOffset()<<endl;
				}
				else
				{
					cout<<"Unpin::Warn: unpin found non-IBTA to unpin.  probably it's unpinned twice.  continuing anyhow."<<endl;
				}
	
				unpins++;
				wrt_insn->SetIndirectBranchTargetAddress(NULL);

				PlacementQueue_t* pq=zo->GetPlacementQueue();
				assert(pq);

				// create a new dollop for the unpinned IBT
				// and add it to the placement queue.
				Dollop_t *newDoll=zo->GetDollopManager()->AddNewDollops(wrt_insn);
				pq->insert(std::pair<Dollop_t*,RangeAddress_t>(newDoll, 0));
			}
		}
	}

	cout<<"#ATTRIBUTE insn_unpin_total_unpins="<<dec<<unpins<<endl;
	cout<<"#ATTRIBUTE insn_unpin_missed_unpins="<<dec<<missed_unpins<<endl;
}


void Unpin_t::DoUnpinForScoops()
jdh8d's avatar
jdh8d committed
{
jdh8d's avatar
jdh8d committed
	int unpins=0;
	int missed_unpins=0;

jdh8d's avatar
jdh8d committed
	for(
		DataScoopSet_t::iterator it=zo->GetFileIR()->GetDataScoops().begin();
		it!=zo->GetFileIR()->GetDataScoops().end();
		++it
	   )
	{
		DataScoop_t* scoop=*it;

		for(
			RelocationSet_t::iterator rit=scoop->GetRelocations().begin(); 
			rit!=scoop->GetRelocations().end();
			rit++
		   )
		{
			Relocation_t* reloc=*rit;

			
			if(reloc->GetType()==string("data_to_insn_ptr"))
			{
				Instruction_t* insn=dynamic_cast<Instruction_t*>(reloc->GetWRT());
				// getWRT returns an BaseObj, but this reloc type expects an instruction
				// safe cast and check.
				assert(insn);

jdh8d's avatar
jdh8d committed

				if(insn->GetIndirectBranchTargetAddress())
				{
					cout<<"Unpin::Found data_to_insn_ptr relocation for pinned insn:"<<dec<<insn->GetBaseID()<<" at "<<hex<<
jdh8d's avatar
jdh8d committed
						insn->GetIndirectBranchTargetAddress()->GetVirtualOffset()<<endl;
				}
				else
				{
					cout<<"Unpin::Warn: unpin found non-IBTA to unpin for insn:"<<dec<<insn->GetBaseID()<<".  probably it's unpinned twice.  continuing anyhow."<<endl;
jdh8d's avatar
jdh8d committed
				}
jdh8d's avatar
jdh8d committed
	
				int found=should_cfi_pin(insn);
jdh8d's avatar
jdh8d committed

				/* don't unpin if we found one */
				if(found)
				{
					cout<<"Unpin::Not unpinning because CFI is requesting a nonce."<<endl;
jdh8d's avatar
jdh8d committed
					missed_unpins++;
jdh8d's avatar
jdh8d committed
				}
				else
				{
jdh8d's avatar
jdh8d committed
					unpins++;
jdh8d's avatar
jdh8d committed
					insn->SetIndirectBranchTargetAddress(NULL);

					PlacementQueue_t* pq=zo->GetPlacementQueue();
					assert(pq);

					// create a new dollop for the unpinned IBT
					// and add it to the placement queue.
					Dollop_t *newDoll=zo->GetDollopManager()->AddNewDollops(insn);
					pq->insert(std::pair<Dollop_t*,RangeAddress_t>(newDoll, 0));
				}
			}
		}
	}
jdh8d's avatar
jdh8d committed

	cout<<"#ATTRIBUTE scoop_unpin_total_unpins="<<dec<<unpins<<endl;
	cout<<"#ATTRIBUTE scoop_unpin_missed_unpins="<<dec<<missed_unpins<<endl;
jdh8d's avatar
jdh8d committed
}

void Unpin_t::DoUpdate()
{
	DoUpdateForScoops();
	DoUpdateForFixedCalls();
}


// scan for instructions that were placed, and now need an update.
void Unpin_t::DoUpdateForFixedCalls()
{
	int unpins=0;
	int missed_unpins=0;

	for(
		InstructionSet_t::iterator it=zo->GetFileIR()->GetInstructions().begin();
		it!=zo->GetFileIR()->GetInstructions().end();
		++it
	   )
	{
		Instruction_t* from_insn=*it;

		for(
			RelocationSet_t::iterator rit=from_insn->GetRelocations().begin(); 
			rit!=from_insn->GetRelocations().end();
			rit++
		   )
		{
			Relocation_t* reloc=*rit;

			
			// this probably won't work on shared objects.
			// complicated with the push64-reloc plugin also rewriting these things?
			if(reloc->GetType()==string("32-bit") || reloc->GetType()==string("push64"))
			{
				// skip if there's no WRT, that means it's unpinned for something besides a fixed call.
				if(reloc->GetWRT()==NULL)
					continue;

				// getWRT returns an BaseObj, but this reloc type expects an instruction
				// safe cast and check.
				Instruction_t* wrt_insn=dynamic_cast<Instruction_t*>(reloc->GetWRT());
				assert(wrt_insn);
				if(should_cfi_pin(wrt_insn)) 
					continue;

				Zipr_SDK::InstructionLocationMap_t &locMap=*(zo->GetLocationMap());
				libIRDB::virtual_offset_t wrt_insn_location=locMap[wrt_insn];
				libIRDB::virtual_offset_t from_insn_location=locMap[from_insn];

				// expecting a 32-bit push, length=5
				assert(from_insn->GetDataBits()[0]==0x68);
				assert(from_insn->GetDataBits().size()==5);
				// down and upcast to ensure we fit in 31-bits.
				assert(wrt_insn_location == (libIRDB::virtual_offset_t)(int)wrt_insn_location);
				assert(sizeof(int)==4); // paranoid.

				unsigned char newpush[5];
				newpush[0]=0x68;
				*(int*)&newpush[1]=(int)wrt_insn_location;

				cout<<"Unpin::Updating insn:"
					<<dec<<from_insn->GetBaseID()<<"@"<<hex<<from_insn_location<<" to point at "
					<<dec<<wrt_insn ->GetBaseID()<<"@"<<hex<<wrt_insn_location <<endl;

				MemorySpace_t &ms=*zo->GetMemorySpace();
				for(unsigned int i=0;i<from_insn->GetDataBits().size();i++)
				{ 
					unsigned char newbyte=newpush[i];
					unsigned char oldbyte= ms[from_insn_location+i];
					ms[from_insn_location+i]=newbyte;

					//cout<<"Updating push["<<i<<"] from "<<hex<<oldbyte<<" to "<<newbyte<<endl;
				}

			}
		}
	}

}

void Unpin_t::DoUpdateForScoops()
jdh8d's avatar
jdh8d committed
{
	for(
		DataScoopSet_t::iterator it=zo->GetFileIR()->GetDataScoops().begin();
		it!=zo->GetFileIR()->GetDataScoops().end();
		++it
	   )
	{
		DataScoop_t* scoop=*it;
		string scoop_contents=scoop->GetContents();

		for(
			RelocationSet_t::iterator rit=scoop->GetRelocations().begin(); 
			rit!=scoop->GetRelocations().end();
			rit++
		   )
		{
			Relocation_t* reloc=*rit;

			if(reloc->GetType()==string("data_to_insn_ptr"))
			{
				virtual_offset_t reloff=reloc->GetOffset();
				Instruction_t* insn=dynamic_cast<Instruction_t*>(reloc->GetWRT());
				// getWRT returns an BaseObj, but this reloc type expects an instruction
				// safe cast and check.
				assert(insn);
				Zipr_SDK::InstructionLocationMap_t &locMap=*(zo->GetLocationMap());
				libIRDB::virtual_offset_t newLoc=locMap[insn];

				cout<<"Unpin::Unpinned data_to_insn_ptr insn moved to "<<hex<<newLoc<<endl;

				int found=should_cfi_pin(insn);
jdh8d's avatar
jdh8d committed

				/* don't unpin if we found one */
				if(found)
				{
					cout<<"Unpin::Skipping update because CFI is requesting a nonce."<<endl;
				}
				else
				{
					// determine how big the ptr is.
					int ptrsize=zo->GetFileIR()->GetArchitectureBitWidth()/8;
					char addr[ptrsize];
		
					// convert it to bytes.
					switch(ptrsize)
					{
						case 4:
							*(int*)addr=newLoc;
							break;
						case 8:
							*(long long*)addr=newLoc;
							break;
						default:
							assert(0);
					}
					// copy in new ptr.
					for(int i=0;i<ptrsize;i++)
						scoop_contents[reloff+i]=addr[i];
				}
				
			}
		}
		scoop->SetContents(scoop_contents);
	}
}



extern "C" 
Zipr_SDK::ZiprPluginInterface_t* GetPluginInterface(
	Zipr_SDK::Zipr_t* zipr_object)
{
	return new Unpin_t(zipr_object);
}