# Resolver (reverse medium, 16 solves) ## Challenge You are trying to get into a malware development forum and an administrator gives you this challenge to solve to get in. According to him, this function is used for dynamic resolving. He wants you to give him the values to pass as parameters to resolve the following functions: GetDateFormatWWorker (val1) IsWow64GuestMachineSupported (val2) SetThreadPreferredUILanguages (val3) Format : Hero{0xval1:0xval2:0xval3} ## Answer We're given an ASM listing screenshot of IDA. You'll find below an annotated transcript. This code is a custom GetProcAddress using function hashing. This hashing technique is used by malware developpers to evade security solutions because imports don't directly rely on the API literal but on hash of it, hence, security solutions cannot automatically find the API names on the code or on the regular import table. It begins by finding the address of the mapped kernel32.dll using the PEB which in 32-bit mode is accessible at `fs+0x30`. It then iterates the export table and starts computing hashes of API names. ``` resolve: push ebp mov ebp, esp sub esp, 10h mov eax, large fs:30h ; TIB+0x30 -> PEB mov eax, [eax+0Ch] ; PEB_LDR_DATA *Ldr mov eax, [eax+14h] ; LIST_ENTRY InMemoryOrderModuleList (FLink) mov eax, [eax] ; (Flink) mov eax, [eax] ; (Flink) mov edi, [eax+10h] ; LDR_MODULE's BaseAddress Kernel32 mov eax, [edi+eax+78h] ; 0x78 is PE offset for export table add eax, edi ; absolute base addr mov edx, [eax+20h] ; AddressOfNames offset mov ebx, [eax+1Ch] ; AddressOfFunctions offset add edx, edi ; abs mov eax, [eax+18h] ; NumberOfNames offset add ecx, edi ; abs mov [ebp+var_4], edx ; stack store AddressOfNames mov [ebp+var_8], ecx ; stack store AddressOfFunctions mov [ebp+var_C], eax ; stack store NumberOfNames xor ecx, ecx test eax, eax jz short _L0 _L1: mov eax, [edx+ecx*4] ; address of name add eax, edi ; abs push ebx push ecx mov esi, eax xor rdx, rdx jmp short _L3 _L2: movsx ecx, bl rol edx, 0Eh ; rotate left accumulator cmp bl, 61h ; 'a' lea eax, [ecx-20h] ; toupper cmovl eax, ecx ; transform to uppercase if necessary add edx, eax ; add to accumulator inc esi ; inc name ptr _L3: mov bl, [esi] ; get letter test bl, bl ; end of api name jnz short _L2 mov eax, edx ; L2 computed value in edx pop ecx pop ebx cmp eax, [ebp+arg_0] jz short _L4 mov edx, [ebp+var_4] ; AddressOfNames inc ecx cmp ecx, [ebp+var_c] ; NumberOfNames jb short _L1 ; There are more names to iterate _L0: xor eax, eax jmp short _END _L4: rva to va ? mov eax, [ebp+var_8] ; AddressOfFunctions movzx eax, word ptr [eax+ecx*2] ; get ordinal mov eax, [ebx+eax*4] ; VMA add eax, edi ; add base addr of kernel32 _END: mov esp, ebp pop ebp retn ``` The hashing algorithm in `_L2` mainly consists of a `rol` and an `add`. We can easily replicate it in C: ```c #include <string.h> #include <stdio.h> #include <ctype.h> #include <stdint.h> #define ROL(S,i) (((S)<<(i)) | ((S)>>(32-(i)))) int resolve(char*s) { uint32_t acc = 0; // edx might not be zeroed for (unsigned i = 0 ; i < strlen(s) ; i++) { acc = ROL(acc, 0xe); acc += toupper(s[i]); } printf("0x%x", acc); } int main() { printf("Hero{"); resolve("GetDateFormatWWorker"); printf(":"); resolve("IsWow64GuestMachineSupported"); printf(":"); resolve("SetThreadPreferredUILanguages"); printf("}\n"); } ``` And the flag: `Hero{0xe3232703:0x89291c8b:0xac881638}`