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 |
|
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 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 Code: GNU_RELRO 0x000000000000034d 0x000000000001234d 0x000000000001234d 0x0000000000000008 0x0000000000000008 R 1 LOAD 0x0000000000000245 0x0000000000012245 0x0000000000012245 0x0000000000000110 0x0000000000000110 RW 1000 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 Code: GNU_RELRO 0x000000000000034d 0x000000000001234d 0x000000000001234d 0x0000000000001008 0x0000000000001008 R 1 LOAD 0x0000000000000245 0x0000000000012245 0x0000000000012245 0x0000000000001110 0x0000000000001110 RW 1000 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 Code: GNU_RELRO 0x000000000000034d 0x000000000001234d 0x000000000001234d 0x0000000000000008 0x0000000000000008 R 1 LOAD 0x0000000000000245 0x0000000000012245 0x0000000000012245 0x0000000000000110 0x0000000000001110 RW 1000 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. |
|||
10 Feb 2019, 09:34 |
|
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] 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 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 Last edited by revolution on 11 Feb 2019, 12:15; edited 1 time in total |
|||
10 Feb 2019, 11:26 |
|
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] 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 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 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. |
|||
11 Feb 2019, 11:59 |
|
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] 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 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 |
|||
12 Feb 2019, 11:40 |
|
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 Some advantages over rewriting the code as PIC
|
|||
12 Feb 2019, 13:24 |
|
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). 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 But for 64-bit it still segfaults. |
|||
16 Feb 2019, 07:03 |
|
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 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 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
|
|||||||||||
16 Feb 2019, 13:37 |
|
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?
|
|||
16 Feb 2019, 17:43 |
|
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? 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. |
|||
16 Feb 2019, 18:10 |
|
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?
|
|||
16 Feb 2019, 18:21 |
|
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.
|
|||
16 Feb 2019, 18:27 |
|
Lost_Ghost123 16 Feb 2019, 18:29
Ok, thank you. Will read into that on my own now.
|
|||
16 Feb 2019, 18:29 |
|
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. |
|||
17 Feb 2019, 16:19 |
|
Goto page Previous 1, 2, 3, 4 < Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.