flat assembler
Message board for the users of flat assembler.

Index > Tutorials and Examples > empty program (in linux)

Author
Thread Post new topic Reply to topic
Potato



Joined: 18 Apr 2020
Posts: 11
Potato 19 Apr 2020, 01:11
Was curious what was the smallest empty program the didn't segfault the system was using fasm in linux x64.

132 bytes.

There is probably a lot more that can be done if the file is abused in a hex editor.

There was a really nice article about doing this with a 32 bit linux os.
https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html

I got here and was pretty chuffed but its still 132 bytes. This seemed so bloated, I was consoling myself thinking 'its 64x, perhaps its a later version of kernel. you don't have time to mess with the elf header.. there was no bloat in the assembly generated code like other assemblers to optimize...'

Code:
format ELF64 executable
        xor rdi,rdi             
        mov rax,60              
        syscall                 
    


Then it hit me - why am i zeroing rdi when I have done nothing to it? Thats a whole instruction step i can remove and it shouldn't affect the program...

Code:
format ELF64 executable
        mov rax,60              
        syscall                 
    


It worked, I remeoved a whole instruction step from the program, giving me 4 valuable bytes.

All this program does now is move 60 into rax then make a system call exiting the program.

So the smallest program I could make that didn't segfault was 128 bytes. Do you guys have a method to reduce it further?
Post 19 Apr 2020, 01:11
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20299
Location: In your JS exploiting you and your system
revolution 19 Apr 2020, 01:24
You can change RAX to EAX
Post 19 Apr 2020, 01:24
View user's profile Send private message Visit poster's website Reply with quote
Potato



Joined: 18 Apr 2020
Posts: 11
Potato 19 Apr 2020, 01:35
revolution wrote:
You can change RAX to EAX


Thank you, now at 127 bytes! Very Happy
Post 19 Apr 2020, 01:35
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20299
Location: In your JS exploiting you and your system
revolution 19 Apr 2020, 01:42
Is this shorter?
Code:
format ELF64 executable
xor eax,eax
or al,60
syscall    
Post 19 Apr 2020, 01:42
View user's profile Send private message Visit poster's website Reply with quote
Potato



Joined: 18 Apr 2020
Posts: 11
Potato 19 Apr 2020, 02:29
revolution wrote:
Is this shorter?
Code:
format ELF64 executable
xor eax,eax
or al,60
syscall    


Yes its a byte shorter, thanks - it clocks in at 126 bytes!

But I though - I wonder if the zeroing op was removed would it actually still work?

Code:
format ELF64 executable
or al,60
syscall    


now this didn't seg fault got exitcode 0, and its 124 bytes.

Code:
00000000   7F 45 4C 46  02 01 01 00  00 00 00 00  00 00 00 00  02 00 3E 00  01 00 00 00  78 00 40 00  00 00 00 00  40 00 00 00  .ELF..............>.....x.@.....@...
00000024   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  40 00 38 00  01 00 40 00  00 00 00 00  01 00 00 00  07 00 00 00  ................@.8...@.............
00000048   00 00 00 00  00 00 00 00  00 00 40 00  00 00 00 00  00 00 40 00  00 00 00 00  7C 00 00 00  00 00 00 00  7C 00 00 00  ..........@.......@.....|.......|...
0000006C   00 00 00 00  00 10 00 00  00 00 00 00  0C 3C 0F 05                                                                   .............<..
    

Here is the hexdump.. I notice there is still a number of bytes of padding but I am not sure if its still considered a legit program if these are removed, once the elf file is starting to be abused like that its probably going to cause issues for things like malware/virus scanners I would guess because its malformed.
Post 19 Apr 2020, 02:29
View user's profile Send private message Reply with quote
Ali.Z



Joined: 08 Jan 2018
Posts: 715
Ali.Z 19 Apr 2020, 02:36
both 'or al,60' and 'mov al,60' are 2 bytes.
but not clearing upper 8bit or 24-bit of EAX is no good, you cant depend on what loader might assign during load time.

_________________
Asm For Wise Humans
Post 19 Apr 2020, 02:36
View user's profile Send private message Reply with quote
Potato



Joined: 18 Apr 2020
Posts: 11
Potato 19 Apr 2020, 03:24
Ali.Z wrote:
both 'or al,60' and 'mov al,60' are 2 bytes.
but not clearing upper 8bit or 24-bit of EAX is no good, you cant depend on what loader might assign during load time.


This is true, assuming value is going to be 0 is asking for trouble.
Post 19 Apr 2020, 03:24
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20299
Location: In your JS exploiting you and your system
revolution 19 Apr 2020, 04:32
Just for fun I printed the entry register contents in my current version of Linux. I don't know if the Linux loader guarantees these values in any way, it might be just my version, but anyhow this is what I see:
Code:
0000000000000000
0000000000000000
0000000000000000
0000000000000000
00007FFD90B57D60
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000    
From this code:
Code:
SYS_WRITE       = 1
SYS_EXIT        = 60
STD_OUTPUT      = 1

