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 Next |
Author |
|
Furs 23 Jan 2019, 21:22
revolution wrote: No. I'm not experienced with GCC. I didn't know about that switch. Is there an example that shows this? It seems it works if it's in .rel.sym? Maybe FASM generates the wrong relocations for ELF. |
|||
23 Jan 2019, 21:22 |
|
Furs 29 Jan 2019, 12:47
revolution, did that work? Or you gave up on relocations?
|
|||
29 Jan 2019, 12:47 |
|
revolution 29 Jan 2019, 13:43
Yes, it works. But the exe doesn't have relocations. The entire code has to be PIC, and the exe has to be marked as PIE.
AFAICT there isn't an ELF relocation format to support repositioning an exe the way that PE does. But I would be happy to learn that that is wrong. |
|||
29 Jan 2019, 13:43 |
|
Tomasz Grysztar 29 Jan 2019, 14:45
revolution wrote: AFAICT there isn't an ELF relocation format to support repositioning an exe the way that PE does. But I would be happy to learn that that is wrong. When I was performing a hiding code in Elf64_Rela experiment, I had a working example, unfortunately I cannot find it now. I may have deleted it out of disappointment. Perhaps I should finally start working on the second chapter of my formats tutorial. It is likely to have such examples when I finish it. |
|||
29 Jan 2019, 14:45 |
|
revolution 29 Jan 2019, 16:28
A writeable executable section is not a good thing. I'd rather write PIC code than deal with corrupted code.
|
|||
29 Jan 2019, 16:28 |
|
Furs 29 Jan 2019, 20:49
revolution wrote: A writeable executable section is not a good thing. I'd rather write PIC code than deal with corrupted code. GCC with -fno-PIC seems to generate relocatable code: Code: static int var; void test(int x) { var = x; } int blah() { return var; } Code: gcc -m32 -O2 -fno-PIC -shared test.c -o test.so objdump -d -M intel test.so Code: test.so: file format elf32-i386 Disassembly of section .init: 00000364 <_init>: 364: 53 push ebx 365: 83 ec 08 sub esp,0x8 368: e8 53 00 00 00 call 3c0 <__x86.get_pc_thunk.bx> 36d: 81 c3 93 1c 00 00 add ebx,0x1c93 373: 8b 83 f8 ff ff ff mov eax,DWORD PTR [ebx-0x8] 379: 85 c0 test eax,eax 37b: 74 05 je 382 <_init+0x1e> 37d: e8 2e 00 00 00 call 3b0 <__gmon_start__@plt> 382: 83 c4 08 add esp,0x8 385: 5b pop ebx 386: c3 ret Disassembly of section .plt: 00000390 <__cxa_finalize@plt-0x10>: 390: ff b3 04 00 00 00 push DWORD PTR [ebx+0x4] 396: ff a3 08 00 00 00 jmp DWORD PTR [ebx+0x8] 39c: 00 00 add BYTE PTR [eax],al ... 000003a0 <__cxa_finalize@plt>: 3a0: ff a3 0c 00 00 00 jmp DWORD PTR [ebx+0xc] 3a6: 68 00 00 00 00 push 0x0 3ab: e9 e0 ff ff ff jmp 390 <_init+0x2c> 000003b0 <__gmon_start__@plt>: 3b0: ff a3 10 00 00 00 jmp DWORD PTR [ebx+0x10] 3b6: 68 08 00 00 00 push 0x8 3bb: e9 d0 ff ff ff jmp 390 <_init+0x2c> Disassembly of section .text: 000003c0 <__x86.get_pc_thunk.bx>: 3c0: 8b 1c 24 mov ebx,DWORD PTR [esp] 3c3: c3 ret 3c4: 66 90 xchg ax,ax 3c6: 66 90 xchg ax,ax 3c8: 66 90 xchg ax,ax 3ca: 66 90 xchg ax,ax 3cc: 66 90 xchg ax,ax 3ce: 66 90 xchg ax,ax 000003d0 <deregister_tm_clones>: 3d0: e8 e4 00 00 00 call 4b9 <__x86.get_pc_thunk.dx> 3d5: 81 c2 2b 1c 00 00 add edx,0x1c2b 3db: 8d 8a 18 00 00 00 lea ecx,[edx+0x18] 3e1: 8d 82 18 00 00 00 lea eax,[edx+0x18] 3e7: 39 c8 cmp eax,ecx 3e9: 74 1d je 408 <deregister_tm_clones+0x38> 3eb: 8b 82 f0 ff ff ff mov eax,DWORD PTR [edx-0x10] 3f1: 85 c0 test eax,eax 3f3: 74 13 je 408 <deregister_tm_clones+0x38> 3f5: 55 push ebp 3f6: 89 e5 mov ebp,esp 3f8: 83 ec 14 sub esp,0x14 3fb: 51 push ecx 3fc: ff d0 call eax 3fe: 83 c4 10 add esp,0x10 401: c9 leave 402: c3 ret 403: 90 nop 404: 8d 74 26 00 lea esi,[esi+eiz*1+0x0] 408: f3 c3 repz ret 40a: 8d b6 00 00 00 00 lea esi,[esi+0x0] 00000410 <register_tm_clones>: 410: e8 a4 00 00 00 call 4b9 <__x86.get_pc_thunk.dx> 415: 81 c2 eb 1b 00 00 add edx,0x1beb 41b: 55 push ebp 41c: 8d 8a 18 00 00 00 lea ecx,[edx+0x18] 422: 8d 82 18 00 00 00 lea eax,[edx+0x18] 428: 29 c8 sub eax,ecx 42a: 89 e5 mov ebp,esp 42c: 53 push ebx 42d: c1 f8 02 sar eax,0x2 430: 89 c3 mov ebx,eax 432: 83 ec 04 sub esp,0x4 435: c1 eb 1f shr ebx,0x1f 438: 01 d8 add eax,ebx 43a: d1 f8 sar eax,1 43c: 74 14 je 452 <register_tm_clones+0x42> 43e: 8b 92 fc ff ff ff mov edx,DWORD PTR [edx-0x4] 444: 85 d2 test edx,edx 446: 74 0a je 452 <register_tm_clones+0x42> 448: 83 ec 08 sub esp,0x8 44b: 50 push eax 44c: 51 push ecx 44d: ff d2 call edx 44f: 83 c4 10 add esp,0x10 452: 8b 5d fc mov ebx,DWORD PTR [ebp-0x4] 455: c9 leave 456: c3 ret 457: 89 f6 mov esi,esi 459: 8d bc 27 00 00 00 00 lea edi,[edi+eiz*1+0x0] 00000460 <__do_global_dtors_aux>: 460: 55 push ebp 461: 89 e5 mov ebp,esp 463: 53 push ebx 464: e8 57 ff ff ff call 3c0 <__x86.get_pc_thunk.bx> 469: 81 c3 97 1b 00 00 add ebx,0x1b97 46f: 83 ec 04 sub esp,0x4 472: 80 bb 18 00 00 00 00 cmp BYTE PTR [ebx+0x18],0x0 479: 75 27 jne 4a2 <__do_global_dtors_aux+0x42> 47b: 8b 83 f4 ff ff ff mov eax,DWORD PTR [ebx-0xc] 481: 85 c0 test eax,eax 483: 74 11 je 496 <__do_global_dtors_aux+0x36> 485: 83 ec 0c sub esp,0xc 488: ff b3 14 00 00 00 push DWORD PTR [ebx+0x14] 48e: e8 0d ff ff ff call 3a0 <__cxa_finalize@plt> 493: 83 c4 10 add esp,0x10 496: e8 35 ff ff ff call 3d0 <deregister_tm_clones> 49b: c6 83 18 00 00 00 01 mov BYTE PTR [ebx+0x18],0x1 4a2: 8b 5d fc mov ebx,DWORD PTR [ebp-0x4] 4a5: c9 leave 4a6: c3 ret 4a7: 89 f6 mov esi,esi 4a9: 8d bc 27 00 00 00 00 lea edi,[edi+eiz*1+0x0] 000004b0 <frame_dummy>: 4b0: 55 push ebp 4b1: 89 e5 mov ebp,esp 4b3: 5d pop ebp 4b4: e9 57 ff ff ff jmp 410 <register_tm_clones> 000004b9 <__x86.get_pc_thunk.dx>: 4b9: 8b 14 24 mov edx,DWORD PTR [esp] 4bc: c3 ret 4bd: 66 90 xchg ax,ax 4bf: 90 nop 000004c0 <test>: 4c0: 8b 44 24 04 mov eax,DWORD PTR [esp+0x4] 4c4: a3 1c 20 00 00 mov ds:0x201c,eax 4c9: c3 ret 4ca: 8d b6 00 00 00 00 lea esi,[esi+0x0] 000004d0 <blah>: 4d0: a1 1c 20 00 00 mov eax,ds:0x201c 4d5: c3 ret Disassembly of section .fini: 000004d8 <_fini>: 4d8: 53 push ebx 4d9: 83 ec 08 sub esp,0x8 4dc: e8 df fe ff ff call 3c0 <__x86.get_pc_thunk.bx> 4e1: 81 c3 1f 1b 00 00 add ebx,0x1b1f 4e7: 83 c4 08 add esp,0x8 4ea: 5b pop ebx 4eb: c3 ret It accesses var via mov eax,ds:0x201c and so on. |
|||
29 Jan 2019, 20:49 |
|
revolution 29 Jan 2019, 21:36
Is the code section from GCC writeable?
Furs wrote: Can't you just make it unwriteable at startup in code? Edit: answer my own question, Yes, if this is to be believed. But only if your kernel is not hardened, apparently. So the code might fail if someone tries to run it on a hardened system. |
|||
29 Jan 2019, 21:36 |
|
Tomasz Grysztar 30 Jan 2019, 11:16
Here's the simplest example that I could quickly prepare (assemble with fasm 1):
Code: format ELF64 executable 3 entry start include 'elf.inc' segment interpreter readable db '/lib64/ld-linux-x86-64.so.2',0 segment dynamic readable 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_HASH,hash dq DT_NULL,0 segment readable writeable rela: Elf64_Rela fixup,0,R_X86_64_64,msg ; replace qword at "fixup" with absolute value of "msg" relasz = $-rela symtab: Elf64_Sym hash: dd 1,1 dd 0 dd 0 strtab: db 0 strsz = $-strtab segment readable executable writeable start: mov rsi,qword 0 fixup = $ - 8 mov edx,msg.len mov edi,1 mov eax,1 syscall xor edi,edi mov eax,60 syscall segment readable writeable msg db 'Hello world!',0xA .len = $ - msg Note that this is not a true relocation, for simplicity I have no symbols in symbol table and the relocation is just an absolute value that overwrites the original value in code (a relocation dealing with actual addresses needs to refer to a symbol, even if just a local one). But this is enough for this demonstration. EDIT: I have the same result for 32-bit version, too: Code: format ELF executable 3 entry start include 'elf.inc' 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_RELA,rela dd DT_RELASZ,relasz dd DT_RELAENT,sizeof.Elf32_Rela dd DT_HASH,hash dd DT_NULL,0 segment readable writeable rela: Elf32_Rela fixup,0,R_386_32,msg ; replace dword at "fixup" with absolute value of "msg" relasz = $-rela symtab: Elf32_Sym hash: dd 1,1 dd 0 dd 0 strtab: db 0 strsz = $-strtab segment readable executable writeable start: mov ecx,dword 0 fixup = $ - 4 mov edx,msg.len mov eax,4 mov ebx,1 int 0x80 mov eax,1 xor ebx,ebx int 0x80 segment readable writeable msg db 'Hello world!',0xA .len = $ - msg |
|||
30 Jan 2019, 11:16 |
|
Furs 30 Jan 2019, 18:25
Tomasz Grysztar wrote: If you remove "writeable" attribute from code segment, it segfaults when trying to apply the relocation (at least on my machines). Using readelf: GCC Code: Elf file type is DYN (Shared object file) Entry point 0x3f0 There are 7 program headers, starting at offset 52 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x00000000 0x00000000 0x005d8 0x005d8 R E 0x1000 LOAD 0x000ef8 0x00001ef8 0x00001ef8 0x00120 0x00128 RW 0x1000 DYNAMIC 0x000f04 0x00001f04 0x00001f04 0x000e8 0x000e8 RW 0x4 NOTE 0x000114 0x00000114 0x00000114 0x00024 0x00024 R 0x4 GNU_EH_FRAME 0x00054c 0x0000054c 0x0000054c 0x00024 0x00024 R 0x4 GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10 GNU_RELRO 0x000ef8 0x00001ef8 0x00001ef8 0x00108 0x00108 R 0x1 Section to Segment mapping: Segment Sections... 00 .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .eh_frame_hdr .eh_frame 01 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss 02 .dynamic 03 .note.gnu.build-id 04 .eh_frame_hdr 05 06 .init_array .fini_array .jcr .dynamic .got FASM Code: Elf file type is EXEC (Executable file) Entry point 0x804915c There are 5 program headers, starting at offset 52 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align INTERP 0x0000d4 0x080480d4 0x080480d4 0x00013 0x00013 R 0x1 [Requesting program interpreter: /lib/ld-linux.so.2] DYNAMIC 0x0000e7 0x080480e7 0x080480e7 0x00048 0x00048 R 0x1 LOAD 0x00012f 0x0804812f 0x0804812f 0x0002d 0x0002d RW 0x1000 LOAD 0x00015c 0x0804915c 0x0804915c 0x0001f 0x0001f RWE 0x1000 LOAD 0x00017b 0x0804a17b 0x0804a17b 0x0000d 0x0000d RW 0x1000 I think you need the GNU_RELRO segment. I don't know how to add it. |
|||
30 Jan 2019, 18:25 |
|
Furs 30 Jan 2019, 18:36
revolution wrote: Is the code section from GCC writeable? According to wikipedia, looks like they have to special-case GCC's trampolines, which is just laughable and screws anyone else's runtime-generated code. Pretty disgusting if you ask me. EDIT: Sorry, to answer your question in case you don't read my reply to Tomasz (heh ), the GCC code segment is not writeable. My guess is that GNU_RELRO thing is why it works (see above). |
|||
30 Jan 2019, 18:36 |
|
Tomasz Grysztar 30 Jan 2019, 18:54
Furs wrote: I can't use objdump on this example, are you sure it's even a correct ELF in the first place? It doesn't even have named sections. I don't know much about ELF, but I know that some sections are treated specially by the loader on Linux. The design of ELF in general is quite complex but clever, and it gives a lot of freedom compared to other formats (like PE/COFF). The same format can handle being an executable, and object, or a mix of both. For example, run-time segments (defined in Program Headers) are mandatory for executable but optional for linkable object, while named sections are mandatory for object but optional for executable. Section headers are usually kept after linking to preserve the information that might be useful for debugging, etc. But everything that is actually necessary for run-time loading is kept in attributes of segments. All the basic ELF examples that come packaged with fasm have always been kept minimal - they do not contain optional structures. Moreover, fasm's executable formatter never even had an option of adding section headers. By tweaking fasmg's macros you could perhaps do that and make a "hybrid" ELF with both segments and sections defined. I should note that ELF was originally designed for System V Unix and not Linux specifically - the implementation in Linux might not be the most faithful one. There are assumptions made by Linux implementation that are not present in the specification and the problems with relocations discussed here are also a demonstration that Linux does not always use the capabilities of this format to full extent. Furs wrote: I think you need the GNU_RELRO segment. I don't know how to add it. I'm going to try it, it is interesting whether segment of this type is enough to change loader's behavior here. |
|||
30 Jan 2019, 18:54 |
|
revolution 30 Jan 2019, 18:57
Furs wrote: I think that hardening is pretty stupid, how is runtime-generated code supposed to work, then? If I got this right, yet another example where security pisses on functionality. Anyhow, the point being that if you want to ensure maximum usability of your code then you can't reasonably expect everyone else to run their kernel in a particular way. However if fasm can be coaxed to make a functioning ASLR capable exe with relocations and a writeable code segment then I still think it is a good improvement. Just for the sake of completeness. No need to limit the users arbitrarily. The thing that makes me cringe is the idea of making code use ASLR for "security" and also making the code writeable to break the security. I think this a flaw in the way Linux load programs. |
|||
30 Jan 2019, 18:57 |
|
Tomasz Grysztar 30 Jan 2019, 19:05
All right, GNU_RELRO seems like a good clue. It is Linux-specific addition:
Quote: PT_GNU_RELRO It is easy to add this segment option into fasmg's elfexe.inc and do some experiments. |
|||
30 Jan 2019, 19:05 |
|
revolution 30 Jan 2019, 19:08
I see that the examples posted by Tomasz have an interpreter section. Does that mean the it is the interpreter that does the relocations? If so then the loader is not processing the relocs at all?
|
|||
30 Jan 2019, 19:08 |
|
Tomasz Grysztar 30 Jan 2019, 19:21
I can confirm that changing code segment to PT_GNU_RELRO allows to apply relocations to it without "writeable" attribute. I am adding this value to possible settings in fasmg's elfexe.inc.
Interestingly, this means that GCC separates the code that needs to be relocated from the "bulk" code, because it still makes a regular code segment and PT_GNU_RELRO is just an additional one. |
|||
30 Jan 2019, 19:21 |
|
Tomasz Grysztar 30 Jan 2019, 19:37
revolution wrote: I see that the examples posted by Tomasz have an interpreter section. Does that mean the it is the interpreter that does the relocations? If so then the loader is not processing the relocs at all? |
|||
30 Jan 2019, 19:37 |
|
revolution 30 Jan 2019, 19:38
So we need to include foreign code into our exe to make it relocatable (i.e. the interpreter .so)? If so then we should just write our own relocator and then we can do whatever we need in whatever format we need, since the outcome is going to be the same.
And that also suggests that a relocatable exe from GCC can't be run on a hardened kernel because it isn't allowed to change segment permissions. |
|||
30 Jan 2019, 19:38 |
|
Tomasz Grysztar 30 Jan 2019, 19:54
What I found when experimenting:
|
|||
30 Jan 2019, 19:54 |
|
revolution 30 Jan 2019, 20:50
Tomasz Grysztar wrote: What I found when experimenting: |
|||
30 Jan 2019, 20:50 |
|
Goto page Previous 1, 2, 3, 4 Next < Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.