;;; hlstub.asm -*- mode: nasm -*- ;; hash-dynlink ELF stub ;; assemble in raw mode, includes elf headers ;; settings; make sure linker script matches ;; remember to update load address if prog_data changes ;; stub + data load address %define STUB_ORG 0x08048000 ;; program bss size (256k) %define PROG_BSS (256*1024) ;; whether to enable error handling or not (saves bytes) %define ERROR_HANDLING 0 ;; settings overridable from command line %ifndef HASH_FILE ;; by default read in "prog-hlstub.hash" %define HASH_FILE "prog-hlstub.hash" %endif %ifndef PROG_FILE ;; by default read in "prog-hlstub.text" %define PROG_FILE "prog-hlstub.text" %endif ;; ELF file header + start of file BITS 32 org STUB_ORG [map symbols] fhdr: db 127, 'ELF' ; elf magic db 1, 1, 1 ; 32-bit, little-endian, v1 header ;; code in the padding stub_entry: mov esi, symhash_data ; esi <- dynamic symbol table mov edi, esi ; edi <- dynamic symbol table jmp short stub_entry_continue ;; code in the padding finished times 9-($-stub_entry) db 0 ; padding bytes dw 2, 3 ; executable file for i386 dd 1 ; v1 file dd stub_entry ; entry point of executable dd phdr-fhdr ; offset to program header dd 0 ; offset to section header: unused dd 0 ; no flags dw fhdr_size ; size of this header dw 32, 3 ; 32-byte program header entries, 3 entries dw 0, 0 ; 0-byte section header entries, none dw 0 ; string table section index fhdr_size: equ $-fhdr ;; Stub code stub_entry_continue: ;; stub_entry up there set esi = edi = symhash_data ;; resolve dynamic symbol references from the hash table mov ecx, symhash_data_size/4 ; ecx <- number of symbols to look up xor ebp, ebp ; ebp <- constant 0 dynlink: lodsd ; eax <- current symbol hash mov edx, eax ; edx <- current symbol hash mov ebx, [dynamic_debug] ; ebx <- r_debug struct addr mov ebx, [ebx+4] ; ebx <- r_map chain start .process_lib: %if ERROR_HANDLING test ebx, ebx jz stub_exit %endif ;; skip no-filename library chain items push ebx mov ebx, [ebx+4] ; ebx <- lib filename mov ebx, [ebx] ; ebx <- first word of filename test bl, bl pop ebx ; ebx <- r_map struct jz .next_lib ;; locate the DT_HASH/DT_STRTAB/DT_SYMTAB entries of lib push ebx push ecx push esi mov ebx, [ebx+8] ; ebx <- lib dynamic section lea ecx, [ebp+4] ; ecx <- 4 (DT_HASH) .dynsecfind: mov esi, ebx ; esi <- lib dynamic section .dynsecloop: lodsd ; eax <- tag cmp eax, ecx lodsd ; eax <- value jnz .dynsecloop ; tag != ecx, go to next one push eax ; save tag value on stack inc ecx ; ecx: DT_HASH -> DT_STRTAB; DT_STRTAB -> DT_SYMTAB cmp cl, 7 jnz .dynsecfind ; more values to find pop esi ; esi <- DT_SYMTAB addr pop ebx ; ebx <- DT_STRTAB addr pop ecx ; ecx <- DT_HASH addr mov ecx, [ecx+4] ; ecx <- number of symbols in lib ;; find the symbol with matching hash .symloop: lodsd ; eax <- symbol name offset in strtab push esi ; save symtab ptr lea esi, [ebx+eax] ; esi <- symbol name ptr push ebx ; save strtab ptr xor eax, eax xor ebx, ebx .hashloop: imul ebx, ebx, byte 0x21 xor ebx, eax lodsb test al, al jnz .hashloop cmp ebx, edx ; set flags from hash-comparison pop ebx ; ebx <- DT_STRTAB addr pop esi ; esi <- symtab ptr lodsd ; eax <- symbol value jz .symfound ; jump if hashes match lodsd lodsd loop .symloop ; no match, move to next sym xor eax, eax ; not found at all, zero and fallthru ;; update our dynamic symbol table if found; else next lib .symfound: pop esi ; esi <- symhash_data ptr pop ecx ; ecx <- number of remaining symhash_data entries pop ebx ; ebx <- current lib r_map struct add eax, [ebx] ; eax <- symbol relocated with lib-base cmp eax, [ebx] .next_lib: mov ebx, [ebx+12] ; ebx <- next lib r_map jz .process_lib ; go to next lib if symbol == base stosd ; write symbol value to hashtable loop dynlink ; go to next symbol, if any remain call esi ; call code after symhash_data ;; Stub termination thing %if ERROR_HANDLING stub_exit: lea eax, [ebp+1] xor ebx, ebx int 80h .loop: jmp .loop %endif ;; ELF program header phdr: ;; program interpreter dd 3 ; PT_INTERP dd interp-fhdr dd interp, interp dd interp_size, interp_size dd 4 ; R-- dd 1 ;; loadable data dd 1 ; PT_LOAD dd 0 dd fhdr, fhdr dd total_size ; file size: stub + payload dd total_size+PROG_BSS ; memory size: stub + payload + bss dd 7 ; RWX dd 0x1000 ;; dynamic linking info dd 2 ; PT_DYNAMIC dd dynamic-fhdr dd dynamic, dynamic dd dynamic_size, dynamic_size dd 6 ; RW- dd 1 phdr_size: equ $-phdr ;; String tables dyn_strtab: ;; string table ;; libdl.so symbols dyn_strtab_libGLU: db 'libGLU.so', 0 ;; symbol table size dyn_strtab_size: equ $-dyn_strtab interp: ;; program interpreter (dynamic linker) db '/lib/ld-linux.so.2', 0 interp_size: equ $-interp ;; Dynamic symbol hash data symhash_data: incbin HASH_FILE symhash_data_size: equ $-symhash_data ;; Payload program data prog_data: incbin PROG_FILE prog_data_size: equ $-prog_data ;; ELF dynamic section align 4, db 0 dynamic: dd 0x01, dyn_strtab_libGLU-dyn_strtab ; NEEDED dd 0x05, dyn_strtab ; STRTAB dd 0x06, 0 ; SYMTAB dynamic_debug: equ $+4 dd 0x15, 0 ; DEBUG ;; NULL in .bss section dynamic_size: equ $-dynamic ;; Total size on disk total_size: equ $-fhdr SECTION .bss resd 2 ; NULL for dynamic section bss_start: