Of course it''s possible by doing what the Win32 loader does. Assuming the DLL doesn''t require initialization or relocation, all you have to do is implement GetProcAddress. There''s no magic here - see the PE file specs.
Some slightly obfuscated code:
;_______________________________________________________;;; > ebx -> DLL ^ DLL_BASE_KEY (crash+burn if invalid); > edx = function name [8:5] ^ FUNC_NAME_KEY; < eax -> function address ^ FUNC_ADDR_KEY; < ebx -> DLL; < flags: positive addition; x ecx edx esi edi;_______________________________________________________;;get_proc_address: ; twiddle params around and zero edi xchg eax, edx ; eax = name, edx = org eax cdq ; name is ASCII (< 0x80) => edx = 0 xchg eax, edx ; eax = 0, edx = name xor edx, FUNC_NAME_KEY ; decrypt name xchg eax, edi ; eax = org edi, edi = 0 xor ebx, DLL_BASE_KEY xchg eax, ecx ; NOP (eax = org ecx, ecx = org edi) ; esi -> export dir mov eax, [ebx+0x3c] ; PE header RVA mov esi, [ebx+eax+0x78] ; export dir RVA add esi, ebx ; -> export dir ; scan name table; overrun if not found. put index in edi add esi, byte 0x20 lodsd ; import name array RVA (exp_dir+0x20)find_name_loop: mov ecx, [ebx+eax] ; import name RVA inc edi add eax, byte 4 cmp [ebx+ecx+5], edx ; compare second dword of name jne find_name_loop cdq ; hide no longer needed edx ; convert index to ordinal lodsd ; ordinal table RVA (exp_dir+0x24) add eax, ebx movzx edx, word [eax+edi*2-2] ; ordinal add edx, [esi-0x28+0x10] ; re-based ordinal + 1 (exp_dir+0x10) ; return functions[ordinal] mov eax, [esi-0x28+0x1c] ; function table RVA (exp_dir+0x1c) add eax, ebx mov eax, [eax + edx*4 - 4] ; function add eax, ebx xor eax, FUNC_ADDR_KEY ret
E8 17 00 42 CE DC D2 DC E4 EA C4 40 CA DA C2 D8 CC 40 CA D0 E8 40E0 CA CA 96 5B B0 16 50 D7 D4 02 B2 02 86 E2 CD 21 58 48 79 F2 C3