- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在尝试在 x86_64(英特尔语法)中动态查找程序在运行时调用和返回的函数数。
为此,我正在使用 ptrace(没有 PTRACE_SYSCALL),我正在检查 RIP 寄存器(包含下一个指令地址),我正在检查他的操作码。我知道如果 LSB 等于 0xE8(根据 Intel 文档,或 http://icube-avr.unistra.fr/fr/images/4/41/253666.pdf 第 105 页),可以找到函数 CALL。
我在 http://ref.x86asm.net/coder64.html 上找到了每条指令, 所以在我的程序中, 每次找到0xE8, 0x9A, 0xF1等... 就找到一个函数入口(CALL或INT指令), 如果是0xC2, 0XC3等... 就是函数leave (RET说明)。
目标是在运行时在每个程序上找到它,我无法访问测试程序的编译、检测或使用 gcc 的魔术工具。
我制作了一个小程序,可以用 gcc -Wall -Wextra your_file.c
编译并通过键入 ./a.out a_program
启动。
这是我的代码:
#include <sys/ptrace.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct user_regs_struct reg_t;
static int8_t increase(pid_t pid, int32_t *status)
{
if (WIFEXITED(*status) || WIFSIGNALED(*status))
return (-1);
if (WIFSTOPPED(*status) && (WSTOPSIG(*status) == SIGINT))
return (-1);
if (ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) == -1)
return (-1);
return (0);
}
int main(int argc, char *argv[])
{
size_t pid = fork();
long address_rip;
uint16_t call = 0;
uint16_t ret = 0;
int32_t status;
reg_t regs;
if (!pid) {
if ((status = ptrace(PTRACE_TRACEME, 0, NULL, NULL)) == -1)
return (1);
kill(getpid(), SIGSTOP);
execvp(argv[1], argv + 1);
} else {
while (42) {
waitpid(pid, &status, 0);
ptrace(PTRACE_GETREGS, pid, NULL, ®s);
address_rip = ptrace(PTRACE_PEEKDATA, pid, regs.rip, NULL);
address_rip &= 0xFFFF;
if ((address_rip & 0x00FF) == 0xC2 || (address_rip & 0x00FF) == 0xC3 ||
(address_rip & 0x00FF) == 0xCA || (address_rip & 0x00FF) == 0xCB ||
(address_rip & 0x00FF) == 0xCF)
ret += 1;
else if ((address_rip & 0x00FF) == 0xE8 || (address_rip & 0x00FF) == 0xF1 ||
(address_rip & 0x00FF) == 0x9A || (address_rip & 0x00FF) == 0xCC ||
(address_rip & 0x00FF) == 0xCD || (address_rip & 0x00FF) == 0xCF)
call += 1;
if (increase(pid, &status) == -1) {
printf("call: %i\tret: %i\n", call, ret);
return (0);
}
}
}
return (0);
}
当我用 a_program
运行它时(这是一个自定义程序,只需输入一些本地函数并执行一些写入系统调用,目标只是跟踪该程序输入/离开函数的数量), 没有错误发生,它工作正常,但我没有相同数量的 CALL 和 RET。例子:
user> ./a.out basic_program
call: 636 ret: 651
(大量的 call 和 ret 是由 LibC 引起的,它在启动你的程序之前进入了很多函数,参见 Parsing Call and Ret with ptrace. )
实际上,这就像我的程序比函数调用更多地返回,但我发现0xFF指令用于(r/m64或r/m16/m32)中的CALL或CALLF,但也用于其他指令,如DEC, INC 或 JMP(这是很常见的指令)。
那么,我该如何区分呢?根据 http://ref.x86asm.net/coder64.html带有“操作码字段”,但我如何找到它?
如果我将 0xFF 添加到我的条件中:
else if ((address_rip & 0x00FF) == 0xE8 || (address_rip & 0x00FF) == 0xF1 ||
(address_rip & 0x00FF) == 0x9A || (address_rip & 0x00FF) == 0xCC ||
(address_rip & 0x00FF) == 0xCD || (address_rip & 0x00FF) == 0xCF ||
(address_rip & 0x00FF) == 0xFF)
call += 1;
如果我启动它:
user> ./a.out basic_program
call: 1152 ret: 651
这对我来说似乎很正常,因为它会计算每个 JMP、DEC 或 INC,所以我需要区分每个 0xFF 指令。我试着这样做:
else if ((address_rip & 0x00FF) == 0xE8 || (address_rip & 0x00FF) == 0xF1 ||
(address_rip & 0x00FF) == 0x9A || (address_rip & 0x00FF) == 0xCC ||
(address_rip & 0x00FF) == 0xCD || (address_rip & 0x00FF) == 0xCF ||
((address_rip & 0x00FF) == 0xFF && ((address_rip & 0x0F00) == 0X02 ||
(address_rip & 0X0F00) == 0X03)))
call += 1;
但它给了我相同的结果。我哪里错了吗?如何找到相同数量的 call 和 ret?
最佳答案
这是一个如何编程的例子。请注意,由于 x86 指令最长可达 16 个字节,因此必须查看 16 个字节以确保获得完整的指令。由于每次查看读取 8 个字节,这意味着您需要查看两次,一次在 regs.rip
处,一次在 8 个字节之后:
peek1 = ptrace(PTRACE_PEEKDATA, pid, regs.rip, NULL);
peek2 = ptrace(PTRACE_PEEKDATA, pid, regs.rip + sizeof(long), NULL);
请注意,这段代码掩盖了很多关于如何处理前缀的细节,并将一堆无效指令检测为函数调用。进一步请注意,如果您想将其用于 32 位代码,则需要更改代码以包含更多 CALL 指令并删除对 REX 前缀的检测:
int iscall(long peek1, long peek2)
{
union {
long longs[2];
unsigned char bytes[16];
} data;
int opcode, reg;
size_t offset;
/* turn peeked longs into bytes */
data.longs[0] = peek1;
data.longs[1] = peek2;
/* ignore relevant prefixes */
for (offset = 0; offset < sizeof data.bytes &&
((data.bytes[offset] & 0xe7) == 0x26 /* cs, ds, ss, es override */
|| (data.bytes[offset] & 0xfc) == 0x64 /* fs, gs, addr32, data16 override */
|| (data.bytes[offset] & 0xf0) == 0x40); /* REX prefix */
offset++)
;
/* instruction is composed of all prefixes */
if (offset > 15)
return (0);
opcode = data.bytes[offset];
/* E8: CALL NEAR rel32? */
if (opcode == 0xe8)
return (1);
/* sufficient space for modr/m byte? */
if (offset > 14)
return (0);
reg = data.bytes[offset + 1] & 0070; /* modr/m byte, reg field */
if (opcode == 0xff) {
/* FF /2: CALL NEAR r/m64? */
if (reg == 0020)
return (1);
/* FF /3: CALL FAR r/m32 or r/m64? */
if (reg == 0030)
return (1);
}
/* not a CALL instruction */
return (0);
}
关于c - 如何使用 ptrace 找到 CALL 和 RET 号码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50179725/
有什么区别: 1) !ret 2) ret != 0 ? if (ret != 0 || stack[fd] == NULL || stack[fd][0] == '\0') { if (!ret
我正在使用 java 客户端 (jrets) 来查询 RETS 服务器。此 rets 服务器不支持 OFFSET 功能。 服务器管理员告诉我使用 MODIFIED 字段作为翻阅记录的一种方式。但是,我
我想从 RETS 服务器下载所有属性(property) list ,包括所有照片 URL。我正在使用 DMQL2 和 PHRETS 库。属性和照片对象存储在 RETS 服务器的不同表中。 要获取所有
各位, 我将使用 RETS 进行真实项目。我有一份文件,说明我的经理实际上在寻找什么。但我真的不知道如何开始使用 RETS MLS,因为它有很多这样的文档。 通过一些研发,我几乎没有想出任何有值(va
我正在尝试在 Microsoft visual studio 2013 on C++ 上编译一个为 linux 编写的程序(是 C 和 C++ 的混合体 (#include .h),我将全部转换为 C
当我尝试像这样编写自己的decay_t时: #include template struct auto_decay { auto operator () () noexcept {
我经常看到通过测试小于零而不是显式定义来检查 POSIX 函数错误的代码,通常唯一的错误代码使用 -1。也就是 ret = function(); if (ret < 0) { ... } 对
考虑以下空 C 程序(标准保证编译器执行隐式 return 0): int main(int argc, char* argv[]) {} 您可以将任何逻辑添加到此函数中来操作 argc 和 argv
我在 ubuntu 12.04 和 auctex 11.86 上运行 emacs 23.3.1。每当我去编译 latex 文档(使用 C-c C-c)时,如果没有错误,一切都编译得很好。但是,如果有任
我有 RETS 元数据文件,我想将其转换为数据库模式,这样我就可以查询我的数据库而不是 RETS 服务器。 有谁知道可以使用 xml 并将其转换为数据库模式的工具?或者可能是数据库模式本身? 一切都包
由于 ret 指令是一个间接调用,x86 上的 ret 指令会停止流水线,还是以某种方式优化为更直接的调用? 最佳答案 根据英特尔优化引用手册,分支预测单元包含一个 Return Stack Buff
我有以下头文件: #include #include #include #include #include #include /** **/ // size: 1B typedef en
我目前正在开发一个网站并从 RETS(房地产交易标准)API 中提取列表。我的一切工作正常,但我的问题是在尝试深入挖掘查询时出现的。作为引用,我正在使用 Node RETS Client但我不确定它与
if(isset($_POST['update'])) { $rets_login_url = $Fetch['rets_url']; $rets_username = $Fetch['rets_us
我有这个函数,它主要由内联汇编码成。 long *toarrayl(int members, ...){ __asm{ push esp mov eax, me
int suma(int* array, int len) { asm(" xor %eax, %eax # resultado = 0 \n"
我对汇编很陌生,我不明白在 proc 结束时你用 ret 写一个数字的确切含义是什么。陈述。 像这样: Function Proc push ax cx . ...body... . pop cx a
我正在尝试使用 PHRETS 从 rets 服务器在 WordPress 中添加帖子。不幸的是,正在添加重复的帖子。我已使用 WP 查询使用元键和值检查现有帖子。当我尝试添加 10 或 50 个帖子时
我有以下程序: SECTION .text main: mov ebx, 10 mov ecx, 50 repeat: inc ebx loop repeat
假设我正在 x86 汇编中编写一个例程,例如“add”,它将两个作为参数传递的数字相加。 在大多数情况下,这是一个非常简单的方法: push ebp mov ebp, esp mov eax, [eb
我是一名优秀的程序员,十分优秀!