flat assembler
Message board for the users of flat assembler.

Index > Linux > ELF executable + relocations/fixups for ASLR?

Goto page Previous  1, 2, 3, 4
Author
Thread Post new topic Reply to topic
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20298
Location: In your JS exploiting you and your system
revolution 09 Feb 2019, 14:36
I edited my local copy of fasm
Code:
--- SOURCE/FORMATS.INC
+++ SOURCE/FORMATS.INC
@@ -3062,11 +3062,26 @@
        jz      instruction_assembled
        mov     byte [ebx+10h],8
        jmp     instruction_assembled
 mark_elf_relocation:
        test    [format_flags],1
-       jnz     invalid_use_of_symbol
+       jz      .not_dynamic
+       cmp     [value_type],2
+       jne     invalid_use_of_symbol
+       push    ebx eax
+       mov     eax,edi
+       sub     eax,[ebp]
+       mov     ebx,[free_additional_memory]
+       inc     [number_of_relocations]
+       add     ebx,4
+       cmp     ebx,[structures_buffer]
+       jae     out_of_memory
+       mov     [free_additional_memory],ebx
+       mov     [ebx-4],eax
+       pop     eax ebx
+       ret
+      .not_dynamic:
        push    ebx
        mov     ebx,[addressing_space]
        cmp     [value_type],3
        je      elf_relocation_relative
        cmp     [value_type],7
@@ -3856,10 +3871,170 @@
        or      [next_pass_needed],-1
       new_elf_segment:
        mov     byte [ebx],1
        and     dword [ebx+18h],0
        mov     word [ebx+1Ch],1000h
+       cmp     word[esi],0x051A        ;fixups?
+       jnz     elf_segment_flags
+       lods    word[esi]
+       or      byte[ebx+18h],1         ;mark as executable
+       pop     edx
+       mov     eax,edi
+       sub     eax,[code_start]
+       mov     [ebx+4],eax
+       and     eax,0FFFh
+       add     eax,edx
+       mov     [ebx+8],eax
+       mov     [ebx+0Ch],eax
+       xor     edx,edx
+       xor     cl,cl
+       not     eax
+       not     edx
+       not     cl
+       add     eax,1
+       adc     edx,0
+       adc     cl,0
+       add     eax,edi
+       adc     edx,0
+       adc     cl,0
+       mov     [ds:ebp],eax
+       mov     [ds:ebp+4],edx
+       mov     [ds:ebp+8],cl
+       push    esi
+       mov     esi,.elf_relocator
+       mov     ecx,.elf_relocator.size
+       rep     movsb
+       mov     esi,[code_start]
+       mov     eax,[ebx + 8]
+       xchg    eax,[esi + 0x18]        ;e_entry
+       mov     [edi - 4],eax
+       mov     esi,[free_additional_memory]
+       mov     ecx,[number_of_relocations]
+       lea     eax,[ecx * 4]
+       sub     esi,eax
+       rep     movsd
+       mov     eax,END_OF_RELOCATIONS
+       stosd
+       pop     esi
+       inc     [number_of_sections]
+       jmp     instruction_assembled
+      .elf_relocator:
+       label .Elf32_Ehdr.e_entry       dword at 0x18
+       label .Elf32_Ehdr.e_phoff       dword at 0x1c
+       label .Elf32_Ehdr.e_phentsize   word at 0x2a
+       label .Elf32_Ehdr.e_phnum       word at 0x2c
+       label .Elf32_Phdr.p_type        dword at 0x00
+       label .Elf32_Phdr.p_offset      dword at 0x04
+       label .Elf32_Phdr.p_paddr       dword at 0x0c
+       label .Elf32_Phdr.p_memsz       dword at 0x14
+       label .Elf32_Phdr.p_flags       dword at 0x18
+       label .Elf32_auxv_t.a_type      dword at 0
+       label .Elf32_auxv_t.a_val       dword at 4
+       ;p_type
+               PT_LOAD                 = 1
+       ;p_flags
+               PF_X                    = 1 shl 0
+               PF_W                    = 1 shl 1
+               PF_R                    = 1 shl 2
+       ;protection flags
+               PROT_READ               = 0x1
+               PROT_WRITE              = 0x2
+               PROT_EXEC               = 0x4
+       ;syscall
+               SYS_MPROTECT            = 125
+               SYS_EXIT_GROUP          = 252
+       ;auxv
+               AT_NULL                 = 0
+               AT_PHDR                 = 3
+               Elf32_auxv_t.size       = 8
+       ;local
+               END_OF_RELOCATIONS      = -1
+               PAGE_ALIGNMENT          = not 0xfff
+       ;the following code section can run anywhere without needing
+       ;any RVAs or relocations, it can be copied into the output as-is
+           .do_relocs:
+               pop     eax                             ;argument count (argc)
+               push    eax
+               cld
+               lea     esi,[esp + 4 + (eax + 1) * 4]   ;skip the args and the final null
+           .skip_environment:
+               lodsd
+               test    eax,eax                         ;last entry in environment?
+               jnz     .skip_environment
+           .scan_auxv:
+               lodsd
+               mov     ecx,eax                         ;ecx = Elf32_auxv_t.a_type
+               lodsd                                   ;eax = Elf32_auxv_t.a_val
+               cmp     ecx,AT_NULL                     ;end of auxv table?
+               jz      .fail
+               cmp     ecx,AT_PHDR
+               jnz     .scan_auxv
+               mov     edi,eax
+               and     edi,PAGE_ALIGNMENT
+               mov     esi,PROT_READ + PROT_WRITE + PROT_EXEC
+               call    .protect
+               mov     ebx,[edi + .Elf32_Ehdr.e_phoff]
+               mov     ecx,[edi + ebx + .Elf32_Phdr.p_paddr]
+               sub     ecx,[edi + ebx + .Elf32_Phdr.p_offset]  ;ecx = program base
+               xor     edx,edx
+               mov     dl,.program_entry - .do_relocs
+               add     edx,[edi + .Elf32_Ehdr.e_entry]
+               sub     edi,ecx
+               lea     esi,[edi + edx + .relocs_data - .program_entry]
+               mov     eax,edx
+           .reloc_loop:
+               add     [edi + eax],edi
+               lodsd
+               cmp     eax,END_OF_RELOCATIONS
+               jnz     .reloc_loop
+               push    dword[edi + edx]                ;after retn we run the program
+               add     edi,ecx
+               xor     esi,esi                         ;restore to original permissions
+           .protect:
+               movzx   ebp,[edi + .Elf32_Ehdr.e_phnum] ;ebp = count of Phdr entries
+           .loop_Phdr:
+               dec     ebp
+               js      .retn
+               movzx   eax,[edi + .Elf32_Ehdr.e_phentsize]
+               imul    eax,ebp
+               add     eax,edi
+               add     eax,[edi + .Elf32_Ehdr.e_phoff] ;eax = current Elf32_Phdr
+               cmp     [eax + .Elf32_Phdr.p_type],PT_LOAD
+               jnz     .loop_Phdr
+               mov     ecx,[eax + .Elf32_Phdr.p_flags]
+               xor     edx,edx
+               assert  PROT_EXEC or PF_X = PROT_READ or PF_R & PROT_WRITE = PF_W
+               repeat 3
+                       shr     ecx,1
+                       rcl     edx,1
+               end repeat
+               or      edx,esi                         ;set the requested flags
+               mov     ebx,[eax + .Elf32_Phdr.p_paddr]
+               mov     ecx,[edi + .Elf32_Ehdr.e_phoff]
+               sub     ebx,[edi + ecx + .Elf32_Phdr.p_paddr]
+               add     ebx,[edi + ecx + .Elf32_Phdr.p_offset]
+               mov     ecx,[eax + .Elf32_Phdr.p_memsz]
+               add     ecx,ebx
+               and     ebx,PAGE_ALIGNMENT
+               sub     ecx,ebx                         ;len
+               add     ebx,edi                         ;addr
+               xor     eax,eax
+               mov     al,SYS_MPROTECT
+               int     0x80
+               test    eax,eax
+               jz      .loop_Phdr
+           .fail:
+               xor     eax,eax
+               mov     al,SYS_EXIT_GROUP
+               or      ebx,-1
+               int     0x80
+           .retn:
+               retn
+           .program_entry:
+               dd      0       ;the program entry address is transferred to here, and it gets relocated
+           .relocs_data:
+      .elf_relocator.size = $ - .elf_relocator
       elf_segment_flags:
        cmp     byte [esi],1Eh
        je      elf_segment_type
        cmp     byte [esi],19h
        jne     elf_segment_flags_ok    
And now I can assemble this
Code:
format ELF dynamic 0
entry start

SYS_EXIT        = 1
SYS_WRITE       = 4
STD_OUTPUT      = 1

segment readable writeable

hello_world:    db      'Running at 0x'
running_at_hex  db      '00000000 Stack at 0x'
stack_at_hex    db      '00000000',10
hello_world_len =       $ - hello_world

segment executable

start:
        mov     ecx,running_at_hex
        mov     edx,hello_world
        call    write_hex
        mov     ecx,stack_at_hex
        mov     edx,esp
        call    write_hex
        mov     eax,SYS_WRITE
        mov     ebx,STD_OUTPUT
        mov     ecx,hello_world
        mov     edx,hello_world_len
        int     0x80
        mov     eax,SYS_EXIT
        xor     ebx,ebx
        int     0x80

write_hex:
        ;ecx = address
        ;edx = value
    .next_nibble:
        mov     eax,edx
        shr     eax,28
        cmp     al,10
        sbb     al,0x69
        das
        mov     [ecx],al
        inc     ecx
        shl     edx,4
        jnz     .next_nibble
        retn

segment gnustack
segment fixups    
It works for 32-bit code. 64-bit will have to come later, but that is less of an issue.

Now we have ASLR with automatic fixups, no RVAs, and a non-writeable executable section. It runs completely stand-alone without any external libraries.

So the only things to change to convert a normal executable and make it ASLR compliant are the first and last lines. The first line replaces "executable" with "dynamic" and the last line is added "segment fixups".
Post 09 Feb 2019, 14:36
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: 20298
Location: In your JS exploiting you and your system
revolution 10 Feb 2019, 09:34
I had some difficulty to get the GNURELRO working correctly.

I started with this code
Code:
format ELF64 executable 0 at 0x10000
entry start

segment gnustack
segment executable

start:
        lea     rdi,[maps]
        mov     esi,O_RDONLY
        xor     edx,edx
        call    [open]
        mov     edx,0x1000
        sub     rsp,rdx
        mov     rdi,rax
        mov     eax,SYS_READ
        mov     rsi,rsp
        syscall
        mov     edx,eax
        mov     eax,SYS_WRITE
        mov     edi,STD_OUTPUT
        mov     rsi,rsp
        syscall
        mov     eax,SYS_EXIT
        xor     edi,edi
        syscall

SYS_READ        = 0
SYS_WRITE       = 1
SYS_OPEN        = 2
SYS_EXIT        = 60
STD_INPUT       = 0
STD_OUTPUT      = 1
O_RDONLY        = 0

DT_NULL         = 0
DT_NEEDED       = 1
DT_STRTAB       = 5
DT_SYMTAB       = 6
DT_RELA         = 7
DT_RELASZ       = 8
DT_RELAENT      = 9
DT_STRSZ        = 10
DT_SYMENT       = 11
DT_BIND_NOW     = 24
DT_FLAGS        = 30
DT_FLAGS_1      = 0x6ffffffb
STB_GLOBAL      = 1
STT_FUNC        = 2
R_X86_64_64     = 1
DF_BIND_NOW     = 0x00000008
DF_1_NOW        = 0x00000001
DF_1_PIE        = 0x08000000

macro Elf64_Sym name,value,size,bind,type,other,shndx {
        dd name+0
        db (bind+0) shl 4 + (type+0)
        db other+0
        dw shndx+0
        dq value+0
        dq size+0
}
macro Elf64_Rela offset,symbol,type,addend {
        dq rva offset+0
        dq (symbol+0) shl 32 + (type+0)
        dq addend+0
}
virtual at 0
        Elf64_Sym
        sizeof.Elf64_Sym = $
        Elf64_Rela
        sizeof.Elf64_Rela = $ - sizeof.Elf64_Sym
end virtual

segment interpreter readable
                db '/lib64/ld-linux-x86-64.so.2'
        strtab:
                db 0
        _libc   db 'libc.so.6',0
        _open   db 'open',0
        strsz   = $ - strtab

maps:   db      '/proc/self/maps',0

segment readable

segment dynamic readable
        dq DT_NEEDED,_libc - strtab
        dq DT_STRTAB,strtab
        dq DT_STRSZ,strsz
        dq DT_SYMTAB,symtab
        dq DT_SYMENT,sizeof.Elf64_Sym
        dq DT_RELA,rela
        dq DT_RELASZ,relasz
        dq DT_RELAENT,sizeof.Elf64_Rela
        dq DT_BIND_NOW,1
        dq DT_FLAGS,DF_BIND_NOW
        dq DT_FLAGS_1,DF_1_NOW or DF_1_PIE
        dq DT_NULL,0
        symtab:
                Elf64_Sym
                Elf64_Sym _open - strtab,0,0,STB_GLOBAL,STT_FUNC,0,0
        rela:
                Elf64_Rela open,1,R_X86_64_64
        relasz  = $ - rela

segment gnurelro readable
        open dq 0
        ;rb 0x1000
segment readable writeable    
Notice the disabled "rb 0x1000" line near the end.

This gives
Code:
00010000-00011000 --xp 00000000 fd:00 16524682                           /home/revolution/testRelro
00011000-00012000 r--p 00000000 fd:00 16524682                           /home/revolution/testRelro
00012000-00013000 rw-p 00000000 fd:00 16524682                           /home/revolution/testRelro    
And the final segment is left as RW. So the loader didn't apply the relro setting for us. We can see the segment is defined correctly
Code:
  GNU_RELRO      0x000000000000034d 0x000000000001234d 0x000000000001234d
                 0x0000000000000008 0x0000000000000008  R      1
  LOAD           0x0000000000000245 0x0000000000012245 0x0000000000012245
                 0x0000000000000110 0x0000000000000110  RW     1000    
If we enable the "rb 0x1000" line and change nothing else we get this
Code:
00010000-00011000 --xp 00000000 fd:00 16524682                           /home/revolution/testRelro
00011000-00012000 r--p 00000000 fd:00 16524682                           /home/revolution/testRelro
00012000-00013000 r--p 00000000 fd:00 16524682                           /home/revolution/testRelro
00013000-00014000 rw-p 00001000 fd:00 16524682                           /home/revolution/testRelro    
So the loader applied the relro setting and the final segment was split into two parts with different permissions. And the only change in the executable is the load size
Code:
  GNU_RELRO      0x000000000000034d 0x000000000001234d 0x000000000001234d
                 0x0000000000001008 0x0000000000001008  R      1
  LOAD           0x0000000000000245 0x0000000000012245 0x0000000000012245
                 0x0000000000001110 0x0000000000001110  RW     1000    
I'm guessing this is a bug in the dynamic linker. I can't find any spec that states the size of the relro segment must span two pages.

Also, is this a bug in fasm? It outputs 4096 zero bytes for that last segment. I thought it wouldn't output uninitialised bytes. But anyhow, this turns out to be useful because if we delete those 4096 zeros and leave the memsiz value the same then the loader once gain won't set the relro permissions
Code:
00010000-00011000 --xp 00000000 fd:00 16524682                           /home/revolution/testRelro
00011000-00012000 r--p 00000000 fd:00 16524682                           /home/revolution/testRelro
00012000-00013000 rw-p 00000000 fd:00 16524682                           /home/revolution/testRelro
00013000-00014000 rw-p 00000000 00:00 0    
With the segment like this
Code:
  GNU_RELRO      0x000000000000034d 0x000000000001234d 0x000000000001234d
                 0x0000000000000008 0x0000000000000008  R      1
  LOAD           0x0000000000000245 0x0000000000012245 0x0000000000012245
                 0x0000000000000110 0x0000000000001110  RW     1000    
So in summary we have to have the relro segment have extra room after the desired memory section so that the linker will apply the permission change and mark it read-only.

It might be easier to use our own code to apply any final permission changes. The linker is not very reliable or predicable for this.
Post 10 Feb 2019, 09:34
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: 20298
Location: In your JS exploiting you and your system
revolution 10 Feb 2019, 11:26
Edit: Please note the changes in the next post. Rater than use the code in this post.

So one more piece of the puzzle is to add support for linkable dynamic executables. This requires the addition of a PHDR segment. The linker uses this to find the location in memory of the code. PHDR must also be the first segment header else the linker crashes because it can't find the code.

So a few small additions to FORMATS.INC and we can get the PHDR automatically injected when it is required.
Code:
--- SOURCE/FORMATS.INC
+++ SOURCE/FORMATS.INC
@@ -3731,14 +3731,49 @@
        mov     ebx,edi
        mov     ecx,20h shr 2
        cmp     [current_pass],0
        je      init_elf_segments
        imul    ecx,[number_of_sections]
+      .find_interpreter:
+       cmp     dword[edi + eax * 4],3  ;PT_INTERP
+       jz      .has_interpreter
+       add     eax,0x20 shr 2
+       cmp     eax,ecx
+       jb      .find_interpreter
+       jmp     init_elf_segments
+      .has_interpreter:
+       or      eax,-1
       init_elf_segments:
+       push    eax
        xor     eax,eax
        rep     stos dword [edi]
-       and     [number_of_sections],0
+       xor     ecx,ecx
+       xchg    ecx,[number_of_sections]
+       pop     eax
+       test    eax,eax
+       jns     .PHDR_okay
+       mov     eax,[code_start]
+       cmp     byte[eax + 0x10],3      ;ET_DYN?
+       jnz     .PHDR_okay
+       inc     [number_of_sections]
+       shl     ecx,5
+       mov     eax,0x34
+       mov     dword[ebx + 0x00],6     ;p_type = PHDR
+       mov     dword[ebx + 0x04],eax   ;p_offset
+       add     eax,[image_base]
+       mov     dword[ebx + 0x08],eax   ;p_vaddr
+       mov     dword[ebx + 0x0c],eax   ;p_paddr
+       mov     dword[ebx + 0x10],ecx   ;p_filesz
+       mov     dword[ebx + 0x14],ecx   ;p_memsz
+       mov     dword[ebx + 0x18],4     ;p_flags = R
+       mov     dword[ebx + 0x1c],1     ;p_align
+       add     ebx,0x20
+       cmp     edi,ebx
+       ja      .PHDR_okay
+       mov     ecx,0x20 shr 2
+       rep     stos dword [edi]
+      .PHDR_okay:
        mov     byte [ebx],1
        mov     word [ebx+1Ch],1000h
        mov     byte [ebx+18h],111b
        mov     ebp,[image_base]
        and     dword [ebx+4],0
@@ -3796,17 +3831,57 @@
        pop     edx
       elf64_exe_base_ok:
        mov     byte [edx+36h],38h
        mov     ebx,edi
        mov     ecx,38h shr 2
+       xor     eax,eax
        cmp     [current_pass],0
        je      init_elf64_segments
        imul    ecx,[number_of_sections]
+      .find_interpreter:
+       cmp     dword[edi + eax * 4],3  ;PT_INTERP
+       jz      .has_interpreter
+       add     eax,0x38 shr 2
+       cmp     eax,ecx
+       jb      .find_interpreter
+       jmp     init_elf64_segments
+      .has_interpreter:
+       or      eax,-1
       init_elf64_segments:
+       push    eax
        xor     eax,eax
        rep     stos dword [edi]
-       and     [number_of_sections],0
+       xor     ecx,ecx
+       xchg    ecx,[number_of_sections]
+       pop     eax
+       test    eax,eax
+       jns     .PHDR_okay
+       mov     eax,[code_start]
+       cmp     byte[eax + 0x10],3      ;ET_DYN?
+       jnz     .PHDR_okay
+       inc     [number_of_sections]
+       imul    ecx,0x38
+       mov     eax,0x40
+       mov     dword[ebx + 0x20],ecx   ;p_filesz
+       mov     dword[ebx + 0x28],ecx   ;p_memsz
+       mov     dword[ebx + 0x00],6     ;p_type = PHDR
+       mov     dword[ebx + 0x04],4     ;p_flags = R
+       mov     dword[ebx + 0x08],eax   ;p_offset
+       add     eax,[image_base]
+       mov     ecx,[image_base_high]
+       adc     ecx,0
+       mov     dword[ebx + 0x10],eax   ;p_vaddr
+       mov     dword[ebx + 0x14],ecx   ;p_vaddr
+       mov     dword[ebx + 0x18],eax   ;p_paddr
+       mov     dword[ebx + 0x1c],ecx   ;p_paddr
+       mov     dword[ebx + 0x30],1     ;p_align
+       add     ebx,0x38
+       cmp     edi,ebx
+       ja      .PHDR_okay
+       mov     ecx,0x38 shr 2
+       rep     stos dword [edi]
+      .PHDR_okay:
        mov     byte [ebx],1
        mov     word [ebx+30h],1000h
        mov     byte [ebx+4],111b
        mov     ebp,[image_base]
        mov     ecx,[image_base_high]
@@ -3972,11 +4047,11 @@
                and     edi,PAGE_ALIGNMENT
                mov     esi,PROT_READ + PROT_WRITE + PROT_EXEC
                call    .protect
                mov     ebx,[edi + .Elf32_Ehdr.e_phoff]
                mov     ecx,[edi + ebx + .Elf32_Phdr.p_paddr]
-               sub     ecx,[edi + ebx + .Elf32_Phdr.p_offset]  ;ecx = program base
+               and     ecx,PAGE_ALIGNMENT              ;ecx = program base
                xor     edx,edx
                mov     dl,.program_entry - .do_relocs
                add     edx,[edi + .Elf32_Ehdr.e_entry]
                sub     edi,ecx
                lea     esi,[edi + edx + .relocs_data - .program_entry]
@@ -4126,11 +4201,18 @@
       merge_elf_header:
        mov     eax,[image_base]
        xor     ecx,ecx
        jmp     elf_segment_separated_base
       close_elf_segment:
+       mov     eax,[code_start]
+       cmp     byte[eax + 0x34 + 0x00],6       ;PHDR
+       jnz     .PHDR_okay
+       cmp     [number_of_sections],1
+       jmp     .PHDR_skip
+      .PHDR_okay:
        cmp     [number_of_sections],0
+      .PHDR_skip:
        jne     finish_elf_segment
        cmp     edi,[symbols_stream]
        jne     first_elf_segment_ok
        or      [merge_segment],-1
        mov     eax,[image_base]    
I created two test programs to test all the variations.

For 32-bit
Code:
USE_LIBC        = 1
IS_DYNAMIC      = 1

if IS_DYNAMIC
        format ELF dynamic 0 at 0
else
        format ELF executable 0 at 0x10000
end if

entry start

segment executable

start:
if USE_LIBC
        push    0 O_RDONLY maps
        call    [open]
        add     esp,4 * 3
else
        mov     eax,SYS_OPEN
        mov     ebx,maps
        mov     ecx,O_RDONLY
        xor     edx,edx
        int     0x80
end if
        mov     edx,0x1000
        sub     esp,edx
        mov     ebx,eax
        mov     eax,SYS_READ
        mov     ecx,esp
        int     0x80
        mov     edx,eax
        mov     eax,SYS_WRITE
        mov     ebx,STD_OUTPUT
        int     0x80
        mov     eax,SYS_EXIT
        xor     ebx,ebx
        int     0x80

SYS_EXIT        = 1
SYS_READ        = 3
SYS_WRITE       = 4
SYS_OPEN        = 5
STD_INPUT       = 0
STD_OUTPUT      = 1
O_RDONLY        = 0

DT_NULL         = 0
DT_NEEDED       = 1
DT_STRTAB       = 5
DT_SYMTAB       = 6
DT_STRSZ        = 10
DT_SYMENT       = 11
DT_REL          = 17
DT_RELSZ        = 18
DT_RELENT       = 19
DT_BIND_NOW     = 24
DT_FLAGS        = 30
DT_FLAGS_1      = 0x6ffffffb
STB_GLOBAL      = 1
STT_FUNC        = 2
R_386_32        = 1
DF_BIND_NOW     = 0x00000008
DF_1_NOW        = 0x00000001
DF_1_PIE        = 0x08000000

macro Elf32_Sym name,value,size,bind,type,other,shndx {
        dd name+0
        dd value+0
        dd size+0
        db (bind+0) shl 4 + (type+0)
        db other+0
        dw shndx+0
}
macro Elf32_Rel offset,symbol,type {
        dd offset+0
        dd (symbol+0) shl 8 + (type+0)
}
virtual at 0
        Elf32_Sym
        sizeof.Elf32_Sym = $
        Elf32_Rel
        sizeof.Elf32_Rel = $ - sizeof.Elf32_Sym
end virtual

if USE_LIBC
        segment interpreter readable
                        db '/lib/ld-linux.so.2'
                strtab:
                        db 0
                _libc   db 'libc.so.6',0
                _open   db 'open',0
                strsz   = $ - strtab

        maps:   db      '/proc/self/maps',0
        segment readable

        segment dynamic readable
                dd DT_NEEDED,_libc - strtab
                dd DT_STRTAB,rva strtab
                dd DT_STRSZ,strsz
                dd DT_SYMTAB,rva symtab
                dd DT_SYMENT,sizeof.Elf32_Sym
                dd DT_REL,rva rel
                dd DT_RELSZ,relsz
                dd DT_RELENT,sizeof.Elf32_Rel
                dd DT_BIND_NOW,1
                dd DT_FLAGS,DF_BIND_NOW
                dd DT_FLAGS_1,DF_1_NOW or DF_1_PIE
                dd DT_NULL,0
                symtab:
                        Elf32_Sym
                        Elf32_Sym _open - strtab,0,0,STB_GLOBAL,STT_FUNC,0,0
                rel:
                        Elf32_Rel open,1,R_386_32
                relsz   = $ - rel
                open dd 0
        segment readable writeable
else
        segment readable
        maps:   db      '/proc/self/maps',0
end if

if IS_DYNAMIC
        segment fixups
end if    
And for 64-bit
Code:
USE_LIBC        = 1
IS_DYNAMIC      = 1

if IS_DYNAMIC
        format ELF64 dynamic 0 at 0
else
        format ELF64 executable 0 at 0x10000
end if

entry start

segment executable

start:
        lea     rdi,[maps]
        mov     esi,O_RDONLY
        xor     edx,edx
if USE_LIBC
        call    [open]
else
        mov     eax,SYS_OPEN
        syscall
end if
        mov     edx,0x1000
        sub     rsp,rdx
        mov     rdi,rax
        mov     eax,SYS_READ
        mov     rsi,rsp
        syscall
        mov     edx,eax
        mov     eax,SYS_WRITE
        mov     edi,STD_OUTPUT
        mov     rsi,rsp
        syscall
        mov     eax,SYS_EXIT
        xor     edi,edi
        syscall

SYS_READ        = 0
SYS_WRITE       = 1
SYS_OPEN        = 2
SYS_EXIT        = 60
STD_INPUT       = 0
STD_OUTPUT      = 1
O_RDONLY        = 0

DT_NULL         = 0
DT_NEEDED       = 1
DT_STRTAB       = 5
DT_SYMTAB       = 6
DT_RELA         = 7
DT_RELASZ       = 8
DT_RELAENT      = 9
DT_STRSZ        = 10
DT_SYMENT       = 11
DT_BIND_NOW     = 24
DT_FLAGS        = 30
DT_FLAGS_1      = 0x6ffffffb
STB_GLOBAL      = 1
STT_FUNC        = 2
R_X86_64_64     = 1
DF_BIND_NOW     = 0x00000008
DF_1_NOW        = 0x00000001
DF_1_PIE        = 0x08000000

