Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

riscv: avoid compressed instructions #2419

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions pwnlib/asm.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ def which_binutils(util, check_version=False):
'i386': ['x86_64', 'amd64'],
'i686': ['x86_64', 'amd64'],
'amd64': ['x86_64', 'i386'],
'arm': ['aarch64'],
'mips64': ['mips'],
'powerpc64': ['powerpc'],
'sparc64': ['sparc'],
Expand Down Expand Up @@ -273,8 +274,8 @@ def _assembler():
'ia64': [gas, '-m%ce' % context.endianness[0]],

# riscv64-unknown-elf-as supports riscv32 as well as riscv64
'riscv32': [gas, '-march=rv32gc', '-mabi=ilp32'],
'riscv64': [gas, '-march=rv64gc', '-mabi=lp64'],
'riscv32': [gas, '-march=rv32g', '-mabi=ilp32'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prevents you from using compressed instructions when manually writing shellcode and compiling using asm, no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, and therefore I am not very sure about this change. I don't know any way to avoid C and yet allow c.* opcodes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we thought about adding assembler command line args to context when adding riscv support? Or just asm?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do this like we do with thumb (separate arch) but I doubt whether it is worth it. Maybe we can revisit this one day.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to make two different archs? That way you could do asm -c riscv32 and asm -c riscv32c or something similar?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add this as a context.subarch similar to what is requested for mips and arm? It's not really a sub-architecture though

'riscv64': [gas, '-march=rv64g', '-mabi=lp64'],
}

assembler = assemblers.get(context.arch, [gas])
Expand Down Expand Up @@ -854,7 +855,7 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
universal_newlines = True
).strip()
if extract and len(relocs.split('\n')) > 1:
log.error('Shellcode contains relocations:\n%s' % relocs)
log.warn('Shellcode contains relocations:\n%s' % relocs)
else:
shutil.copy(step2, step3)

Expand Down
3 changes: 3 additions & 0 deletions pwnlib/elf/maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@
'aarch64':
'ee058ed24eeeadf26eecc5f26eaeecf28fcd8cd2efa5adf22f0ccef26f0ee0f2ee3fbfa980f39fd2e0ffbff2e0ffdff2e0fffff2e1633f8be2031faa080780d2010000d4222080d22e2088d2c20102caff6322cbe1633f8be80780d2010000d402041f8b4f2080d26c2080d28f010fcae0030faae1633f8b080880d2010000d4'
'e0031faaa80b80d2010000d4',
'riscv64':
'b73e736593ce3e76938e0e80b77f726f9b8fff02939e0e02b3cefe012338d1ffb77e73009b8e1e06b76f2f6d93cfcf66939e0e02b3cefe01233cd1ff130101ff1345c0f9b355014033c6ff019348707c93c8f87f73000000374600003301c140b35501409348007c93c8f87f73000000335605401335f07fb35501409348f07b93c8f87f73000000'
'33c5ff019348207a93c8f87f73000000',
}
15 changes: 15 additions & 0 deletions pwnlib/shellcraft/templates/riscv64/linux/cat2.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<%
from pwnlib.shellcraft.riscv64 import open, syscall, mov
from pwnlib.shellcraft import common
%>
<%page args="filename, fd=1, length=0x4000"/>
<%docstring>
Opens a file and writes its contents to the specified file descriptor.
Uses an extra stack buffer and must know the length.
</%docstring>

${open(filename)}
${mov('a2', length)}
sub sp, sp, a2
${syscall('SYS_read', 'a0', 'sp', 'a2')}
${syscall('SYS_write', fd, 'sp', 'a0')}
12 changes: 12 additions & 0 deletions pwnlib/shellcraft/templates/riscv64/linux/open.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<%
from pwnlib import shellcraft
%>
<%page args="filename, flags=0, mode='a3'"/>
<%docstring>
Opens a file
</%docstring>
<%
AT_FDCWD=-100
%>
${shellcraft.pushstr(filename)}
${shellcraft.syscall('SYS_openat', AT_FDCWD, 'sp', flags, mode)}
73 changes: 44 additions & 29 deletions pwnlib/shellcraft/templates/riscv64/linux/syscall.asm
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,23 @@ Example:

