Welcome Guest ( Log In | Register )

Outline · [ Standard ] · Linear+

 RE challenge: Can you disassemble this ELF64?

views
     
TSMatQuasar
post Jun 23 2023, 05:37 PM, updated 3y ago

Casual
***
Validating
329 posts

Joined: Jun 2023
The FASM code as below (can be compiled by FASM on any operating system, but the assembled binary is targeting Linux x64):

CODE
format elf64 executable
mov rax,0x02EB206F6C6C6548
mov rax,0x02EB0A646C726F57
mov rax,0x9003EB00000005E8
mov rax,0x909090909007EB5B
mov rax,0x909004EBE7738D48
mov rax,0x9003EB00000006BA
mov rax,0x9003EB00000001B8
mov rax,0x9003EB00000001BF
mov rax,0x9090909006EB050F
mov rax,0x909004EBF1738D48
mov rax,0x9003EB00000006BA
mov rax,0x9003EB00000001B8
mov rax,0x9003EB00000001BF
mov rax,0x9090909006EB050F
mov rax,0x9003EB000000E7B8
mov rax,0x9090909006EB050F
jmp $$ + 22    


This prints "Hello World", but can you find out why a series of "MOV RAX, qword" can prints text string in terminal?

rclxs0.gif

This post has been edited by MatQuasar: Jul 20 2023, 05:50 PM


Attached File(s)
Attached File  hw.zip ( 459bytes ) Number of downloads: 4
epud2 P
post Jun 24 2023, 12:47 AM

New Member
*
Probation
9 posts

Joined: Aug 2021
CODE
equivalent c code.
#include <unistd.h>

int main(){
const char* h = "Hello ";
const char* w = "World\n";

write(1, (void*)h, sizeof(h));
write(1, (void*)w, sizeof(w));
}


%rax|System call|%rdi|%rsi |%rdx
1|sys_write|unsigned int fd |const char *buf |size_t count

you set rdi with file descriptor. 1 mean stdout
rsi where your string is
rdx is your string length
rax is syscall no for sys_write

you call sys_write twice


That mov instructions turn into a series of byte values after compilation. Then you jump to certain offset within that byte value. Hard for me to explain it, but it is like obfuscation technique to confuse disassembler
TSMatQuasar
post Jun 24 2023, 03:15 AM

Casual
***
Validating
329 posts

Joined: Jun 2023
QUOTE(epud2 @ Jun 24 2023, 12:47 AM)
CODE
equivalent c code.
#include <unistd.h>

int main(){
const char* h = "Hello ";
const char* w = "World\n";

write(1, (void*)h, sizeof(h));
write(1, (void*)w, sizeof(w));
}


%rax|System call|%rdi|%rsi |%rdx
1|sys_write|unsigned int fd |const char *buf |size_t count

you set rdi with file descriptor. 1 mean stdout
rsi where your string is
rdx is your string length
rax is syscall no for sys_write

you call sys_write twice
That mov instructions turn into a series of byte values after compilation. Then you jump to certain offset within that byte value. Hard for me to explain it, but it is like obfuscation technique to confuse disassembler
*
Nice, how did you able to solve it? Manual tweaking, or using plain debugger?

It is crafted by author of ARM assembler (FASMARM), he and Tomasz (author of x86/x64 assembler) are experts in this field.

This is his original code (with macro language) to create the program at Post #1:

CODE

format elf64 executable

SYS_WRITE       = 1
SYS_EXIT_GROUP  = 231
STD_OUTPUT      = 1
entry main

macro encode inst& {
       local   ..start
       db      0x48, 0xb8
   ..start:
       inst
       assert $ - ..start <= 6
       jmp     ..start + 10
       while $ - ..start < 8
               nop
       end while
}

main:
encode  hello:  db      'Hello '
encode  world:  db      'World',10
encode  begin:  call    pc
encode  pc:     pop     rbx
encode          lea     rsi,[rbx + hello - begin - 5]
encode          mov     edx,6
encode          mov     eax,SYS_WRITE
encode          mov     edi,STD_OUTPUT
encode          syscall
encode          lea     rsi,[rbx + world - begin - 5]
encode          mov     edx,6
encode          mov     eax,SYS_WRITE
encode          mov     edi,STD_OUTPUT
encode          syscall
encode          mov     eax,SYS_EXIT_GROUP
encode          syscall
               jmp     begin    


Hope you can make the best use of your skill, @epud2!
epud2 P
post Jun 24 2023, 11:47 PM

New Member
*
Probation
9 posts

Joined: Aug 2021
Thanks. I wish the same for you as well

I fisrt tried to copy paste the code on a program that ran by x64dbg. It kinda stupid but it is still valid asm instructions which mean i can step through the instructions.
However i'm stuck with the jump instruction. Don't really know what the jump is, so I moved on to another thing.

This time I modified the code as PE binary and debug it with x64dbg. I already have fasm in my PC so it was easy thing to do.
As I step over the code I noticed instructions that move 6 and 1 into register. And I also noticed syscall instruction.
Stepping over syscall caused an error and it is expected since the code is supposed to run on linux.

The reason I debug in windows was because i'm not sure if i still have a debugger in my linux vm.
When i ran my linux machine i found out i do still have it. It is called edb, quite similar to x64dbg, written in qt and open source.
TSMatQuasar
post Jun 27 2023, 12:06 AM

Casual
***
Validating
329 posts

Joined: Jun 2023
I didn't use debugger, but this is how I tweaked it so that it disassembles correctly in IDA Free:

CODE
format elf64 executable
mov rax,0x02EB206F6C6C6548
mov rax,0x02EB0A646C726F57

;mov rax,0x9003EB00000005E8

nop
nop
dq 0x9003EB00000005E8

mov rax,0x909090909007EB5B
mov rax,0x909004EBE7738D48
mov rax,0x9003EB00000006BA
mov rax,0x9003EB00000001B8
mov rax,0x9003EB00000001BF
mov rax,0x9090909006EB050F
mov rax,0x909004EBF1738D48
mov rax,0x9003EB00000006BA
mov rax,0x9003EB00000001B8
mov rax,0x9003EB00000001BF
mov rax,0x9090909006EB050F
mov rax,0x9003EB000000E7B8
mov rax,0x9090909006EB050F
jmp $$ + 22    


If I understand correctly, "MOV RAX,qword" is 2-byte opcode, so I change it with NOP in order to show the real Assembly code beneath it.

Actually the "JMP $$ +22" will jump directly the code after the double NOP.



 

Change to:
| Lo-Fi Version
0.0182sec    0.54    6 queries    GZIP Disabled
Time is now: 24th December 2025 - 09:33 PM