#!/usr/bin/env python3
"""
crack_api_hash.py : casse le hash d'API observe dans FUN_1402e9920 (routine
de resolution d'API par hash du dropper Crytox, reutilisee telle quelle par
le shellcode injecte). Algorithme exact reconstruit depuis la decompilation :

  hash_module = somme roulante (ROR32 7 bits) sur chaque OCTET du nom du
                module en UTF-16LE (Length en octets, pas en caracteres),
                lettres ASCII minuscules repliees en majuscules (>0x60 -> -0x20)
  hash_func   = somme roulante (ROR32 11 bits) sur chaque octet du nom de
                fonction en ASCII, NULL TERMINATEUR INCLUS (une rotation de
                plus avec +0 a la fin)
  hash_final  = (hash_module + hash_func) mod 2**32

Usage : crack_api_hash.py HASH1 [HASH2 ...] (hex ou decimal)
"""
import sys


def ror32(x, n):
    x &= 0xFFFFFFFF
    return ((x >> n) | (x << (32 - n))) & 0xFFFFFFFF


def hash_module(name):
    # Reconstruit empiriquement (voir runbook.md) : le nul terminateur UTF-16
    # (2 octets zero) fait bien partie de la somme roulante, contrairement a
    # une UNICODE_STRING.Length classique qui l'exclurait.
    data = name.upper().encode("utf-16-le") + b"\x00\x00"
    h = 0
    for b in data:
        h = (ror32(h, 7) + b) & 0xFFFFFFFF
    return h


def hash_func(name):
    data = name.encode("ascii") + b"\x00"
    h = 0
    for b in data:
        h = (ror32(h, 11) + b) & 0xFFFFFFFF
    return h


MODULES = [
    "kernel32.dll", "ntdll.dll", "advapi32.dll", "user32.dll", "ws2_32.dll",
    "wininet.dll", "shell32.dll", "gdi32.dll", "ole32.dll", "msvcrt.dll",
    "crypt32.dll", "bcrypt.dll", "shlwapi.dll", "iphlpapi.dll", "shell32.dll",
    "netapi32.dll", "psapi.dll", "wtsapi32.dll", "mpr.dll", "urlmon.dll",
    "wininet.dll", "oleaut32.dll", "comctl32.dll", "userenv.dll",
]