macro Elf64_Sym name,value,size,bind,type,other,shndx {
        dd name+0
        db (bind+0) shl 4 + (type+0)
        db other+0
        dw shndx+0
        dq value+0
        dq size+0
}
macro Elf64_Rela offset,symbol,type,addend {
        dq rva offset+0
        dq (symbol+0) shl 32 + (type+0)
        dq addend+0
}
virtual at 0
        Elf64_Sym
        sizeof.Elf64_Sym = $
        Elf64_Rela
        sizeof.Elf64_Rela = $ - sizeof.Elf64_Sym
end virtual

if USE_LIBC
        segment interpreter readable
                        db '/lib64/ld-linux-x86-64.so.2'
                strtab:
                        db 0
                _libc   db 'libc.so.6',0
                _open   db 'open',0
                strsz   = $ - strtab

        maps:   db      '/proc/self/maps',0
        segment readable

        segment dynamic readable
                dq DT_NEEDED,_libc - strtab
                dq DT_STRTAB,rva strtab
                dq DT_STRSZ,strsz
                dq DT_SYMTAB,rva symtab
                dq DT_SYMENT,sizeof.Elf64_Sym
                dq DT_RELA,rva rela
                dq DT_RELASZ,relasz
                dq DT_RELAENT,sizeof.Elf64_Rela
                dq DT_BIND_NOW,1
                dq DT_FLAGS,DF_BIND_NOW
                dq DT_FLAGS_1,DF_1_NOW or DF_1_PIE
                dq DT_NULL,0
                symtab:
                        Elf64_Sym
                        Elf64_Sym _open - strtab,0,0,STB_GLOBAL,STT_FUNC,0,0
                rela:
                        Elf64_Rela open,1,R_X86_64_64
                relasz  = $ - rela
                open dq 0
        segment readable writeable
else
        segment readable
        maps:   db      '/proc/self/maps',0
end if    
So I think the last remaining part needed to complete this is adding relocations for 64-bit code.


Last edited by revolution on 11 Feb 2019, 12:15; edited 1 time in total
Post 10 Feb 2019, 11:26
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: 20298
Location: In your JS exploiting you and your system
revolution 11 Feb 2019, 11:59
I added 64-bit fixups. And I changed a few minor things to make it more consistent and make use of more of the existing fasm code. I'll post the full changes I made, rather than just the incremental changes. Compared to the current v1.73.08.
Code:
--- SOURCE/FORMATS.INC
+++ SOURCE/FORMATS.INC
@@ -3062,11 +3062,47 @@
        jz      instruction_assembled
        mov     byte [ebx+10h],8
        jmp     instruction_assembled
 mark_elf_relocation:
        test    [format_flags],1
-       jnz     invalid_use_of_symbol
+       jz      .not_dynamic
+       cmp     [value_type],2
+       jz      .elf32_reloc
+       cmp     [value_type],4
+       jne     invalid_use_of_symbol
+       push    ebx eax edx
+       mov     eax,edi
+       xor     edx,edx
+       sub     eax,[ebp]
+       sbb     edx,[ebp+4]
+       sub     eax,[image_base]
+       sbb     edx,[image_base_high]
+       mov     ebx,[free_additional_memory]
+       inc     [number_of_relocations]
+       add     ebx,8
+       cmp     ebx,[structures_buffer]
+       jae     out_of_memory
+       mov     [free_additional_memory],ebx
+       mov     [ebx-8],eax
+       mov     [ebx-4],edx
+       pop     edx eax ebx
+       ret
+      .elf32_reloc:
+       push    ebx eax
+       mov     eax,edi
+       sub     eax,[ebp]
+       sub     eax,[image_base]
+       mov     ebx,[free_additional_memory]
+       inc     [number_of_relocations]
+       add     ebx,4
+       cmp     ebx,[structures_buffer]
+       jae     out_of_memory
+       mov     [free_additional_memory],ebx
+       mov     [ebx-4],eax
+       pop     eax ebx
+       ret
+      .not_dynamic:
        push    ebx
        mov     ebx,[addressing_space]
        cmp     [value_type],3
        je      elf_relocation_relative
        cmp     [value_type],7
@@ -3713,17 +3749,53 @@
        pop     edx
       elf_exe_base_ok:
        mov     byte [edx+2Ah],20h
        mov     ebx,edi
        mov     ecx,20h shr 2
+       xor     eax,eax
        cmp     [current_pass],0
        je      init_elf_segments
        imul    ecx,[number_of_sections]
+      .find_interpreter:
+       cmp     dword[edi + eax * 4],3  ;PT_INTERP
+       jz      .has_interpreter
+       add     eax,0x20 shr 2
+       cmp     eax,ecx
+       jb      .find_interpreter
+       jmp     init_elf_segments
+      .has_interpreter:
+       or      eax,-1
       init_elf_segments:
+       push    eax
        xor     eax,eax
        rep     stos dword [edi]
-       and     [number_of_sections],0
+       xor     ecx,ecx
+       xchg    ecx,[number_of_sections]
+       pop     eax
+       test    eax,eax
+       jns     .PHDR_okay
+       mov     eax,[code_start]
+       cmp     byte[eax + 0x10],3      ;ET_DYN?
+       jnz     .PHDR_okay
+       inc     [number_of_sections]
+       shl     ecx,5
+       mov     eax,0x34
+       mov     dword[ebx + 0x00],6     ;p_type = PHDR
+       mov     dword[ebx + 0x04],eax   ;p_offset
+       add     eax,[image_base]
+       mov     dword[ebx + 0x08],eax   ;p_vaddr
+       mov     dword[ebx + 0x0c],eax   ;p_paddr
+       mov     dword[ebx + 0x10],ecx   ;p_filesz
+       mov     dword[ebx + 0x14],ecx   ;p_memsz
+       mov     dword[ebx + 0x18],4     ;p_flags = R
+       mov     dword[ebx + 0x1c],1     ;p_align
+       add     ebx,0x20
+       cmp     edi,ebx
+       ja      .PHDR_okay
+       mov     ecx,0x20 shr 2
+       rep     stos dword [edi]
+      .PHDR_okay:
        mov     byte [ebx],1
        mov     word [ebx+1Ch],1000h
        mov     byte [ebx+18h],111b
        mov     ebp,[image_base]
        and     dword [ebx+4],0
@@ -3781,17 +3853,57 @@
        pop     edx
       elf64_exe_base_ok:
        mov     byte [edx+36h],38h
        mov     ebx,edi
        mov     ecx,38h shr 2
+       xor     eax,eax
        cmp     [current_pass],0
        je      init_elf64_segments
        imul    ecx,[number_of_sections]
+      .find_interpreter:
+       cmp     dword[edi + eax * 4],3  ;PT_INTERP
+       jz      .has_interpreter
+       add     eax,0x38 shr 2
+       cmp     eax,ecx
+       jb      .find_interpreter
+       jmp     init_elf64_segments
+      .has_interpreter:
+       or      eax,-1
       init_elf64_segments:
+       push    eax
        xor     eax,eax
        rep     stos dword [edi]
-       and     [number_of_sections],0
+       xor     ecx,ecx
+       xchg    ecx,[number_of_sections]
+       pop     eax
+       test    eax,eax
+       jns     .PHDR_okay
+       mov     eax,[code_start]
+       cmp     byte[eax + 0x10],3      ;ET_DYN?
+       jnz     .PHDR_okay
+       inc     [number_of_sections]
+       imul    ecx,0x38
+       mov     eax,0x40
+       mov     dword[ebx + 0x20],ecx   ;p_filesz
+       mov     dword[ebx + 0x28],ecx   ;p_memsz
+       mov     dword[ebx + 0x00],6     ;p_type = PHDR
+       mov     dword[ebx + 0x04],4     ;p_flags = R
+       mov     dword[ebx + 0x08],eax   ;p_offset
+       add     eax,[image_base]
+       mov     ecx,[image_base_high]
+       adc     ecx,0
+       mov     dword[ebx + 0x10],eax   ;p_vaddr
+       mov     dword[ebx + 0x14],ecx   ;p_vaddr
+       mov     dword[ebx + 0x18],eax   ;p_paddr
+       mov     dword[ebx + 0x1c],ecx   ;p_paddr
+       mov     dword[ebx + 0x30],1     ;p_align
+       add     ebx,0x38
+       cmp     edi,ebx
+       ja      .PHDR_okay
+       mov     ecx,0x38 shr 2
+       rep     stos dword [edi]
+      .PHDR_okay:
        mov     byte [ebx],1
        mov     word [ebx+30h],1000h
        mov     byte [ebx+4],111b
        mov     ebp,[image_base]
        mov     ecx,[image_base_high]
@@ -3836,10 +3948,17 @@
        mov     [ebx+1Ch],edx
        jmp     instruction_assembled
 elf_segment:
        bt      [format_flags],0
        jnc     illegal_instruction
+       cmp     word[esi],0x051A        ;fixups?
+       jnz     .reloc_count_okay
+       lods    word[esi]
+       cmp     [number_of_relocations],0
+       jz      instruction_assembled
+       sub     esi,2
+      .reloc_count_okay:
        test    [format_flags],8
        jnz     elf64_segment
        call    close_elf_segment
        push    eax
        call    create_addressing_space
@@ -3856,10 +3975,157 @@
        or      [next_pass_needed],-1
       new_elf_segment:
        mov     byte [ebx],1
        and     dword [ebx+18h],0
        mov     word [ebx+1Ch],1000h
+       cmp     word[esi],0x051A        ;fixups?
+       jnz     elf_segment_flags
+       lods    word[esi]
+       or      byte[ebx+18h],1         ;mark as executable
+       pop     edx
+       mov     eax,edi
+       sub     eax,[code_start]
+       mov     [ebx+4],eax
+       and     eax,0FFFh
+       add     eax,edx
+       mov     [ebx+8],eax
+       mov     [ebx+0Ch],eax
+       push    eax esi
+       mov     esi,.elf_relocator
+       mov     ecx,.elf_relocator.size
+       rep     movsb
+       mov     esi,[code_start]
+       mov     eax,[ebx + 8]
+       xchg    eax,[esi + 0x18]        ;e_entry
+       mov     [edi - 4],eax
+       mov     esi,[free_additional_memory]
+       mov     ecx,[number_of_relocations]
+       lea     eax,[ecx * 4]
+       sub     esi,eax
+       rep     movsd
+       mov     eax,END_OF_RELOCATIONS
+       stosd
+       pop     esi eax
+       xor     edx,edx
+       jmp     elf_segment_addressing_setup
+      .elf_relocator:
+       label .Elf32_Ehdr.e_entry       dword at 0x18
+       label .Elf32_Ehdr.e_phoff       dword at 0x1c
+       label .Elf32_Ehdr.e_phentsize   word at 0x2a
+       label .Elf32_Ehdr.e_phnum       word at 0x2c
+       label .Elf32_Phdr.p_type        dword at 0x00
+       label .Elf32_Phdr.p_offset      dword at 0x04
+       label .Elf32_Phdr.p_paddr       dword at 0x0c
+       label .Elf32_Phdr.p_memsz       dword at 0x14
+       label .Elf32_Phdr.p_flags       dword at 0x18
+       label .Elf32_auxv_t.a_type      dword at 0
+       label .Elf32_auxv_t.a_val       dword at 4
+       ;p_type
+               PT_LOAD                 = 1
+       ;p_flags
+               PF_X                    = 1 shl 0
+               PF_W                    = 1 shl 1
+               PF_R                    = 1 shl 2
+       ;protection flags
+               PROT_READ               = 0x1
+               PROT_WRITE              = 0x2
+               PROT_EXEC               = 0x4
+       ;syscall
+               SYS_MPROTECT            = 125
+               SYS_EXIT_GROUP          = 252
+       ;auxv
+               AT_NULL                 = 0
+               AT_PHDR                 = 3
+               Elf32_auxv_t.size       = 8
+       ;local
+               END_OF_RELOCATIONS      = -1
+               PAGE_ALIGNMENT          = not 0xfff
+       ;the following code section can run anywhere without needing
+       ;any RVAs or relocations, it can be copied into the output as-is
+           .do_relocs:
+               pop     eax                             ;argument count (argc)
+               push    eax
+               cld
+               lea     esi,[esp + 4 + (eax + 1) * 4]   ;skip the args and the final null
+           .skip_environment:
+               lodsd
+               test    eax,eax                         ;last entry in environment?
+               jnz     .skip_environment
+           .scan_auxv:
+               lodsd
+               mov     ecx,eax                         ;ecx = Elf32_auxv_t.a_type
+               lodsd                                   ;eax = Elf32_auxv_t.a_val
+               cmp     ecx,AT_NULL                     ;end of auxv table?
+               jz      .fail
+               cmp     ecx,AT_PHDR
+               jnz     .scan_auxv
+               mov     edi,eax
+               and     edi,PAGE_ALIGNMENT
+               mov     esi,PROT_READ + PROT_WRITE + PROT_EXEC
+               call    .protect
+               mov     ebx,[edi + .Elf32_Ehdr.e_phoff]
+               mov     ecx,[edi + ebx + .Elf32_Phdr.p_paddr]
+               sub     ecx,[edi + ebx + .Elf32_Phdr.p_offset]  ;ecx = program base
+               xor     edx,edx
+               mov     dl,.program_entry - .do_relocs
+               add     edx,[edi + .Elf32_Ehdr.e_entry]
+               sub     edx,ecx
+               neg     ecx
+               add     ecx,edi                         ;ecx = relocation offset
+               lea     esi,[edi + edx + .relocs_data - .program_entry]
+               mov     eax,edx
+           .reloc_loop:
+               add     [edi + eax],ecx
+               lodsd
+               cmp     eax,END_OF_RELOCATIONS
+               jnz     .reloc_loop
+               push    dword[edi + edx]                ;after retn we run the program
+               xor     esi,esi                         ;restore to original permissions
+           .protect:
+               movzx   ebp,[edi + .Elf32_Ehdr.e_phnum] ;ebp = count of Phdr entries
+           .loop_Phdr:
+               dec     ebp
+               js      .retn
+               movzx   eax,[edi + .Elf32_Ehdr.e_phentsize]
+               imul    eax,ebp
+               add     eax,edi
+               add     eax,[edi + .Elf32_Ehdr.e_phoff] ;eax = current Elf32_Phdr
+               cmp     [eax + .Elf32_Phdr.p_type],PT_LOAD
+               jnz     .loop_Phdr
+               mov     ecx,[eax + .Elf32_Phdr.p_flags]
+               xor     edx,edx
+               assert  PROT_EXEC or PF_X = PROT_READ or PF_R & PROT_WRITE = PF_W
+               repeat 3
+                       shr     ecx,1
+                       rcl     edx,1
+               end repeat
+               or      edx,esi                         ;set the requested flags
+               mov     ebx,[eax + .Elf32_Phdr.p_paddr]
+               mov     ecx,[edi + .Elf32_Ehdr.e_phoff]
+               sub     ebx,[edi + ecx + .Elf32_Phdr.p_paddr]
+               add     ebx,[edi + ecx + .Elf32_Phdr.p_offset]
+               mov     ecx,[eax + .Elf32_Phdr.p_memsz]
+               add     ecx,ebx
+               and     ebx,PAGE_ALIGNMENT
+               sub     ecx,ebx                         ;len
+               add     ebx,edi                         ;addr
+               xor     eax,eax
+               mov     al,SYS_MPROTECT
+               int     0x80
+               test    eax,eax
+               jz      .loop_Phdr
+           .fail:
+               xor     eax,eax
+               mov     al,SYS_EXIT_GROUP
+               or      ebx,-1
+               int     0x80
+           .retn:
+               retn
+           .program_entry:
+               dd      0       ;the program entry address is transferred to here, and it gets relocated
+           .relocs_data:
+      .elf_relocator.size = $ - .elf_relocator
       elf_segment_flags:
        cmp     byte [esi],1Eh
        je      elf_segment_type
        cmp     byte [esi],19h
        jne     elf_segment_flags_ok
@@ -3951,11 +4217,18 @@
       merge_elf_header:
        mov     eax,[image_base]
        xor     ecx,ecx
        jmp     elf_segment_separated_base
       close_elf_segment:
+       mov     eax,[code_start]
+       cmp     byte[eax + 0x34 + 0x00],6       ;PHDR
+       jnz     .PHDR_okay
+       cmp     [number_of_sections],1
+       jmp     .PHDR_skip
+      .PHDR_okay:
        cmp     [number_of_sections],0
+      .PHDR_skip:
        jne     finish_elf_segment
        cmp     edi,[symbols_stream]
        jne     first_elf_segment_ok
        or      [merge_segment],-1
        mov     eax,[image_base]
@@ -4018,10 +4291,143 @@
        or      [next_pass_needed],-1
       new_elf64_segment:
        mov     byte [ebx],1
        and     dword [ebx+4],0
        mov     word [ebx+30h],1000h
+       cmp     word[esi],0x051A        ;fixups?
+       jnz     elf64_segment_flags
+       lods    word[esi]
+       or      byte[ebx+4],1           ;mark as executable
+       pop     edx eax
+       mov     ecx,edi
+       sub     ecx,[code_start]
+       mov     [ebx+8],ecx
+       and     ecx,0FFFh
+       add     eax,ecx
+       adc     edx,0
+       mov     [ebx+0x10],eax
+       mov     [ebx+0x14],edx
+       mov     [ebx+0x18],eax
+       mov     [ebx+0x1c],edx
+       push    edx eax esi
+       mov     esi,.elf64_relocator
+       mov     ecx,.elf64_relocator.size
+       rep     movsb
+       mov     esi,[code_start]
+       xchg    eax,[esi + 0x18]        ;e_entry
+       xchg    edx,[esi + 0x1c]        ;e_entry
+       mov     [edi - 8],eax
+       mov     [edi - 4],edx
+       mov     esi,[free_additional_memory]
+       mov     ecx,[number_of_relocations]
+       lea     eax,[ecx * 8]
+       sub     esi,eax
+       add     ecx,ecx
+       rep     movsd
+       mov     eax,END_OF_RELOCATIONS
+       stosd
+       stosd
+       pop     esi eax edx
+       jmp     elf_segment_addressing_setup
+      .elf64_relocator:
+       label .Elf64_Ehdr.e_entry       qword at 0x18
+       label .Elf64_Ehdr.e_phoff       qword at 0x20
+       label .Elf64_Ehdr.e_phentsize   word at 0x36
+       label .Elf64_Ehdr.e_phnum       word at 0x38
+       label .Elf64_Phdr.p_type        dword at 0x00
+       label .Elf64_Phdr.p_flags       dword at 0x04
+       label .Elf64_Phdr.p_offset      qword at 0x08
+       label .Elf64_Phdr.p_paddr       qword at 0x18
+       label .Elf64_Phdr.p_memsz       qword at 0x28
+       label .Elf64_auxv_t.a_type      qword at 0
+       label .Elf64_auxv_t.a_val       qword at 8
+       SYS64_MPROTECT                  = 10
+       SYS64_EXIT_GROUP                = 231
+       Elf64_auxv_t.size               = 16
+       ;the following code section can run anywhere without needing
+       ;any RVAs or relocations, it can be copied into the output as-is
+               use64
+           .do_relocs:
+               pop     rax                             ;argument count (argc)
+               push    rax
+               cld
+               lea     rsi,[rsp + 8 + (rax + 1) * 8]   ;skip the args and the final null
+           .skip_environment:
+               lodsq
+               test    rax,rax                         ;last entry in environment?
+               jnz     .skip_environment
+           .scan_auxv:
+               lodsq
+               mov     r13,rax                         ;ecx = Elf64_auxv_t.a_type
+               lodsq                                   ;eax = Elf64_auxv_t.a_val
+               cmp     r13,AT_NULL                     ;end of auxv table?
+               jz      .fail
+               cmp     r13,AT_PHDR
+               jnz     .scan_auxv
+               mov     r15,rax
+               and     r15,PAGE_ALIGNMENT              ;r15 = elf header
+               mov     r14,rax                         ;r14 = PHDR
+               mov     r13,[rax + .Elf64_Phdr.p_paddr]
+               movzx   r12,[r15 + .Elf64_Ehdr.e_phnum]
+               movzx   r10,[r15 + .Elf64_Ehdr.e_phentsize];r10 = PHDR size
+               imul    r12,r10
+               add     r12,rax                         ;r12 = end of PHDRs
+               sub     r13,[rax + .Elf64_Phdr.p_offset]
+               neg     r13
+               add     r13,r15                         ;r13 = relocation offset
+               mov     ebx,PROT_READ + PROT_WRITE + PROT_EXEC
+               call    .protect
+               lea     rdx,[.program_entry]
+               lea     rsi,[rdx + .relocs_data - .program_entry]
+               sub     rdx,r15
+               mov     rax,rdx
+           .reloc_loop:
+               add     [r15 + rax],r13
+               lodsq
+               cmp     rax,END_OF_RELOCATIONS
+               jnz     .reloc_loop
+               push    qword[r15 + rdx]                ;after retn we run the program
+               xor     ebx,ebx                         ;restore to original permissions
+           .protect:
+               mov     r9,r12
+           .loop_Phdr:
+               sub     r9,r10
+               cmp     r9,r14
+               jb      .retn
+               cmp     [r9 + .Elf64_Phdr.p_type],PT_LOAD
+               jnz     .loop_Phdr
+               mov     ecx,[r9 + .Elf64_Phdr.p_flags]
+               xor     edx,edx
+               assert  PROT_EXEC or PF_X = PROT_READ or PF_R & PROT_WRITE = PF_W
+               repeat 3
+                       shr     ecx,1
+                       rcl     edx,1
+               end repeat
+               or      edx,ebx                         ;set the requested flags
+               mov     rdi,[r9 + .Elf64_Phdr.p_paddr]
+               mov     rsi,[r9 + .Elf64_Phdr.p_memsz]
+               add     rdi,r13
+               add     rsi,rdi
+               and     rdi,PAGE_ALIGNMENT              ;addr
+               sub     rsi,rdi                         ;len
+               xor     eax,eax
+               mov     al,SYS64_MPROTECT
+               syscall
+               test    rax,rax
+               jz      .loop_Phdr
+           .fail:
+               xor     eax,eax
+               mov     al,SYS64_EXIT_GROUP
+               or      rdi,-1
+               syscall
+           .retn:
+               retn
+               use32
+           .program_entry:
+               dq 0            ;the program entry address is transferred to here, and it gets relocated
+           .relocs_data:
+      .elf64_relocator.size = $ - .elf64_relocator
       elf64_segment_flags:
        cmp     byte [esi],1Eh
        je      elf64_segment_type
        cmp     byte [esi],19h
        jne     elf64_segment_flags_ok
@@ -4105,11 +4511,18 @@
        mov     eax,[image_base]
        mov     edx,[image_base_high]
        xor     ecx,ecx
        jmp     elf64_segment_separated_base
       close_elf64_segment:
+       mov     eax,[code_start]
+       cmp     byte[eax + 0x40 + 0x00],6       ;PHDR
+       jnz     .PHDR_okay
+       cmp     [number_of_sections],1
+       jmp     .PHDR_skip
+      .PHDR_okay:
        cmp     [number_of_sections],0
+      .PHDR_skip:
        jne     finish_elf64_segment
        cmp     edi,[symbols_stream]
        jne     first_elf64_segment_ok
        or      [merge_segment],-1
        mov     eax,[image_base]    
The standard 64-bit linker (aka the interpreter can be used with program bases up to 1TB. The standard 32-bit linker can be used with program bases up to 4GB.

And to show a fix for the problem with GNURELRO I used these two programs.

32-bit
Code:
USE_LIBC        = 1
IS_DYNAMIC      = 1
USE_GNURELRO    = 0

if IS_DYNAMIC
        format ELF dynamic 0 at 1 shl 32 - 0x5000
else
        format ELF executable 0 at 1 shl 32 - 0x4000
end if

entry start

segment gnustack
segment executable

start:
if USE_LIBC & ~ USE_GNURELRO
        mov     edx,PROT_READ
        mov     ecx,external_links_length + rva external_links and 0xfff
        mov     ebx,external_links - rva external_links and 0xfff
        mov     eax,SYS_MPROTECT
        int     0x80
end if
if USE_LIBC
        push    0 O_RDONLY maps
        call    [open]
        add     esp,4 * 3
else
        mov     eax,SYS_OPEN
        mov     ebx,maps
        mov     ecx,O_RDONLY
        xor     edx,edx
        int     0x80
end if
        mov     edx,0x1000
        sub     esp,edx
        mov     ebx,eax
        mov     eax,SYS_READ
        mov     ecx,esp
        int     0x80
        mov     edx,eax
        mov     eax,SYS_WRITE
        mov     ebx,STD_OUTPUT
        int     0x80
        mov     eax,SYS_EXIT
        xor     ebx,ebx
        int     0x80

SYS_EXIT        = 1
SYS_READ        = 3
SYS_WRITE       = 4
SYS_OPEN        = 5
SYS_MPROTECT    = 125
STD_INPUT       = 0
STD_OUTPUT      = 1
O_RDONLY        = 0
PROT_READ       = 0x1

DT_NULL         = 0
DT_NEEDED       = 1
DT_STRTAB       = 5
DT_SYMTAB       = 6
DT_STRSZ        = 10
DT_SYMENT       = 11
DT_REL          = 17
DT_RELSZ        = 18
DT_RELENT       = 19
DT_BIND_NOW     = 24
DT_FLAGS        = 30
DT_FLAGS_1      = 0x6ffffffb
STB_GLOBAL      = 1
STT_FUNC        = 2
R_386_32        = 1
DF_BIND_NOW     = 0x00000008
DF_1_NOW        = 0x00000001
DF_1_PIE        = 0x08000000

macro Elf32_Sym name,value,size,bind,type,other,shndx {
        dd name+0
        dd value+0
        dd size+0
        db (bind+0) shl 4 + (type+0)
        db other+0
        dw shndx+0
}
macro Elf32_Rel offset,symbol,type {
        dd rva offset+0
        dd (symbol+0) shl 8 + (type+0)
}
virtual at 0
        Elf32_Sym
        sizeof.Elf32_Sym = $
        Elf32_Rel
        sizeof.Elf32_Rel = $ - sizeof.Elf32_Sym
end virtual

if USE_LIBC
        segment interpreter readable
                        db '/lib/ld-linux.so.2'
                strtab:
                        db 0
                _libc   db 'libc.so.6',0
                _open   db 'open',0
                strsz   = $ - strtab

        maps:   db      '/proc/self/maps',0

        segment dynamic readable
                dd DT_NEEDED,_libc - strtab
                dd DT_STRTAB,rva strtab
                dd DT_STRSZ,strsz
                dd DT_SYMTAB,rva symtab
                dd DT_SYMENT,sizeof.Elf32_Sym
                dd DT_REL,rva rel
                dd DT_RELSZ,relsz
                dd DT_RELENT,sizeof.Elf32_Rel
                dd DT_BIND_NOW,1
                dd DT_FLAGS,DF_BIND_NOW
                dd DT_FLAGS_1,DF_1_NOW or DF_1_PIE
                dd DT_NULL,0
                symtab:
                        Elf32_Sym
                        Elf32_Sym _open - strtab,0,0,STB_GLOBAL,STT_FUNC,0,0
                rel:
                        Elf32_Rel open,1,R_386_32
                relsz   = $ - rel
    if USE_GNURELRO
        segment gnurelro readable
    end if
                external_links:
                open dd 0
                external_links_length = $ - external_links
        segment readable writeable
else
        segment readable
        maps:   db      '/proc/self/maps',0
end if

segment fixups    
And 64-bit
Code:
USE_LIBC        = 1
IS_DYNAMIC      = 1
USE_GNURELRO    = 0

if IS_DYNAMIC
        format ELF64 dynamic 0 at 1 shl 40 - 0x1000
else
        format ELF64 executable 0 at 1 shl 40 - 0x1000
end if

entry start

segment gnustack
segment executable

start:
if USE_LIBC & ~ USE_GNURELRO
        mov     edx,PROT_READ
        mov     esi,external_links_length + rva external_links and 0xfff
        mov     rdi,external_links - rva external_links and 0xfff
        mov     eax,SYS_MPROTECT
        syscall
end if
        mov     rdi,maps
        mov     esi,O_RDONLY
        xor     edx,edx
if USE_LIBC
        call    [open]
else
        mov     eax,SYS_OPEN
        syscall
end if
        mov     edx,0x1000
        sub     rsp,rdx
        mov     rdi,rax
        mov     eax,SYS_READ
        mov     rsi,rsp
        syscall
        mov     edx,eax
        mov     eax,SYS_WRITE
        mov     edi,STD_OUTPUT
        mov     rsi,rsp
        syscall
        mov     eax,SYS_EXIT
        xor     edi,edi
        syscall

SYS_READ        = 0
SYS_WRITE       = 1
SYS_OPEN        = 2
SYS_MPROTECT    = 10
SYS_EXIT        = 60
STD_INPUT       = 0
STD_OUTPUT      = 1
O_RDONLY        = 0
PROT_READ       = 0x1

DT_NULL         = 0
DT_NEEDED       = 1
DT_STRTAB       = 5
DT_SYMTAB       = 6
DT_RELA         = 7
DT_RELASZ       = 8
DT_RELAENT      = 9
DT_STRSZ        = 10
DT_SYMENT       = 11
DT_BIND_NOW     = 24
DT_FLAGS        = 30
DT_FLAGS_1      = 0x6ffffffb
STB_GLOBAL      = 1
STT_FUNC        = 2
R_X86_64_64     = 1
DF_BIND_NOW     = 0x00000008
DF_1_NOW        = 0x00000001
DF_1_PIE        = 0x08000000

macro Elf64_Sym name,value,size,bind,type,other,shndx {
        dd name+0
        db (bind+0) shl 4 + (type+0)
        db other+0
        dw shndx+0
        dq value+0
        dq size+0
}
macro Elf64_Rela offset,symbol,type,addend {
        dq rva offset+0
        dq (symbol+0) shl 32 + (type+0)
        dq addend+0
}
virtual at 0
        Elf64_Sym
        sizeof.Elf64_Sym = $
        Elf64_Rela
        sizeof.Elf64_Rela = $ - sizeof.Elf64_Sym
end virtual

if USE_LIBC
        segment interpreter readable
                        db '/lib64/ld-linux-x86-64.so.2'
                strtab:
                        db 0
                _libc   db 'libc.so.6',0
                _open   db 'open',0
                strsz   = $ - strtab

        maps:   db      '/proc/self/maps',0

        segment dynamic readable
                dq DT_NEEDED,_libc - strtab
                dq DT_STRTAB,rva strtab
                dq DT_STRSZ,strsz
                dq DT_SYMTAB,rva symtab
                dq DT_SYMENT,sizeof.Elf64_Sym
                dq DT_RELA,rva rela
                dq DT_RELASZ,relasz
                dq DT_RELAENT,sizeof.Elf64_Rela
                dq DT_BIND_NOW,1
                dq DT_FLAGS,DF_BIND_NOW
                dq DT_FLAGS_1,DF_1_NOW or DF_1_PIE
                dq DT_NULL,0
                symtab:
                        Elf64_Sym
                        Elf64_Sym _open - strtab,0,0,STB_GLOBAL,STT_FUNC,0,0
                rela:
                        Elf64_Rela open,1,R_X86_64_64
                relasz  = $ - rela
    if USE_GNURELRO
        segment gnurelro readable
    end if
                external_links:
                open dq 0
                external_links_length = $ - external_links
        segment readable writeable
else
        segment readable
        maps:   db      '/proc/self/maps',0
end if

segment fixups    
I've set the program bases to the maximum possible, but they can be anything else below also. Naturally the static exe has to be above 64kB, we can't allocate the first 64kB.

Something "fun" happens when the base address is placed above 1TB. All the GNU ELF tools either crash or show nonsense information. This includes the Linux loader also. I guess they are all using a common source base since they all exhibit the same problem

It is also possible in fasm to set the base in the 32-bit source to 0xfffff000 and have it wrap around to 0 when a new segment is added. Same for 64-bit with 2^64-0x1000. But both of these can't be run, of course, the loader either crashes or refuses to recognise it.
Post 11 Feb 2019, 11:59
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: 20298
Location: In your JS exploiting you and your system
revolution 12 Feb 2019, 11:40
I chased down a few bugs, added code to check for memory overflow, optimised the relocation code, etc. So this is the first production ready code. Once again I show the full differences from v1.73.08.
Code:
--- SOURCE/FORMATS.INC
+++ SOURCE/FORMATS.INC
@@ -3062,11 +3062,30 @@
        jz      instruction_assembled
        mov     byte [ebx+10h],8
        jmp     instruction_assembled
 mark_elf_relocation:
        test    [format_flags],1
-       jnz     invalid_use_of_symbol
+       jz      .not_dynamic
+       cmp     [value_type],2
+       jz      .elf_reloc
+       cmp     [value_type],4
+       jne     invalid_use_of_symbol
+      .elf_reloc:
+       push    ebx eax
+       mov     eax,edi
+       sub     eax,[ebp]
+       sub     eax,[image_base]
+       mov     ebx,[free_additional_memory]
+       inc     [number_of_relocations]
+       add     ebx,4
+       cmp     ebx,[structures_buffer]
+       jae     out_of_memory
+       mov     [free_additional_memory],ebx
+       mov     [ebx-4],eax
+       pop     eax ebx
+       ret
+      .not_dynamic:
        push    ebx
        mov     ebx,[addressing_space]
        cmp     [value_type],3
        je      elf_relocation_relative
        cmp     [value_type],7
@@ -3681,10 +3700,13 @@
        call    write
        jc      write_failed
        jmp     output_written
 
 format_elf_exe:
+       mov     eax,[number_of_sections]
+       shl     eax,5
+       add     edi,eax
        add     esi,2
        or      [format_flags],1
        cmp     byte [esi],'('
        jne     elf_exe_brand_ok
        inc     esi
@@ -3710,20 +3732,60 @@
        cmp     [value_type],0
        jne     invalid_use_of_symbol
        mov     [image_base],eax
        pop     edx
       elf_exe_base_ok:
+       mov     eax,[number_of_sections]
+       shl     eax,5
+       sub     edi,eax
        mov     byte [edx+2Ah],20h
        mov     ebx,edi
        mov     ecx,20h shr 2
+       xor     eax,eax
        cmp     [current_pass],0
        je      init_elf_segments
        imul    ecx,[number_of_sections]
+      .find_interpreter:
+       cmp     dword[edi + eax * 4],3  ;PT_INTERP
+       jz      .has_interpreter
+       add     eax,0x20 shr 2
+       cmp     eax,ecx
+       jb      .find_interpreter
+       jmp     init_elf_segments
+      .has_interpreter:
+       or      eax,-1
       init_elf_segments:
+       push    eax
+       xor     eax,eax
+       rep     stos dword [edi]
+       xor     ecx,ecx
+       xchg    ecx,[number_of_sections]
+       pop     eax
+       test    eax,eax
+       jns     .PHDR_okay
+       mov     eax,[code_start]
+       cmp     byte[eax + 0x10],3      ;ET_DYN?
+       jnz     .PHDR_okay
+       inc     [number_of_sections]
+       shl     ecx,5
+       mov     eax,0x34
+       mov     dword[ebx + 0x00],6     ;p_type = PHDR
+       mov     dword[ebx + 0x04],eax   ;p_offset
+       add     eax,[image_base]
+       mov     dword[ebx + 0x08],eax   ;p_vaddr
+       mov     dword[ebx + 0x0c],eax   ;p_paddr
+       mov     dword[ebx + 0x10],ecx   ;p_filesz
+       mov     dword[ebx + 0x14],ecx   ;p_memsz
+       mov     dword[ebx + 0x18],4     ;p_flags = R
+       mov     dword[ebx + 0x1c],1     ;p_align
+       add     ebx,0x20
+       cmp     edi,ebx
+       ja      .PHDR_okay
+       mov     ecx,0x20 shr 2
        xor     eax,eax
        rep     stos dword [edi]
-       and     [number_of_sections],0
+      .PHDR_okay:
        mov     byte [ebx],1
        mov     word [ebx+1Ch],1000h
        mov     byte [ebx+18h],111b
        mov     ebp,[image_base]
        and     dword [ebx+4],0
@@ -3747,10 +3809,13 @@
        mov     [ebx+4],edx
        mov     [ebx+8],cl
        mov     [symbols_stream],edi
        jmp     format_defined
       format_elf64_exe:
+       mov     eax,[number_of_sections]
+       imul    eax,0x38
+       add     edi,eax
        add     esi,2
        or      [format_flags],1
        cmp     byte [esi],'('
        jne     elf64_exe_brand_ok
        inc     esi
@@ -3778,20 +3843,64 @@
        jne     invalid_use_of_symbol
        mov     [image_base],eax
        mov     [image_base_high],edx
        pop     edx
       elf64_exe_base_ok:
+       mov     eax,[number_of_sections]
+       imul    eax,0x38
+       sub     edi,eax
        mov     byte [edx+36h],38h
        mov     ebx,edi
        mov     ecx,38h shr 2
+       xor     eax,eax
        cmp     [current_pass],0
        je      init_elf64_segments
        imul    ecx,[number_of_sections]
+      .find_interpreter:
+       cmp     dword[edi + eax * 4],3  ;PT_INTERP
+       jz      .has_interpreter
+       add     eax,0x38 shr 2
+       cmp     eax,ecx
+       jb      .find_interpreter
+       jmp     init_elf64_segments
+      .has_interpreter:
+       or      eax,-1
       init_elf64_segments:
+       push    eax
+       xor     eax,eax
+       rep     stos dword [edi]
+       xor     ecx,ecx
+       xchg    ecx,[number_of_sections]
+       pop     eax
+       test    eax,eax
+       jns     .PHDR_okay
+       mov     eax,[code_start]
+       cmp     byte[eax + 0x10],3      ;ET_DYN?
+       jnz     .PHDR_okay
+       inc     [number_of_sections]
+       imul    ecx,0x38
+       mov     eax,0x40
+       mov     dword[ebx + 0x20],ecx   ;p_filesz
+       mov     dword[ebx + 0x28],ecx   ;p_memsz
+       mov     dword[ebx + 0x00],6     ;p_type = PHDR
+       mov     dword[ebx + 0x04],4     ;p_flags = R
+       mov     dword[ebx + 0x08],eax   ;p_offset
+       add     eax,[image_base]
+       mov     ecx,[image_base_high]
+       adc     ecx,0
+       mov     dword[ebx + 0x10],eax   ;p_vaddr
+       mov     dword[ebx + 0x14],ecx   ;p_vaddr
+       mov     dword[ebx + 0x18],eax   ;p_paddr
+       mov     dword[ebx + 0x1c],ecx   ;p_paddr
+       mov     dword[ebx + 0x30],1     ;p_align
+       add     ebx,0x38
+       cmp     edi,ebx
+       ja      .PHDR_okay
+       mov     ecx,0x38 shr 2
        xor     eax,eax
        rep     stos dword [edi]
-       and     [number_of_sections],0
+      .PHDR_okay:
        mov     byte [ebx],1
        mov     word [ebx+30h],1000h
        mov     byte [ebx+4],111b
        mov     ebp,[image_base]
        mov     ecx,[image_base_high]
@@ -3836,10 +3945,17 @@
        mov     [ebx+1Ch],edx
        jmp     instruction_assembled
 elf_segment:
        bt      [format_flags],0
        jnc     illegal_instruction
+       cmp     word[esi],0x051A        ;fixups?
+       jnz     .reloc_count_okay
+       lods    word[esi]
+       cmp     [number_of_relocations],0
+       jz      instruction_assembled
+       sub     esi,2
+      .reloc_count_okay:
        test    [format_flags],8
        jnz     elf64_segment
        call    close_elf_segment
        push    eax
        call    create_addressing_space
@@ -3856,10 +3972,163 @@
        or      [next_pass_needed],-1
       new_elf_segment:
        mov     byte [ebx],1
        and     dword [ebx+18h],0
        mov     word [ebx+1Ch],1000h
+       cmp     word[esi],0x051A        ;fixups?
+       jnz     elf_segment_flags
+       mov     ecx,[number_of_relocations]
+       lea     ecx,[edi + ecx * 4 + .elf_relocator.size + 0x100]
+       cmp     ecx,[tagged_blocks]
+       ja      out_of_memory
+       lods    word[esi]
+       or      byte[ebx+18h],1         ;mark as executable
+       ;for each load segment, copy the flags into the shadow space
+       mov     eax,[code_start]
+       add     eax,0x34 - 0x20
+      .loop_shadow_flags:
+       add     eax,0x20
+       cmp     eax,ebx
+       ja      .done_shadow_flags
+       cmp     dword[eax],1    ;PT_LOAD
+       jnz     .loop_shadow_flags
+       mov     ecx,[eax+0x18]
+       shr     ecx,1
+       rcl     edx,1
+       shr     ecx,1
+       rcl     edx,1
+       shr     ecx,1
+       rcl     edx,1
+       shl     edx,29
+       or      edx,6           ;set R and W
+       or      [eax+0x18],edx
+       jmp     .loop_shadow_flags
+      .done_shadow_flags:
+       pop     edx
+       mov     eax,edi
+       sub     eax,[code_start]
+       mov     [ebx+4],eax
+       and     eax,0FFFh
+       add     eax,edx
+       mov     [ebx+8],eax
+       mov     [ebx+0Ch],eax
+       push    eax esi
+       mov     esi,.elf_relocator
+       mov     ecx,.elf_relocator.size
+       rep     movsb
+       mov     esi,[code_start]
+       xchg    eax,[esi + 0x18]        ;e_entry
+       sub     eax,[ebx + 8]
+       add     [edi - 4],eax           ;update the jmp instruction
+       mov     esi,[free_additional_memory]
+       mov     ecx,[number_of_relocations]
+       lea     eax,[ecx * 4]
+       sub     esi,eax
+       rep     movsd
+       mov     eax,END_OF_RELOCATIONS
+       stosd
+       pop     esi eax
+       xor     edx,edx
+       jmp     elf_segment_addressing_setup
+      .elf_relocator:
+       label .Elf32_Ehdr.e_entry       dword at 0x18
+       label .Elf32_Ehdr.e_phoff       dword at 0x1c
+       label .Elf32_Ehdr.e_phentsize   word at 0x2a
+       label .Elf32_Ehdr.e_phnum       word at 0x2c
+       label .Elf32_Phdr.p_type        dword at 0x00
+       label .Elf32_Phdr.p_offset      dword at 0x04
+       label .Elf32_Phdr.p_paddr       dword at 0x0c
+       label .Elf32_Phdr.p_memsz       dword at 0x14
+       label .Elf32_Phdr.p_flags       dword at 0x18
+       label .Elf32_auxv_t.a_type      dword at 0
+       label .Elf32_auxv_t.a_val       dword at 4
+       ;p_type
+               PT_LOAD                 = 1
+       ;p_flags
+               PF_X                    = 1 shl 0
+               PF_W                    = 1 shl 1
+               PF_R                    = 1 shl 2
+       ;protection flags
+               PROT_READ               = 0x1
+               PROT_WRITE              = 0x2
+               PROT_EXEC               = 0x4
+       ;syscall
+               SYS_MPROTECT            = 125
+               SYS_EXIT_GROUP          = 252
+       ;auxv
+               AT_NULL                 = 0
+               AT_PHDR                 = 3
+               Elf32_auxv_t.size       = 8
+       ;local
+               END_OF_RELOCATIONS      = -1
+               PAGE_ALIGNMENT          = not 0xfff
+       ;the following code section can run anywhere without needing
+       ;any RVAs or relocations, it can be copied into the output as-is
+           .do_relocs:
+               pop     eax                             ;argument count (argc)
+               push    eax
+               cld
+               lea     esi,[esp + 4 + (eax + 1) * 4]   ;skip the args and the final null
+           .skip_environment:
+               lodsd
+               test    eax,eax                         ;last entry in environment?
+               jnz     .skip_environment
+           .scan_auxv:
+               lodsd
+               mov     ecx,eax                         ;ecx = Elf32_auxv_t.a_type
+               lodsd                                   ;eax = Elf32_auxv_t.a_val
+               assert  AT_NULL = 0
+               test    ecx,ecx                         ;end of auxv table?
+               jz      .fail
+               cmp     ecx,AT_PHDR
+               jnz     .scan_auxv
+               mov     edi,eax
+               and     edi,PAGE_ALIGNMENT              ;edi = elf header
+               mov     ebp,[eax + .Elf32_Phdr.p_offset]
+               sub     ebp,[eax + .Elf32_Phdr.p_paddr] ;ebp = -(program base)
+               lea     esi,[ebp + .relocs_data - .do_relocs]
+               add     esi,[edi + .Elf32_Ehdr.e_entry]
+               add     ebp,edi                         ;ebp = relocation offset
+               add     esi,edi
+               lodsd
+           .reloc_loop:
+               add     [edi + eax],ebp
+               lodsd
+               cmp     eax,END_OF_RELOCATIONS
+               jnz     .reloc_loop
+               movzx   esi,[edi + .Elf32_Ehdr.e_phnum] ;esi = count of Phdr entries
+           .loop_Phdr:
+               dec     esi
+               js      .run
+               movzx   eax,[edi + .Elf32_Ehdr.e_phentsize]
+               imul    eax,esi
+               add     eax,edi
+               add     eax,[edi + .Elf32_Ehdr.e_phoff]
+               cmp     [eax + .Elf32_Phdr.p_type],PT_LOAD
+               jnz     .loop_Phdr
+               mov     edx,[eax + .Elf32_Phdr.p_flags]
+               shr     edx,29                          ;get the flags from the shadow space
+               mov     ebx,[eax + .Elf32_Phdr.p_paddr]
+               add     ebx,ebp                         ;addr
+               mov     ecx,[eax + .Elf32_Phdr.p_memsz]
+               add     ecx,ebx
+               and     ebx,PAGE_ALIGNMENT
+               sub     ecx,ebx                         ;len
+               xor     eax,eax
+               mov     al,SYS_MPROTECT
+               int     0x80
+               test    eax,eax
+               jz      .loop_Phdr
+           .fail:
+               xor     eax,eax
+               mov     al,SYS_EXIT_GROUP
+               or      ebx,-1
+               int     0x80
+           .run:
+               jmp     near .do_relocs                 ;this is updated to jump to the entry address
+           .relocs_data:
+      .elf_relocator.size = $ - .elf_relocator
       elf_segment_flags:
        cmp     byte [esi],1Eh
        je      elf_segment_type
        cmp     byte [esi],19h
        jne     elf_segment_flags_ok
@@ -3951,11 +4220,18 @@
       merge_elf_header:
        mov     eax,[image_base]
        xor     ecx,ecx
        jmp     elf_segment_separated_base
       close_elf_segment:
+       mov     eax,[code_start]
+       cmp     byte[eax + 0x34 + 0x00],6       ;PHDR
+       jnz     .PHDR_okay
+       cmp     [number_of_sections],1
+       jmp     .PHDR_skip
+      .PHDR_okay:
        cmp     [number_of_sections],0
+      .PHDR_skip:
        jne     finish_elf_segment
        cmp     edi,[symbols_stream]
        jne     first_elf_segment_ok
        or      [merge_segment],-1
        mov     eax,[image_base]
@@ -4018,10 +4294,149 @@
        or      [next_pass_needed],-1
       new_elf64_segment:
        mov     byte [ebx],1
        and     dword [ebx+4],0
        mov     word [ebx+30h],1000h
+       cmp     word[esi],0x051A        ;fixups?
+       jnz     elf64_segment_flags
+       mov     ecx,[number_of_relocations]
+       lea     ecx,[edi + ecx * 4 + .elf64_relocator.size + 0x100]
+       cmp     ecx,[tagged_blocks]
+       ja      out_of_memory
+       lods    word[esi]
+       or      byte[ebx+4],1           ;mark as executable
+       ;for each load segment, copy the flags into the shadow space
+       mov     eax,[code_start]
+       add     eax,0x40 - 0x38
+      .loop_shadow_flags:
+       add     eax,0x38
+       cmp     eax,ebx
+       ja      .done_shadow_flags
+       cmp     dword[eax],1    ;PT_LOAD
+       jnz     .loop_shadow_flags
+       mov     ecx,[eax+4]
+       shr     ecx,1
+       rcl     edx,1
+       shr     ecx,1
+       rcl     edx,1
+       shr     ecx,1
+       rcl     edx,1
+       shl     edx,29
+       or      edx,6           ;set R and W
+       or      [eax+4],edx
+       jmp     .loop_shadow_flags
+      .done_shadow_flags:
+       pop     edx eax
+       mov     ecx,edi
+       sub     ecx,[code_start]
+       mov     [ebx+8],ecx
+       and     ecx,0FFFh
+       add     eax,ecx
+       adc     edx,0
+       mov     [ebx+0x10],eax
+       mov     [ebx+0x14],edx
+       mov     [ebx+0x18],eax
+       mov     [ebx+0x1c],edx
+       push    edx eax esi
+       mov     esi,.elf64_relocator
+       mov     ecx,.elf64_relocator.size
+       rep     movsb
+       mov     esi,[code_start]
+       xchg    eax,[esi + 0x18]        ;e_entry low
+       xchg    edx,[esi + 0x1c]        ;e_entry high
+       sub     eax,[ebx+0x10]
+       add     [edi - 4],eax           ;update the jmp instruction
+       mov     esi,[free_additional_memory]
+       mov     ecx,[number_of_relocations]
+       lea     eax,[ecx * 4]
+       sub     esi,eax
+       rep     movsd
+       mov     eax,END_OF_RELOCATIONS
+       stosd
+       pop     esi eax edx
+       jmp     elf_segment_addressing_setup
+      .elf64_relocator:
+       label .Elf64_Ehdr.e_entry       qword at 0x18
+       label .Elf64_Ehdr.e_phoff       qword at 0x20
+       label .Elf64_Ehdr.e_phentsize   word at 0x36
+       label .Elf64_Ehdr.e_phnum       word at 0x38
+       label .Elf64_Phdr.p_type        dword at 0x00
+       label .Elf64_Phdr.p_flags       dword at 0x04
+       label .Elf64_Phdr.p_offset      qword at 0x08
+       label .Elf64_Phdr.p_paddr       qword at 0x18
+       label .Elf64_Phdr.p_memsz       qword at 0x28
+       label .Elf64_auxv_t.a_type      qword at 0
+       label .Elf64_auxv_t.a_val       qword at 8
+       SYS64_MPROTECT                  = 10
+       SYS64_EXIT_GROUP                = 231
+       Elf64_auxv_t.size               = 16
+       ;the following code section can run anywhere without needing
+       ;any RVAs or relocations, it can be copied into the output as-is
+               use64
+           .do_relocs:
+               pop     rax                             ;argument count (argc)
+               push    rax
+               cld
+               lea     rsi,[rsp + 8 + (rax + 1) * 8]   ;skip the args and the final null
+           .skip_environment:
+               lodsq
+               test    rax,rax                         ;last entry in environment?
+               jnz     .skip_environment
+           .scan_auxv:
+               lodsq
+               mov     rcx,rax                         ;rcx = Elf64_auxv_t.a_type
+               lodsq                                   ;rax = Elf64_auxv_t.a_val
+               assert  AT_NULL = 0
+               test    rcx,rcx                         ;end of auxv table?
+               jz      .fail
+               cmp     rcx,AT_PHDR
+               jnz     .scan_auxv
+               mov     r15,rax
+               mov     r14,rax                         ;r14 = PHDR
+               and     r15,PAGE_ALIGNMENT              ;r15 = elf header
+               mov     r13,[rax + .Elf64_Phdr.p_offset]
+               sub     r13,[rax + .Elf64_Phdr.p_paddr]
+               add     r13,r15                         ;r13 = relocation offset
+               lea     rsi,[.relocs_data]
+               lodsd
+           .reloc_loop:
+               add     [r15 + rax],r13
+               lodsd
+               cmp     eax,END_OF_RELOCATIONS
+               jnz     .reloc_loop
+               movzx   ebx,[r15 + .Elf64_Ehdr.e_phnum]
+           .loop_Phdr:
+               dec     ebx
+               js      .run
+               movzx   eax,[r15 + .Elf64_Ehdr.e_phentsize]
+               imul    eax,ebx
+               add     rax,r14
+               cmp     [rax + .Elf64_Phdr.p_type],PT_LOAD
+               jnz     .loop_Phdr
+               mov     edx,[rax + .Elf64_Phdr.p_flags]
+               shr     edx,29                          ;get the flags from the shadow space
+               mov     rdi,[rax + .Elf64_Phdr.p_paddr]
+               add     rdi,r13
+               mov     rsi,[rax + .Elf64_Phdr.p_memsz]
+               add     rsi,rdi
+               and     rdi,PAGE_ALIGNMENT              ;addr
+               sub     rsi,rdi                         ;len
+               xor     eax,eax
+               mov     al,SYS64_MPROTECT
+               syscall
+               test    rax,rax
+               jz      .loop_Phdr
+           .fail:
+               xor     eax,eax
+               mov     al,SYS64_EXIT_GROUP
+               or      rdi,-1
+               syscall
+           .run:
+               jmp     near .do_relocs                 ;this is updated to jump to the entry address
+           .relocs_data:
+               use32
+      .elf64_relocator.size = $ - .elf64_relocator
       elf64_segment_flags:
        cmp     byte [esi],1Eh
        je      elf64_segment_type
        cmp     byte [esi],19h
        jne     elf64_segment_flags_ok
@@ -4105,11 +4520,18 @@
        mov     eax,[image_base]
        mov     edx,[image_base_high]
        xor     ecx,ecx
        jmp     elf64_segment_separated_base
       close_elf64_segment:
+       mov     eax,[code_start]
+       cmp     byte[eax + 0x40 + 0x00],6       ;PHDR
+       jnz     .PHDR_okay
+       cmp     [number_of_sections],1
+       jmp     .PHDR_skip
+      .PHDR_okay:
        cmp     [number_of_sections],0
+      .PHDR_skip:
        jne     finish_elf64_segment
        cmp     edi,[symbols_stream]
        jne     first_elf64_segment_ok
        or      [merge_segment],-1
        mov     eax,[image_base]    
I was able to assemble fasm itself as a dynamic executable. fasm runs perfectly fine as a dynamically relocatable stand-alone executable by changing only one line (change 'executable' to 'dynamic' in the format line) and adding only one line (place 'segment fixups' at then end) in the file FASM.ASM

Memory protection settings should be set to the desired final value. That is the final value wanted when the code it up and running. For files that have fixups you will see in the on-disc file that all the loadable segments are R/W enabled. This is to allow the linker, loader and relocator to do their respective jobs. After it is all prepared the protections are adjusted according to the settings used in the source. So for example in this source ...
Code:
USE_LIBC        = 1
IS_DYNAMIC      = 1

if IS_DYNAMIC
        format ELF dynamic 3 at 1 shl 32 - 0x10000
else
        format ELF executable 3 at 1 shl 32 - 0x10000
end if

entry start

segment gnustack
segment executable

start:
if USE_LIBC
        push    0 O_RDONLY maps
        call    [open]
        add     esp,4 * 3
else
        mov     eax,SYS_OPEN
        mov     ebx,maps
        mov     ecx,O_RDONLY
        xor     edx,edx
        int     0x80
end if
        mov     edx,0x1000
        sub     esp,edx
        mov     ebx,eax
        mov     eax,SYS_READ
        mov     ecx,esp
        int     0x80
        mov     edx,eax
        mov     eax,SYS_WRITE
        mov     ebx,STD_OUTPUT
        int     0x80
        mov     eax,SYS_EXIT
        xor     ebx,ebx
        int     0x80

segment
db 0
segment readable
db 0
segment writeable
db 0
segment readable writeable
db 0
segment executable
db 0
segment executable readable
db 0
segment executable writeable
db 0
segment executable readable writeable
db 0

SYS_EXIT        = 1
SYS_READ        = 3
SYS_WRITE       = 4
SYS_OPEN        = 5
SYS_MPROTECT    = 125
STD_INPUT       = 0
STD_OUTPUT      = 1
O_RDONLY        = 0
PROT_READ       = 0x1

DT_NULL         = 0
DT_NEEDED       = 1
DT_STRTAB       = 5
DT_SYMTAB       = 6
DT_STRSZ        = 10
DT_SYMENT       = 11
DT_REL          = 17
DT_RELSZ        = 18
DT_RELENT       = 19
DT_BIND_NOW     = 24
DT_FLAGS        = 30
DT_FLAGS_1      = 0x6ffffffb
STB_GLOBAL      = 1
STT_FUNC        = 2
R_386_32        = 1
DF_BIND_NOW     = 0x00000008
DF_1_NOW        = 0x00000001
DF_1_PIE        = 0x08000000

macro Elf32_Sym name,value,size,bind,type,other,shndx {
        dd name+0
        dd value+0
        dd size+0
        db (bind+0) shl 4 + (type+0)
        db other+0
        dw shndx+0
}
macro Elf32_Rel offset,symbol,type {
        dd rva offset+0
        dd (symbol+0) shl 8 + (type+0)
}
virtual at 0
        Elf32_Sym
        sizeof.Elf32_Sym = $
        Elf32_Rel
        sizeof.Elf32_Rel = $ - sizeof.Elf32_Sym
end virtual

if USE_LIBC
        segment interpreter readable
                        db '/lib/ld-linux.so.2'
                strtab:
                        db 0
                _libc   db 'libc.so.6',0
                _open   db 'open',0
                strsz   = $ - strtab

        maps:   db      '/proc/self/maps',0

        segment dynamic readable
                dd DT_NEEDED,_libc - strtab
                dd DT_STRTAB,rva strtab
                dd DT_STRSZ,strsz
                dd DT_SYMTAB,rva symtab
                dd DT_SYMENT,sizeof.Elf32_Sym
                dd DT_REL,rva rel
                dd DT_RELSZ,relsz
                dd DT_RELENT,sizeof.Elf32_Rel
                dd DT_BIND_NOW,1
                dd DT_FLAGS,DF_BIND_NOW
                dd DT_FLAGS_1,DF_1_NOW or DF_1_PIE
                dd DT_NULL,0
                symtab:
                        Elf32_Sym
                        Elf32_Sym _open - strtab,0,0,STB_GLOBAL,STT_FUNC,0,0
                rel:
                        Elf32_Rel open,1,R_386_32
                relsz   = $ - rel
    if ~ IS_DYNAMIC
        segment gnurelro readable
    end if
                external_links:
                open dd 0
                external_links_length = $ - external_links
    if IS_DYNAMIC
        segment readable
    else
        segment readable writeable
    end if
else
        segment readable
        maps:   db      '/proc/self/maps',0
end if

segment fixups    
... I can set the protection to readonly for the external_links section, but the file will still load and run correctly because the linker sees a writeable segment. But the in runtime output you will see the segment is marked as readonly.

Although the gnurelro segment is there for when there are no fixups, it doesn't work correctly using the existing Linux GNU linker. In that case you end up with writeable linking gates. If you need to fix that you can manually change the permissions in the main body of the code. AFAICT this is a bug in the linker, so if you need it fixed then the GNU folks are the ones to talk to for that. For now it suffices to add this code, which is also posted above in the previous post
Code:
        mov     edx,PROT_READ
        mov     ecx,external_links_length + rva external_links and 0xfff
        mov     ebx,external_links - rva external_links and 0xfff
        mov     eax,SYS_MPROTECT
        int     0x80    
Post 12 Feb 2019, 11:40
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: 20298
Location: In your JS exploiting you and your system
revolution 12 Feb 2019, 13:24
Some overhead statistics on assembling fasm as a non-PIC relocatable dynamic executable
Code:
   32 bytes for one program header entry
  134 bytes fixups and protection code
15416 bytes in 3854 relocation entries
    4 bytes relocation end marker
-----
15586 bytes total overhead    
So about a 15% overhead. I don't know how this overhead compares to a PIC version of the code. I haven't tried it. But it is likely to be at least the same size. And with 3854 places we would need to adjust, and likely more places to change because of fewer available registers, it might end up a larger overhead.

Some advantages over rewriting the code as PIC
  • It keeps the same performance as before
  • Ensures we are not introducing bugs by manually changing the source
  • Remains completely compatible with future updates
Post 12 Feb 2019, 13:24
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: 20298
Location: In your JS exploiting you and your system
revolution 16 Feb 2019, 07:03
Tomasz Grysztar wrote:
If you remove "writeable" attribute from code segment, it segfaults when trying to apply the relocation (at least on my machines).
Adding DF_TEXTREL solves this. But only for 32-bit.
Code:
format ELF executable 3
entry start

DT_NULL         = 0
DT_NEEDED       = 1
DT_STRTAB       = 5
DT_SYMTAB       = 6
DT_STRSZ        = 10
DT_SYMENT       = 11
DT_REL          = 17
DT_RELSZ        = 18
DT_RELENT       = 19
DT_FLAGS        = 30
DF_TEXTREL      = 4
R_386_32        = 1

macro Elf32_Sym name,value,size,bind,type,other,shndx {
        dd name+0
        dd value+0
        dd size+0
        db (bind+0) shl 4 + (type+0)
        db other+0
        dw shndx+0
}
macro Elf32_Rel offset,symbol,type {
        dd rva offset+0
        dd (symbol+0) shl 8 + (type+0)
}
virtual at 0
        Elf32_Sym
        sizeof.Elf32_Sym = $
        Elf32_Rel
        sizeof.Elf32_Rel = $ - sizeof.Elf32_Sym
end virtual

segment interpreter readable
        db '/lib/ld-linux.so.2',0

segment dynamic readable
  dd DT_STRTAB,strtab
  dd DT_STRSZ,strsz
  dd DT_SYMTAB,symtab
  dd DT_SYMENT,sizeof.Elf32_Sym
  dd DT_REL,rel
  dd DT_RELSZ,relsz
  dd DT_RELENT,sizeof.Elf32_Rel
  dd DT_FLAGS,DF_TEXTREL
  dd DT_NULL,0

segment readable writeable

  rel:
        Elf32_Rel fixup,0,R_386_32
  relsz = $-rel

  symtab:
        Elf32_Sym

  strtab:
        db 0
  strsz = $-strtab

segment executable

start:

        mov     ecx,msg
fixup = $ - 4
        mov     edx,msg.len
        mov     eax,4
        mov     ebx,1
        int     0x80

        mov     eax,1
        xor     ebx,ebx
        int     0x80

segment readable

msg db 'Hello world!',0xA
.len = $ - msg    
And the interpreter leaves the permissions as execute-only.

But for 64-bit it still segfaults.
Post 16 Feb 2019, 07:03
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: 20298
Location: In your JS exploiting you and your system
revolution 16 Feb 2019, 13:37
The last piece to add is the ability to customise the relocation data to fit the application. If the system is not running Linux then the inbuilt "segment fixups" code might not work. As an alternative the "data fixups"/"end data" paradigm, already used in PE format, can be used.

Here are two fully working examples showing how this works. There are two methods of relocation to choose from. 1) The standard ELF specification REL and RELA entries in the dynamic table, and 2) A custom format with custom code.

32-bit
Code:
USE_LIBC        = 1
SELF_RELOCATE   = 0

format ELF dynamic 0 at 1 shl 32 - 0x10000
entry start

segment gnustack

segment executable

start:
        if SELF_RELOCATE
                call    do_relocs
            .eip:
        end if
        if USE_LIBC
                push    0 O_RDONLY maps
                call    [open]
                add     esp,4 * 3
        else
                mov     eax,SYS_OPEN
                mov     ebx,maps
                mov     ecx,O_RDONLY
                xor     edx,edx
                int     0x80
        end if
        mov     edx,0x1000
        sub     esp,edx
        mov     ebx,eax
        mov     eax,SYS_READ
        mov     ecx,esp
        int     0x80
        mov     edx,eax
        mov     eax,SYS_WRITE
        mov     ebx,STD_OUTPUT
        int     0x80
        mov     eax,SYS_EXIT_GROUP
        xor     ebx,ebx
        int     0x80

if SELF_RELOCATE

END_OF_RELOCATIONS      = -1
PAGE_ALIGNMENT          = not 0xfff
PT_LOAD                 = 1
PF_X                    = 1 shl 0
PF_W                    = 1 shl 1
PF_R                    = 1 shl 2
PROT_READ               = 0x1
PROT_WRITE              = 0x2
PROT_EXEC               = 0x4