>>> print(pwnlib.shellcraft.riscv64.linux.syscall('SYS_execve', 1, 'sp', 2, 0).rstrip())
/* call execve(1, 'sp', 2, 0) */
c.li a0, 1
c.mv a1, sp
c.li a2, 2
c.li a3, 0
/* mv a7, 0xdd */
xori a7, zero, 0x722
sltiu a0, zero, 0x7ff | 1
sra a1, sp, zero
xori a2, zero, 0x7ff ^ 2
xori a2, a2, 0x7ff
xor a3, t6, t6
xori a7, zero, 0x7ff ^ SYS_execve /* 0xdd */
xori a7, a7, 0x7ff
ecall
>>> print(pwnlib.shellcraft.riscv64.linux.syscall('SYS_execve', 2, 1, 0, 20).rstrip())
/* call execve(2, 1, 0, 0x14) */
c.li a0, 2
c.li a1, 1
c.li a2, 0
c.li a3, 0x14
/* mv a7, 0xdd */
xori a7, zero, 0x722
xori a0, zero, 0x7ff ^ 2
xori a0, a0, 0x7ff
sltiu a1, zero, 0x7ff | 1
xor a2, t6, t6
xori a3, zero, 0x7ff ^ 0x14
xori a3, a3, 0x7ff
xori a7, zero, 0x7ff ^ SYS_execve /* 0xdd */
xori a7, a7, 0x7ff
ecall
>>> print(pwnlib.shellcraft.riscv64.linux.syscall().rstrip())
Expand All @@ -42,39 +43,53 @@ Example:
ecall
>>> print(pwnlib.shellcraft.riscv64.linux.syscall('a3', None, None, 1).rstrip())
/* call syscall('a3', ?, ?, 1) */
c.li a2, 1
c.mv a7, a3
sltiu a2, zero, 0x7ff | 1
sra a7, a3, zero
ecall
>>> print(pwnlib.shellcraft.riscv64.linux.syscall(
... 'SYS_mmap', 0, 0x1000,
... 'PROT_READ | PROT_WRITE | PROT_EXEC',
... 'MAP_PRIVATE',
... -1, 0).rstrip())
/* call mmap(0, 0x1000, 'PROT_READ | PROT_WRITE | PROT_EXEC', 'MAP_PRIVATE', -1, 0) */
c.li a0, 0
c.lui a1, 1 /* mv a1, 0x1000 */
c.li a2, 7
c.li a3, 2
c.li a4, 0xffffffffffffffff
c.li a5, 0
/* mv a7, 0xde */
xori a7, zero, 0x721
xor a0, t6, t6
li a1, 0x1000
xori a2, zero, 0x7ff ^ (PROT_READ | PROT_WRITE | PROT_EXEC) /* 7 */
xori a2, a2, 0x7ff
xori a3, zero, 0x7ff ^ MAP_PRIVATE /* 2 */
xori a3, a3, 0x7ff
xori a4, zero, -1
xor a5, t6, t6
xori a7, zero, 0x7ff ^ SYS_mmap /* 0xde */
xori a7, a7, 0x7ff
ecall
>>> print(pwnlib.shellcraft.openat('AT_FDCWD', '/home/pwn/flag').rstrip())
/* openat(fd='AT_FDCWD', file='/home/pwn/flag', oflag=0) */
/* push b'/home/pwn/flag\x00' */
li t4, 0x77702f656d6f682f
lui t4, 0xfffff & ((0x77702f65 >> 12) + 1)
xori t4, t4, 0x7ff & 0x77702f65
addi t4, t4, -0x800
lui t6, 0xfffff & ((0x6d6f682f >> 12) + 1)
xori t6, t6, 0x7ff & 0x6d6f682f
addi t6, t6, -0x800
slli t4, t4, 0x20
xor t4, t4, t6
sd t4, -16(sp)
li t4, 0x67616c662f6e
lui t4, 0xfffff & (~0x6761 >> 12)
xori t4, t4, ~0x7ff | 0x6761
addi t4, t4, -0x800
lui t6, 0xfffff & ((0x6c662f6e >> 12) + 1)
xori t6, t6, 0x7ff & 0x6c662f6e
addi t6, t6, -0x800
slli t4, t4, 0x20
xor t4, t4, t6
sd t4, -8(sp)
addi sp, sp, -16
c.mv a1, sp
xori a0, zero, 0xffffffffffffff9c
c.li a2, 0
sra a1, sp, zero
xori a0, zero, AT_FDCWD /* -0x64 */
xor a2, t6, t6
/* call openat() */
/* mv a7, 0x38 */
xori a7, zero, 0x7c7
xori a7, zero, 0x7ff ^ SYS_openat /* 0x38 */
xori a7, a7, 0x7ff
ecall
</%docstring>
Expand Down
98 changes: 56 additions & 42 deletions pwnlib/shellcraft/templates/riscv64/mov.asm
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import six
log = getLogger('pwnlib.shellcraft.riscv64.mov')
%>
<%page args="dst, src"/>
<%page args="dst, src, c=False"/>
<%docstring>
Move src into dst without newlines and null bytes.