FUNCS = [
    "GetProcAddress", "LoadLibraryA", "LoadLibraryW", "GetModuleHandleA",
    "CreateFileA", "CreateFileW", "ReadFile", "WriteFile", "CloseHandle",
    "SetFilePointer", "SetFilePointerEx", "GetFileSize", "GetFileSizeEx",
    "DeleteFileA", "DeleteFileW", "MoveFileA", "MoveFileW", "MoveFileExA",
    "MoveFileExW", "CopyFileA", "CopyFileW", "FindFirstFileA", "FindFirstFileW",
    "FindNextFileA", "FindNextFileW", "FindClose", "SetFileAttributesA",
    "SetFileAttributesW", "GetFileAttributesA", "GetFileAttributesW",
    "GetLogicalDrives", "GetLogicalDriveStringsA", "GetLogicalDriveStringsW",
    "GetDriveTypeA", "GetDriveTypeW", "GlobalAlloc", "GlobalFree", "GlobalLock",
    "GlobalUnlock", "LocalAlloc", "LocalFree", "VirtualAlloc", "VirtualAllocEx",
    "VirtualFree", "VirtualFreeEx", "VirtualProtect", "VirtualProtectEx",
    "WriteProcessMemory", "ReadProcessMemory", "CreateRemoteThread",
    "CreateRemoteThreadEx", "NtCreateThreadEx", "OpenProcess", "CloseHandle",
    "CreateToolhelp32Snapshot", "Process32First", "Process32FirstW",
    "Process32Next", "Process32NextW", "TerminateProcess", "ExitProcess",
    "GetCurrentProcess", "GetCurrentProcessId", "GetCurrentThread",
    "GetCurrentThreadId", "GetModuleFileNameA", "GetModuleFileNameW",
    "GetTickCount", "GetTickCount64", "GetSystemTimeAsFileTime",
    "GetSystemTime", "GetLocalTime", "QueryPerformanceCounter",
    "QueryPerformanceFrequency", "Sleep", "SleepEx", "WaitForSingleObject",
    "CreateThread", "ExitThread", "GetLastError", "SetLastError",
    "RegOpenKeyA", "RegOpenKeyW", "RegOpenKeyExA", "RegOpenKeyExW",
    "RegSetValueExA", "RegSetValueExW", "RegQueryValueExA", "RegQueryValueExW",
    "RegCloseKey", "RegDeleteValueA", "RegDeleteValueW", "RegCreateKeyExA",
    "RegCreateKeyExW", "CryptAcquireContextA", "CryptAcquireContextW",
    "CryptGenKey", "CryptImportKey", "CryptExportKey", "CryptEncrypt",
    "CryptDecrypt", "CryptDestroyKey", "CryptReleaseContext",
    "CryptGenRandom", "CryptCreateHash", "CryptHashData", "CryptDeriveKey",
    "CryptSetKeyParam", "CryptGetKeyParam", "SystemFunction036",
    "ShellExecuteA", "ShellExecuteW", "WinExec", "CreateProcessA",
    "CreateProcessW", "IsDebuggerPresent", "CheckRemoteDebuggerPresent",
    "GetSystemDirectoryA", "GetSystemDirectoryW", "GetWindowsDirectoryA",
    "GetWindowsDirectoryW", "GetTempPathA", "GetTempPathW", "GetEnvironmentVariableA",
    "GetEnvironmentVariableW", "ExpandEnvironmentStringsA", "ExpandEnvironmentStringsW",
    "WNetOpenEnumA", "WNetOpenEnumW", "WNetEnumResourceA", "WNetEnumResourceW",
    "WNetCloseEnum", "InternetOpenA", "InternetOpenW", "InternetConnectA",
    "InternetConnectW", "HttpOpenRequestA", "HttpOpenRequestW",
    "HttpSendRequestA", "HttpSendRequestW", "InternetReadFile",
    "InternetCloseHandle", "InternetSetOptionA", "socket", "connect", "send",
    "recv", "closesocket", "WSAStartup", "WSACleanup", "gethostbyname",
    "inet_addr", "htons", "GetAdaptersInfo", "GetComputerNameA",
    "GetComputerNameW", "GetUserNameA", "GetUserNameW", "OpenSCManagerA",
    "OpenSCManagerW", "OpenServiceA", "OpenServiceW", "ControlService",
    "StartServiceA", "StartServiceW", "CreateMutexA", "CreateMutexW",
    "OpenMutexA", "OpenMutexW", "MessageBoxA", "MessageBoxW",
    "FindResourceA", "FindResourceW", "LoadResource", "LockResource",
    "SizeofResource", "GetProcessHeap", "HeapAlloc", "HeapFree", "HeapReAlloc",
    "SetThreadPriority", "SetPriorityClass", "GetSystemInfo",
    "AdjustTokenPrivileges", "OpenProcessToken", "LookupPrivilegeValueA",
    "LookupPrivilegeValueW", "DuplicateTokenEx", "ImpersonateLoggedOnUser",
    "CreateProcessAsUserA", "CreateProcessAsUserW", "RtlAddFunctionTable",
    "AddVectoredExceptionHandler", "SetUnhandledExceptionFilter",
    "UnhandledExceptionFilter", "RaiseException", "GetCommandLineA",
    "GetCommandLineW", "CreateFileMappingA", "CreateFileMappingW",
    "MapViewOfFile", "MapViewOfFileEx", "UnmapViewOfFile", "FlushViewOfFile",
    "SetEndOfFile", "LockFile", "UnlockFile", "GetTempFileNameA",
    "GetTempFileNameW", "CreateDirectoryA", "CreateDirectoryW",
    "RemoveDirectoryA", "RemoveDirectoryW", "GetFullPathNameA",
    "GetFullPathNameW", "lstrlenA", "lstrlenW", "lstrcpyA", "lstrcpyW",
    "lstrcatA", "lstrcatW", "wsprintfA", "wsprintfW", "WideCharToMultiByte",
    "GetVersion", "GetVersionExA", "GetVersionExW", "GetNativeSystemInfo",
    "IsWow64Process", "SetErrorMode", "FlushFileBuffers", "GetOverlappedResult",
    "DeviceIoControl", "GetVolumeInformationA", "GetVolumeInformationW",
    "QueryDosDeviceA", "QueryDosDeviceW", "ReadFile", "ReadFileEx",
]


def build_table():
    table = {}
    for m in MODULES:
        hm = hash_module(m)
        for f in FUNCS:
            hf = hash_func(f)
            total = (hm + hf) & 0xFFFFFFFF
            table.setdefault(total, []).append(f"{m}!{f}")
    return table


def main():
    if len(sys.argv) < 2:
        sys.exit(__doc__)
    table = build_table()
    for arg in sys.argv[1:]:
        val = int(arg, 0) & 0xFFFFFFFF
        hits = table.get(val)
        print(f"{hex(val)} -> {hits if hits else 'PAS TROUVE'}")


if __name__ == "__main__":
    main()