format ELF64 executable

        push    rax rcx rdx rbx rsp rbp rsi rdi r8 r9 r10 r11 r12 r13 r14 r15
        mov     rdi,rsp
        mov     rdx,16
        call    print_hex
        mov     eax,SYS_EXIT
        syscall

print_hex:
        ;rdi = input values
        ;rdx = input qword length
        push    rdi rsi rdx rcx
        mov     r12,rdx
        sub     rsp,32
        mov     r13,rdi
    .loop_qword:
        dec     r12
        mov     rax,[r13+r12*8]
        mov     rsi,rsp
        call    write_hex
        mov     eax,SYS_WRITE
        mov     edi,STD_OUTPUT
        mov     rsi,rsp
        mov     byte[rsi+16],10
        mov     edx,17
        syscall
        test    r12,r12
        jnz     .loop_qword
        add     rsp,32
        pop     rcx rdx rsi rdi
        retn

write_hex:
        ;rsi = address
        ;rax = value
        lea     r8,[hex_table]
        mov     r10,16
    .next_nibble:
        mov     r9,rax
        shr     r9,60
        mov     r9b,[r8 + r9]
        mov     [rsi],r9b
        inc     rsi
        shl     rax,4
        dec     r10
        jnz     .next_nibble
        retn

hex_table       db      '0123456789ABCDEF'    
So all the registers are zero except for RSP.
Post 19 Apr 2020, 04:32
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20299
Location: In your JS exploiting you and your system
revolution 19 Apr 2020, 05:13
Based upon the previous assumption of a zero value in RAX we can do this:
Code:
format ELF64 executable at 0x50f3cb000
entry $ - 0x27
segment executable    
It really works. Can you see why?
Post 19 Apr 2020, 05:13
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8351
Location: Kraków, Poland
Tomasz Grysztar 19 Apr 2020, 09:08
Potato wrote:
There is probably a lot more that can be done if the file is abused in a hex editor.
I would suggest using an existing template for manual creation of ELF, like the one in my formats tutorial. This allows to do all the same things that you could do with hex editor, but in a much better annotated form and with option to compute/resolve various fields from their intended expressions.
Post 19 Apr 2020, 09:08
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20299
Location: In your JS exploiting you and your system
revolution 20 Apr 2020, 04:24
A version that makes a different assumption.
Code:
format ELF64 executable at 0x13eb580000
entry $ - 0x26
segment executable writeable
rb 0x050f3cb0 - 0x78    
If you run it with fewer than 255 arguments then should work fine. With 255 or more arguments then it will fail.

The advantage here is that it doesn't make any use of undocumented behaviour.
Post 20 Apr 2020, 04:24
View user's profile Send private message Visit poster's website Reply with quote
Potato



Joined: 18 Apr 2020
Posts: 11
Potato 20 Apr 2020, 12:43
Tomasz Grysztar wrote:
I would suggest using an existing template for manual creation of ELF...


Thank you - I will have a read through the examples.

revolution wrote:
It really works. Can you see why?


Sorry I cannot find the reason. If I were messing with core memory I can understand one program cannot share the memory space of another, but the registers are needed to have potential side effects for passing of exit return values to the O/S right? and arguments/cpu states that can happen from outside the program during run time?

The last example i will have a play with and get back to you. I have edb so i can better understand the program flow. Thanks for the intresting example.

Unrelated but intresting...

I was suprised that the legacy int 0x80 is such a bad idea on modern linux. It makes you wonder how much legacy code has performance issues now because of the non implementation of syscall.
Post 20 Apr 2020, 12:43
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20299
Location: In your JS exploiting you and your system
revolution 20 Apr 2020, 20:33
Potato wrote:
I was suprised that the legacy int 0x80 is such a bad idea on modern linux. It makes you wonder how much legacy code has performance issues now because of the non implementation of syscall.
It would only be noticeable on programs that rely upon heavy usage of the the OS calls. My guess is that it doesn't make much of a difference for the average program.

We can easily construct test cases that show large differences, but they are very far from the average case.
Post 20 Apr 2020, 20:33
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20299
Location: In your JS exploiting you and your system
revolution 22 Apr 2020, 05:53
I expected this test to fail, but Linux allows the stack segment to be executable:
Code:
format elf64 executable
entry $ - 0x10
segment executable writeable
rb 0x102464ff - 0x78    
This one is different in that it relies upon the command line to supply the executable code. It can be run like this:
Code:
fasm program_name.asm && printf -v x '\x31\xff\x31\xc0\xb0\x3c\x0f\x05' && ./program_name $x    
Post 22 Apr 2020, 05:53
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8351
Location: Kraków, Poland
Tomasz Grysztar 22 Apr 2020, 10:13
Note that you may be able to choose the attributes of the stack using the "gnustack" segment entry.
Post 22 Apr 2020, 10:13
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20299
Location: In your JS exploiting you and your system
revolution 22 Apr 2020, 10:44
Tomasz Grysztar wrote:
Note that you may be able to choose the attributes of the stack using the "gnustack" segment entry.
Adding another segment makes the exe larger. Sad
Post 22 Apr 2020, 10:44
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.