zydis/assets/PinTestTool/ZydisTestTool.cpp

287 lines
8.9 KiB
C++
Raw Permalink Normal View History

/**
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "pin.H"
#include "xed-interface.h"
#include <iostream>
#include <iomanip>
#include <fstream>
#include <set>
#include <Zydis/Zydis.h>
/* ========================================================================== */
/* TLS struct */
/* ========================================================================== */
struct ThreadData
{
CONTEXT ctx;
ZydisInstructionDecoder decoder;
ThreadData()
{
ZydisDecoderInitInstructionDecoderEx(
&decoder, ZYDIS_DISASSEMBLER_MODE_64BIT, NULL, 0
);
}
};
/* ========================================================================== */
/* Global variables */
/* ========================================================================== */
TLS_KEY tls_key;
std::ostream* out = &cerr;
PIN_LOCK unique_iforms_lock;
std::set<xed_iform_enum_t> unique_iforms;
/* ========================================================================== */
/* Tables */
/* ========================================================================== */
struct RegMapping
{
REG pin;
ZydisRegister zy;
};
RegMapping reg_mapping[] = {
// 64-bit GP register
{REG_RAX, ZYDIS_REGISTER_RAX},
{REG_RBX, ZYDIS_REGISTER_RBX},
{REG_RCX, ZYDIS_REGISTER_RCX},
{REG_RDX, ZYDIS_REGISTER_RDX},
{REG_RSP, ZYDIS_REGISTER_RSP},
{REG_RBP, ZYDIS_REGISTER_RBP},
{REG_RSI, ZYDIS_REGISTER_RSI},
{REG_RDI, ZYDIS_REGISTER_RDI},
{REG_R8, ZYDIS_REGISTER_R8 },
{REG_R9, ZYDIS_REGISTER_R9 },
{REG_R10, ZYDIS_REGISTER_R10},
{REG_R11, ZYDIS_REGISTER_R11},
{REG_R12, ZYDIS_REGISTER_R12},
{REG_R13, ZYDIS_REGISTER_R13},
{REG_R14, ZYDIS_REGISTER_R14},
{REG_R15, ZYDIS_REGISTER_R15},
// Segment registers
{REG_SEG_ES, ZYDIS_REGISTER_ES},
{REG_SEG_SS, ZYDIS_REGISTER_SS},
{REG_SEG_SS, ZYDIS_REGISTER_SS},
{REG_SEG_CS, ZYDIS_REGISTER_CS},
{REG_SEG_DS, ZYDIS_REGISTER_DS},
{REG_SEG_FS, ZYDIS_REGISTER_FS},
{REG_SEG_GS, ZYDIS_REGISTER_GS},
// Mask registers
{REG_K0, ZYDIS_REGISTER_K0},
{REG_K1, ZYDIS_REGISTER_K1},
{REG_K2, ZYDIS_REGISTER_K2},
{REG_K3, ZYDIS_REGISTER_K3},
{REG_K4, ZYDIS_REGISTER_K4},
{REG_K5, ZYDIS_REGISTER_K5},
{REG_K6, ZYDIS_REGISTER_K6},
{REG_K7, ZYDIS_REGISTER_K7},
// TODO: XMM, YMM, ZMM, ST, TR
// Special registers
{REG_MXCSR, ZYDIS_REGISTER_MXCSR},
};
/* ========================================================================== */
/* Command line switches */
/* ========================================================================== */
KNOB<string> knob_out_file(
KNOB_MODE_WRITEONCE, "pintool", "o", "", "Output file name"
);
KNOB<bool> know_unique_iform(
KNOB_MODE_WRITEONCE, "pintool", "unique_iform", "0",
"Only instrument one instruction per iform"
);
KNOB<bool> omit_op_checks(
KNOB_MODE_WRITEONCE, "pintool", "omit_op_checks", "0",
"Skip verification of operand write assumptions"
);
KNOB<bool> omit_flag_checks(
KNOB_MODE_WRITEONCE, "pintool", "omit_flag_checks", "1",
"Skip verification of flag write assumptions"
);
/* ========================================================================== */
/* Instrumentation callbacks */
/* ========================================================================== */
VOID PIN_FAST_ANALYSIS_CALL pre_ins_cb(THREADID tid, const CONTEXT* ctx)
{
ThreadData *tls = static_cast<ThreadData*>(PIN_GetThreadData(tls_key, tid));
PIN_SaveContext(ctx, &tls->ctx);
}
VOID PIN_FAST_ANALYSIS_CALL post_ins_cb(THREADID tid, const CONTEXT* post_ctx)
{
ThreadData *tls = static_cast<ThreadData*>(PIN_GetThreadData(tls_key, tid));
// Get IPs.
ADDRINT pre_ip = PIN_GetContextReg(&tls->ctx, REG_INST_PTR);
ADDRINT post_ip = PIN_GetContextReg(post_ctx, REG_INST_PTR);
// If the IP didn't change, we're probably dealing with a rep.
// Skip instruction until last execution where fallthrough kicks in.
ADDRINT ip_diff = post_ip - pre_ip;
if (!ip_diff) return;
// Disassemble previously executed instruction.
ZydisMemoryInput input;
ZydisInputInitMemoryInput(&input, (void*)pre_ip, 15);
ZydisDecoderSetInput(&tls->decoder, (ZydisCustomInput*)&input);
ZydisInstructionInfo insn_info;
ZydisStatus decode_status = ZydisDecoderDecodeNextInstruction(
&tls->decoder, &insn_info
);
// Can we decode it?
if (!ZYDIS_SUCCESS(decode_status)) {
*out << "Decoding failure" << endl;
goto error;
}
// Does the length look like what we expected?
if (insn_info.length != ip_diff) {
*out << "Instruction length mismatch (expected "
<< dec << ip_diff << ", got " << (int)insn_info.length
<< ')' << endl;
goto error;
}
// Analyze operand effects.
if (!omit_op_checks) {
for (const RegMapping* map = reg_mapping
; map < reg_mapping + sizeof reg_mapping / sizeof reg_mapping[0]
; ++map) {
ADDRINT pre_reg_val = PIN_GetContextReg(&tls->ctx, map->pin);
ADDRINT post_reg_val = PIN_GetContextReg(post_ctx, map->pin);
// Did the instruction touch this register?
if (pre_reg_val != post_reg_val) {
*out << "Reg value changed ("
<< ZydisRegisterGetString(map->zy)
<< ")!" << endl;
}
}
}
// Analyze flag effects.
if (!omit_flag_checks) {
ADDRINT prev_flags = PIN_GetContextReg(&tls->ctx, REG_GFLAGS);
ADDRINT new_flags = PIN_GetContextReg(post_ctx, REG_GFLAGS);
ADDRINT changed_flags = prev_flags ^ new_flags;
if (changed_flags) {
// TODO: implement once flag infos are available.
}
}
return;
error:
// Always print raw bytes on error.
*out << "Raw bytes: ";
for (size_t i = 0; i < 15; ++i) {
*out << setfill('0') << setw(2) << hex
<< (int)((uint8_t*)pre_ip)[i] << ' ';
}
*out << endl;
}
VOID instruction(INS ins, VOID *v)
{
if (!INS_HasFallThrough(ins)) return;
xed_decoded_inst_t* xed = INS_XedDec(ins);
xed_iform_enum_t iform = xed_decoded_inst_get_iform_enum(xed);
if (know_unique_iform.Value()) {
PIN_GetLock(&unique_iforms_lock, 0);
if (unique_iforms.find(iform) != unique_iforms.end()) {
PIN_ReleaseLock(&unique_iforms_lock);
return;
}
unique_iforms.insert(iform);
*out << iform << endl;
PIN_ReleaseLock(&unique_iforms_lock);
}
INS_InsertCall(
ins, IPOINT_BEFORE, (AFUNPTR)&pre_ins_cb,
IARG_FAST_ANALYSIS_CALL, IARG_THREAD_ID, IARG_CONST_CONTEXT,
IARG_END
);
INS_InsertCall(
ins, IPOINT_AFTER, (AFUNPTR)&post_ins_cb,
IARG_FAST_ANALYSIS_CALL, IARG_THREAD_ID, IARG_CONST_CONTEXT,
IARG_END
);
}
VOID thread_start(THREADID tid, CONTEXT *ctx, INT32 flags, VOID* v)
{
ThreadData* tls = new ThreadData;
PIN_SetThreadData(tls_key, tls, tid);
}
int main(int argc, char *argv[])
{
if (PIN_Init(argc, argv)) {
cerr << KNOB_BASE::StringKnobSummary() << endl;
return 1;
}
// Open output file.
string file_name = knob_out_file.Value();
if (!file_name.empty()) {
out = new std::ofstream(file_name.c_str());
}
// Init TLS.
tls_key = PIN_CreateThreadDataKey(0);
PIN_InitLock(&unique_iforms_lock);
// Register hooks.
PIN_AddThreadStartFunction(&thread_start, NULL);
INS_AddInstrumentFunction(&instruction, NULL);
// Start the program, never returns.
PIN_StartProgram();
return 0;
}
/* ========================================================================== */