Expand All @@ -26,30 +26,26 @@ Args:
Example:

>>> print(shellcraft.riscv64.mov('t0', 0).rstrip())
c.li t0, 0
>>> print(shellcraft.riscv64.mov('t0', 0x2000).rstrip())
c.lui t0, 2 /* mv t0, 0x2000 */
xor t0, t6, t6
>>> print(shellcraft.riscv64.mov('t0', 0x2000, c=True).rstrip())
c.lui t0, 0xfffff & (0x2000 >> 12)
>>> print(shellcraft.riscv64.mov('t5', 0x601).rstrip())
xori t5, zero, 0x601
>>> print(shellcraft.riscv64.mov('t5', 0x600).rstrip())
/* mv t5, 0x600 */
xori t5, zero, 0x1ff
xori t5, zero, 0x7ff ^ 0x600
xori t5, t5, 0x7ff
>>> print(shellcraft.riscv64.mov('t6', 0x181f).rstrip())
/* mv t6, 0x181f */
lui t6, 0xffffe
xori t6, t6, 0xfffffffffffff81f
lui t6, 0xfffff & (~0x181f >> 12)
xori t6, t6, ~0x7ff | 0x181f
>>> print(shellcraft.riscv64.mov('t5', 0x40b561f).rstrip())
/* mv t5, 0x40b561f */
lui t5, 0x40b5
xori t5, t5, 0x61f
lui t5, 0xfffff & (0x40b561f >> 12)
xori t5, t5, 0x7ff & 0x40b561f
>>> print(shellcraft.riscv64.mov('t0', 0xcafebabe).rstrip())
li t0, 0xcafebabe
>>> print(shellcraft.riscv64.mov('a0', 't2').rstrip())
>>> print(shellcraft.riscv64.mov('a0', 't2', c=True).rstrip())
c.mv a0, t2
>>> print(shellcraft.riscv64.mov('t1', 'sp').rstrip())
c.mv t6, sp
c.mv t1, t6 /* mv t1, sp */
>>> print(shellcraft.riscv64.mov('t1', 'sp', c=True).rstrip())
sra t1, sp, zero

</%docstring>
<%
Expand All @@ -66,7 +62,6 @@ if isinstance(src, str) and src not in registers.riscv:

src_reg = registers.riscv.get(src, None)
dst_reg = registers.riscv[dst]
tmp = 't6' if dst_reg != registers.riscv['t6'] else 't4'

