Intel Pinでcmptraceを作ってみる
「Intel Pinを使ってみる」では、Intel Pinをダウンロードし、付属しているコードを使ってみた。 ここでは、実際にコードを書き、cmp命令のトレースを行うコードcmptraceを作ってみる。
環境
Ubuntu 14.04.4 LTS 64bit版
$ uname -a Linux vm-ubuntu64 3.19.0-25-generic #26~14.04.1-Ubuntu SMP Fri Jul 24 21:16:20 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 14.04.4 LTS Release: 14.04 Codename: trusty $ gcc --version gcc (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4
なお、Intel Pinの実行ファイルが32 bit用バイナリであるため、ここでは「64 bitのUbuntu Linuxで32 bitの実行ファイルを動かす方法のメモ」に書いた方法で32 bit用バイナリも実行できるようにしてある。
プログラムコードを書いてみる
リファレンスを参考にしつつ、cmp命令のトレースを行うコードを書くと次のようになる。
// cmptrace.cpp #include <cstdio> #include "pin.H" VOID print_cmp_mem(VOID *ip, UINT64 * addr, ADDRINT value) { fprintf(stderr, "[%016lx] cmp 0x%lx, 0x%lx\n", (UINT64)ip, *addr, value); } VOID print_cmp_reg(VOID *ip, ADDRINT lvalue, ADDRINT rvalue) { fprintf(stderr, "[%016lx] cmp 0x%lx, 0x%lx\n", (UINT64)ip, lvalue, rvalue); } VOID Instruction(INS ins, VOID *v) { if (INS_Opcode(ins) == XED_ICLASS_CMP) { if (INS_MemoryOperandCount(ins) == 1) { if (INS_OperandIsImmediate(ins, 1)) { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)print_cmp_mem, IARG_INST_PTR, IARG_MEMORYOP_EA, 0, IARG_ADDRINT, INS_OperandImmediate(ins, 1), IARG_END); } else if (INS_OperandIsReg(ins, 0)) { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)print_cmp_mem, IARG_INST_PTR, IARG_MEMORYOP_EA, 0, IARG_REG_VALUE, INS_OperandReg(ins, 0), IARG_END); } else { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)print_cmp_mem, IARG_INST_PTR, IARG_MEMORYOP_EA, 0, IARG_REG_VALUE, INS_OperandReg(ins, 1), IARG_END); } } else { if (INS_OperandIsImmediate(ins, 1)) { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)print_cmp_reg, IARG_INST_PTR, IARG_REG_VALUE, INS_OperandReg(ins, 0), IARG_ADDRINT, INS_OperandImmediate(ins, 1), IARG_END); } else { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)print_cmp_reg, IARG_INST_PTR, IARG_REG_VALUE, INS_OperandReg(ins, 0), IARG_REG_VALUE, INS_OperandReg(ins, 1), IARG_END); } } } } int main(int argc, char *argv[]) { PIN_Init(argc, argv); INS_AddInstrumentFunction(Instruction, 0); PIN_StartProgram(); return 0; }
上のコードにおいて、Instruction関数の中では、cmp命令が取るオペランドのパターンに応じて分岐を行っている。 そして、print_cmp_mem、print_cmp_reg関数で実際にメモリやレジスタに入っている値を標準エラー出力に表示する。
コードをコンパイルして実行してみる。
$ make cmptrace.test TARGET=intel64 $ ../../../pin -t obj-intel64/cmptrace.so -- /bin/ls 2>cmptrace.log buffer_linux.cpp divide_by_zero_linux.c follow_child_tool.cpp inscount2.cpp makefile pinatrace.cpp stack-debugger-tutorial.sln thread_unix.c buffer_windows.cpp divide_by_zero_windows.c fork_app.cpp inscount_tls.cpp makefile.rules pin.log stack-debugger-tutorial.vcxproj thread_win.c cmptrace.cpp emudiv.cpp fork_jit_tool.cpp invocation.cpp malloc_mt.cpp proccount.cpp stack-debugger-tutorial.vcxproj.filters w_malloctrace.cpp cmptrace.log fibonacci.cpp imageload.cpp isampling.cpp malloctrace.cpp replacesigprobed.cpp statica.cpp countreps.cpp follow_child_app1.cpp inscount0.cpp itrace.cpp nonstatica.cpp safecopy.cpp staticcount.cpp detach.cpp follow_child_app2.cpp inscount1.cpp little_malloc.c obj-intel64 stack-debugger.cpp strace.cpp $ head cmptrace.log [00007f051296cb1f] cmp 0xe, 0x21 [00007f051296cb1f] cmp 0x4, 0x21 [00007f051296cb1f] cmp 0x6ffffef5, 0x21 [00007f051296cb2b] cmp 0x10a, 0xf [00007f051296cd35] cmp 0xeffffef5, 0xfffffffffffffffc [00007f051296cd5e] cmp 0xffffffffffffff0a, 0xb [00007f051296cece] cmp 0xa, 0xa [00007f051296cb1f] cmp 0x5, 0x21 [00007f051296cb1f] cmp 0x6, 0x21 [00007f051296cb1f] cmp 0xa, 0x21
上の結果から、命令アドレスとそのとき実際に比較されている値の組が出力されていることがわかる。
使い道について
次のようにして、何らかの要因で終了しているプログラムの終了要因の特定に利用できるかもしれない。
$ ../../../pin -t obj-intel64/cmptrace.so -- /bin/grep 2>cmptrace.log $ grep -v 00007f cmptrace.log [0000000000403ec0] cmp 0x0, 0x0 [00000000004255e1] cmp 0x1, 0x1 [0000000000419742] cmp 0x5, 0x6 [000000000040622f] cmp 0x4257b0, 0x0 [0000000000403094] cmp 0xffffffcf, 0x9 [00000000004031b0] cmp 0x7ffd784776b0, 0x7ffd784776b0 [0000000000403112] cmp 0xffffffff, 0xffffffffffffffff [00000000004031fc] cmp 0x0, 0x2 [000000000040320e] cmp 0x0, 0x0 [0000000000403ada] cmp 0x0, 0x0 [0000000000403245] cmp 0xffffffffffffffff, 0x0 [0000000000403259] cmp 0xffffffffffffffff, 0x0 [0000000000403267] cmp 0x0, 0x0 [0000000000403274] cmp 0x0, 0x0 [0000000000403281] cmp 0x0, 0x0 [00000000004032b2] cmp 0x2000, 0x8000 [00000000004032c8] cmp 0x0, 0x0 [0000000000403d5f] cmp 0x1, 0x1 Usage: /bin/grep [OPTION]... PATTERN [FILE]... Try '/bin/grep --help' for more information.