do_relocs:
        label .Elf32_Ehdr.e_entry       dword at 0x18
        label .Elf32_Ehdr.e_phoff       dword at 0x1c
        label .Elf32_Ehdr.e_phentsize   word at 0x2a
        label .Elf32_Ehdr.e_phnum       word at 0x2c
        label .Elf32_Phdr.p_type        dword at 0x00
        label .Elf32_Phdr.p_offset      dword at 0x04
        label .Elf32_Phdr.p_paddr       dword at 0x0c
        label .Elf32_Phdr.p_memsz       dword at 0x14
        label .Elf32_Phdr.p_flags       dword at 0x18
        mov     eax,[esp]
        mov     ecx,[eax - start.eip + self_rel]
        lea     edi,[ecx + eax - start.eip + self_rel];edi = elf header
        mov     ebp,PROT_READ + PROT_WRITE + PROT_EXEC
        call    .protect
        mov     eax,edi
        add     eax,[edi + .Elf32_Ehdr.e_phoff]
        mov     ebp,[eax + .Elf32_Phdr.p_offset]
        sub     ebp,[eax + .Elf32_Phdr.p_paddr] ;ebp = -(image base)
        mov     esi,[esp]
        lea     esi,[esi - start.eip + self_rel + 4]
        add     ebp,edi                         ;ebp = relocation offset
        jmp     .reloc_next
    .reloc_loop:
        add     [edi + eax],ebp
    .reloc_next:
        lodsd
        cmp     eax,END_OF_RELOCATIONS
        jnz     .reloc_loop
        xor     ebp,ebp                         ;restore to original permissions
    .protect:
        movzx   esi,[edi + .Elf32_Ehdr.e_phnum] ;esi = count of Phdr entries
    .loop_Phdr:
        dec     esi
        js      .retn
        movzx   eax,[edi + .Elf32_Ehdr.e_phentsize]
        mov     ecx,[edi + .Elf32_Ehdr.e_phoff]
        imul    eax,esi
        add     eax,edi
        add     eax,ecx
        cmp     [eax + .Elf32_Phdr.p_type],PT_LOAD
        jnz     .loop_Phdr
        mov     ebx,[eax + .Elf32_Phdr.p_flags]
        xor     edx,edx
        assert  PROT_EXEC or PF_X = PROT_READ or PF_R & PROT_WRITE = PF_W
        repeat 3
                shr     ebx,1
                rcl     edx,1
        end repeat
        or      edx,ebp                         ;set the requested flags
        mov     ebx,[eax + .Elf32_Phdr.p_paddr]
        sub     ebx,[edi + ecx + .Elf32_Phdr.p_paddr]
        add     ebx,[edi + ecx + .Elf32_Phdr.p_offset]
        mov     ecx,[eax + .Elf32_Phdr.p_memsz]
        add     ecx,ebx
        and     ebx,PAGE_ALIGNMENT
        sub     ecx,ebx                         ;len
        add     ebx,edi                         ;addr
        xor     eax,eax
        mov     al,SYS_MPROTECT
        int     0x80
        test    eax,eax
        jz      .loop_Phdr
    .fail:
        xor     eax,eax
        mov     al,SYS_EXIT_GROUP
        or      ebx,-1
        int     0x80
    .retn:
        retn
end if

SYS_READ        = 3
SYS_WRITE       = 4
SYS_OPEN        = 5
SYS_MPROTECT    = 125
SYS_EXIT_GROUP  = 252
STD_INPUT       = 0
STD_OUTPUT      = 1
O_RDONLY        = 0

DT_NULL         = 0
DT_NEEDED       = 1
DT_STRTAB       = 5
DT_SYMTAB       = 6
DT_STRSZ        = 10
DT_SYMENT       = 11
DT_REL          = 17
DT_RELSZ        = 18
DT_RELENT       = 19
DT_BIND_NOW     = 24
DT_FLAGS        = 30
DT_FLAGS_1      = 0x6ffffffb
STB_GLOBAL      = 1
STT_FUNC        = 2
R_386_32        = 1
DF_TEXTREL      = 0x00000004
DF_BIND_NOW     = 0x00000008
DF_1_NOW        = 0x00000001
DF_1_PIE        = 0x08000000

macro Elf32_Sym name,value,size,bind,type,other,shndx {
        dd name+0
        dd value+0
        dd size+0
        db (bind+0) shl 4 + (type+0)
        db other+0
        dw shndx+0
}
macro Elf32_Rel offset,symbol,type {
        dd rva offset+0
        dd (symbol+0) shl 8 + (type+0)
}
virtual at 0
        Elf32_Sym
        sizeof.Elf32_Sym = $
        Elf32_Rel
        sizeof.Elf32_Rel = $ - sizeof.Elf32_Sym
end virtual

virtual
    ..fixups::
    ..start_fixups:
        data fixups
        ;the first dword is the offset to the ELF header
        ;the second dword is the image base
        ;the following dword pairs are the offset and addend respectively
        end data
    ..end_fixups:
end virtual

if USE_LIBC | ~ SELF_RELOCATE
    segment interpreter readable
                db '/lib/ld-linux.so.2'
        strtab:
                db 0
        if USE_LIBC
                _libc   db 'libc.so.6',0
                _open   db 'open',0
        end if
        strsz   = $ - strtab
