Intel Pin-JIT模式和Probe模式下库函数的替换

这篇文章主要介绍一下Intel Pin在JIT模式和Probe模式下对库换数的替换,以及实现中有哪写需要注意的地方。

JIT模式就是对插桩的代码进行即时编译,然后缓存起来使用

Probe模式就是在要插桩的函数入口前面插入一条跳转指令,跳转到新的替换函数处执行,不在原来执行代码上进行修改

具体的大家可以参考Pin官方手册

https://software.intel.com/sites/landingpage/pintool/docs/65163/Pin/html/index.html

下面进入正文

在编写程序过程中,我们有时候需要需要关注一下某些库函数的调用,方便我们我们监测。比如我们想关注应用程序中共享变量的情况,共享变量主要就是静态变量,全局变量,以及堆变量(new或是malloc或是realloc或是calloc),以堆变量为例,我们就必须要监测malloc等函数,GCC中提供了LD_PRELOAD预加载库技术,通过这种技术,我们可以对一些库函数进行重写,然后在链接的时候首先加载我们自己写的动态链接库,后面加载正常的库函数时候由于符号表中已经有了我们重写的函数,那么就不会重定位到真正的库函数的位置。这篇文章主要使用Intel的Pin 二进制动态插桩框架来达到同样的效果,如果对Pin不了解的同学建议去看一下上面给出的手册链接。

使用JIT进行的malloc函数替换,根据代码解释

#include "pin.H"
#include <string.h>
#include <iostream>
using namespace std;


void * MallocWrapper( CONTEXT * ctxt, AFUNPTR pf_malloc, size_t size)
{  // Simulate out-of-memory every so often
    void * res;
    cout<<"=====Enter the MallocWrapper=======\n";
    cout<<"Malloc Size:"<<size<<endl;
    PIN_CallApplicationFunction(ctxt, PIN_ThreadId(),
        CALLINGSTD_DEFAULT,  pf_malloc,
        PIN_PARG(void *),  &res, PIN_PARG(size_t), size, PIN_PARG_END());
    return res;  }
 
VOID ImageLoad(IMG img, VOID *v) { 
    // Pin callback. Registered by IMG_AddInstrumentFunction
    if (strstr(IMG_Name(img).c_str(), "libc.so") ||
         strstr(IMG_Name(img).c_str(), "MSVCR80") || 
         strstr(IMG_Name(img).c_str(), "MSVCR90")) {

        RTN mallocRtn = RTN_FindByName(img, "malloc");
        //build a function proto
        PROTO protoMalloc = PROTO_Allocate( PIN_PARG(void *), CALLINGSTD_DEFAULT,
        "malloc", PIN_PARG(size_t), PIN_PARG_END() );
        //replace the application function
        RTN_ReplaceSignature(mallocRtn, AFUNPTR(MallocWrapper),
        IARG_PROTOTYPE, protoMalloc,IARG_CONST_CONTEXT,
        IARG_ORIG_FUNCPTR,IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
        IARG_END);
    } 
}

int main(int argc, CHAR *argv[]) 
{
    PIN_InitSymbols();
    PIN_Init(argc,argv);
    IMG_AddInstrumentFunction(ImageLoad, 0);
    PIN_StartProgram();                
}

对于ImageLoad函数

首先根据malloc函数名找到malloc函数所在的库以及对应的Routine,RTN_FindByName主要干的就是这个

其次,对插桩的函数重新生成原型,替换原有的malloc函数原型

PROTO_Allocate就是生成一个原始函数原型,PIN_PARG(void *)表示的是返回类型,malloc函数的返回类型为void *,CALLINGSTD_DEFAULT表示编译这个函数的调用标准(一般不用改变),PIN_PARG(size_t)表示就是malloc中的传递的参数,这里参数传递的是类型就是size_t,最后必须加上IARG_END表示截止,这样返回的就是一个指向函数原型的指针。

RTN_ReplaceSignature表示就是在JIT模式对函数进行替换,mallocRtn表示就是malloc函数的Routine,AFUNPTR(MallocWrapper)表示就是替换函数的指针,IARG_PROTOTYPE允许你定义原始函数原型,因此后面加上我们之前定义好的原始函数原型protoMalloc,IARG_CONST_CONTEXT就是我们包装函数需要传递的第一个参数,IARG_ORIG_FUNCPTR表示原始函数指针(可以通过这个调用原始函数),最后一个参数是我们传递给原始malloc函数的,因此使用IARG_FUNCARG_ENTRYPOINT_VALUE,0表示传递给原始malloc的第一个参数,IARG_END表示截止。

再来看一下我们的包装函数MallocWrapper

我们首先打印一些信息,然后通过PIN_CallApplicationFunction调用我们原始函数,参数中需要注意的就是pf_malloc就是我们要调用的原始函数指针, PIN_PARG(void *),  &res,表示原始函数返回类型和值,PIN_PARG(size_t), size表示原始函数的参数类型和值,这样的话我们就能够调用原始函数了。

