Packet & .new

Packet : {...}

花括号表示一instruction packet ,在 packet内,多条指令逻辑上并发,但有上限,硬件允许受限的依赖前递。

.new: 在packet标识需要用到的前递

rX.new:引用本 packet 内刚产生的 rX。

p0.new:引用本 packet 内刚产生的谓词 p0。

在 Hexagon 里,谓词 (predicate) 就是布尔寄存器,专门用来做“条件执行” 和 “条件跳转”。

比如

{ if (p0.new) jump:nt loc_2552C
p0 = cmp.gt(r7, ##0x7FE ) }

p0.new就是后面cmp获得的谓词。

注意packet里的两个指令是没有先后顺序的。

寄存器与寄存器对

  • 通用寄存器: r0-r31
  • 谓词寄存器: p0 - ?
  • 特殊寄存器: sp (栈指针) 、 lr (返回地址) 、 pcugp (user global pointer)
  • 寄存器对: rY:X 表示64-bit值 (低 32 位通常在右侧寄存器 rX)

内存访问指令

load/store

  • memw(addr) : 32-bit
  • memh(addr) : 16-bit
  • memb(addr): 8-bit
  • memub(addr): unsigned 8-bit
  • memd(addr): 64-bit load

通过等号可以很轻松的区分是load还是store

post-increment

$rY = mem^*(rA++ #imm)$

用C语言的形式来理解 非常像

rY = *rA++

r2 = memub(r1++ #1):读 *(uint8_t*)r1,然后 r1++

##imm / @pcrel(大立即数/重定位)

  • add(pc, ##sym@pcrel):得到符号地址(PC 相对重定位),类似 “取全局地址/常量池地址”。
  • memw(r0 + ##-0x101EC):带大立即数偏移的寻址(常见于访问 GOT/全局区/固定偏移表)。

算数与逻辑

加减

  • add(rA , rB) / add(rA , #imm)
  • sub(rA,rB) / sub(rA, #imm)
  • neg(rX) : rX = -rX

位运算

and \ or \ xor \not

形式和加减差不多

移位

  • asl(rX, #n) : 算术左移
  • lsr(rX, #n): 逻辑右移

其他

addasl(rA, rB, $n)

r13 = addasl(r1, r0, #1)r13 = r1 + (r0 << 1)

mpyi(rX , #imm) : 乘立即数

比较、谓词与条件执行

比较生成谓词

  • p0 = cmp.eq(rA, rB/#imm):相等
  • p0 = cmp.gt(rA, rB/#imm):有符号大于
  • p0 = cmp.gtu(rA, rB/#imm):无符号大于

5.2 谓词化执行(Predication)

  • if (p0) insn
  • if (!p0) insn
  • if (p0.new):使用同 packet 内刚生成的 p0

位测试/置位/清位

  • tstbit(rX, #b):测试位 b(返回谓词/用于 p0)
  • setbit(rX, #b):设置位 b
  • clrbit(rX, #b):清除位 b
  • bitsclr(rX, #mask_or_bit): 测试位是否为0

控制流

跳转

  • jump label:无条件跳转
  • if (p0) jump:t label:条件跳转
  • :t / :nt:分支形式/预测提示。对语义可先忽略,当作 jump。

6.2 调用

  • call func:直接调用(目标在指令里)
  • callr rX:间接调用(函数指针)
    • 例:r0 = memw(r17++ #4); callr r0:遍历函数指针表并调用

6.3 返回/栈帧

  • allocframe(#imm):建立栈帧
  • deallocframe:撤销栈帧
  • dealloc_return:撤栈并返回(组合指令)
  • jumpr lr:跳回返回地址(return)

硬件零开销循环

  • loop0(target, rN):设置 loop0,重复执行从当前位置到 :endloop0 的区间 rN 次,回跳目标为 target
  • :endloop0:循环结束边界标记(不是普通指令)
{ loop0(loc_219DC, r10) ... }

... } :endloop0

通常相当于 for (i = 0; i < r10; i++) { ... }

原子/锁:memw_locked

  • r0 = memw_locked(addr):带 reservation 的 load(类似 load-linked)

  • memw_locked(addr, p0) = rX:条件 store(类似 store-conditional),成功与否反映在谓词 p0(或由汇编约定给出)

reservation: 是指 CPU 在执行一条“锁定加载” 时,建立的仅对于该线程有效的监视状态。如果立刻尝试“条件写回”同一个位置,并且在此期间没人改过它,就允许这次写回成功

trap0(#1)

用于进入内核 类似int8

lilac-ctf Gateway

这个题目就用到了 hexagon 架构。

交互逻辑中有输入的就只有输入ip

尝试输入一个很长的内容

❯ ./qemu-hexagon pwn
=== Lilac Gate Way ===
1. Manage.
2. Reset.
3. Exit.
1
=== Lilac Gate Way ===
1. Register Service.
2. Delete Service.
3. Show Service.
172.16.0.1:7777|Location Lookup Serviceaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Input ip:port|description
Example:
172.16.0.1:7777|Location Lookup Service
<<[1] 8122 bus error ./qemu-hexagon pwn

出现了bus error 应该是越界了

对于这种陌生的架构 IDA中可以通过定位字符串的方式定位函数。

找 “172.16.0.1:7777|Location Lookup Service”

很快就找到了 在 sub_20FD0:

重命名为register

可以猜测出在hexagon中应该是通过r0在传参数

这里面用了三个关键函数

有一个显然时puts

还有 剩下两个

可以用strace来跟踪系统调用进行猜测

这里我最初的判断是错误的 我猜测第一个是readinput

第二个是parse

后来看 xref 发现 几乎每次put后都跟着readinput 说明它应该不是readinput 可能是刷新缓冲区的操作。

那么后面那个就是readinput

前面一个指令r0 = add(fp, #-0x68) 中看,这样推测也更为合理。

然后就需要考虑hexagon的栈结构。搜索后发现和x86差不多。

可以写脚本先输入(0x6D)字符验证。

[*] Got EOF while reading in interactive
$
[*] Interrupted
[*] Process '/home/juryorca/pwn/lilac/Gateway/qemu-hexagon' stopped with exit code -7 (SIGBUS) (pid 12940)

果然直接bus error了

而如果是0x69 0x6a这样还能稍微撑一会 应该是栈被迁移的特征

show的时候会少显示一个字符 猜测是回车前的字符自动截断变成\x00

然后就是找gadget的事情了,这里我直接搬一个网上wp找的gedget

0x217e4:
text:000217E4 { r17:16 = memd(sp + #0x10+var_8)
.text:000217E6 r19:18 = memd(sp + #0x10+var_10) }
.text:000217E8 { dealloc_return }

.text:000214F4                 { r0 = r16 }
.text:000214F8 { r1 = r17 }
.text:000214FC { r2 = r18 }
.text:00021500 { r6 = r19 }
.text:00021504 { trap0(#1) }

栈的地址未知 可以通过栈迁移的方式 。

从程序的交互行为中可以推测出 应该是存储了输出到某个值

应该是存储到了bss段 这个逆向我实在看不懂

https://rocketma.dev/2026/01/27/gateway/ 提到了动调 有这个应该好调很多。

目前来看应该是存到了bss 开头位置

然后打ROP