From cbc946054734c6d5d38427c174b0fa2b8d9b1f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joel=20H=C3=B6ner?= Date: Fri, 6 Jan 2017 02:04:21 +0100 Subject: [PATCH] Added tool for testing Zydis against CPU behaviour (Intel PIN) --- assets/PinTestTool/ZydisExportConfig.h | 41 ++++ assets/PinTestTool/ZydisTestTool.cpp | 286 +++++++++++++++++++++++++ assets/PinTestTool/makefile | 21 ++ assets/PinTestTool/makefile.rules | 89 ++++++++ 4 files changed, 437 insertions(+) create mode 100644 assets/PinTestTool/ZydisExportConfig.h create mode 100755 assets/PinTestTool/ZydisTestTool.cpp create mode 100644 assets/PinTestTool/makefile create mode 100644 assets/PinTestTool/makefile.rules diff --git a/assets/PinTestTool/ZydisExportConfig.h b/assets/PinTestTool/ZydisExportConfig.h new file mode 100644 index 0000000..8bc50d3 --- /dev/null +++ b/assets/PinTestTool/ZydisExportConfig.h @@ -0,0 +1,41 @@ + +#ifndef ZYDIS_EXPORT_H +#define ZYDIS_EXPORT_H + +#ifdef ZYDIS_STATIC_DEFINE +# define ZYDIS_EXPORT +# define ZYDIS_NO_EXPORT +#else +# ifndef ZYDIS_EXPORT +# ifdef Zydis_EXPORTS + /* We are building this library */ +# define ZYDIS_EXPORT +# else + /* We are using this library */ +# define ZYDIS_EXPORT +# endif +# endif + +# ifndef ZYDIS_NO_EXPORT +# define ZYDIS_NO_EXPORT +# endif +#endif + +#ifndef ZYDIS_DEPRECATED +# define ZYDIS_DEPRECATED __attribute__ ((__deprecated__)) +#endif + +#ifndef ZYDIS_DEPRECATED_EXPORT +# define ZYDIS_DEPRECATED_EXPORT ZYDIS_EXPORT ZYDIS_DEPRECATED +#endif + +#ifndef ZYDIS_DEPRECATED_NO_EXPORT +# define ZYDIS_DEPRECATED_NO_EXPORT ZYDIS_NO_EXPORT ZYDIS_DEPRECATED +#endif + +#define DEFINE_NO_DEPRECATED 0 +#if DEFINE_NO_DEPRECATED +# define ZYDIS_NO_DEPRECATED +#endif + +#endif diff --git a/assets/PinTestTool/ZydisTestTool.cpp b/assets/PinTestTool/ZydisTestTool.cpp new file mode 100755 index 0000000..07fc9b9 --- /dev/null +++ b/assets/PinTestTool/ZydisTestTool.cpp @@ -0,0 +1,286 @@ +/** + * + * + * 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 +#include +#include +#include +#include + +/* ========================================================================== */ +/* 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 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 knob_out_file( + KNOB_MODE_WRITEONCE, "pintool", "o", "", "Output file name" +); + +KNOB know_unique_iform( + KNOB_MODE_WRITEONCE, "pintool", "unique_iform", "0", + "Only instrument one instruction per iform" +); + +KNOB omit_op_checks( + KNOB_MODE_WRITEONCE, "pintool", "omit_op_checks", "0", + "Skip verification of operand write assumptions" +); + +KNOB 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(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(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; +} + +/* ========================================================================== */ diff --git a/assets/PinTestTool/makefile b/assets/PinTestTool/makefile new file mode 100644 index 0000000..da26698 --- /dev/null +++ b/assets/PinTestTool/makefile @@ -0,0 +1,21 @@ +############################################################## +# +# DO NOT EDIT THIS FILE! +# +############################################################## + +# If the tool is built out of the kit, PIN_ROOT must be specified in the make invocation and point to the kit root. +ifdef PIN_ROOT +CONFIG_ROOT := $(PIN_ROOT)/source/tools/Config +else +CONFIG_ROOT := ../Config +endif +include $(CONFIG_ROOT)/makefile.config +include makefile.rules +include $(TOOLS_ROOT)/Config/makefile.default.rules + +############################################################## +# +# DO NOT EDIT THIS FILE! +# +############################################################## diff --git a/assets/PinTestTool/makefile.rules b/assets/PinTestTool/makefile.rules new file mode 100644 index 0000000..4a1ba92 --- /dev/null +++ b/assets/PinTestTool/makefile.rules @@ -0,0 +1,89 @@ +############################################################## +# +# This file includes all the test targets as well as all the +# non-default build rules and test recipes. +# +############################################################## + + +############################################################## +# +# Test targets +# +############################################################## + +###### Place all generic definitions here ###### + +# This defines tests which run tools of the same name. This is simply for convenience to avoid +# defining the test name twice (once in TOOL_ROOTS and again in TEST_ROOTS). +# Tests defined here should not be defined in TOOL_ROOTS and TEST_ROOTS. +TEST_TOOL_ROOTS := ZydisTestTool + +OBJECT_ROOTS += \ + zydis/src/Decoder \ + zydis/src/Formatter \ + zydis/src/Input \ + zydis/src/InstructionDetails \ + zydis/src/InstructionTable \ + zydis/src/Mnemonic \ + zydis/src/Register \ + zydis/src/Utils \ + zydis/src/Zydis + +# This defines the tests to be run that were not already defined in TEST_TOOL_ROOTS. +TEST_ROOTS := + +# This defines the tools which will be run during the the tests, and were not already defined in +# TEST_TOOL_ROOTS. +TOOL_ROOTS := + +# This defines the static analysis tools which will be run during the the tests. They should not +# be defined in TEST_TOOL_ROOTS. If a test with the same name exists, it should be defined in +# TEST_ROOTS. +# Note: Static analysis tools are in fact executables linked with the Pin Static Analysis Library. +# This library provides a subset of the Pin APIs which allows the tool to perform static analysis +# of an application or dll. Pin itself is not used when this tool runs. +SA_TOOL_ROOTS := + +# This defines all the applications that will be run during the tests. +APP_ROOTS := + +# This defines any additional object files that need to be compiled. +OBJECT_ROOTS := + +# This defines any additional dlls (shared objects), other than the pintools, that need to be compiled. +DLL_ROOTS := + +# This defines any static libraries (archives), that need to be built. +LIB_ROOTS := + +###### Define the sanity subset ###### + +# This defines the list of tests that should run in sanity. It should include all the tests listed in +# TEST_TOOL_ROOTS and TEST_ROOTS excluding only unstable tests. +SANITY_SUBSET := $(TEST_TOOL_ROOTS) $(TEST_ROOTS) + + +############################################################## +# +# Test recipes +# +############################################################## + +# This section contains recipes for tests other than the default. +# See makefile.default.rules for the default test rules. +# All tests in this section should adhere to the naming convention: .test + + +############################################################## +# +# Build rules +# +############################################################## + +ADDITIONAL_INCLUDES := -Izydis/include -I. +TOOL_CFLAGS += $(ADDITIONAL_INCLUDES) +TOOL_CXXFLAGS += $(ADDITIONAL_INCLUDES) + +$(OBJDIR)ZydisTestTool$(PINTOOL_SUFFIX): $(OBJDIR)ZydisTestTool$(OBJ_SUFFIX) $(OBJDIR)zydis/src/Decoder$(OBJ_SUFFIX) $(OBJDIR)zydis/src/Formatter$(OBJ_SUFFIX) $(OBJDIR)zydis/src/Input$(OBJ_SUFFIX) $(OBJDIR)zydis/src/InstructionDetails$(OBJ_SUFFIX) $(OBJDIR)zydis/src/InstructionTable$(OBJ_SUFFIX) $(OBJDIR)zydis/src/Mnemonic$(OBJ_SUFFIX) $(OBJDIR)zydis/src/Register$(OBJ_SUFFIX) $(OBJDIR)zydis/src/Utils$(OBJ_SUFFIX) $(OBJDIR)zydis/src/Zydis$(OBJ_SUFFIX) + $(LINKER) $(TOOL_LDFLAGS) $(LINK_EXE)$@ $^ $(TOOL_LPATHS) $(TOOL_LIBS)