吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2528|回复: 9
收起左侧

[Android 原创] luckfollowme arm学习篇5 - 认识arm

  [复制链接]
2016976438 发表于 2023-4-26 22:39
本帖最后由 2016976438 于 2023-4-26 22:44 编辑

luckfollowme arm学习篇5 - 认识arm

上一章各位都运行起来了吧

这一章让我们一起分析分析 当中的 arm 指令

简介

我只能勉强带你入门,因为我也只是刚入门的水准,arm 那么多指令 记不住 根本记不住,只能按照例子来,见到啥说啥。

我只说 arm64 ,剩余 arm32 如果大伙有需要的话我在写一章

貌似 arm 也叫做 aarch 遇到的时候注意点就是的了

寄存器

在讲解 指令之前 ,先了解些基本概念。

首先 arm64 中有 x0~x31 的寄存器。

32 个寄存器都是64位的。

有时候我们只需要存储32 位的。还有 w0~w31 的写法,他们会存储在64位中 低32位的位置

当然您还会看到cpsr 这种状态寄存器。

了解过 x86 的人来说会比较熟悉。

最常见的就是

N(Nagative) 结果是否是负数

Z(Zero) 结果是否为 0

C(Carry) 结果是否进位

V(Overflow) 在x86 是 o 标志位

一般计算结果的标志位可用于 条件判断 。 如 Z = 1 代表 两边结果相等

当然每个寄存器您也不用特意记,后面用到的时候我在提出来,这样记得更清楚一点。

目前您只需要了解最基本的:

x30 链接寄存器

x31 堆栈寄存器

还有个程序计数器 PC 用于记录当前指令执行的地址

示例

我们将之前的代码改成 如下的:

#include "stdio.h"

int _global_sum;
// 使用 标准 C 约束 生成的 方法名不会变得很奇怪
extern "C"
{
    int sum(int a, int b)
    {

        //4. 测试加法 和 局部变量
        int sum = a + b;
        //5. 测试状态寄存器 和 条件跳转
        if (sum > 10)
        {
            //6. 测试全局变量
            _global_sum = sum;
        }
        // 7. 测试返回值
        return _global_sum;
    }
}

int main()
{
    //1. 测试b 指令 死循环
    while (true)
    {
        //2. 测试局部变量赋值指令
        int a = 10;
        int b = 20;
        //3. 测试传参 和 调用方法
        int ret = sum(a, b);
        printf("sum:%d\n", ret);

        getchar();
    }
    return 0;
}

然后使用 ndk_builder.py adb_execute_arm64.bat 执行起来

强调一点。 如果您通过 netstat 没有看到 23946 端口 。 那么请重新启动 ida pro android_server64 并执行端口转发 adb forward tcp:23946 tcp:23946

如果您的android 模拟器出现问题 。 它的 avd 应该在 Android\sdk\system-images\android-apiLevel 目录下,删掉重新创建即可

Ida Pro Debugger

启动完砸门的程序后 您 俺任意键他会重复执行 方便我们调试分析

然后 用 ida pro 进行 debuger 附加动态分析

ida pro 当然也支持静态分析(拖入 elf 文件进去即可)

但是我们要看栈的情况来分析接下来的指令,所以用 debugger附加的形式

话不多说,先来分析下ida pro 一些基本界面

1.首先您进来注意右边的寄存器。 注意观察 x30  还有 SP(x31) 和 PC

其中 X30 是 linker 地址 可以理解为程序的返回后地址

SP 是当前的栈顶指针

PC 是当前执行指令的地址

在寄存器的右边 就是 状态寄存器 对应着 PSR

在最下面是 stack view 栈视图 后面会说

</p>

02.png

</p>

2.其次您需要做些准备,进入到main方法后,将我图中被 ida pro 自动分析符号 的 按Q 转换成偏移数字。

并在图中所示的地方下个断点。

</p>

01.png

03.png

</p>

下的断点的地方刚刚是在死循环的内部。

为什么不在上面的的代码上:

