使用 Intel Pin 时跟踪不匹配的 CALL 和 RET 指令数

Posted

技术标签:

【中文标题】使用 Intel Pin 时跟踪不匹配的 CALL 和 RET 指令数【英文标题】:Number of CALL and RET instructions tracking mismatch while using Intel Pin 【发布时间】:2015-06-25 01:45:55 【问题描述】:

我正在尝试使用 Intel Pintool 来监控 x86-64 机器 (Mac Pro) 上的 CALLRET 指令。我将IARG_INST_PTR(下面提到)传递给docount函数并使用指令指针通过检查操作码来推断指令(CALL是0xe8,RET是来自Intel x86-64 manual的0xc3。但是,似乎此检查并不完全准确,因为对于任何使用此逻辑检测的给定二进制文件,我注意到 RET 的数量比 CALL 的数量要多。

INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_CONTEXT,
    IARG_INST_PTR, IARG_END);

谁能指点一下我做错了什么?

我从/tools/ManualExamples/inscount0.cpp借来了模板。要找到它,请搜索文件名here。

【问题讨论】:

编译后的代码真的有可能比call拥有更多的ret吗?如果 (a) 返回 1;否则,如果 (b) 返回 2;否则返回 3;内部函数可能会生成多个ret 除非代码有问题,否则不应该有任何不匹配。这意味着我的 CALL/RET 检测逻辑一定是有缺陷的。我希望得到一些指示来纠正同样的问题。 @SeverinPappadeux - 多个返回语句不会导致单独的 RET 指令,而是会导致 JMP 指令到 RET,并在 RAX 寄存器中具有适当的返回值。 嗯,执行时间会更长,因为它是 JMP+RET 而不是 RET。但是代码大小会更大。我猜想在某些优化设置下,编译器可能更喜欢到处发出 RET 而不是 JMP+RET @user1983710 我之前的例子是完全错误的。我对其进行了重新设计,CALL 和 RET 之间的差异很大,CALL 比 RET 多(在 Windows 上的简单控制台程序上测试,即ipconfig.exe):CALL: 176298RET: 170374。我仍在试图找出原因(尽管它可能与系统内部有关)。我会尝试构建一个 pintool 来记录可能不匹配的 CALL/RET 对。你有我的 +1! 【参考方案1】:

call 和 ret 指令之间并不总是匹配,因为函数可能会被异常、goto-like 语句、longjumps、信号等中断......所以如果你想重新协调调用和 ret,你可以想要考虑所有这些。

这已经讨论过好几次了,尤其是here

【讨论】:

【参考方案2】:

有various versions of CALLs with different opcodes,所以你不能只检查0xE8。完整列表可在 Intel 手册的调用程序部分中找到:

操作码指令说明 E8 cw CALL rel16 调用近、相对、相对于下一条指令的位移 E8 cd CALL rel32 调用近、相对、相对于下一条指令的位移 32 位位移符号在 64 位模式下扩展到 64 位。 FF /2 CALL r/m16 调用近,绝对间接,在 r/m16 中给出的地址。 FF /2 CALL r/m32 近调用,绝对间接,在 r/m32 中给出的地址。 FF /2 CALL r/m64 近调用,绝对间接,在 r/m64 中给出的地址。 9A cd CALL ptr16:16 调用远,绝对,操作数中给出的地址。 9A cp CALL ptr16:32 调用远,绝对,操作数中给出的地址。 FF /3 CALL m16:16 调用远,m16:16 中给出的绝对间接地址。 在 32 位模式下:如果选择器指向一个门,那么 RIP = 取自门的 32 位零扩展位移;别的 RIP = 距离远指针的零扩展 16 位偏移 说明中引用。 FF /3 CALL m16:32 在 64 位模式下:如果选择器指向一个门,那么 RIP = 取自门的 64 位位移;别的 RIP = 距离远指针的零扩展 32 位偏移 说明中引用。 REX.W FF /3 CALL m16:64 在 64 位模式下:如果选择器指向一个门,那么 RIP = 取自门的 64 位位移;别的 RIP = 距远指针的 64 位偏移量 说明中引用。

RET

操作码* 指令说明 C3 RET 接近返回调用过程。 CB RET 返回调用过程。 C2 iw RET imm16 接近返回调用过程并从堆栈中弹出 imm16 字节。 CA iw RET imm16 Far 返回调用过程并从堆栈中弹出 imm16 字节。

请注意,上面包含相同操作码的行仅适用于不同的模式(16/32/64 位)

【讨论】:

嗨,我试过这些(我不确定如何匹配 REX.W + FF 指令),但现在 - 调用计数为 68183,RET 计数为 44971 bool isRet(unsigned int valAtInstrPtr)无符号整数 foo = (valAtInstrPtr & 0xFF);返回 foo == 0xc3 ||富== 0xcb ||富== 0xc2 ||富== 0xca; bool isCall(unsigned int valAtInstrPtr) unsigned int foo = (valAtInstrPtr & 0xFF);返回 foo == 0xe8 ||富== 0xff ||富== 0x9a; 把代码放到backticks 或者几乎没有人能读到你写的东西。 REX prefix 是 0x4X。 0xFF 不是前缀,而是表示 2 字节操作码,因此您需要检查下一个字节。但是为什么不将二进制/十六进制输出输入反汇编程序并计数呢?如果你想自己解析它,你必须了解 x86_64 指令格式。它们不仅仅是一个操作码字节,然后是许多参数 你应该区分远调用和近调用;主流操作系统的普通用户空间代码不会有任何远调用。

以上是关于使用 Intel Pin 时跟踪不匹配的 CALL 和 RET 指令数的主要内容,如果未能解决你的问题,请参考以下文章

Intel Pin 获取函数参数编号

目标代码重定位和 Intel Pin 交互

Intel pin:检测运行过程

Intel Pin 3.0 不识别 MPX 指令?

Intel Pin:测量空仪器开销

Intel Pin Tool 权限错误