1

For a task I need to create simple shellcode, but it is not allowed that it contains \x80.

Notice: To make a system call on linux, like write or exit, you need among others this line: int 0x80, which in the end will produce shellcode including \x80.

Nevertheless I need to make system calls, so my idea now is to use a variable for the interrupt vector number. For example 0x40 and then multiply it with 2, so in the end there will be a \x40 but not a \x80 in the shellcode.

The problem is that the int is not taking a variable as an argument, I tried this for a test:

section .data

nr db 0x80

section .text

global _start

_start:

xor eax, eax
inc eax

xor ebx, ebx
mov ebx, 0x1

int [nr]

And get

error: invalid combination of opcode and operands

How could I get my idea working? Or do you have a different solution for the problem?

PS. sysenter and syscall are not working -> Illegal instruction

I am using nasm on a x86-32bit machine.

Krupuk
  • 21
  • 4
  • 4
    Is self-modifying code allowed? – harold Dec 05 '15 at 19:56
  • 2
    `int` doesn't take memory operands. Just immediate values. See http://x86.renejeschke.de/html/file_module_x86_id_142.html – Michael Petch Dec 05 '15 at 19:57
  • You can try calling `__kernel_vsyscall` in the VDSO. That's the only other system call interface other than `int 0x80`, `sysenter` and `syscall` (and it ends up using one of those). Though finding its address might be difficult. – Ross Ridge Dec 05 '15 at 19:59
  • 1
    Just a curiosity, but under normal circumstances shell code with a value 0x00 poses a problem. Curious what design consideration makes 0x80 a problem? – Michael Petch Dec 05 '15 at 20:02
  • @harold We never talked about self-modifying code, so I guess it is allowed. But how can the code modify itself? Can please add an answer. – Krupuk Dec 05 '15 at 20:03
  • @MichaelPetch There are several filtering schemes out there being employed by programs that only allow alphanumeric characters to be passed into their buffers. So \0x80 won't work. – Krupuk Dec 05 '15 at 20:50
  • Your question didn't mention it, so I asked. – Michael Petch Dec 05 '15 at 20:56
  • 1
    As an extension to what Ross Ridge mentioned, you may be able to do it via `call near [gs:10H]` on your 32-bit Linux. This is an indirect jump through the address at gs:0x10 which is often the VDSO syscall trampoline address. – Michael Petch Dec 05 '15 at 21:24
  • Oops I meant indirect _CALL_ not _JUMP_ in my last comment – Michael Petch Dec 05 '15 at 21:35
  • Unfortunately that will result in a value of 0xff appearing. If I were you I'd switch to a shellcode encoding method that allows 0x80. – Michael Petch Dec 05 '15 at 21:49
  • @MichaelPetch Thanks for your information Michael. Can you please direct me to some sources regarding "shellcode encoding method"? – Krupuk Dec 05 '15 at 22:10
  • 1
    Is there the possibility to push a whole instruction like int 0x80 to the stack and execute the instruction from the stack? Isn't it this how it's done? How is int 0x80 exactly executed? Will it be pushed to the stack? – Krupuk Dec 05 '15 at 23:05

1 Answers1

2

maybe something like this, but never use it in serious code!

format ELF executable
use32
entry start

segment executable  writeable
  start:
  ;<some code>
  inc byte [ here + 1 ] ;<or some other math>
  jmp here
  here:
  int 0x7f
segment readable  writeable

(this is fasm-code)

sivizius
  • 450
  • 2
  • 14
  • thanks for your answer, but I do not get how this will call int 0x80? – Krupuk Dec 05 '15 at 23:17
  • 2
    1. some code calculate the value 0x80 and write it into al, 2. a mov write this value after `int`, because its an 2 byte opcode: 0xCD 0x80, 3. and finally execute it. – sivizius Dec 06 '15 at 02:07
  • 1
    Ah, now I understand thank you! I tried your code with nasm, and I get `Program received signal SIGSEGV, Segmentation fault.` On this line `mov byte [ here + 1 ], al`. But why? And what is the solution for that? – Krupuk Dec 06 '15 at 14:01
  • 1
    @Krupuk Your `.text` section probably isn't writable. Also you need a `jmp instruction (eg. `jmp here`) between the code modification and the modified code. This lets the CPU know the code has been modified and that it should discard any cached copied of it. – Ross Ridge Dec 06 '15 at 19:08
  • If it only has to run once, `inc byte [here+1]` to convert `int 0x7f` into `int 0x80`. That's probably the smallest encoding. Or an idempotent solution: `and byte [here+1], 0x88` / `int 0x81`. The `and` and `int` immediate operands intersect down to `0x80`. Does self-modifying code even work properly when the modification happens that close to `EIP`? I assume the store triggers an OOO pipeline flush, and a self-modifying stall, if it's detected in time. – Peter Cordes Dec 06 '15 at 19:24
  • 1
    @PeterCordes Intel guarantees self-modified code works no matter the distance if there's a jump in between or a serializing instruction. See Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 3, 8.1.3 Handling Self- and Cross-Modifying Code – Ross Ridge Dec 07 '15 at 03:34
  • @RossRidge: Thanks, IDK why I didn't think of just looking for official documentation :P So this code is *not* guaranteed. Prob. a `jmp here / here: int 0x88` is the cheapest way (2 bytes and doesn't clobber any registers.) – Peter Cordes Dec 07 '15 at 04:42
  • In 64-bit code, maybe just use `syscall` (`0F 0E`) instead of `int 0x80`! related: [What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?](https://stackoverflow.com/questions/46087730/what-happens-if-you-use-the-32-bit-int-0x80-linux-abi-in-64-bit-code). Of course, in 32-bit code you can get a PC-relative pointer with `call` / `pop` and use that instead of RIP-relative addressing. – Peter Cordes Sep 23 '17 at 13:38
  • @PeterCordes »PS. sysenter and syscall are not working -> Illegal instruction« – sivizius Sep 23 '17 at 13:51
  • Are you injecting this code into a 32-bit binary? `syscall` should definitely not `#UD` in 64-bit mode. Are you sure you're testing it correctly? Did `inc` one of the bytes of `syscall` so it wasn't `syscall` anymore? Both its bytes are 7-bit-safe already. – Peter Cordes Sep 23 '17 at 13:57
  • I noticed the OP did say 32-bit. Your code will assemble to a RIP-relative addressing mode, so in 32-bit it will decode differently. Or if FASM assembles it to an absolute address, then obviously it's not position-independent! – Peter Cordes Sep 23 '17 at 13:58
  • I usually do amd64-stuff so I accidentally used ELF64 and use64. – sivizius Sep 23 '17 at 14:13