FAIRYFAR-INTERNAL
 
  FAIRYFAR-INTERNAL  |  SITEMAP  |  ABOUT-ME  |  HOME  
C语言的3种打桩方法总结

转自:C语言的3种打桩方法总结

前段时间项目组相对很久以前的工程做单元测试,由于比较久远内部逻辑复杂,外部依赖都比较多,所以想通过打桩的方式(不但要对外部接口还得可以对内部函数)进行函数级别的单元测试。调查了几个方法记录一下,以备以后参照。

google的cmockery

框架实现优点很多,请大家自行google。

但cmockery不能对内部函数进行打桩,所以不满足我们要求。

宏定义

利用C编译器预编译的特点,通过宏定义实现利用C编译器的预编译特点,通过宏定义替换需要打桩的函数。直接上代码。

main.c

snippet.c
#include "test.h"
 
// 把func1替换成func1_stub_行号
#define func1 func1_(__LINE__)
#define func1_(line) func1__(line)
#define func1__(line) func1_stub_ ## line
 
// 把func2替换成func2_stub_行号
#define func2 func2_(__LINE__)
#define func2_(line) func2__(line)
#define func2__(line) func2_stub_ ## line
 
//test函数中func1为第5行
void func1_stub_5(void)
{
    printf("func1_stub\n");
}
 
//test函数中func1为第6行
void func2_stub_6(void)
{
    printf("func2_stub\n");
}
 
int main()
{
    test();
    return 0;
}

test.h

snippet.c
#include <stdio.h>
 
void test(void);
void func1(void);
void func2(void);

test.c

snippet.c
#include "test.h"
 
void test(void)
{
    func1();
    func2();
}
 
void func1(void)
{
    printf("func1()\n");
}
void func2(void)
{
    printf("func2()\n");
}

修改函数内存地址,通过Jump指令跳转到stub函数

第一种

demo.c

snippet.c
#include <stdio.h>
#include <stdint.h>
#include <sys/mman.h>
#include <unistd.h>
 
void bar()
{
        printf("function bar\n");
}
 
void foo()
{
        printf("function foo\n");
}
 
void monkey_patch(void* old_function, void* new_function)
{
        void* page;
 
        page = (void*)((uint64_t)old_function & ~(getpagesize()-1));
        if(mprotect(page, getpagesize(), PROT_WRITE|PROT_READ|PROT_EXEC)) {
                perror("mprotect");
        }
        ((uint8_t*)old_function)[0] = 0xeb; /* jmp */
        /*
         * Number of bytes to jump relative to the eip which is 2 bytes past
         * the address of this instruction. Cannot jump more than 128 bytes.
         */
        ((uint8_t*)old_function)[1] = new_function - old_function - 2;
}
 
int main()
{
	foo();
	monkey_patch(foo, bar);
	foo();
	return 0;
}

第二种

stub.h

snippet.c
#ifndef __STUB_H__
#define __STUB_H__
 
#ifdef __cplusplus
exern "C"
{
#endif
 
#define CODESIZE 5U
 
struct func_stub
{
	void *fn;
	unsigned char code_buf[CODESIZE];
};
 
int stub_init();
void stub_set(struct func_stub *pstub, void *fn, void *fn_stub);
void stub_reset(struct func_stub *pstub);
 
#ifdef __cplusplus
}
#endif
 
#endif

stub.c

snippet.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <errno.h>
#include <limits.h>
#include <sys/mman.h>
#include "stub.h"
 
static long pagesize = -1;
 
static inline void *pageof(const void* p)
{
	return (void *)((unsigned long)p & ~(pagesize - 1));
}
 
void stub_set(struct func_stub *pstub, void *fn, void *fn_stub)
{
    pstub->fn = fn;
    memcpy(pstub->code_buf, fn, CODESIZE);
 
    if (-1 == mprotect(pageof(fn), pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC))
    {
        perror("mprotect to w+r+x faild");
        exit(errno);
    }
 
    *(unsigned char *)fn = (unsigned char)0xE9;
    *(unsigned int *)((unsigned char *)fn + 1) = (unsigned char *)fn_stub - (unsigned char *)fn - CODESIZE;
 
    if (-1 == mprotect(pageof(fn), pagesize * 2, PROT_READ | PROT_EXEC))
    {
        perror("mprotect to r+x failed");
        exit(errno);
    }
 
    return;
}
 
void stub_reset(struct func_stub *pstub)
{
    if (NULL == pstub->fn)
    {
        return;
    }
 
    if (-1 == mprotect(pageof(pstub->fn), pagesize * 2, PROT_READ | PROT_WRITE | PROT_EXEC))
    {
        perror("mprotect to w+r+x faild");
        exit(errno);
    }
 
    memcpy(pstub->fn, pstub->code_buf, CODESIZE);
 
    if (-1 == mprotect(pageof(pstub->fn), pagesize * 2, PROT_READ | PROT_EXEC))
    {
        perror("mprotect to r+x failed");
        exit(errno);
    }
 
    memset(pstub, 0, sizeof(struct func_stub));
    return;
}
 
int stub_init(void)
{
        int ret;
        pagesize = sysconf(_SC_PAGE_SIZE);
 
        ret = 0;
        if (pagesize < 0)
        {
                perror("get system _SC_PAGE_SIZE configure failed");
                ret = -1;
        }
        return ret;
}

main.c

snippet.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "stub.h"
 
void f1()
{
    printf("f1\n");
    return;
}
 
void f2()
{
    printf("f2\n");
    return;
}
 
void *_memset(void *s, int ch, size_t n)
{
    printf("-memset\n");
    return s;
}
 
int main()
{
    char ac[10] = {1};
    struct func_stub stub;
    if (-1 == stub_init())
    {
         printf("faild to init stub\n");
         return 0;
    }
    stub_set(&stub, (void *)memset, (void *)_memset);
 
    memset(ac, 0, 10);
    stub_reset(&stub);
 
    memset(ac, 0, 10);
    printf("ac[0] = %hhu\n", ac[0]);
 
    stub_set(&stub, (void *)f1, (void *)f2);
 
    f1();
    stub_reset(&stub);
    f1();
 
    return 0;
}


打赏作者以资鼓励: