mirror of https://github.com/x64dbg/zydis
Added tool for testing Zydis against CPU behaviour (Intel PIN)
This commit is contained in:
parent
5b63557f3c
commit
cbc9460547
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
|
@ -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!
|
||||
#
|
||||
##############################################################
|
|
@ -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)
|
Loading…
Reference in New Issue