You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

411 lines
13 KiB

/*
* Copyright 2002-2019 Intel Corporation.
*
* This software is provided to you as Sample Source Code as defined in the accompanying
* End User License Agreement for the Intel(R) Software Development Products ("Agreement")
* section 1.L.
*
* This software and the related documents are provided as is, with no express or implied
* warranties, other than those that are expressly stated in the License.
*/
/*! @file
* This file contains a guard tool against inadvertent or adversary
* attempts to modify program text.
* Note that self-modifying code will not run under this tool, as its
* attempts to execute modified program text will be aborted.
*
* The tool works by making a backup copy of all text pages belonging to
* the application (and shared libraries) before the first instruction of
* the application is executed. Before jitting of every trace, we check
* (compare against backup copy) that none of the instructions belonging
* to the trace have been tampered with. If a trace is used out of the
* code cache, no such checking is done - the assumption is that
* only original code needs to be checked.
*
* In Fedora core2, the kernel creates some code that is not part of an
* image. To make this tool work, we would also have to find that code
*
*/
#include <iostream>
#include <string>
#include <map>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include "pin.H"
using std::flush;
using std::map;
using std::cerr;
using std::endl;
using std::string;
/* ===================================================================== */
/* Commandline Switches */
/* ===================================================================== */
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage()
{
cerr <<
"\n"
"This pin tool guards against runtime program text modifications\n"
"such as buffer overflows.\n"
"\n";
cerr << KNOB_BASE::StringKnobSummary();
cerr << endl;
return 1;
}
/* ===================================================================== */
class SANDBOX
{
private:
// types
typedef map<const char *, const char *> AddrMap;
// constants
static const ADDRINT Kilo = 1024;
static const ADDRINT Mega = Kilo * Kilo;
static const ADDRINT PageSize = 4 * Kilo;
static const ADDRINT PageMask = PageSize - 1;
// members
AddrMap _pages;
AddrMap _deferredRanges;
// functions
const char * Addr2Page(const char * addr)
{
return reinterpret_cast<const char *>(reinterpret_cast<ADDRINT>(addr) & ~PageMask);
}
ADDRINT Addr2Offset(const char * addr)
{
return reinterpret_cast<ADDRINT>(addr) & PageMask;
}
const char * AllocatePage(const char * page);
VOID ProtectPage(const char * page);
VOID RecordPage(const char * page);
VOID RecordPageRange(const char * beginPage, const char * endPage);
VOID RecordAddressRange(const char * beginAddr, const char * endAddr);
// report error - print message and terminate the application
VOID Error(string msg)
{
static string errorPrefix = "ERROR:\n";
PrintMessage(errorPrefix + msg);
PIN_ExitProcess(1);
}
// report warning - print message and continue running
VOID Warning(string msg)
{
static string warningPrefix = "WARNING:\n";
PrintMessage(warningPrefix + msg);
}
// print a message
VOID PrintMessage(string msg);
public:
VOID RecordIns(INS ins);
VOID CheckAddressRange(const char * beginAddr, const char * endAddr);
VOID CheckAddressRangeDeferred(const char * beginAddr, const char * endAddr)
{
_deferredRanges[beginAddr] = endAddr;
}
VOID HandlePendingChecks(const char * beginAddr, const char * endAddr);
VOID HandlePendingChecks();
};
static SANDBOX sandbox;
VOID SANDBOX::PrintMessage(string msg)
{
if (msg.length() > 0 && msg[0] != '\n')
{
msg = "\n" + msg;
}
string::size_type pos = msg.length() > 1 ? msg.length() - 2 : 0;
// prefix every line
for (pos = msg.find_last_of('\n', pos);
pos != string::npos;
pos = (pos == 0 ? string::npos : msg.find_last_of('\n', pos-1)))
{
if (pos == msg.length() - 1)
{
msg += "Fence: \n";
}
else
{
msg.insert(pos + 1, "Fence: ");
}
}
cerr << msg << endl << flush;
fflush(NULL);
}
// allocate a new page
const char * SANDBOX::AllocatePage(const char * page)
{
const char * pageFrameStart = reinterpret_cast<const char *>
(mmap(0, PageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
if (pageFrameStart == MAP_FAILED)
{
Error("Can't get page frame for page " + hexstr(page) + "\n" + strerror(errno));
}
_pages[page] = pageFrameStart;
return pageFrameStart;
}
// protect a page read-only
VOID SANDBOX::ProtectPage(const char * page)
{
int result = mprotect(const_cast<char *>(page), PageSize, PROT_READ);
if (result != 0)
{
Error("Can't read-protect page " + hexstr(page) + "\n" + strerror(errno));
}
}
// record one page
VOID SANDBOX::RecordPage(const char * page)
{
const char * pageFrameStart = _pages[page];
if (pageFrameStart != NULL)
{
return; // already recorded
}
pageFrameStart = AllocatePage(page);
memcpy(const_cast<char *>(pageFrameStart), page, PageSize);
ProtectPage(pageFrameStart);
}
// record pages in given range
VOID SANDBOX::RecordPageRange(const char * beginPage, const char * endPage)
{
for (const char * page = beginPage; page <= endPage; page += PageSize)
{
RecordPage(page);
}
}
// record bytes in given address range
VOID SANDBOX::RecordAddressRange(const char * beginAddr, const char * endAddr)
{
const char * beginPage = Addr2Page(beginAddr);
const char * endPage = Addr2Page(endAddr);
RecordPageRange(beginPage, endPage);
}
// record the page(s) occupied by the instruction
VOID SANDBOX::RecordIns(INS ins)
{
const ADDRINT beginAddr = INS_Address(ins);
const ADDRINT endAddr = beginAddr + INS_Size(ins) - 1;
RecordAddressRange(reinterpret_cast<const char *>(beginAddr), reinterpret_cast<const char *>(endAddr));
}
/* ===================================================================== */
// check bytes in given address range
VOID SANDBOX::CheckAddressRange(const char * beginAddr, const char * endAddr)
{
const char * beginPage = Addr2Page(beginAddr);
const char * endPage = Addr2Page(endAddr);
for (const char * page = beginPage; page <= endPage; page += PageSize)
{
const char * pageEnd = page + PageSize - 1;
const char * beginCheckAddr = beginAddr > page ? beginAddr : page;
const char * endCheckAddr = endAddr < pageEnd ? endAddr : pageEnd;
size_t size = endCheckAddr - beginCheckAddr + 1;
const char * pageFrameStart = _pages[page];
if (pageFrameStart == NULL)
{
// note: this might also trigger if PIN has not found all the code
// of a routine, so try to distinguish between the two:
IMG img = IMG_FindByAddress(reinterpret_cast<ADDRINT>(beginAddr));
if (IMG_Valid(img))
{
bool AddressInCodeSection = false;
for (SEC sec = IMG_SecHead(img); SEC_Valid(sec); sec = SEC_Next(sec))
{
ADDRINT secBegin = SEC_Address(sec);
ADDRINT secEnd = secBegin + SEC_Size(sec);
if (reinterpret_cast<ADDRINT>(endCheckAddr) >= secBegin &&
reinterpret_cast<ADDRINT>(endCheckAddr) <= secEnd)
{
if (SEC_TYPE_EXEC == SEC_Type(sec))
{
// This code is part of a code section in a known image but Pin failed to parse it
// into a routine.
AddressInCodeSection = true;
break;
}
}
}
if (AddressInCodeSection)
{
// The current block is part of a code section in a known image but Pin did not find a routine
// it belongs to in the image. We ignore the block on account that Pin simply failed to parse
// the image correctly.
Warning("Instruction address range " + hexstr(beginCheckAddr) +
" - " + hexstr(endCheckAddr) + " was not found\n"
"during initial code discovery.\n" +
"\n"
"However, it is part of a code section in a known image.\n"
"It's likely that Pin simply failed to parse the image correctly.\n");
continue;
}
}
// The code block is either not part of an image or does not belong to a code section within an image.
Warning("Instruction address range " + hexstr(beginCheckAddr) +
" - " + hexstr(endCheckAddr) + " was not found\n"
"during initial code discovery.\n" +
"\n"
"The application is trying to execute instructions which were not in\n"
"the original application.\n"
"This might be due to a buffer overflow.\n");
}
else
{
const char * pageFrameBeginCheckAddr = pageFrameStart + Addr2Offset(beginCheckAddr);
if (memcmp(beginCheckAddr, pageFrameBeginCheckAddr, size) != 0)
{
Warning("Instruction address range " + hexstr(beginCheckAddr) +
" - " + hexstr(endCheckAddr) + " is corrupted.\n" +
"\n"
"The application code has been corrupted during execution.\n"
"This might be due to a buffer overflow.\n");
}
}
}
}
VOID SANDBOX::HandlePendingChecks(const char * beginAddr, const char * endAddr)
{
AddrMap::iterator it = _deferredRanges.begin();
AddrMap::iterator end = _deferredRanges.end();
while (it != end)
{
AddrMap::iterator tempit = it;
++it;
if (tempit->first >= beginAddr && tempit->second <= endAddr)
{
CheckAddressRange(tempit->first, tempit->second);
_deferredRanges.erase(tempit);
}
}
}
VOID SANDBOX::HandlePendingChecks()
{
// No need to erase iterators from the list here because this function is only called once, just before exit.
AddrMap::const_iterator it = _deferredRanges.begin();
AddrMap::const_iterator end = _deferredRanges.end();
for (; it != end; ++it)
{
CheckAddressRange(it->first, it->second);
}
}
/* ===================================================================== */
VOID Trace(TRACE trace, VOID *v)
{
const INS beginIns = BBL_InsHead(TRACE_BblHead(trace));
const INS endIns = BBL_InsTail(TRACE_BblTail(trace));
const ADDRINT beginAddr = INS_Address(beginIns);
const ADDRINT endAddr = INS_Address(endIns) + INS_Size(endIns) - 1;
// Handle the case of ifuncs:
// Some code of an image may be executed while loading the image in order to resolve the ifuncs.
// This happens before Pin gives the image load callback, therefore the tool still considers this
// code undiscovered.
IMG img = IMG_FindByAddress(beginAddr);
if (!IMG_Valid(img))
{
sandbox.CheckAddressRangeDeferred(reinterpret_cast<const char *>(beginAddr),
reinterpret_cast<const char *>(endAddr));
}
else
{
sandbox.CheckAddressRange(reinterpret_cast<const char *>(beginAddr), reinterpret_cast<const char *>(endAddr));
}
}
/* ===================================================================== */
VOID Image(IMG img, VOID * v)
{
for (SEC sec = IMG_SecHead(img); SEC_Valid(sec); sec = SEC_Next(sec))
{
for (RTN rtn = SEC_RtnHead(sec); RTN_Valid(rtn); rtn = RTN_Next(rtn))
{
RTN_Open(rtn);
for (INS ins = RTN_InsHead(rtn); INS_Valid(ins); ins = INS_Next(ins))
{
sandbox.RecordIns(ins);
}
RTN_Close(rtn);
}
}
sandbox.HandlePendingChecks(reinterpret_cast<const char *>(IMG_LowAddress(img)),
reinterpret_cast<const char *>(IMG_HighAddress(img)));
}
VOID Fini(INT32 code, VOID *v)
{
sandbox.HandlePendingChecks();
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
int main(int argc, CHAR *argv[])
{
PIN_InitSymbols();
if (PIN_Init(argc,argv))
{
return Usage();
}
TRACE_AddInstrumentFunction(Trace, 0);
IMG_AddInstrumentFunction(Image, 0);
PIN_AddFiniFunction(Fini, 0);
PIN_StartProgram(); // Never returns
return 0;
}