大家编译成Pin Tool后,可以用小程序测试(也可从这里参考我的代码)

结果类似如下:

=====Enter the MallocWrapper======= Malloc Size:5 =====Enter the MallocWrapper======= Malloc Size:5 =====Enter the MallocWrapper======= Malloc Size:120 =====Enter the MallocWrapper======= Malloc Size:12 =====Enter the MallocWrapper======= Malloc Size:784 使用Probe进行malloc函数替换,代码如下:

typedef VOID * ( *PF_MALLOC )( size_t );

void * MallocWrapper(PF_MALLOC pf_malloc,size_t size)
{  // Simulate out-of-memory every so often
    cout<<"=====Enter the MallocWrapper=======\n";
    cout<<"Malloc Size:"<<size<<endl;
    return pf_malloc(size);
}
 
// Pin calls this function every time a new img is loaded.
// It is best to do probe replacement when the image is loaded,
// because only one thread knows about the image at this time.
VOID ImageLoad( IMG img, VOID *v )
{
    // See if malloc() is present in the image.  If so, replace it.
    RTN rtn = RTN_FindByName( img, "malloc" );
    
    if (RTN_Valid(rtn))
    {
        // Define a function prototype of the orig func
        PROTO proto_malloc = PROTO_Allocate( PIN_PARG(void *), 
                           CALLINGSTD_DEFAULT, "malloc", 
                           PIN_PARG(int), PIN_PARG_END() );
        
        // Replace the application routine with the replacement function.
        RTN_ReplaceSignatureProbed(rtn, AFUNPTR(MallocWrapper),
                                   IARG_PROTOTYPE, proto_malloc,
                                   IARG_ORIG_FUNCPTR,
                                   IARG_FUNCARG_ENTRYPOINT_VALUE, 0,
                                   IARG_END);

        // Free the function prototype.
        PROTO_Free( proto_malloc );
    }
}
 
int main(int argc, CHAR *argv[]) {
    PIN_InitSymbols();     
    PIN_Init(argc,argv);
    IMG_AddInstrumentFunction(ImageLoad, 0);
    PIN_StartProgramProbed();                
}

和之前的JIT模式很类似,只是这里的话,我们可以直接通过原始函数指针来调用(因为在Probe模式中不支持CONTEXT),还有就是替换函数变成了RTN_ReplaceSignatureProbed,程序启动函数变成了PIN_StartProgramProbed。

同时还需要注意的是,如果以Probe方式启动的话,那么系统需要支持特定的库,具体参考这里

以Probe方式运行效率更高,但是有很多Pin的一些功能支持Probe,如果不是特别在意效率的话,建议大家就是用JIT模式,使用简单并且功能齐全。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java一日一条

50个常见的 Java 错误及避免方法(第三部分)

当我们尝试调用带有错误参数的Java代码时,通常会产生此Java错误消息(@ghacksnews):

16130
来自专栏逸鹏说道

我这么玩Web Api(二)

数据验证,全局数据验证与单元测试 目录 一、模型状态 - ModelState 二、数据注解 - Data Annotations 三、自定义数据注解 四、全局...

54760
来自专栏刘望舒

LeakCanary看这一篇文章就够了

LeakCanary是Square公司基于MAT开源的一个内存泄漏检测工具,在发生内存泄漏的时候LeakCanary会自动显示泄漏信息。

3.3K50
来自专栏屈定‘s Blog

造轮子--Excel报表工具

由于公司内部之前对于excel封装操作并不是很方便,而且对于特殊的需求不是很容易满足,这个月的任务是迁移部分业务小报表顺便重构下,因此这里造个轮子,便于导入和导...

22530
来自专栏大内老A

EnterLib PIAB又一个BUG?

在《这是EnterLib PIAB的BUG吗?》一文中我们讨论了PIAB关于抽象基类的BUG,今天又发现了一个新的问题。问题的起因源于《IoC+AOP的简单实现...

18990
来自专栏码字搬砖

spark连接kafka工具类

版权声明:本文为博主原创,欢迎转载,转载请标明出处 Blog Address:http://blog.csdn.net/jsjsjs1789 ...

37010
来自专栏坚毅的PHP

zookeeper学习系列:二、api实践

上一章我们知道zookeeper的简介,启动,设置节点以及结构性能。本小节我们来玩玩api,获取下数据。 php版本: http://anykoro.sinaa...

40640
来自专栏Android 开发学习

JsBridge 源码分析

19930
来自专栏恰童鞋骚年

ASP.Net请求处理机制初步探索之旅 - Part 5 ASP.Net MVC请求处理流程

开篇:上一篇我们了解了在WebForm模式下一个Page页面的生命周期,它经历了初始化Init、加载Load以及呈现Render三个重要阶段,其中构造了页面控件...

14830
来自专栏Flutter入门

Weex是如何在Android客户端上跑起来的

Weex可以通过自己设计的DSL,书写.we文件或者.vue文件来开发界面,整个页面书写分成了3段,template、style、script,借鉴了成熟的MV...

52950

扫码关注云+社区

领取腾讯云代金券