Skip to content

Commit 532d8aa

Browse files
committed
Add shellcraft support for LoongArch64
1 parent 2c344b5 commit 532d8aa

File tree

10 files changed

+294
-0
lines changed

10 files changed

+294
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Shellcraft module containing generic LoongArch64 shellcodes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Shellcraft module containing LoongArch64 shellcodes for Linux.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<%
2+
from pwnlib.shellcraft import loong64, pretty
3+
from pwnlib.constants import Constant
4+
from pwnlib.abi import linux_loong64_syscall as abi
5+
from six import text_type
6+
%>
7+
<%page args="syscall = None, arg0 = None, arg1 = None, arg2 = None, arg3 = None, arg4=None, arg5=None"/>
8+
<%docstring>
9+
Args: [syscall_number, \*args]
10+
Does a syscall
11+
12+
Any of the arguments can be expressions to be evaluated by :func:`pwnlib.constants.eval`.
13+
14+
Example:
15+
16+
>>> print(pwnlib.shellcraft.loong64.linux.syscall('SYS_execve', 1, 'sp', 2, 0).rstrip())
17+
/* call execve(1, 'sp', 2, 0) */
18+
c.li a0, 1
19+
c.mv a1, sp
20+
c.li a2, 2
21+
c.li a3, 0
22+
/* mv a7, 0xdd */
23+
xori a7, zero, 0x722
24+
xori a7, a7, 0x7ff
25+
ecall
26+
>>> print(pwnlib.shellcraft.loong64.linux.syscall('SYS_execve', 2, 1, 0, 20).rstrip())
27+
/* call execve(2, 1, 0, 0x14) */
28+
c.li a0, 2
29+
c.li a1, 1
30+
c.li a2, 0
31+
c.li a3, 0x14
32+
/* mv a7, 0xdd */
33+
xori a7, zero, 0x722
34+
xori a7, a7, 0x7ff
35+
ecall
36+
>>> print(pwnlib.shellcraft.loong64.linux.syscall().rstrip())
37+
/* call syscall() */
38+
ecall
39+
>>> print(pwnlib.shellcraft.loong64.linux.syscall('a7', 'a0', 'a1').rstrip())
40+
/* call syscall('a7', 'a0', 'a1') */
41+
/* setregs noop */
42+
ecall
43+
>>> print(pwnlib.shellcraft.loong64.linux.syscall('a3', None, None, 1).rstrip())
44+
/* call syscall('a3', ?, ?, 1) */
45+
c.li a2, 1
46+
c.mv a7, a3
47+
ecall
48+
>>> print(pwnlib.shellcraft.loong64.linux.syscall(
49+
... 'SYS_mmap', 0, 0x1000,
50+
... 'PROT_READ | PROT_WRITE | PROT_EXEC',
51+
... 'MAP_PRIVATE',
52+
... -1, 0).rstrip())
53+
/* call mmap(0, 0x1000, 'PROT_READ | PROT_WRITE | PROT_EXEC', 'MAP_PRIVATE', -1, 0) */
54+
c.li a0, 0
55+
c.lui a1, 1 /* mv a1, 0x1000 */
56+
c.li a2, 7
57+
c.li a3, 2
58+
c.li a4, 0xffffffffffffffff
59+
c.li a5, 0
60+
/* mv a7, 0xde */
61+
xori a7, zero, 0x721
62+
xori a7, a7, 0x7ff
63+
ecall
64+
>>> print(pwnlib.shellcraft.openat('AT_FDCWD', '/home/pwn/flag').rstrip())
65+
/* openat(fd='AT_FDCWD', file='/home/pwn/flag', oflag=0) */
66+
/* push b'/home/pwn/flag\x00' */
67+
li t4, 0x77702f656d6f682f
68+
sd t4, -16(sp)
69+
li t4, 0x67616c662f6e
70+
sd t4, -8(sp)
71+
addi sp, sp, -16
72+
c.mv a1, sp
73+
xori a0, zero, 0xffffffffffffff9c
74+
c.li a2, 0
75+
/* call openat() */
76+
/* mv a7, 0x38 */
77+
xori a7, zero, 0x7c7
78+
xori a7, a7, 0x7ff
79+
ecall
80+
</%docstring>
81+
<%
82+
if isinstance(syscall, (str, text_type, Constant)) and str(syscall).startswith('SYS_'):
83+
syscall_repr = str(syscall)[4:] + "(%s)"
84+
args = []
85+
else:
86+
syscall_repr = 'syscall(%s)'
87+
if syscall is None:
88+
args = ['?']
89+
else:
90+
args = [pretty(syscall, False)]
91+
92+
for arg in [arg0, arg1, arg2, arg3, arg4, arg5]:
93+
if arg is None:
94+
args.append('?')
95+
else:
96+
args.append(pretty(arg, False))
97+
while args and args[-1] == '?':
98+
args.pop()
99+
syscall_repr = syscall_repr % ', '.join(args)
100+
101+
registers = abi.register_arguments
102+
arguments = [syscall, arg0, arg1, arg2, arg3, arg4, arg5]
103+
regctx = dict(zip(registers, arguments))
104+
%>\
105+
%if any(a is not None for a in arguments):
106+
${loong64.setregs(regctx)}
107+
%endif
108+
syscall
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../common/linux/syscalls
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<%
2+
from pwnlib.util import lists, packing, fiddling, misc
3+
from pwnlib.constants import eval, Constant
4+
from pwnlib.context import context as ctx # Ugly hack, mako will not let it be called context
5+
from pwnlib.log import getLogger
6+
from pwnlib.shellcraft import loong64, registers, pretty, okay
7+
import six
8+
log = getLogger('pwnlib.shellcraft.loong64.mov')
9+
%>
10+
<%page args="dst, src"/>
11+
<%docstring>
12+
Move src into dst without newlines and null bytes.
13+
14+
If src is a string that is not a register, then it will locally set
15+
`context.arch` to `'loong64'` and use :func:`pwnlib.constants.eval` to evaluate the
16+
string. Note that this means that this shellcode can change behavior depending
17+
on the value of `context.os`.
18+
19+
Args:
20+
21+
dst (str): The destination register.
22+
src (str): Either the input register, or an immediate value.
23+
24+
Example:
25+
26+
>>> print(shellcraft.loong64.mov('t0', 0).rstrip())
27+
addi.d $t0, $r0, 0
28+
>>> print(shellcraft.loong64.mov('t0', 0x2000).rstrip())
29+
addi.d $t0, $r0, 2
30+
lu52i.d $t0, $t0, 0
31+
>>> print(shellcraft.loong64.mov('t0', 0xcafebabe).rstrip())
32+
addi.d $t0, $r0, 202
33+
lu52i.d $t0, $t0, -21
34+
lu52i.d $t0, $t0, -1346
35+
>>> print(shellcraft.loong64.mov('t1', 'sp').rstrip())
36+
addi.d $t1, $sp, 0
37+
38+
</%docstring>
39+
<%
40+
if not isinstance(dst, str) or dst not in registers.loong64:
41+
log.error("Unknown register %r", dst)
42+
return
43+
44+
if isinstance(src, str) and src not in registers.loong64:
45+
src = eval(src)
46+
47+
if isinstance(src, str) and src not in registers.loong64:
48+
log.error("Unknown register %r", src)
49+
return
50+
51+
src_reg = registers.loong64.get(src, None)
52+
dst_reg = registers.loong64[dst]
53+
54+
# If source register is zero, treat it as immediate 0
55+
if src_reg == 0:
56+
src = 0
57+
src_reg = None
58+
%>
59+
60+
% if dst_reg == 0 or dst_reg == src_reg:
61+
/* ld ${dst}, ${src} is a noop */
62+
% elif src_reg is not None:
63+
addi.d $${dst}, $${src}, 0
64+
% else:
65+
## Source is an immediate, normalize to [0, 2**64)
66+
67+
<% src = packing.unpack(packing.pack(src, word_size=64), word_size=64, sign=False) %>
68+
## Immediates are always sign-extended to 64-bit
69+
70+
% if src == 0:
71+
addi.d $${dst}, $r0, 0
72+
% else:
73+
<%
74+
parts = []
75+
fullsrc = src
76+
while src != 0:
77+
parts.append(packing.unpack(packing.pack((src & 0xfff), word_size=12, sign=False), word_size=12, sign=True))
78+
src = src >> 12
79+
%>
80+
% for idx, part in enumerate(reversed(parts)):
81+
% if idx == 0:
82+
addi.d $${dst}, $r0, ${part}
83+
% else:
84+
lu52i.d $${dst}, $${dst}, ${part}
85+
% endif
86+
% endfor
87+
% endif
88+
% endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<%docstring>LoongArch64 nop instruction.</%docstring>
2+
addi.d $r0, $r0, 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<%
2+
from pwnlib.shellcraft import loong64
3+
from pwnlib import constants
4+
from pwnlib.shellcraft import registers
5+
from six import text_type, binary_type
6+
%>
7+
<%page args="value"/>
8+
<%docstring>
9+
Pushes a value onto the stack.
10+
11+
Register t8 is not guaranteed to be preserved.
12+
</%docstring>
13+
<%
14+
is_reg = value in registers.loong64
15+
16+
if not is_reg and isinstance(value, (binary_type, text_type)):
17+
try:
18+
value = constants.eval(value)
19+
except (ValueError, AttributeError):
20+
pass
21+
%>
22+
% if not is_reg:
23+
${loong64.mov('t8', value)}
24+
<% value = 't8' %>\
25+
%endif
26+
addi.d $sp, $sp, -8
27+
st.d $${value}, $sp, 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<%
2+
from pwnlib.regsort import regsort
3+
from pwnlib.constants import Constant, eval
4+
from pwnlib.shellcraft import registers
5+
from pwnlib.shellcraft import loong64
6+
%>
7+
<%page args="reg_context, stack_allowed = True"/>
8+
<%docstring>
9+
Sets multiple registers, taking any register dependencies into account
10+
(i.e., given eax=1,ebx=eax, set ebx first).
11+
12+
Args:
13+
reg_context (dict): Desired register context
14+
stack_allowed (bool): Can the stack be used?
15+
16+
Example:
17+
18+
>>> print(shellcraft.setregs({'t0':1, 'a3':'0'}).rstrip())
19+
addi.d $a3, $r0, 0
20+
addi.d $t0, $r0, 1
21+
>>> print(shellcraft.setregs({'a0':'a1', 'a1':'a0', 'a2':'a1'}).rstrip())
22+
addi.d $a2, $a1, 0
23+
xor $a1, $a1, $a0 /* xchg a1, a0 */
24+
xor $a0, $a0, $a1
25+
xor $a1, $a1, $a0
26+
</%docstring>
27+
<%
28+
reg_context = {k:v for k,v in reg_context.items() if v is not None}
29+
sorted_regs = regsort(reg_context, registers.loong64)
30+
%>
31+
% if sorted_regs:
32+
% for how, src, dst in regsort(reg_context, registers.loong64):
33+
% if how == 'xchg':
34+
${loong64.xor(dst, dst, src)} /* xchg ${dst}, ${src} */
35+
${loong64.xor(src, src, dst)}
36+
${loong64.xor(dst, dst, src)}
37+
% else:
38+
${loong64.mov(src, dst)}
39+
% endif
40+
% endfor
41+
% endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<%docstring>A trap instruction.</%docstring>
2+
break 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<%
2+
from pwnlib.shellcraft import loong64
3+
from pwnlib.shellcraft import registers
4+
%>
5+
<%page args="dst,rs1,rs2"/>
6+
<%docstring>
7+
XOR two registers rs1 and rs2, store result in register dst.
8+
9+
Register t4 is not guaranteed to be preserved.
10+
</%docstring>
11+
<%
12+
if not isinstance(dst, str) or dst not in registers.loong64:
13+
log.error("Unknown register %r", dst)
14+
return
15+
if not isinstance(rs1, str) or rs1 not in registers.loong64:
16+
log.error("Unknown register %r", rs1)
17+
return
18+
if not isinstance(rs2, str) or rs2 not in registers.loong64:
19+
log.error("Unknown register %r", rs2)
20+
return
21+
22+
%>
23+
xor $${dst}, $${rs1}, $${rs2}

0 commit comments

Comments
 (0)