From:https://zhuanlan.zhihu.com/p/29145513
win10 arm 汇编环境
Windows 平台下搭建 ARM 汇编集成环境:https://jingyan.baidu.com/article/4b52d70288bfcdfc5c774ba5.html
要调试 ARM 程序,我们需要:
本篇教程将基于 x86 平台的 Ubuntu 20,介绍如何搭建 ARM 的交叉编译、运行和调试环境。
Ubuntu 20 的源中提供了多个 arm-gcc 的软件包,以 gcc 5 为例可以通过 "apt search" 命令找到 "gcc-5-arm-linux-gnueabi" 和 "gcc-5-arm-linux-gnueabihf" 两个软件包。这两个软件包安装的编译工具是一样的,只是与浮点数相关的默认编译选项不同。由于我们虚拟的环境没有 FPU,只需要安装 "gcc-5-arm-linux-gnueabi" 就可以了。
安装完成后可以在 "/usr/bin/arm-linux-gnueabi-*" 找到相关的编译工具链,包含常用的 gcc、as 和 ld 等。
只要使用如下两条命令,就可以实现对 ARM 汇编的编译:
$ arm-linux-gnueabi-as [source file] –o [object file]
$ arm-linux-gnueabi-ld [object file] –o [executable file]
可以使用如下命令编译经典的 "hello world" 程序,用于后续章节的实验:
$ arm-linux-gnueabi-gcc-5 hello.c –g –o hello -static
示例截图:
From:Ubuntu 18.04安装arm-linux-gcc交叉编译器 :https://www.cnblogs.com/tansuoxinweilai/p/11602830.html
我们都知道 Ubuntu 有一个专门用来安装软件的工具 apt,我们可以用它来全自动安装 arm-linux-gcc。
首先 Ctrl+Alt+T 弹出终端,使用如下命令进行 arm-linux-gcc 的安装:
sudo apt-get install gcc-arm-linux-gnueabihf
使用如下命令进行 arm-linux-g++ 的安装:
sudo apt-get install g++-arm-linux-gnueabihf
如果要卸载时使用如下命令进行移除,arm-linux-gcc 的卸载:
sudo apt-get remove gcc-arm-linux-gnueabihf
arm-linux-g++ 的卸载:
sudo apt-get remove g++-arm-linux-gnueabihf
64 位的 Ubuntu 系统,那就安装64位的arm-linux-gcc交叉编译器,直接安装就能成功:
例如:arm-linux-gcc-4.6.4-arm-x86_64.tar.bz2
下载地址:https://pan.baidu.com/s/1xuh8M8bQHfZt_w6h4vRKeg 提取码:uk85
1. 先把下载好的安装包移动到根目录下的tmp目录中( /tmp )
2. 使用 tar 命令解压安装包,即在Terminal中输入以下命令:( 前面的 sudo 表示使用 root 权限执行该命令 )
sudo tar -xjvf /tmp/arm-linux-gcc-4.6.4-arm-x86_64.tar.bz2 -C /
注意是大写的字母 C,此命令会把安装包解压到根目录下的 opt 的 TuxamitoSoftToolchains里面(/opt/TuxamitoSoftToolchains)
如图逐层查看找到 gcc-4.6.4 所在的位置:/opt/TuxamitoSoftToolchains/arm-arm1176jzfssf-linux-gnueabi
3. 解压完成后,再在(/usr/local)中创建一个新目录 arm,即在 Terminal 中输入以下命令:
sudo mkdir /usr/local/arm
创建 arm 目录成功后,还需要给它解放全部权限,即在 Terminal 中输入以下命令:
sudo chmod 777 /usr/local/arm
4. 在解压出来的目录中找到并把整个 gcc-4.6.4目录复制到刚刚建好的arm目录中,命令如下:
先 cd 切换到 gcc-4.6.4 所在目录(切换后先ls看一下有没有 gcc-4.6.4 目录):
cd /opt/TuxamitoSoftToolchains/arm-arm1176jzfssf-linux-gnueabi/
再执行 cp 复制命令,-r 表示整个目录以及里面的任何东西
sudo cp -r gcc-4.6.4 /usr/local/arm
5.打开(/etc/profile)配置环境变量和库变量,目的是以后可以在任何位置使用该交叉编译器,命令如下:
sudo vi /etc/profile
用 vi 或者 vim 打开后,在文件最后添加两行,并输入以下代码:第一行是添加执行程序的环境变量,第二行是库文件的路径
export PATH=$PATH:/usr/local/arm/gcc-4.6.4/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/arm/gcc-4.6.4/lib
然后保存退出即可。
6. 使用 source 命令重新加载生效该配置文件
source /etc/profile
7.检验是否安装成功,在 Terminal 输入以下命令输出版本信息:
arm-linux-gcc -v
结果如图所示:得到刚刚安装的4.6.4版
再随便写一个 1.c 文件,能编译成功说明已经完美安装。例如:
arm-linux-gcc 1.c -o pp
再 file 命令查看编译后的是不是 arm 的可执行文件:
file pp
可以看到编译后的可执行文件是在 32-bit 的 ARM 架构上运行的。
注意:有些做完上述步骤还是不能用arm-linux-gcc的话,出现如下图所示错误:
这和时候需要在 “/home/用户名” 目录下的 ".bashrc" 隐藏文件下加上和 “/etc/profile” 一样的两句
export PATH=$PATH:/usr/local/arm/gcc-4.6.4/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/arm/gcc-4.6.4/lib
这个 ".bashrc" 是一个隐藏文件,需要 ls -a 命令才能看见!用户名就是你自己的linux登录账号。
同样用 vi或者vim打开它,在最后添加两行:
QEMU 来创建一个,[教程在这]:https://blog.csdn.net/zerokkqq/article/details/79621769
最简单的运行环境是使用 qemu-user-static 模拟运行静态编译的可执行程序。
我们可以使用如下命令模拟运行上一节创建的 hello 程序:
# 首先安装 qemu-user-static,若已安装可以忽略这一步
$ sudo apt install qemu-user-static
# 直接执行 hello 程序
$ qemu-arm-static hello
# 启动 gdbserver 等待 gdb 连接
$ qemu-arm-static –g [gdbserver port] hello
上述命令运行后会启动一个 qemu 自带的 gdbserver,监听你通过 "-g" 选项指定的端口。可以在另一个窗口中启动 gdb 进行远程调试(远程调试的细节,将在第三章介绍)。
Linux 下 ARM 程序的编译运行及调试:https://www.jianshu.com/p/dc8e263d6466
安装 qemu
sudo apt-get install qemu qemu-arm-static qemu-kvm-extras
arm 程序的编译运行
arm-linux-gcc -o hello-arm hello.c
qemu-arm hello-arm
安装 gdb-multiarch
sudo apt-get install gdb-multiarch
arm 程序的调试
qemu-arm -g 1234 hello-arm
gdb-multiarch hello-arm
ARM汇编学习(一)搭建ARM汇编模拟环境( 图文教程 ):https://www.veryarm.com/65170.html
qemu-user-static 的方式比较简单,但功能也很局限,Azeria-labs 的教程中介绍了另一种方法,使用 qemu 创建一台虚拟树莓派。首先你需要安装 qemu-system :
$ sudo apt install qemu-system
为了虚拟一台树莓派,你还需要下载专为树莓派定制的debian镜像(raspbian)和支持树莓派的内核文件。
raspbian镜像下载地址:https://www.raspberrypi.org/downloads/raspbian/
树莓派内核下载地址:https://github.com/dhruvvyas90/qemu-rpi-kernel
Raspbian 的镜像有两个版本,一个带图形界面的完整版和一个没有图形界面的 lite 版本,对于我们的实验而言 lite 版本就足够了。内核文件有多个,选择内核版本最新的那个就可以了。下载完上述文件后,创建一个“arm_vm”目录,将上述文件一起放置在该目录下。然后执行如下命令:
$ unzip .zip
$ fdisk –l
你应该可以看到,类似如下内容:
Disk 2017-08-16-raspbian-stretch-lite.img: 1.7 GiB, 1854418944 bytes, 3621912 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xee397c53
Device Boot Start End Sectors Size Id Type
2017-08-16-raspbian-stretch-lite.img1 8192 93813 85622 41.8M c W95 FAT32 (LBA)
2017-08-16-raspbian-stretch-lite.img2 94208 3621911 3527704 1.7G 83 Linux
注意标红的部分,可以看到文件系统从94208扇区开始。我们将这个值乘以512,本例中为“94208 * 512=48234496”,这就是文件系统其实位置的偏移字节数,在下面的命令中我们会用到:
$ sudo mkdir /mnt/raspbian
$ sudo mount -v -o offset=48234496 -t ext4 [path-of-your-img-file.img] /mnt/raspbian
$ sudo vi /mnt/raspbian/etc/ld.so.preload
将上述文件中的所有内容用“#”注释掉,保存修改并退出。
$ sudo vi /mnt/raspbian/etc/fstab
如果fstab文件中有出现mmcblk0字符串,那么将“/dev/mmcblk0p1”替换为“/dev/sda1”,将“/dev/mmcblk0p2”替换为“/dev/sda2”,保存后退出。至此,系统配置的修改完成,可以将“/mnt/raspbian”卸载掉。
$ sudo umount /mnt/raspbian
你可以进入“arm_vm”目录,使用如下脚本启动虚拟机:
#!/usr/bin/env bash
qemu-system-arm -kernel kernel-qemu-4.4.34-jessie \
-cpu arm1176 \
-m 256 \
-M versatilepb \
-serial stdio \
-append "root=/dev/sda2 rootfstype=ext4 rw" \
-drive format=raw,file=2017-08-16-raspbian-stretch-lite.img \
-redir tcp:5022::22 \# 为ssh预留
-redir tcp:3011::3011 \# 为gdbserver预留,用于远程调试
-no-reboot 1> /dev/null 2>&1 &
虚拟机启动后默认的登录密码是“raspberry”。为了更方便的使用虚拟机,我们需要开启ssh服务,并设置开机启动。
$ sudo service ssh start
$ sudo update-rc.d ssh enable
此时,你应该已经可以使用如下命令,通过ssh访问虚拟机了:
$ ssh [email protected] -p 5022
我们可以使用scp命令通过ssh,将上一节编译的hello程序上传到虚拟机中执行:
scp -P 5022 hello [email protected]:/tmp
进入虚拟机的tmp目录,可以看到我们上传的hello程序尝试执行,应该会输出久违的“hello world!”,说明我们的交叉编译环境搭建是正确的。至此我们的虚拟树莓派环境搭建完毕。
调试环境的搭建是最重要的也是坑最多的。为了模拟真实IoT安全实战中远程调试的场景,我们将介绍如何交叉编译gdbserver并上传至虚拟机进行远程调试。为了获得类似pwndbg那样强大的调试效果,我们将介绍如何安装使用专为IoT安全设计的gef增强脚本。
在使用gdb进行调试之前,我们需要先安装gdb-multiarch。顾名思义,它是gdb支持多中硬件体系架构的版本。之所以要安装gdb-multiarch,是因为Ubuntu默认安装的gdb只支持x86/x64架构,你可以启动gdb然后输入命令“set
architecture arm”查看,gdb会提示错误。
# 安装gdb-multiarch
$ sudo apt install gdb-multiarch
# 启动gdb-multiarch
$ gdb-multiarch
在分析IoT设备的安全性时,我们往往需要上传gdbserver进行远程调试。在我们的实验环境中(事实上我们的Raspbian系统自带gdb),我们也可以模拟搭建一个远程调试环境。首先,我们需要获取gdb的源码(包含了gdb源码和gdbserver源码),版本需要与我们本地的gdb版本一致,因为gdbserver需要与gdb版本保持一致,否则容易出现非预期的问题。你可以在这个地址,找到gdb各版本的源码:http://ftp.gnu.org/gnu/gdb/。
下载解压后进入“gdb-
$CC="arm-linux-gnueabi-gcc-5" CXX="arm-linux-gnueabi-g++-5" ./configure
--target=arm-linux-gnueabi --host="arm-linux-gnueabi"
--prefix="setup-directory"
$ make install
然后,在你通过“--prefix”选项指定的路径下,就可以找到编译完成的gdbserver了。使用file命令查看,应该可以看到类似如下输出:
$ file arm-linux-gnueabi-gdbserver
arm-linux-gnueabi-gdbserver:
ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically
linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 3.2.0,
BuildID[sha1]=32ad2025951ee428276ac2fbadb199bfd39e2278, not stripped
使用scp将gdbserver上传到我们的虚拟树莓派中并启动:
$ ln -s arm-linux-gnueabi-gdbserver gdbserver
$ gdbserver 0.0.0.0:2333 hello
Process hello created; pid = 702
Listening on port 2333
至此,我们的远程调试环境搭建完毕,下一节,我们将引入gef增强脚本。
gef是一个支持多种硬件体系结构的gdb增强脚本,非常适合IoT安全领域应对多变的硬件平台。你可以参考github主页(https://github.com/hugsy/gef)的README,进行安装配置。不过需要注意的是,gef依赖的第三方模块keystone-engine需要手动安装,因为pip源提供的安装是无效的。建议先通过pip安装,如果安装后gef的部分功能仍无法使用,可以卸载通过pip安装的第三方模块,在github上(https://github.com/keystone-engine/keystone)下载最新源码,手动编译安装(参见:http://www.keystone-engine.org/docs/)。
安装完成后开启gdb调试,你将看到类似如下的界面:
首先设置目标硬件体系架构为arm:
gef> set architecture arm
我们使用gef-remote命令连接gdbserver,如果使用gdb自带的“target remote”命令会出现一些非预期的问题(参见:https://github.com/hugsy/gef/issues/7)。
gef> gef-remote –q 127.0.0.1:2333
你应该能看到类似如下的输出:
至此,我们的调试环境配置完毕了。
[1] gef官方文档,http://gef.readthedocs.io/en/master/
[2] gdb调试利器,http://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/gdb.html
[3] gdb中应该知道的几个调试方法,https://coolshell.cn/articles/3643.html
本文由看雪翻译小组 ljcnaix 原创 转载请注明来自看雪社区
ARM 汇编 与 ARM GNU汇编 区别:https://blog.csdn.net/tabactivity/article/details/90054443
ARM 汇编开发,有两种开发方式,一种是使用 ARM 汇编,一种是使用 ARM GNU 汇编。两种汇编开发,使用的汇编指令是完全一样的,区别是宏指令,伪指令,伪操作不一样。其实两种开发方式的区别在于所使用的编译工具不一样。
对于 ARM 汇编,使用的是 ARM 公司开发的编译器,而 ARM GNU 汇编,是使用 GNU 为 ARM 指令集开发的编译器,也就是arm-gcc。
两种常用的 ARM 的编译开发环境
DS5:ARM 提供的集成开发软件。使用的是 ARM 提供的工具链进行程序编译
GNU 开发环境: 由 GNU 的汇编器 as,交叉编译器 gcc,和 链接器ld 等组成
伪操作 |
语法格式 |
作用 |
GBLA |
GBLA Varible |
声明一个全局的算术变量,并将其初始化为0 |
GBLL |
GBLL Varible |
声明一个全局的逻辑变量,并将其初始化成{FALSE} |
GBLS |
GBLS Varible |
声明一个全局的字符串变量,并将其初始化成空串 |
LCLA |
LCLA Varible |
声明一个局部的算术变量,并将其初始化为0 |
LCLL |
LCLL Varible |
声明一个局部的逻辑变量,并将其初始化成{FALSE} |
LCLS |
LCLS Varible |
声明一个局部的字符串变量,并将其初始化成空串 |
SETA |
SETA Varible expr |
给一个全局或局部算术变量赋值 |
SETL |
SETL Varible expr |
给一个全局或局部逻辑变量赋值 |
SETS |
SETS Varible expr |
给一个全局或局部字符串变量赋值 |
RLIST |
name LIST {list of registers} |
为一个通用寄存器列表定义名称 |
CN |
name CN expr |
为一个协处理器的寄存器定义名称 |
CP |
name CP expr |
为一个协处理器定义名称 |
DN/SN |
name DN/SN expr |
DN/SN为一个双精度/单精度的VFP寄存器定义名称 |
FN |
name FN expr |
为一个FPA浮点寄存器定义名称 |
LTORG |
LTONG |
声明一个数据缓冲池(文字池)的开始 |
MAP |
MAP expr {, base-register} |
定义一个结构化的内存表(storage map)的首地址 |
FIELD |
{label} FIELF expr |
定义一个结构化内存表中的数据域 |
SPACE |
{label} SPACE expr |
分配一块连续内存单元,并用0初始化 |
DCB |
{label} DCB expr {,expr}.. |
分配一块字节内存单元,并用expr初始化 |
DCD/ DCDU |
{label} DCD/DCDU expr {,expr}… |
分配一块字内存单元, 并用expr初始化 |
DCDO |
{label} DCDO expr {,expr}… |
分配一块字对齐的字内存单元, 并用expr初始化 |
DCFD/DCFDU |
{label} DCFD{U} fpliteral ,{,fpliteral}… |
为双精度的浮点数分配字对齐的内存单元 |
DCFS/DCFSU |
{label} DCFS{U} fpliteral ,{,fpliteral}… |
为单精度的浮点数分配字对齐的内存单元 |
DCI |
{label} DCI expr, {expr}… |
ARM代码分配一段字对齐的内存单元,填充expr(二进制指令码),THUMB代码中,分配一段半字对齐的半字内存单元。 |
DCQ/ DCQU |
{label} DCQ{U} {-} literal, {, {-} literal}… |
分配一段以双字(8个字节)为单位的内存 |
DCW/DCWU |
{label} DCW{U} {-} literal, {, {-} literal}… |
DCW用于分配一段半字对齐的半字内存单元 |
1、AREA:创建一段新的程序代码或数据区。
格式 : AREA name, {,attr,} …
其中,name是程序段名, atrr是段名属性
对于属性,有以下一些:
CODE: 用于定义代码段,默认为是READONLY
DATA: 用于定于数据段,默认为READWRITE
READONLY: 指定本段的内容只读
READWRITE: 指定本段的内容可读可写
ALIGN: 指定对齐为2次幂
COMMON: 定义通用的段。不包含任何用户的代码和数据。各源文件中同名的COMMON属性段共享同一段存储单元
6、IMPORT
格式: IMPORT 标号,[,WEAK]
表示该引用的标号在其他源文件中,单要在当前文件中引用。
WEAK表示找不到该标号时,也不报错,一般该标号置为0,如果是B 或BL指令用到该标号,该指令置为nop。
该标号会加入到当前源文件的符号表中。
ARM伪指令包括: ADR, ADRL,LDR ,NOP
THUMB伪指令包括:ADR, LDR, NOP
伪指令 |
语法格式 |
作用 |
ADR |
ADR{cond} register, expr |
将基于PC或基于寄存器的地址值读取到寄存器中。小范围的地址读取 |
ADRL |
ADRL{cond} register, expr |
将给予PC或基于寄存器的地址值读取到寄存器中。中等范围的地址读取 |
LDR |
LDR {cond} register, =[expr|label] |
将一个32位的立即数或者一个地址值读取到寄存器中。大范围的地址读取 |
NON |
NOP |
在汇编时,被替换成空操作 |
伪操作 |
语法格式 |
作用 |
.byte |
.byte expr {,expr}… |
分配一段字节内存单元,并用expr初始化 |
.hword/.short |
.hword expr {,expr}… |
分配一段半字内存单元,并用expr初始化 |
.ascii |
.ascii expr {,expr}… |
定义字符串expr |
.asciz/.string |
.asciz expr {,expr}… |
定义字符串expr(会增加/0为结束符) |
.floar/.single |
.float expr {,expr}… |
定义32bit IEEE浮点数expr |
.double |
.doubel expr {,expr}… |
定义64bit IEEE浮点数expr |
.word/.long/.int |
.word expr {,expr}… |
分配一段字内存单元,并用expr初始化 |
.fill |
.fill repeat {,size} {,value} |
分配一段字节内存单元,用sieze长度value填充repeat次 |
.zero |
.zero size |
分配一段字节内存单元,并用0填充内存 |
.space/.skip |
.space size, {,value} |
分配一段内存单元,用value将内存初始化 |
.section |
.section expr |
定义一个段 |
.text |
.text {subsection} |
代码段, |
.data |
.data{subsection} |
数据段 |
.bss |
.bss{subsection} |
bss段 |
.cond 16/.thumb |
.code 16/.thumb |
表示之后的汇编指令使用THUMB指令集 |
.code 32/.arm |
.code 32/.arm |
表示之后的汇编指令使用ARM指令集 |
.end |
.end |
标记汇编文件的结束 |
.include |
.include "filename" |
将一个源文件包含到当前源文件中 |
.align/.balign |
.align {alignment} {,fill},{max} |
通过填充字节使当前位置满足一定的对齐格式 |
两种开发环境下的汇编代码,有较多不同的点,主要是符号及伪操作的不同。
ARM汇编的伪操作符 |
GNU汇编的伪操作符 |
INLCUDE |
.include |
NUM EQU 25 |
.equ NUM, 25 |
EXPORT |
.global |
IMPORT |
.extern |
DCD |
.long |
IF: DEF: |
.ifdef |
ELSE |
.else |
ENDIF |
.endif |
OR |
| |
SHL |
<< |
RN |
.req |
GBLA |
.global |
NUM SETA 16 |
.equ NUM , 16 |
MACRO |
.macro |
MEND |
.endm |
END |
.end |
AREA WORD, CODE, READONLY |
.text |
AREA BLOCK, DATE, READWRITE |
.data |
CODE32 |
.arm |
CODE16 |
.thumb |
LTORG |
.ltorg |
% |
.fill |
ENTRY |
ENTRY: |
ldr x0,=0xff |
ldr x0,=0xff |
原文链接:http://www.lujun.org.cn/?p=3943