else
    segment readable writeable
end if
maps:   db      '/proc/self/maps',0
if USE_LIBC | ~ SELF_RELOCATE
    segment dynamic readable
    if USE_LIBC
        dd DT_NEEDED,_libc - strtab
    end if
        dd DT_STRTAB,rva strtab
        dd DT_STRSZ,strsz
        dd DT_SYMTAB,rva symtab
        dd DT_SYMENT,sizeof.Elf32_Sym
        dd DT_REL,rva rel
        dd DT_RELSZ,relsz
        dd DT_RELENT,sizeof.Elf32_Rel
        dd DT_BIND_NOW,1
    if SELF_RELOCATE
        dd DT_FLAGS,DF_BIND_NOW
    else
        dd DT_FLAGS,DF_BIND_NOW or DF_TEXTREL
    end if
        dd DT_FLAGS_1,DF_1_NOW or DF_1_PIE
        dd DT_NULL,0
        symtab:
                Elf32_Sym
    if USE_LIBC
                Elf32_Sym _open - strtab,0,0,STB_GLOBAL,STT_FUNC,0,0
    end if
        rel:
    if ~ SELF_RELOCATE
        load .image_base dword from ..fixups:..start_fixups + 4
        while % * 8 + 8 <= ..end_fixups - ..start_fixups
                load .offset dword from ..fixups:..start_fixups + 8 + (% - 1) * 8
                ;the 'addend' value at the next dword is not needed for Rel entries, but is needed for Rela entries
                Elf32_Rel .offset + .image_base,0,R_386_32
        end while
    end if
    if USE_LIBC
                Elf32_Rel open,1,R_386_32
    end if
        relsz   = $ - rel
    if USE_LIBC
                open dd 0
    end if
    segment readable writeable
end if
if SELF_RELOCATE
        self_rel:
                load .header_offset dword from ..fixups:..start_fixups + 0
                dd .header_offset
        while % * 8 + 8 <= ..end_fixups - ..start_fixups
                load .offset dword from ..fixups:..start_fixups + 8 + (% - 1) * 8
                ;the 'addend' value at the next dword is not used
                dd .offset
        end while
        dd END_OF_RELOCATIONS
end if    
64-bit
Code:
USE_LIBC        = 1
SELF_RELOCATE   = 0

format ELF64 dynamic 0 at 1 shl 40 - 0x1000
entry start

segment gnustack

if SELF_RELOCATE
        segment executable
else
        segment executable writeable
end if

start:
        if SELF_RELOCATE
                call    do_relocs
        end if
        mov     rdi,maps
        mov     esi,O_RDONLY
        xor     edx,edx
        if USE_LIBC
                call    [open]
        else
                mov     eax,SYS_OPEN
                syscall
        end if
        mov     edx,0x1000
        sub     rsp,rdx
        mov     rdi,rax
        mov     eax,SYS_READ
        mov     rsi,rsp
        syscall
        mov     edx,eax
        mov     eax,SYS_WRITE
        mov     edi,STD_OUTPUT
        mov     rsi,rsp
        syscall
        mov     eax,SYS_EXIT_GROUP
        xor     edi,edi
        syscall

if SELF_RELOCATE

END_OF_RELOCATIONS      = -1
PAGE_ALIGNMENT          = not 0xfff
PT_LOAD                 = 1
PF_X                    = 1 shl 0
PF_W                    = 1 shl 1
PF_R                    = 1 shl 2
PROT_READ               = 0x1
PROT_WRITE              = 0x2
PROT_EXEC               = 0x4

do_relocs:
        label .Elf64_Ehdr.e_entry       qword at 0x18
        label .Elf64_Ehdr.e_phoff       qword at 0x20
        label .Elf64_Ehdr.e_phentsize   word at 0x36
        label .Elf64_Ehdr.e_phnum       word at 0x38
        label .Elf64_Phdr.p_type        dword at 0x00
        label .Elf64_Phdr.p_flags       dword at 0x04
        label .Elf64_Phdr.p_offset      qword at 0x08
        label .Elf64_Phdr.p_paddr       qword at 0x18
        label .Elf64_Phdr.p_memsz       qword at 0x28
        lea     r15,[self_rel]
        movsxd  rax,[r15]
        add     r15,rax
        mov     r14,r15
        add     r14,[r15 + .Elf64_Ehdr.e_phoff]
        mov     ebx,PROT_READ + PROT_WRITE + PROT_EXEC
        call    .protect
        mov     r13,[r14 + .Elf64_Phdr.p_offset]
        sub     r13,[r14 + .Elf64_Phdr.p_paddr]
        add     r13,r15                         ;r13 = relocation offset
        lea     rsi,[self_rel + 4]
        jmp     .reloc_next
    .reloc_loop:
        add     [r15 + rax],r13
    .reloc_next:
        lodsd
        cmp     eax,END_OF_RELOCATIONS
        jnz     .reloc_loop
        xor     ebx,ebx                         ;restore to original permissions
    .protect:
        movzx   ebp,[r15 + .Elf64_Ehdr.e_phnum] ;ebp = count of Phdr entries
    .loop_Phdr:
        dec     ebp
        js      .retn
        movzx   eax,[r15 + .Elf64_Ehdr.e_phentsize]
        imul    eax,ebp
        add     rax,r14
        cmp     [rax + .Elf64_Phdr.p_type],PT_LOAD
        jnz     .loop_Phdr
        mov     ecx,[rax + .Elf64_Phdr.p_flags]
        xor     edx,edx
        assert  PROT_EXEC or PF_X = PROT_READ or PF_R & PROT_WRITE = PF_W
        repeat 3
                shr     ecx,1
                rcl     edx,1
        end repeat
        or      edx,ebx                         ;set the requested flags
        mov     rdi,[rax + .Elf64_Phdr.p_paddr]
        sub     rdi,[r14 + .Elf64_Phdr.p_paddr]
        add     rdi,[r14 + .Elf64_Phdr.p_offset]
        mov     rsi,[rax + .Elf64_Phdr.p_memsz]
        add     rsi,rdi
        and     rdi,PAGE_ALIGNMENT
        sub     rsi,rdi                         ;len
        add     rdi,r15                         ;addr
        xor     eax,eax
        mov     al,SYS_MPROTECT
        syscall
        test    rax,rax
        jz      .loop_Phdr
    .fail:
        xor     eax,eax
        mov     al,SYS_EXIT_GROUP
        or      rdi,-1
        syscall
    .retn:
        retn
end if

SYS_READ        = 0
SYS_WRITE       = 1
SYS_OPEN        = 2
SYS_MPROTECT    = 10
SYS_EXIT_GROUP  = 231
STD_INPUT       = 0
STD_OUTPUT      = 1
O_RDONLY        = 0

DT_NULL         = 0
DT_NEEDED       = 1
DT_STRTAB       = 5
DT_SYMTAB       = 6
DT_RELA         = 7
DT_RELASZ       = 8
DT_RELAENT      = 9
DT_STRSZ        = 10
DT_SYMENT       = 11
DT_BIND_NOW     = 24
DT_FLAGS        = 30
DT_FLAGS_1      = 0x6ffffffb
STB_GLOBAL      = 1
STT_FUNC        = 2
R_X86_64_64     = 1
DF_TEXTREL      = 0x00000004
DF_BIND_NOW     = 0x00000008
DF_1_NOW        = 0x00000001
DF_1_PIE        = 0x08000000

macro Elf64_Sym name,value,size,bind,type,other,shndx {
        dd name+0
        db (bind+0) shl 4 + (type+0)
        db other+0
        dw shndx+0
        dq value+0
        dq size+0
}
macro Elf64_Rela offset,symbol,type,addend {
        dq rva offset+0
        dd type+0
        dd symbol+0
        dq rva addend+0
}
virtual at 0
        Elf64_Sym
        sizeof.Elf64_Sym = $
        Elf64_Rela
        sizeof.Elf64_Rela = $ - sizeof.Elf64_Sym
end virtual

virtual
    ..fixups::
    ..start_fixups:
        data fixups
        ;the first qword is the offset to the ELF header
        ;the second qword is the image base
        ;the following dword pairs are the offset and addend respectively
        end data
    ..end_fixups:
end virtual

if USE_LIBC | ~ SELF_RELOCATE
    segment interpreter readable
                db '/lib64/ld-linux-x86-64.so.2'
        strtab:
                db 0
        if USE_LIBC
                _libc   db 'libc.so.6',0
                _open   db 'open',0
        end if
        strsz   = $ - strtab
else
    segment readable writeable
end if
maps:   db      '/proc/self/maps',0
if USE_LIBC | ~ SELF_RELOCATE
    segment dynamic readable
    if USE_LIBC
        dq DT_NEEDED,_libc - strtab
    end if
        dq DT_STRTAB,rva strtab
        dq DT_STRSZ,strsz
        dq DT_SYMTAB,rva symtab
        dq DT_SYMENT,sizeof.Elf64_Sym
        dq DT_RELA,rva rela
        dq DT_RELASZ,relasz
        dq DT_RELAENT,sizeof.Elf64_Rela
        dq DT_BIND_NOW,1
    if SELF_RELOCATE
        dd DT_FLAGS,DF_BIND_NOW
    else
        dd DT_FLAGS,DF_BIND_NOW or DF_TEXTREL
    end if
        dq DT_FLAGS_1,DF_1_NOW or DF_1_PIE
        dq DT_NULL,0
        symtab:
                Elf64_Sym
    if USE_LIBC
                Elf64_Sym _open - strtab,0,0,STB_GLOBAL,STT_FUNC,0,0
    end if
        rela:
    if ~ SELF_RELOCATE
        load .image_base qword from ..fixups:..start_fixups + 8
        while % * 8 + 16 <= ..end_fixups - ..start_fixups
                load .offset dword from ..fixups:..start_fixups + 16 + (% - 1) * 8
                load .addend dword from ..fixups:..start_fixups + 16 + (% - 1) * 8 + 4
                Elf64_Rela .offset + .image_base,0,R_X86_64_64,.addend + .image_base
        end while
    end if
    if USE_LIBC
                Elf64_Rela open,1,R_X86_64_64
    end if
        relasz  = $ - rela
    if USE_LIBC
                open dq 0
    end if
    segment readable writeable
end if
if SELF_RELOCATE
        self_rel:
                load .header_offset dword from ..fixups:..start_fixups + 0
                dd .header_offset               ;only the lower 32-bits is needed
        while % * 8 + 16 <= ..end_fixups - ..start_fixups
                load .offset dword from ..fixups:..start_fixups + 16 + (% - 1) * 8
                ;the 'addend' value at the next dword is not used
                dd .offset
        end while
        dd END_OF_RELOCATIONS
end if    
Set the value of SELF_RELOCATE to non-zero to use the custom relocator code.

The USE_LIBC value is just to show how to link to a library and apply relocations together.

Attached is the full version of FORMATS.INC


Description: ELF executable/dynamic + relocs/fixups
Download
Filename: FORMATS.INC
Filesize: 95.17 KB
Downloaded: 655 Time(s)

Post 16 Feb 2019, 13:37
View user's profile Send private message Visit poster's website Reply with quote
Lost_Ghost123



Joined: 12 Feb 2019
Posts: 29
Lost_Ghost123 16 Feb 2019, 17:43
Sorry for a dumb question, but can you tell me what the hell are relocations, which formats support them and what is their purpose?
Post 16 Feb 2019, 17:43
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20298
Location: In your JS exploiting you and your system
revolution 16 Feb 2019, 18:10
Lost_Ghost123 wrote:
Sorry for a dumb question, but can you tell me what the hell are relocations, which formats support them and what is their purpose?
It's not a dumb question.

Relocations allow the executable code to be moved to another place in the address space. So instead of always running at some fixed address, like say 0x8048000, it can run from a random address each time.

ELF and PE formats both support relocation data. But in theory you can make any format, even binary format, support relocations by making the code Position Independent Code (PIC). PIC is when the code references memory address as an offset from EIP (or RIP). But for 32-bit x86 code PIC has a negative impact on code size and performance because there are not many natural instructions that use EIP as the base. Only CALL and JMP use EIP offsets, all other instructions have to emulate that behaviour. So instead of using PIC, relocations shift all the fixed offsets to a new place and we can keep the same performance and also have it run from different places.

It's main purpose is a mitigation technique against malware and buffer overflow exploits. By using a random address a lot of exploits will fail because they can't know where things are in the memory space.
Post 16 Feb 2019, 18:10
View user's profile Send private message Visit poster's website Reply with quote
Lost_Ghost123



Joined: 12 Feb 2019
Posts: 29
Lost_Ghost123 16 Feb 2019, 18:21
Is this issue of memory randomisation, but not through PIC only relevant in assembler? Do other compilers/HLL/interpretators/VMs support that?
Post 16 Feb 2019, 18:21
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20298
Location: In your JS exploiting you and your system
revolution 16 Feb 2019, 18:27
It is relevant to all code generators. You can either choose PIC and write "slow" code, or use a fixed address and write "fast" code. In a lot of ways it is tied to the CPU used. Some CPUs have no trouble with it, others (like the 386) have difficulty.
Post 16 Feb 2019, 18:27
View user's profile Send private message Visit poster's website Reply with quote
Lost_Ghost123



Joined: 12 Feb 2019
Posts: 29
Lost_Ghost123 16 Feb 2019, 18:29
Ok, thank you. Will read into that on my own now.
Post 16 Feb 2019, 18:29
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20298
Location: In your JS exploiting you and your system
revolution 17 Feb 2019, 16:19
revolution wrote:
It's main purpose is a mitigation technique against malware and buffer overflow exploits. By using a random address a lot of exploits will fail because they can't know where things are in the memory space.
But the main purpose for my usage is to avoid having to convert a large body of code to PIC. Now I can make that same code ASLR compliant with no effort. We have computers in front of us, lets use them to do all the tedious work we would not enjoy and would make many mistakes.
Post 17 Feb 2019, 16:19
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:  
Goto page Previous  1, 2, 3, 4

< 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.