1

I am learning x64 shellcode on exploit.courses linux containers and I have a problem running a hello world x64 shellcode I wrote. I am trying to move the "Hi there" buffer directly to a register so I don't use the .data section.

section .data

;msg db "Hi there"

section .text

global _start
_start:
;zeroed out these registers
xor rax, rax
xor rbx, rbx
xor rsi, rsi
xor rdi, rdi
xor rdx, rdx

;write (int fd, char *msg, unsigned int len);
mov rax,1 ; syscall 1 is write in 64bit arch
mov rdi,1 ; rdi is fd
mov rbx, 0x6572656874206948
mov rdx, 9; rdx is size (9 for null byte)
syscall ; instead of int 0x80 for 32 bit architecture

;exit (int ret)
mov rax, 60 ; syscall 60 is exit in 64bit arch
mov rdi, 0 ; rdi is error code
syscall

I assemble the code and run it:

$nasm -f elf64 -o print2.o print2.asm
$ld -o print2 print2.o               
$./print2

And nothing happens though print2 seems to exit normally... Could someone explain why?

Sorry if this question has already been asked. I tried looking for a similar one but couldn't find anything.

Community
  • 1
  • 1
warsang
  • 15
  • 6
  • 2
    Use `strace`. Why do you expect it to print anything when you pass rsi=NULL as the buffer? `rbx` is not one of the syscall arg registers. See https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-x86-64. And you always need to pass a pointer to data in memory for `write()`. See also https://stackoverflow.com/tags/x86/info – Peter Cordes Oct 12 '17 at 07:24
  • @PeterCordes sorry I see only now your comment – invictus1306 Oct 12 '17 at 07:51
  • @invictus1306: I'm sure this is a duplicate of something, but I didn't take the time to go looking. You don't need to apologize for posting an answer even when there's already a comment with the answer. SO wants real answers. The only thing wrong is answering a question that should be closed as a duplicate of one of many passing data instead of a pointer questions (except this wasn't even in the correct register, so IDK). – Peter Cordes Oct 12 '17 at 07:56
  • @PeterCordes I got it, for sure If I saw your comment before, I would never post. I also think that in SO, the 90% of the asked question already exist, these could be a little bit different(like in this case (rbx/rsi)), but the content is the same. – invictus1306 Oct 12 '17 at 08:14
  • @invictus1306: Yeah, exactly. So mostly beginner questions should be closed as duplicates to already-asked ones. Or if someone wants to write a better answer to a better-asked new question, we can close the old questions as duplicates of new ones. There are always going to be actual interesting new questions, especially as new stuff like AVX512 comes along, though. – Peter Cordes Oct 12 '17 at 08:19
  • The `write()` can't take content from register, so even if you have "Hi there" in register, you will either have to write it somewhere into memory to use `write`, or use your own output routine which processes register value as data. One way to fix your code is to do something like `lea rsi,[rip+rbx_mov_instruction+opcode_length]` to use the instruction opcode as source of content. Also the length of data is 8, there's no null byte in your data and `rbx` has only 8 bytes, not 9. Other way is to store `rbx` into stack memory, and set `rsi` to point there. And other is in invictus answer. – Ped7g Oct 12 '17 at 08:22
  • 1
    I **guess** `lea rsi,[rip+2]` right ahead that `mov rbx,'Hi there'` would do (didn't verify in debugger if I got the idea about `rip` value right, I would expect it to point to the `mov rbx,...` as is case relative jumps, but I usually use labels in source and let assembler to sort it out). It's still more reasonable to put the data into code segment after the code, to avoid the wasted `mov rbx,` opcode to save 2 bytes, it's just matter of adjusting that `rsi` load with proper offset to the data, wherever they land. – Ped7g Oct 12 '17 at 08:31
  • @Ped7g: good point about RIP-relative `lea`. (I added [a comment on the answer about that](https://stackoverflow.com/questions/46703399/x64-helloworld-shellcode-not-printing-anything#comment80389484_46704213), using correct NASM syntax with a way to still avoid `00` bytes in the machine code.) – Peter Cordes Oct 13 '17 at 02:03

1 Answers1

1

As first steps look at the write documentation

   ssize_t write(int fd, const void *buf, size_t count);

The second arguments must be a const void *

But for linux the calling convention is:

 RDI, RSI, RDX, RCX, R8, R9, XMM0–7

Then your implementation is not correct.

You should do something like this

global _start


_start:

    jmp short msg

    routine:

    ...
    pop rsi         ;address of the string from the stack
    ...


    msg:
    call routine    
    db 'Hi here' 
invictus1306
  • 587
  • 1
  • 4
  • 19
  • This is 64-bit code. Use a RIP-relative `LEA rsi, [rel msg]` instead of `jmp` / `call`. Or to avoid `00` in the machine code of the rel32 displacement, jump over the string so the `LEA` offset will be negative (FF instead of 00 in the high bytes). That's the same thing that makes the backwards `call` not contain `00` bytes. Or put your `msg` before your NOP-sled jump target. – Peter Cordes Oct 13 '17 at 02:01