SUB             SP, SP, #0x20
STP             X29, X30, [SP,#0x10]
ADD             X29, SP, #0x10
STUR            WZR, [X29,#-4]
B               loc_74C717E714

因为我们直接启动程序后 直接跑到我们的死循环内部了,上面的代码是不会在执行的了。

其实我们可以通过 ida pro 创建进程来直接调试,这样可以在程序一开始的地方就断下来,但那种有点麻烦。

不过我们可以通过分析砸门另一个 sum 方法 来分析上面的指令。

因为 上面的指令如

SUB             SP, SP, #0x20

明显是给 sp 栈空间分配大小的,按道理标准的调用约定 都会完成这种指令。

对于 常见调用约定  一般都是部分参数入寄存器 其余参数从右到左入栈 被调用的方法实现栈平衡 随后返回值放入第一个寄存器 如 eax| r0 | x0  

下面是我们将要分析的完整汇编指令,我们将逐步分析:

MOV             W8, #0xA
STR             W8, [SP,#8]
MOV             W8, #0x14
STR             W8, [SP,#4]
LDR             W0, [SP,#8]
LDR             W1, [SP,#4]
BL              sum

STR             W0, [SP]
LDR             W1, [SP]
ADRL            X0, aSumD               ; "sum:%d\n"
BL              unk_74C717E790

BL              unk_74C717E7A0

B               loc_74C717E714

MOV

接下来您按 F9 运行程序,然后在程序输入任意键重新进入断点

接下来您会断第一条语句上:

MOV             W8, #0xA

这段指令的意思是 向 x8 寄存器赋值 0xA 也就是 16

注意 w8 意思是低32位

此时我的 x8 寄存器是 0x000000000000003FF8 步过后 会变成 0x000000000000000A

STR

接下来看 STR 指令:

STR             W8, [SP,#8]

STR 您也可以读成 store register 意思是储存寄存器。

它可以将 寄存器的值 储存在内存中。

上面意思您可以理解成 [SP + 0x8] = w8

也就是 栈地址 + 8 赋值 w8 的值

点击 stack view 后 在点击 寄存器上的 SP(x31) 跳到当前的栈顶:

0000007FC2455540  000000140000001E
0000007FC2455548  000000000000000A

0000007FC2455540 是当前的栈顶

执行完 STR             W8, [SP,#8] 指令后 堆栈下面的  0000007FC2455548 会变成 A,由于上一次循环设置过了,所以我这里本身就是 A

经过上面的说明,您应该也能分析,接下的指令:

MOV             W8, #0x14 # 将 0x14 放到 x8
STR             W8, [SP,#4]        # 将 由于 w8 是低 32 位 所以 sp 只加 4

注意一点的是 sp + 4 的位置 还是看之前栈的数值:

0000007FC2455540  000000140000001E
分开32 位是 00000014 0000001E

您可能会有疑惑,为什么不是 0000001E 00000014 而是反过来的?

这就要设计到小端排序的问题了。

小端排序是什么意思呢?

我们通过 hex view 进行分析。

先点击 hex view 在点击 寄存器的 SP 然后您应该就可以看到如下所示:

</p>

04.png

</p>

正确的数值是 : 1E 00 00 00 14 00 00 00

此时您更懵逼了,但是您可以先抽出32 位的来看。

14 00 00 00

由于小端排序是低地址储存高位字节。

所以我们取出数据 得从最右边开始取,转换过来就是:

00 00 00 14 

这种结果就是对的。

那如果是 64 位呢?

                 1E 00 00 00 14 00 00 00 
转换成          00 00 00 14 00 00 00 1E        

此时就对了。

然后我们整合下面的指令的意思就是 [ sp + 8 ] = 10   [sp+4] = 20

MOV             W8, #0xA
STR             W8, [SP,#8]
MOV             W8, #0x14
STR             W8, [SP,#4]

对应着 c 命令

 //2. 测试局部变量赋值指令
 int a = 10;
 int b = 20;

而且您应该也能分析出来 局部变量都是放在栈空间的

LDR

接下来在分析一下: LDR 指令

MOV             W8, #0xA
STR             W8, [SP,#8]
MOV             W8, #0x14
STR             W8, [SP,#4]
LDR             W0, [SP,#8]
LDR             W1, [SP,#4]
BL              sum

STR             W0, [SP]
LDR             W1, [SP]
ADRL            X0, aSumD               ; "sum:%d\n"
BL              unk_74C717E790

BL              unk_74C717E7A0

B               loc_74C717E714

STR 是从 寄存器 到 内存。

LDR (Store Register) 则是从 内存到寄存器

那么下面您可以直接翻译成:

LDR             W0, [SP,#8]                # w0 = [sp+0x8] = 10
LDR             W1, [SP,#4]                # w1 = [sp+0x4] = 20

很明显这是从 栈取出局部变量 给 寄存器

B

B (branch) 用于跳转某个地址上,一般用于跳到子方法上

而下面指令 BL 带 L 的可以理解为记录 linker X30 寄存器,也就是返回地址,因为调用完返回后我们得返回到之前执行的地方。

接下来我将带我的地址的汇编和寄存器赋值一下让你们参考下:

hello2:00000074C717E728 LDR             W1, [SP,#4]
hello2:00000074C717E72C BL              sum

hello2:00000074C717E730 STR             W0, [SP]
x29         0000007FC2455550
x30         00000074C717E748
sp                 0000007FC2455540
pc                 00000074C717E72C

然后按 F8 执行完 下面的指令后:

hello2:00000074C717E72C BL              sum

注意看我的寄存器,很明显 x30 也就是 linker 变成 bl 指令下面的地址了:

x29         0000007FC2455550
x30         00000074C717E730
sp                 0000007FC2455540
pc                 00000074C717E730

hello2:00000074C717E730 STR             W0, [SP]

根据调用约定 部分参数放在寄存器中

结合 BL 跳转指令

下面我们将 指令整合起来看

# 1. 局部变量放在栈中
# [sp + 8] = 10
MOV             W8, #0xA
STR             W8, [SP,#8]
# [sp + 4] = 20
MOV             W8, #0x14
STR             W8, [SP,#4]
# 2. 取出局部变量 然后传参到 sum 方法
LDR             W0, [SP,#8]
LDR             W1, [SP,#4]
BL              sum

# 3. 根据调用约定 sum执行结束后会还原栈 随后把返回值放在 x0 
# 把返回值放在栈中 也就是局部变量中
STR             W0, [SP]

这些应该对应 c 代码中的

   //2. 测试局部变量赋值指令
        int a = 10;
        int b = 20;
        //3. 测试传参 和 调用方法
        int ret = sum(a, b);

伪指令 ADRL

下面一条指令是

ADRL            X0, aSumD

这是一条计算地址的指令。 但实际  arm64 中并没有这条指令

这条指令就是伪指令。

一般来说 arm 指令都是 4字节

如何看指令字节码呢?

点击 Options->General

opcode bytes 输入 10

</p>

05.png

</p>

</p>

06.png

</p>

可以看到它实际指令的字节码是 00 00 00 90 00 20 15 91

我们把他放入在线网站转换一下  https://armconverter.com/

ADRL            X0, 0x7B13862548
转换成
adrp x0, #0x7b13862000
add x0, x0, #0x548

可以看到 ADRL 实际是 adrp 和 add  两个指令而来

adrp 实际也是个伪指令

个人感觉

adrp 应该是取当前 pc 内存对齐后的数值,然后行为类似 mov

可能mov 因为字节码长度限制的原因 不能把这么大的数直接放入 x0,所以用 adrp 伪指令

整理以下意思应该是这样的:

adrp x0, #0x7b13862000        # 获取pc内存对齐后的地址 0x7b13862000 复制给 x0
add x0, x0, #0x548                # x0 = x0 + 0x548

这种行为很像  获取 基址 + 属性地址偏移

我们双击这条指令的 0x7B13862548  的值进行跳转

ADRL            X0, 0x7B13862548 

</p>

07.png

</p>

这应该就是我们 printf 里面字符串的参数了

 printf("sum:%d\n", ret);

也可以分析出里面 DCB 伪指令就是分配储存数据的

那么 ADRL 伪指令就是将储存 "sum:%d\n" 字符串的 指针给放到 X0 寄存器中

ADRL            X0, 0x7B13862548 

整合分析

# 0. 死循环记录地址
loc_7B13862714

#1.设置局部变量 到栈中 
MOV             W8, #0xA
STR             W8, [SP,#8]
MOV             W8, #0x14
STR             W8, [SP,#4]

# 2. 取出局部变量 作为参数调用 sum方法
LDR             W0, [SP,#8]
LDR             W1, [SP,#4]
BL              sum
# 3. 取出调用 sum 的返回值 给 栈中
STR             W0, [SP]

# 4. 取出sum 返回值 又放到 x1 寄存器 
# 取出储存 "sum:%d\n" 数据的 指针放到 x0 寄存器
# 调用 unk_74C717E790(printf) 方法  传递 x0 x1 参数
LDR             W1, [SP]
ADRL            X0, aSumD               ; "sum:%d\n"
BL              unk_74C717E790

# 5. getchar
BL              unk_74C717E7A0

# 6. 跳到上面的死循环
B               loc_74C717E714

下一章我会给大家分析下 sum 内部方法 的 调用约定 和 栈平衡

免费评分

参与人数 4吾爱币 +4 热心值 +4 收起 理由
debug_cat + 1 + 1 谢谢@Thanks!日常打卡~
woyucheng + 1 + 1 谢谢@Thanks!
allspark + 1 + 1 用心讨论,共获提升!
coder9527 + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

xixicoco 发表于 2023-4-27 01:19
好的,欢迎科普文章
Dreadnought 发表于 2023-4-27 09:44
han109014 发表于 2023-4-27 09:56
laustar 发表于 2023-4-27 10:06
        用心讨论,共获提升!
howe1 发表于 2023-4-27 10:50
感谢分享
xcyzh1992 发表于 2023-4-27 10:51
谢谢分享,学习中
aonima 发表于 2023-4-27 13:07
感谢分享
kerwenfly 发表于 2023-4-29 08:21
收藏了,有空慢慢没研究~
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 11:42

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表