摘要:linux程序运行的状态以及如何推导调用栈。

1、背景知识

1、ARM64寄存器介绍:

2、STP指令详解(ARMV8手册):

我们先看一下指令格式(64bit),以及指令对于寄存机执行结果的影响

类型1、STP <Xt1>, <Xt2>, [<Xn|SP>], #<imm>

将Xt1和Xt2存入Xn|SP对应的地址内存中,然后,将Xn|SP的地址变更为Xn|SP + imm偏移量的新地址

类型2、STP <Xt1>, <Xt2>, [<Xn|SP>, #<imm>]!

将Xt1和Xt2存入Xn|SP的地址自加imm对应的地址内存中,然后,将Xn|SP的地址变更为Xn|SP + imm的offset偏移量后的新地址

类型3、STP <Xt1>, <Xt2>, [<Xn|SP>{, #<imm>}]

将Xt1和Xt2存入Xn|SP的地址自加imm对应的地址内存中

手册中有三种操作码,我们只讨论程序中涉及的后两种

Pseudocode如下:

Shared decode for all encodings
integer n = UInt(Rn);
integer t = UInt(Rt);
integer t2 = UInt(Rt2);
if L:opc<0> == '01' || opc == '11' then UNDEFINED;
integer scale = 2 + UInt(opc<1>);
integer datasize = 8 << scale;
bits(64) offset = LSL(SignExtend(imm7, 64), scale);
boolean tag_checked = wback || n != 31;
Operation for all encodings
bits(64) address;
bits(datasize) data1;
bits(datasize) data2;
constant integer dbytes = datasize DIV 8;
boolean rt_unknown = FALSE;
if HaveMTEExt() thenSetNotTagCheckedInstruction(!tag_checked);
if wback && (t == n || t2 == n) && n != 31 thenConstraint c = ConstrainUnpredictable();assert c IN {Constraint_NONE, Constraint_UNKNOWN, Constraint_UNDEF, Constraint_NOP};case c ofwhen Constraint_NONE rt_unknown = FALSE; // value stored is pre-writebackwhen Constraint_UNKNOWN rt_unknown = TRUE; // value stored is UNKNOWNwhen Constraint_UNDEF UNDEFINED;when Constraint_NOP EndOfInstruction();
if n == 31 thenCheckSPAlignment();address = SP[];
elseaddress = X[n];
if !postindex thenaddress = address + offset;
if rt_unknown && t == n thendata1 = bits(datasize) UNKNOWN;
elsedata1 = X[t];
if rt_unknown && t2 == n thendata2 = bits(datasize) UNKNOWN;
elsedata2 = X[t2];
Mem[address, dbytes, AccType_NORMAL] = data1;
Mem[address+dbytes, dbytes, AccType_NORMAL] = data2;
if wback thenif postindex thenaddress = address + offset;if n == 31 thenSP[] = address;elseX[n] = address;

红色部分对应推栈的关键逻辑,其他汇编指令含义可自行参考armv8手册或者度娘。

2、一个例子

熟悉了上面的部分,接下来我们看一个实例:

C代码如下:

相关的几个函数反汇编如下(和推栈相关的一般只有入口两条指令):

main\f3\f4\strlen

我们通过gdb运行后,可以看到strlen地方会触发SEGFAULT,引发进程挂掉

上述通过代码编译后,没有strip,因此elf文件是带着符号的

查看运行状态(info register):关注$29、$30、SP、PC四个寄存器

一个核心的思想:CPU执行的是指令而不是C代码,函数调用和返回实际是在线程栈上面的压栈和弹栈的过程

接下来我们来看上面的调用关系在当前这个任务栈是如何玩的:

函数调用在栈中的关系(call function压栈,地址递减;return弹栈,地址递增):

以下是推栈的过程(划重点

再回头来看之前的汇编:

main\f3\f4\strlen

从当前的sp开始,frame 0是strlen,这块没有开栈,因此上一级的调用函数仍然是x30,因此推导:frame1调用为f3

函数f3的起始入口汇编:

(gdb) x/2i f30x400600 <f3>: stp   x29, x30, [sp,#-48]!0x400604 <f3+4>:      mov x29, sp

可以看到,f3函数开辟的栈空间为48字节,因此,倒推frame2的栈顶为当前的sp + 48字节:0xfffffffff2c0

(gdb) x/gx 0xfffffffff2c0+8
0xfffffffff2c8:    0x000000000040065c
(gdb) x/i 0x000000000040065c0x40065c <f4+36>:    mov w0, #0x0                       // #0
frame2的函数为sp+8:0x000000000040065c -> <f4+36>

继续从sp = 0xfffffffff2c0倒推frame1的函数

函数f4的起始入口汇编为:

(gdb) x/2i f40x400638 <f4>: stp   x29, x30, [sp,#-48]!0x40063c <f4+4>:      mov x29, sp

可以看到,f4函数开辟的栈空间也是为48字节,因此,倒推frame3的栈顶为当前的0xfffffffff2c0 + 48字节:0xfffffffff2f0

frame2的函数为0xfffffffff2c0 + 8:0x000000000040065c -> <f4+36>
(gdb) x/gx 0xfffffffff2f0+8
0xfffffffff2f8:    0x0000000000400684
(gdb) x/i 0x00000000004006840x400684 <main+28>:       mov w0, #0x0                       // #0

因此frame3的函数为main函数,main函数对应的栈顶为0xfffffffff320

至此推导结束(有兴趣的同学可以继续推导,可以看到libc如何拉起main的过程)

总结:

推栈的关键:

  • 当前的现场
  • 熟悉cpu体系架构的开栈的方式

3、实战讲解

现场有如下的core:可以看到,所有的符号找不到,加载了符号表依然不好使,解析不出来实际的调用栈

(gdb) bt
#0  0x0000ffffaeb067bc in ?? () from /lib64/libc.so.6
#1  0x0000aaaad15cf000 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)

先看info register,关注x29、x30、sp、pc四个寄存器的值

推导任务栈:

先将sp内容导出:

下图实际已先将结果标出,我们下面来详细描述如何推导

pc代表当前执行的函数指令,如果当前指令未开栈,一般情况x30代表上一级的frame调用当前函数的下一条指令,查看汇编,可以反解为如下函数

(gdb) x/i 0xaaaacd3de4fc0xaaaacd3de4fc <PGXCNodeConnStr(char const*, int, char const*, char const*, char const*, char const*, int, char const*)+108>: mov x27, x0

找到栈顶函数后,查看该函数的栈操作:

(gdb) x/6i PGXCNodeConnStr0xaaaacd3de490 <PGXCNodeConnStr(char const*, int, char const*, char const*, char const*, char const*, int, char const*)>: sub  sp, sp, #0xd00xaaaacd3de494 <PGXCNodeConnStr(char const*, int, char const*, char const*, char const*, char const*, int, char const*)+4>:      stp   x29, x30, [sp,#80]0xaaaacd3de498 <PGXCNodeConnStr(char const*, int, char const*, char const*, char const*, char const*, int, char const*)+8>:      add  x29, sp, #0x50

可以看到,上一级的frame存在了当前的sp + 0xd0 - 0x80也就是0xfffec4cebd40 + 0xd0 - 0x80 = 0xfffec4cebd90的地方,而栈底在0xfffec4cebd40+ 0xd0 = 0xfffec4cebe10的地方

因此就找到了下一级的frame对应的栈顶和上一级的LR返回指令,反解,可以得到函数build_node_conn_str

(gdb) x/i 0x0000aaaacd414e080xaaaacd414e08 <build_node_conn_str(Oid, DatabasePool*)+224>:     mov x21, x0

继续重复上述推导,可以看到这个函数build_node_conn_str开了176字节的栈,

(gdb) x/4i build_node_conn_str0xaaaacd414d28 <build_node_conn_str(Oid, DatabasePool*)>:     stp   x29, x30, [sp,#-176]!0xaaaacd414d2c <build_node_conn_str(Oid, DatabasePool*)+4>: mov x29, sp

因此继续用0xfffec4cebe10 + 176 = 0xfffec4cebec0

查看调用者0xfffec4cebe10+8为reload_database_pools

继续看reload_database_pools

(gdb) x/8i reload_database_pools0xaaaacd4225e8 <reload_database_pools(PoolAgent*)>:       sub   sp, sp, #0x1c00xaaaacd4225ec <reload_database_pools(PoolAgent*)+4>:  adrp x5, 0xaaaad15cf0000xaaaacd4225f0 <reload_database_pools(PoolAgent*)+8>:   adrp x3, 0xaaaacf0ed0000xaaaacd4225f4 <reload_database_pools(PoolAgent*)+12>: adrp x4, 0xaaaaceeed000 <_ZN4llvm18ConvertUTF8toUTF16EPPKhS1_PPtS3_NS_15ConversionFlagsE>0xaaaacd4225f8 <reload_database_pools(PoolAgent*)+16>: add  x3, x3, #0x9e00xaaaacd4225fc <reload_database_pools(PoolAgent*)+20>: adrp x1, 0xaaaacf0ee000 <_ZZ25PoolManagerGetConnectionsP4ListS0_E8__func__+24>0xaaaacd422600 <reload_database_pools(PoolAgent*)+24>:         stp   x29, x30, [sp,#-96]!

实际开栈0x220字节,因此这一层frame的栈底为0xfffec4cebec0 + 0x220 = 0xfffec4cec0e0

因此得到基本的调用关系的结构如下

以上基本可以够用来分析问题了,因此不需要再继续推导

TIPS:arm架构下一般调用都会使用这种指令,

stp x29, x30, [sp,#immediate]! 有叹号或者无叹号

因此在每一层的frame都保存了上一层frame的栈顶地址和LR指令,通过准确找到底层的frame 0栈顶后,就可以快速推导出所有的调用关系(红色虚线圈出来的部分),函数的反解依赖符号表,只要原始的elf文件的symbol段没有strip掉,是都可以找到对应的函数符号(通过readelf -S查看即可)

找到Frame后,每一层frame里面的内容,结合汇编基本就可以用来推导过程变量了。

本文分享自华为云社区《代码 or 指令,浅析ARM架构下的函数的调用过程》,原文作者:K______。

点击关注,第一时间了解华为云新鲜技术~

代码 or 指令,浅析ARM架构下的函数的调用过程相关推荐

  1. 【Android 逆向】函数拦截 ( ARM 架构下的插桩拦截 | 完整代码示例 )

    文章目录 一.ARM 架构下的插桩拦截 二.完整代码示例 一.ARM 架构下的插桩拦截 ARM 架构下的跳转指令 : 下面的二进制数都是十六进制数 ; 323232 位指令 ; 04 F0 1F E5 ...

  2. Arm 架构下的中断

    中断的处理分为三个部分: 1.中断检测: arm架构下,中断监测部分的代码是需要用户自己开发的,可以参考ambaIntrCtl.c,需要实现函数xxxIntLvlVecChk.xxxIntLvlVec ...

  3. 史上最全!!!ARM架构下的NVIDIA Xavier安装ROS-Melodic以及使用速腾激光雷达+A-loam获取点云图

    ** 一.ARM架构下的NVIDIA Xavier 切换国内的源 ** 这里不要换成AMD架构的PC平台的软件源,需要换成配套ARM使用的源. 首先备份下之前的 source.list sudo cp ...

  4. ARM架构下部署docker

    ARM架构下部署docker 1 环境要求 2 安装与配置docker 2.1? 下载Docker静态包 2.2 移值docker静态包以及组件 2.3 配置docker.service文件 2.4 ...

  5. 案例一: 使用IDA PRO+OllyDbg+PEview 追踪windows API 动态链接库函数的调用过程。 首先用文本编辑器写一个C++源程序名为StackFrame.cpp ,代码如下:

    案例一: 使用IDA PRO+OllyDbg+PEview 追踪windows API 动态链接库函数的调用过程. 首先用文本编辑器写一个C++源程序名为StackFrame.cpp ,代码如下: 1 ...

  6. 第6.3章:ARM架构下手动编译StarRocks(拓展篇)

    2023/4/17更新: 下文教程中CentOS 7下使用的centos-release-scl源里的devtoolset-10的gcc/g++将_GLIBCXX_USE_CXX11_ABI强制关闭了 ...

  7. 基于ARM架构下的PI数据库远程存储

        随着信息技术的进步,计算机技术在工厂的控制层和管理层都得到了充分和广泛的应用.而当计算机技术的应用要求进一部深入和提升时,势必要求把生产控制层和管理层连接起来.实时数据库系统能够及时地把控制层 ...

  8. ARM架构下在qemu模拟器中汇编实现:统计输入字符串长度并且判断是否回文

    参考文章: 在Ubuntu中安装Qemu模拟ARM架构 objdump(Linux)反汇编命令使用指南 如何在x64的Ubuntu系统下安装64bit的交叉编译工具aarch64-linux-gnu- ...

  9. ARM架构下使用NEON向量化指令集入门基础

    一.NEON简介         ARM NEON技术是基于SIMD的理念而设计出的,它是一种64位和128位混合的SIMD技术,主要应用场景是音视频处理,图像视觉计算,信号处理应用等需要密集计算的场 ...

最新文章

  1. 窗体的ControlBox属性
  2. layui动态生成的下拉框被遮住
  3. 01.WPF中制作无边框窗体
  4. linux文件系统添加pcm,linux下用sox批量将pcm文件加wav头、批量修改采样率、切音频...
  5. SocialFish-kali下社会工程学钓鱼工具
  6. 小夕说,不了解动态空间增长的程序喵都是假喵(下)
  7. Python调用Java代码部署及初步使用
  8. informix多张表UPDATE时不能用别名
  9. python编写程序、计算1+3+5+7......+99-在python中实现求输出1-3+5-7+9-......101的和
  10. pycharm使用总结
  11. PAT 1045 快速排序(25)(STL-set+思路+测试点分析)
  12. 牛批!妹子一口气拿下BAT、美团、vivo、爱奇艺等公司Offer面经总结
  13. jQuery 的第一个例子
  14. windowskb2685811补丁_Win7/8.1 KB2685811、KB2685813和KB2670838蓝屏补丁下载汇总 (32位+64位)...
  15. 迅雷,暴风影音,QQ这些软件是什么工具和语言编的?
  16. 谷歌搜索没有相机图标_教您如何在Google上搜索图片
  17. 怎么查二手苹果手机价格
  18. 迅雷极速版阻止自动更新(亲自摸索出来,可用)
  19. BAT批处理如何去写Windows防火墙规则
  20. 关于数据库的递归查询

热门文章

  1. Bootstrap 插件的选项
  2. 获取网关_阿里二面问了这道题:如何设计一个微服务网关系统
  3. 大话西游2服务器维护,大话西游2:9.10维护解读:灵犀调整全服上线,去疾调整是好是坏?...
  4. oracle 其他用户表主键,Oracle中查看所有的表,用户表,列名,主键,外键
  5. Flask框架 - 初识
  6. docker build no such file or directory
  7. HDU 3564 Another LIS
  8. 单片机特殊功能寄存器
  9. 实现算法2.11、2.12的程序
  10. 浅析MSIL中间语言——基础篇