对ftrace源码的阅读学习ptrace等获取函数调用

https://github.com/leviathansecurity/ftrace

http://blog.51cto.com/haidragon/2134709?tdsourcetag=s_pctim_aiomsg

 

 

 

1、解析elf,获取SHT_SYMTAB和SHT_DYNSYM符号的

st_name目标文件的符号字符串表的索引,其中包含符号名称的字符

st_value

  • 在可重定位文件中,st_value 包含所定义符号的节偏移。st_value 表示从 st_shndx 所标识的节的起始位置的偏移。
  • 在可执行文件和共享目标文件中,st_value 包含虚拟地址。为使这些文件的符号更适用于运行时链接程序,节偏移(文件解释)会替换为与节编号无关的虚拟地址(内存解释)
  • 获取.plt节动态链接表 的地址,并替换对应动态链接地址

2、获取/proc/%d/maps堆栈信息包括(第一个基地址、[heap]、[stack]、其他的)地址与地址范围大小

3、ptrace单步运行程序,获取寄存器值(PTRACE_GETREGS),对eip指针进行追踪,并且在程序基址范围内

ptrace (PTRACE_SINGLESTEP, h->pid, NULL, NULL);

(1)对程序函数调用顺序的追踪,主要是查看跳转指令如下:

struct branch_instr branch_table[64] = {

            {"jo",  0x70},

            {"jno", 0x71},  {"jb", 0x72},  {"jnae", 0x72},  {"jc", 0x72},  {"jnb", 0x73},

            {"jae", 0x73},  {"jnc", 0x73}, {"jz", 0x74},    {"je", 0x74},  {"jnz", 0x75},

            {"jne", 0x75},  {"jbe", 0x76}, {"jna", 0x76},   {"jnbe", 0x77}, {"ja", 0x77},

            {"js",  0x78},  {"jns", 0x79}, {"jp", 0x7a},    {"jpe", 0x7a}, {"jnp", 0x7b},

            {"jpo", 0x7b},  {"jl", 0x7c},  {"jnge", 0x7c},  {"jnl", 0x7d}, {"jge", 0x7d},

            {"jle", 0x7e},  {"jng", 0x7e}, {"jnle", 0x7f},  {"jg", 0x7f},  {"jmp", 0xeb},

            {"jmp", 0xe9},  {"jmpf", 0xea}, {NULL, 0}

};

(2)控制流追踪: 判断eip是否为跳转指令(并且在程序基址范围,本程序函数跳转)->单步执行进入函数->

//获取地址所在节的节名

                    sh_src = get_section_by_range(h, eip);

                    //获取地址所在节的节名

                    sh_dst = get_section_by_range(h, current_ip);

                    //输出信息

                    printf("%s(CONTROL FLOW CHANGE [%s]):%s Jump from %s 0x%lx into %s 0x%lx\n",    YELLOW, branch->mnemonic, WHITE,

                    !sh_src?"<unknown section>":sh_src, eip,

                    !sh_dst?"<unknown section>":sh_src, current_ip);

(3)函数返回值和参数的获取:判断函数调用call(e8)->计算函数地址(eip + offset + 5) ->判断函数地址是经过重定位的还是本地库的(重定位符号取值:

for (i = 0; i < ehdr64->e_shnum; i++) {

                                if (!strcmp(&h->elf64->StringTable[shdr64[i].sh_name], ".plt")) {

                                        for (k = 0, j = 0; j < shdr64[i].sh_size; j += 16) {

                                                if (j >= 16) {

                            h->dsyms[k++].value = shdr64[i].sh_addr + j;

                                                }

                                        }

                    break;

                                }

)->对函数汇编code进行追踪(根据获取的函数地址,读取堆栈的值)->根据函数调用约定(

/* X86Y64只支持这一点,基本上在这里解析这个调用约定:

            mov    %rsp,%rbp

            mov    $0x6,%r9d

            mov    $0x5,%r8d

            mov    $0x4,%ecx

            mov    $0x3,%edx

            mov    $0x2,%esi

            mov    $0x1,%edi

            callq  400144 <func>

    */

)读取mov    %rsp,%rbp向下的参数的值,并且转换为字符串->遍历读取寄存器(获取参数)->函数的返回地址就等于函数调用前的eip的下一个地址(calldata.retaddr = eip + 5;)->获取到参数后在返回地址手动下个断点->继续ptrace单步运行程序直到到返回地址的断点处断下->获取eax的值(返回值)(

if (calldp->retaddr == eip) {

                    snprintf(output, sizeof(output), "%s(RETURN VALUE) %s%s = %lx\n", RED, WHITE, calldp->string, eax);

)

(4)获取elf缺失的符号调用:只需要不判断符号地址就ok

(5)通过信号结束程序调用,如:ctrl+c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值