├── 0 └── dynamic0.asm ├── 1 └── dynamic1.asm ├── 2 ├── dynamic2.asm └── so.asm ├── 3 ├── dynamic3.asm └── so.asm ├── 4 ├── dynamic4.asm └── so.asm └── README.md /0/dynamic0.asm: -------------------------------------------------------------------------------- 1 | ; linuxthor 2 | ; 3 | ; simple 'fake' dynamic ELF example 4 | ; 5 | ; adding a crafted .dynamic section means we arrange for the loader 6 | ; to load something for us and then pass control.. 7 | ; 8 | ; ./dynamic0: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), 9 | ; dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2 10 | ; 11 | ; assemble with: 12 | ; nasm -f elf64 -o dynamic0.o dynamic0.asm 13 | ; ld -o dynamic0 dynamic0.o 14 | 15 | BITS 64 16 | 17 | global _start 18 | _start: 19 | mov rax, 1 ; sys_write 20 | mov rdi, 1 21 | mov rsi, string 22 | mov rdx, len 23 | syscall 24 | 25 | mov rax, 60 ; sys_exit 26 | mov rdi, 0 27 | syscall 28 | 29 | section .data 30 | string db "don't panic - I'm dynamic!",0x0d,0x0a,0 31 | len equ $ - string 32 | 33 | section .interp 34 | zig db '/lib64/ld-linux-x86-64.so.2',0 35 | zag db 'libc.so.6',0 36 | 37 | section .dynamic 38 | dq 5, zag ; STRTAB 39 | dq 1, 0 ; NEEDED (libc.so.6) 40 | dq 6, 0 ; SYMTAB 41 | dq 0, 0 ; NULL (ends section) 42 | -------------------------------------------------------------------------------- /1/dynamic1.asm: -------------------------------------------------------------------------------- 1 | ; linuxthor 2 | ; 3 | ; dynamic ELF load-me-do with a weird twist 4 | ; 5 | ; we can ask the loader to load some PIE ELF file for us (e.g /bin/bash - 6 | ; not just .so libraries) it will be placed in memory along with it's 7 | ; dependencies (e.g libc.so, libtinfo.so etc) 8 | ; 9 | ; we can also use some code in memory to do something in a ROP gadget 10 | ; way.. 11 | ; 12 | ; assemble with: 13 | ; nasm -f elf64 -o dynamic1.o dynamic1.asm 14 | ; ld -o dynamic1 dynamic1.o 15 | ; 16 | 17 | BITS 64 18 | 19 | global _start 20 | _start: 21 | push mungo ; "works on my machine" guaranteed: 22 | add rdx, 899 ; <_dl_fini+899>: retq 23 | jmp rdx 24 | 25 | ; we shouldn't get here.. 26 | mov rax, 1 ; sys_write 27 | mov rdi, 1 28 | mov rsi, string 29 | mov rdx, len 30 | syscall 31 | 32 | mov rax, 60 ; sys_exit 33 | mov rdi, 0 34 | syscall 35 | 36 | mungo: ; we should end up here.. 37 | mov rax, 60 ; sys_exit 38 | mov rdi, 2 39 | syscall 40 | 41 | section .data 42 | string db 'yello world!',0x0d,0x0a,0 43 | len equ $ - string 44 | 45 | ; usefully the loader will place /bin/bash in memory and all 46 | ; the dependencies of that (e.g libc.so, libtinfo.so etc) 47 | ; before passing over to _start.. 48 | 49 | section .interp 50 | zig db '/lib64/ld-linux-x86-64.so.2',0 51 | zag db '/bin/bash',0 52 | 53 | section .dynamic 54 | dq 5, zag 55 | dq 1, 0 56 | dq 6, 0 57 | dq 0, 0 58 | -------------------------------------------------------------------------------- /2/dynamic2.asm: -------------------------------------------------------------------------------- 1 | ; linuxthor 2 | ; 3 | ; dynamic ELF "non executing" file example 4 | ; 5 | ; the code in _start won't execute.. the loader ./0.so (see so.asm) 6 | ; will perform some actions and terminate 7 | ; 8 | ; ./dynamic2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), 9 | ; dynamically linked, interpreter ./0.so, not stripped 10 | ; 11 | ; assemble with: 12 | ; (n.b:- build the .so first per notes in so.asm) 13 | ; nasm -f elf64 -o dynamic2.o dynamic2.asm 14 | ; ld -o dynamic2 dynamic2.o 15 | 16 | BITS 64 17 | 18 | global _start 19 | _start: 20 | mov rax, 1 ; sys_write 21 | mov rdi, 1 22 | mov rsi, string 23 | mov rdx, len 24 | syscall 25 | 26 | mov rax, 60 ; sys_exit 27 | mov rdi, 0 28 | syscall 29 | 30 | section .data 31 | string db 'in the main program!',0x0d,0x0a,0 32 | len equ $ - string 33 | 34 | section .interp 35 | zig db './0.so',0 36 | zag db 'libc.so.6',0 37 | 38 | section .dynamic 39 | dq 5, zag 40 | dq 1, 0 41 | dq 6, 0 42 | dq 0, 0 43 | -------------------------------------------------------------------------------- /2/so.asm: -------------------------------------------------------------------------------- 1 | ; linuxthor 2 | ; 3 | ; shared object loader example 4 | ; 5 | ; assemble with: 6 | ; nasm -f elf64 so.asm -o so.o 7 | ; gcc -shared so.o -o 0.so 8 | 9 | BITS 64 10 | 11 | global _init:function 12 | 13 | _init: 14 | mov rax, 1 15 | mov rdi, 1 16 | lea rsi, [rel $ + 26] 17 | mov rdx, len 18 | syscall 19 | 20 | mov rax, 60 21 | mov rdi, 0 22 | syscall 23 | 24 | strg db 'in the loader!',0x0a,0x0d,0 25 | len equ $ - strg 26 | 27 | -------------------------------------------------------------------------------- /3/dynamic3.asm: -------------------------------------------------------------------------------- 1 | ; linuxthor 2 | ; 3 | ; dynamic ELF 'loader executed function' example 4 | ; 5 | ; we arrange for the loader to load our shared object at which point code 6 | ; in the well known function _init is executed (see so.asm) 7 | ; 8 | ; assemble with: 9 | ; (build shared object per so.asm) 10 | ; nasm -f elf64 -o dynamic3.o dynamic3.asm 11 | ; ld -o dynamic3 dynamic3.o 12 | ; 13 | 14 | BITS 64 15 | 16 | global _start 17 | _start: 18 | mov rax, 1 19 | mov rdi, 1 20 | mov rsi, string 21 | mov rdx, len 22 | syscall 23 | 24 | mov rax, 60 25 | mov rdi, 0 26 | syscall 27 | 28 | section .data 29 | string db 'in the main program!',0x0d,0x0a,0 30 | len equ $ - string 31 | 32 | section .interp 33 | zig db '/lib64/ld-linux-x86-64.so.2',0 34 | zag db './0.so',0 35 | 36 | section .dynamic 37 | dq 5, zag 38 | dq 1, 0 39 | dq 6, 0 40 | dq 0, 0 41 | -------------------------------------------------------------------------------- /3/so.asm: -------------------------------------------------------------------------------- 1 | ; linuxthor 2 | ; 3 | ; shared object with _init function 4 | ; 5 | ; assemble with: 6 | ; nasm -f elf64 so.asm -o so.o 7 | ; gcc -shared so.o -o 0.so 8 | 9 | BITS 64 10 | 11 | global _init:function 12 | 13 | _init: 14 | mov rax, 1 15 | mov rdi, 1 16 | mov rsi, string 17 | mov rdx, len 18 | syscall 19 | 20 | ret 21 | 22 | section .data 23 | string db 'in the loader',0x0a,0x0d,0 24 | len equ $ - string 25 | 26 | -------------------------------------------------------------------------------- /4/dynamic4.asm: -------------------------------------------------------------------------------- 1 | ; linuxthor 2 | ; 3 | ; dynamic ELF loader 'preloader loader' example 4 | ; 5 | ; an example of using a shared object as an interpreter that will re-execute the ELF file 6 | ; under the control of the standard system linker 7 | ; 8 | ; in this way some actions can be taken before the main program executes 9 | ; 10 | ; assemble with: 11 | ; (build shared object per so.asm) 12 | ; nasm -f elf64 -o dynamic4.o dynamic4.asm 13 | ; ld -o dynamic4 dynamic4.o 14 | ; 15 | 16 | BITS 64 17 | 18 | global _start 19 | _start: 20 | mov rax, 1 21 | mov rdi, 1 22 | mov rsi, string 23 | mov rdx, len 24 | syscall 25 | 26 | mov rax, 60 27 | mov rdi, 0 28 | syscall 29 | 30 | section .data 31 | string db 'in the main program!',0x0d,0x0a,0 32 | len equ $ - string 33 | 34 | section .interp 35 | zig db './0.so',0 36 | zag db 'libc.so.6',0 37 | 38 | section .dynamic 39 | dq 5, zag 40 | dq 1, 0 41 | dq 6, 0 42 | dq 0, 0 43 | -------------------------------------------------------------------------------- /4/so.asm: -------------------------------------------------------------------------------- 1 | ; linuxthor 2 | ; 3 | ; shared object ELF 'preloading' via dynamic interpreter 4 | ; 5 | ; this shared object is loaded as the interpreter for some ELF file (as specified in 6 | ; it's .dynamic section) where it performs an action before passing back control by: 7 | ; 8 | ; 1) resolving the full path to the ELF via /proc/self/exe 9 | ; 2) executing the linux dynamic linker passing the ELF path 10 | ; 11 | ; the code is quite messy as it uses rip relative addressing 12 | ; 13 | ; (this is a complete hack / PoC but it works!) 14 | ; 15 | ; assemble with: 16 | ; nasm -f elf64 so.asm -o so.o 17 | ; gcc -shared so.o -o 0.so 18 | ; 19 | 20 | global _init:function 21 | _init: 22 | mov rax, 1 ; sys_write 23 | mov rdi, 1 24 | lea rsi, [rel $ + (msg - $)] 25 | mov rdx, len 26 | syscall 27 | 28 | mov rax, 89 ; sys_readlink 29 | lea rdi, [rel $ + (rdl - $)] 30 | lea rsi, [rsp + 64] 31 | mov rdx, 64 32 | syscall 33 | 34 | mov byte [(rsp + 64) + rax], 0 35 | lea rbx, [rsp + 64] 36 | 37 | mov rax, 59 ; sys_execve 38 | lea rdi, [rel $ + (cmd - $)] 39 | mov [rsp], rdi 40 | mov [rsp+8], rbx 41 | mov rsi, rsp 42 | mov rdx, 0 43 | syscall 44 | 45 | mov rax, 60 ; sys_exit 46 | mov rdi, 0 47 | syscall 48 | 49 | cmd db '/lib64/ld-linux-x86-64.so.2',0 50 | rdl db '/proc/self/exe',0 51 | msg db 'i am dynamic loader',0x0d,0x0a,0 52 | len equ $ - msg 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iamdynamic 2 | 3 | _"..don't panic.. it's dynamic.."_ 4 | 5 | Linux assembly language examples showing the minimum required to be recognised and loaded as a dynamic ELF file plus these "few weird tricks!" 6 | 7 | The things that distinguish a dynamic ELF file from a static one are the presence of: 8 | 9 | 1) an .interp section naming the loader/helper (e.g /lib64/ld-linux-x86-64.so.2) 10 | 2) a .dynamic section which can contain various mandatory(_-ish_) fields and other optional fields 11 | 12 | [dynamic0](https://github.com/linuxthor/iamdynamic/blob/master/0/dynamic0.asm) 13 | 14 | This example shows the very minimum required to be recognised as a dynamic ELF and to arrange for the loader to pass control to our code. 15 | ``` 16 | ./dynamic0: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, not stripped 17 | ``` 18 | 19 | [dynamic1](https://github.com/linuxthor/iamdynamic/blob/master/1/dynamic1.asm) 20 | 21 | This example demonstrates that the loader will load some library or (position independant) ELF binary into memory for us (e.g /bin/bash) and also it's dependencies. We can use code that we can locate in memory in a ROP-ish way. 22 | ``` 23 | 00400000-00602000 rwxp 00000000 fd:01 396646 /home/j100/dynamics/1/dynamic1 24 | 7ffff729c000-7ffff7483000 r-xp 00000000 fd:01 5903028 /lib/x86_64-linux-gnu/libc-2.27.so 25 | 7ffff7483000-7ffff7683000 ---p 001e7000 fd:01 5903028 /lib/x86_64-linux-gnu/libc-2.27.so 26 | 7ffff7683000-7ffff7687000 r-xp 001e7000 fd:01 5903028 /lib/x86_64-linux-gnu/libc-2.27.so 27 | 7ffff7687000-7ffff7689000 rwxp 001eb000 fd:01 5903028 /lib/x86_64-linux-gnu/libc-2.27.so 28 | 7ffff7689000-7ffff768d000 rwxp 00000000 00:00 0 29 | 7ffff768d000-7ffff7690000 r-xp 00000000 fd:01 5903051 /lib/x86_64-linux-gnu/libdl-2.27.so 30 | 7ffff7690000-7ffff788f000 ---p 00003000 fd:01 5903051 /lib/x86_64-linux-gnu/libdl-2.27.so 31 | 7ffff788f000-7ffff7890000 r-xp 00002000 fd:01 5903051 /lib/x86_64-linux-gnu/libdl-2.27.so 32 | 7ffff7890000-7ffff7891000 rwxp 00003000 fd:01 5903051 /lib/x86_64-linux-gnu/libdl-2.27.so 33 | 7ffff7891000-7ffff78b6000 r-xp 00000000 fd:01 5903186 /lib/x86_64-linux-gnu/libtinfo.so.5.9 34 | 7ffff78b6000-7ffff7ab6000 ---p 00025000 fd:01 5903186 /lib/x86_64-linux-gnu/libtinfo.so.5.9 35 | 7ffff7ab6000-7ffff7aba000 r-xp 00025000 fd:01 5903186 /lib/x86_64-linux-gnu/libtinfo.so.5.9 36 | 7ffff7aba000-7ffff7abb000 rwxp 00029000 fd:01 5903186 /lib/x86_64-linux-gnu/libtinfo.so.5.9 37 | 7ffff7abb000-7ffff7bbf000 r-xp 00000000 fd:01 5243701 /bin/bash 38 | 7ffff7bbf000-7ffff7dbe000 ---p 00104000 fd:01 5243701 /bin/bash 39 | 7ffff7dbe000-7ffff7dc2000 r-xp 00103000 fd:01 5243701 /bin/bash 40 | 7ffff7dc2000-7ffff7dcb000 rwxp 00107000 fd:01 5243701 /bin/bash 41 | 7ffff7dcb000-7ffff7dd5000 rwxp 00000000 00:00 0 42 | 7ffff7dd5000-7ffff7dfc000 r-xp 00000000 fd:01 5903000 /lib/x86_64-linux-gnu/ld-2.27.so 43 | 7ffff7fb6000-7ffff7fb8000 rwxp 00000000 00:00 0 44 | 7ffff7ff5000-7ffff7ff7000 rwxp 00000000 00:00 0 45 | 7ffff7ff7000-7ffff7ffa000 r--p 00000000 00:00 0 [vvar] 46 | 7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso] 47 | 7ffff7ffc000-7ffff7ffd000 r-xp 00027000 fd:01 5903000 /lib/x86_64-linux-gnu/ld-2.27.so 48 | 7ffff7ffd000-7ffff7ffe000 rwxp 00028000 fd:01 5903000 /lib/x86_64-linux-gnu/ld-2.27.so 49 | 7ffff7ffe000-7ffff7fff000 rwxp 00000000 00:00 0 50 | 7ffffffde000-7ffffffff000 rwxp 00000000 00:00 0 [stack] 51 | ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] 52 | ``` 53 | 54 | [dynamic2](https://github.com/linuxthor/iamdynamic/blob/master/2/dynamic2.asm) 55 | 56 | This is where things get a bit more weird and interesting! The file specifies a shared object as it's loader that performs some actions and exits - therefore the program code observed in this file __never executes__. Any attempt to analyse the actions taken by this program (without also examining the loader) would be a waste of time! 57 | ``` 58 | dynamic2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter ./0.so, not stripped 59 | ``` 60 | 61 | [dynamic3](https://github.com/linuxthor/iamdynamic/blob/master/3/dynamic3.asm) 62 | 63 | In this example we use the system loader and ask it to load our shared object - this has the side effect of automatically executing code in the \_init function of that library. This can serve as the basis for some anti-debug technique as the code is executed very early. 64 | 65 | e.g gdb: 66 | ``` 67 | (gdb) b _start 68 | Breakpoint 1 at 0x400150 69 | (gdb) b _init 70 | Function "_init" not defined. 71 | Make breakpoint pending on future shared library load? (y or [n]) y 72 | Breakpoint 2 (_init) pending. 73 | (gdb) run 74 | Starting program: /home/j100/dynamics/3/dynamic3 75 | warning: Probes-based dynamic linker interface failed. 76 | Reverting to original interface. 77 | 78 | in the loader <== HERE 79 | 80 | Breakpoint 1, 0x0000000000400150 in _start () 81 | ``` 82 | 83 | [dynamic4](https://github.com/linuxthor/iamdynamic/blob/master/4/dynamic4.asm) 84 | 85 | This example specifies the included shared object as it's loader. It then executes the system loader as an ELF binary (valid on Linux) and asks it to execute the example file under the control of the system loader. As the loader is re-executing our file we don't need to know the origina entrypoint (which isn't always useful anyway depending on how things are loaded in memory etc..) 86 | 87 | Meaning.. Changing the .interp section of some ELF binary to add a loader like the one in dynamic4 is a way to take control of execution before the normal entry point is reached.. 88 | 89 | e.g we take a copy of /bin/echo and change the .interp section so that the loader is ./0.so and not /lib64/ld-linux-x86-64.so.2 and our library code will be executed - control is then given back to echo by executing /lib64/ld-linux-x86-64.so.2 asking to re-execute the binary which will, this time, ignore the library set in .interp and enter the program via it's usual entrypoint 90 | ``` 91 | file ./echo 92 | ./echo: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter ./0.so, for GNU/Linux 3.2.0, BuildID[sha1]=057373f1356c861e0ec5b52c72804c86c6842cd5, stripped 93 | bash$ ./echo I AM THE WALRUS 94 | i am dynamic loader 95 | I AM THE WALRUS 96 | ``` 97 | --------------------------------------------------------------------------------