# If source register is zero, treat it as immediate 0
if src_reg == 0:
Expand All @@ -82,49 +77,68 @@ encodes_no_newline = lambda a, not_a: not (a & 0xf == 0 or (a & 0xff0) >> 8 in [
% elif src_reg is not None:
## Source is a register
## Special case where c.mv would produce a newline
% if src_reg == 2 and dst_reg % 2 == 0:
c.mv ${tmp}, ${src}
c.mv ${dst}, ${tmp} /* mv ${dst}, ${src} */
% else:
% if c and not (src_reg == 2 and dst_reg % 2 == 0):
c.mv ${dst}, ${src}
% endif
% elif (src_reg >> 1) not in (0, 10):
sra ${dst}, ${src}, zero
% else:
not ${dst}, ${src}
not ${dst}, ${dst}
% endif
% else:
## Source is an immediate, normalize to [0, 2**64)

<% src = packing.unpack(packing.pack(src, word_size=64), word_size=64, sign=False) %>
<% srcn = src & 0xffffffffffffffff %>
## Immediates are always sign-extended to 64-bit

## 6-bit immediate for c.li
% if src < 0x20 or src >= 0xffffffffffffffe0:
c.li ${dst}, ${pretty(src)}
% if c and (srcn < 0x20 or srcn >= 0xffffffffffffffe0):

c.li ${dst}, ${pretty(src)}
## 6-bit immediate for c.lui
% elif dst_reg != 2 and src & 0xfff == 0 and ((src>>12) < 0x20 or (src>>12) >= 0xffffffffffffffe0):
c.lui ${dst}, ${pretty(src>>12)} /* mv ${dst}, ${pretty(src)} */

% elif c and (dst_reg != 2 and srcn & 0xfff == 0 and ((srcn>>12) < 0x20 or (srcn>>12) >= 0xffffffffffffffe0)):
c.lui ${dst}, 0xfffff & (${pretty(src)} >> 12)
## 12-bit immediate
% elif src < 0x800 or src >= 0xfffffffffffff800:
% if src & 0xf == 0 or (src & 0xfff) >> 8 in [0, 10]:
/* mv ${dst}, ${pretty(src)} */
xori ${dst}, zero, ${pretty(src ^ 0x7ff)}
xori ${dst}, ${dst}, ${pretty(0x7ff)}
% elif srcn < 0x800 or srcn >= 0xfffffffffffff800:
% if srcn == 0:
xor ${dst}, t6, t6
% elif srcn == 1:
sltiu ${dst}, zero, 0x7ff | ${pretty(src)}
% elif src & 0xf == 0 or (src & 0xfff) >> 8 in [0, 10]:
xori ${dst}, zero, 0x7ff ^ ${pretty(src)}
xori ${dst}, ${dst}, 0x7ff
% else:
xori ${dst}, zero, ${pretty(src)}
% endif

## 32-bit immediate with lui and xori
% elif (src < 0x80000000 or src >= 0xffffffff80000000) and src & 0x800 == 0 and encodes_no_newline(src, src):
/* mv ${dst}, ${pretty(src)} */
lui ${dst}, ${pretty(src >> 12)}
xori ${dst}, ${dst}, ${pretty(src & 0xfff)}
% elif (src < 0x80000000 or src >= 0xffffffff80000000) and src & 0x800 == 0x800 and encodes_no_newline(src, ~src):
/* mv ${dst}, ${pretty(src)} */
lui ${dst}, ${pretty((~src >> 12) & 0xfffff)}
xori ${dst}, ${dst}, ${pretty(src & 0xfff | 0xfffffffffffff000)}
% elif (srcn < 0x80000000 or srcn >= 0xffffffff80000000) and srcn & 0x800 == 0 and encodes_no_newline(srcn, srcn):
lui ${dst}, 0xfffff & (${pretty(src)} >> 12)
xori ${dst}, ${dst}, 0x7ff & ${pretty(src)}
% elif (srcn < 0x80000000 or srcn >= 0xffffffff80000000) and srcn & 0x800 == 0x800 and encodes_no_newline(srcn, srcn + 0x800):
lui ${dst}, 0xfffff & ((${pretty(src)} >> 12) + 1)
xori ${dst}, ${dst}, 0x7ff & ${pretty(src)}
addi ${dst}, ${dst}, -0x800
% elif (srcn < 0x80000000 or srcn >= 0xffffffff80000000) and encodes_no_newline(srcn, ~srcn):
lui ${dst}, 0xfffff & (~${pretty(src)} >> 12)
xori ${dst}, ${dst}, ~0x7ff | ${pretty(src)}
% if not srcn & 0x800:
addi ${dst}, ${dst}, -0x800
% endif

## 64-bit immediate with lui, addi, and slli
## FIXME: Make this null and newline free
% elif srcn > 0xfffffffff and srcn < 0xffffffff00000000:
% if src & 0x80000000:
${riscv64.mov(dst, ~src >> 32)}
${riscv64.mov('t6', src | ~0x7fffffff)}
% else:
${riscv64.mov(dst, src >> 32)}
${riscv64.mov('t6', src & 0x7fffffff)}
% endif
slli ${dst}, ${dst}, 0x20
xor ${dst}, ${dst}, t6
% else:
## FIXME: Make this null and newline free
li ${dst}, ${pretty(src)}

% endif
Expand Down
36 changes: 19 additions & 17 deletions pwnlib/shellcraft/templates/riscv64/pushstr.asm
Original file line number Diff line number Diff line change
Expand Up @@ -16,55 +16,57 @@ Example:
addi sp, sp, -8
>>> print(shellcraft.riscv64.pushstr('a').rstrip())
/* push b'a\x00' */
/* mv t4, 0x61 */
xori t4, zero, 0x79e
xori t4, zero, 0x7ff ^ 0x61
xori t4, t4, 0x7ff
sd t4, -8(sp)
addi sp, sp, -8
>>> print(shellcraft.riscv64.pushstr('aa').rstrip())
/* push b'aa\x00' */
li t4, 0x6161
lui t4, 0xfffff & (~0x6161 >> 12)
xori t4, t4, ~0x7ff | 0x6161
addi t4, t4, -0x800
sd t4, -8(sp)
addi sp, sp, -8
>>> print(shellcraft.riscv64.pushstr('aaaa').rstrip())
/* push b'aaaa\x00' */
/* mv t4, 0x61616161 */
lui t4, 0x61616
xori t4, t4, 0x161
lui t4, 0xfffff & (0x61616161 >> 12)
xori t4, t4, 0x7ff & 0x61616161
sd t4, -8(sp)
addi sp, sp, -8
>>> print(shellcraft.riscv64.pushstr('aaaaa').rstrip())
/* push b'aaaaa\x00' */
li t4, 0x6161616161
xori t4, zero, 0x7ff ^ 0x61
xori t4, t4, 0x7ff
lui t6, 0xfffff & (0x61616161 >> 12)
xori t6, t6, 0x7ff & 0x61616161
slli t4, t4, 0x20
xor t4, t4, t6
sd t4, -8(sp)
addi sp, sp, -8
>>> print(shellcraft.riscv64.pushstr('aaaa', append_null = False).rstrip())
/* push b'aaaa' */
/* mv t4, 0x61616161 */
lui t4, 0x61616
xori t4, t4, 0x161
lui t4, 0xfffff & (0x61616161 >> 12)
xori t4, t4, 0x7ff & 0x61616161
sd t4, -8(sp)
addi sp, sp, -8
>>> print(shellcraft.riscv64.pushstr(b'\xc3').rstrip())
/* push b'\xc3\x00' */
/* mv t4, 0xc3 */
xori t4, zero, 0x73c
xori t4, zero, 0x7ff ^ 0xc3
xori t4, t4, 0x7ff
sd t4, -8(sp)
addi sp, sp, -8
>>> print(shellcraft.riscv64.pushstr(b'\xc3', append_null = False).rstrip())
/* push b'\xc3' */
/* mv t4, 0xc3 */
xori t4, zero, 0x73c
xori t4, zero, 0x7ff ^ 0xc3
xori t4, t4, 0x7ff
sd t4, -8(sp)
addi sp, sp, -8
>>> print(enhex(asm(shellcraft.riscv64.pushstr("/bin/sh"))))
b79e39349b8e7e7bb20e938ebe34b60e938efe22233cd1ff6111
b78e97ff93cefeb2938e0e80b76f696e93cfff22939e0e02b3cefe01233cd1ff130181ff
>>> print(enhex(asm(shellcraft.riscv64.pushstr(""))))
232c01fe6111
232c01fe130181ff
>>> print(enhex(asm(shellcraft.riscv64.pushstr("\x00", append_null = False))))
232c01fe6111
232c01fe130181ff

Args:
string (str): The string to push.
Expand Down
Loading
Loading