Added tool for testing Zydis against CPU behaviour (Intel PIN)

This commit is contained in:
Joel Höner 2017-01-06 02:04:21 +01:00
parent 5b63557f3c
commit cbc9460547
4 changed files with 437 additions and 0 deletions

View File

@ -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

View File

@ -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 <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;
}
/* ========================================================================== */

View File

@ -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!
#
##############################################################

View File

@ -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: <testname>.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)