Rapid IAT Parsing in Python

I think the best (and worst) thing about working in cybersecurity is that there’s always more to learn. Even though I work in the IR space, I haven’t spent nearly as much time diving into malware analysis as I’ve wanted to. So when I was introduced to TCM’s Practical Malware Analysis and Triage course, I immediately knew how I’d be spending my weekend.

It ended up being one of those courses you can’t casually move through. I flew through all the material in less than a week — it honestly felt like reading a book where you have to know what happens next. I’d highly recommend it to anyone in DFIR, or honestly anyone who finds malware even remotely interesting.

A big part of malware analysis is examining the code before you ever run it or observe its behavior. This static analysis gives you critical context — it helps you build a baseline understanding of what the program is designed to do and highlights indicators to watch for when you eventually execute it.

For malware targeting Windows systems, that often means looking at the Import Address Table (IAT). The IAT is essentially a list inside a Windows executable that records all the external functions the program imports from other libraries. And as you might imagine, the functions a program calls can tell you a lot about its intentions.

There are certain Windows API functions that malware authors commonly leverage to achieve persistence, process injection, credential access, network communication, and other malicious objectives. If you see those functions referenced in an executable, it can be a good clue that the program might be doing something less than benign.

The issue? Even simple programs can have hundreds of entries in their IAT. Manually reviewing each one to determine whether anything stands out takes time.

There’s a fantastic open-source resource at https://malapi.io/ that categorizes Windows API functions based on the techniques they’re commonly associated with in malware. It’s incredibly helpful — but you still have to manually cross-reference the functions from your sample against the database. And again, those IATs can be large.

While working through the labs, I realized it would be fairly straightforward to write a script that references the malAPI database and accepts a list of IAT entries, then outputs which of those functions are categorized as potentially malicious. I tossed the idea into Claude and had it generate a script that does exactly that.

It’s not pretty, but it works. And it might be useful for anyone taking the course or experimenting with malware analysis on their own. I’ve included the code below.

#!/usr/bin/env python3

# MalAPI database organized by category
MALAPI_DB = {
    "Injection": [
        "CreateFileMapping",
        "CreateFileMappingA",
        "CreateRemoteThread",
        "CreateRemoteThreadEx",
        "GetModuleHandle",
        "GetProcAddress",
        "GetThreadContext",
        "GetWindowThreadProcessId",
        "IsWow64Process",
        "LoadLibrary",
        "LoadLibraryA",
        "LoadLibraryEx",
        "LoadLibraryExA",
        "LoadLibraryExW",
        "LoadLibraryW",
        "MapViewOfFile",
        "MapViewOfFile2",
        "MapViewOfFile3",
        "MapViewOfFileEx",
        "NtMapViewOfSection",
        "OpenProcess",
        "Process32First",
        "Process32Next",
        "QueueUserAPC",
        "ReadProcessMemory",
        "ResumeThread",
        "RtlCreateUserThread",
        "SetThreadContext",
        "SuspendThread",
        "Thread32First",
        "Thread32Next",
        "Toolhelp32ReadProcessMemory",
        "VirtualAlloc",
        "VirtualAllocEx",
        "VirtualProtect",
        "VirtualProtectEx",
        "WriteProcessMemory",
        "ZwMapViewOfSection",
        "ZwUnmapViewOfSection"
    ],
    "Evasion": [
        "CopyFileA",
        "CreateServiceA",
        "CreateServiceW",
        "CryptAcquireContext",
        "DeleteFileA",
        "EnumProcesses",
        "EnumProcessModules",
        "FindFirstFileA",
        "FindFirstFileW",
        "FindNextFileA",
        "FindNextFileW",
        "FindResourceA",
        "FindResourceW",
        "GetFileAttributesA",
        "GetFileAttributesW",
        "GetModuleFileNameA",
        "GetModuleFileNameW",
        "GetModuleHandleA",
        "GetModuleHandleW",
        "GetSystemDirectoryA",
        "GetSystemDirectoryW",
        "GetSystemTime",
        "GetSystemTimeAsFileTime",
        "GetTempPathA",
        "GetTempPathW",
        "GetTickCount",
        "GetWindowsDirectoryA",
        "GetWindowsDirectoryW",
        "IsDebuggerPresent",
        "LdrLoadDll",
        "MoveFileA",
        "MoveFileW",
        "MoveFileExA",
        "MoveFileExW",
        "OutputDebugStringA",
        "OutputDebugStringW",
        "ReadFile",
        "RegCloseKey",
        "RegCreateKeyA",
        "RegCreateKeyExA",
        "RegCreateKeyExW",
        "RegCreateKeyW",
        "RegDeleteKeyA",
        "RegDeleteKeyW",
        "RegDeleteValueA",
        "RegDeleteValueW",
        "RegEnumKeyA",
        "RegEnumKeyExA",
        "RegEnumKeyExW",
        "RegEnumKeyW",
        "RegEnumValueA",
        "RegEnumValueW",
        "RegOpenKeyA",
        "RegOpenKeyExA",
        "RegOpenKeyExW",
        "RegOpenKeyW",
        "RegQueryValueExA",
        "RegQueryValueExW",
        "RegSetValueExA",
        "RegSetValueExW",
        "SetFileAttributesA",
        "SetFileAttributesW",
        "SetFileTime",
        "SetWindowsHookEx",
        "SetWindowsHookExA",
        "SetWindowsHookExW",
        "SizeofResource",
        "Sleep",
        "StartServiceA",
        "StartServiceW",
        "UnhandledExceptionFilter",
        "WinExec",
        "WriteFile",
        "ZwQueryInformationProcess"
    ],
    "Spying": [
        "AttachThreadInput",
        "BitBlt",
        "CallNextHookEx",
        "CreateCompatibleBitmap",
        "CreateCompatibleDC",
        "DeleteDC",
        "DeleteObject",
        "EnumWindows",
        "FindWindowA",
        "FindWindowW",
        "GetAsyncKeyState",
        "GetClipboardData",
        "GetDC",
        "GetDCEx",
        "GetForegroundWindow",
        "GetKeyboardState",
        "GetKeyState",
        "GetMessageA",
        "GetMessageW",
        "GetRawInputData",
        "GetWindowDC",
        "MapVirtualKeyA",
        "MapVirtualKeyW",
        "PeekMessageA",
        "PeekMessageW",
        "PostMessageA",
        "PostMessageW",
        "RegisterHotKey",
        "RegisterRawInputDevices",
        "ReleaseDC",
        "SelectObject",
        "SendMessageA",
        "SendMessageW",
        "SetClipboardData",
        "SetWindowsHookExA",
        "SetWindowsHookExW"
    ],
    "Internet": [
        "FtpPutFileA",
        "HttpOpenRequestA",
        "HttpOpenRequestW",
        "HttpSendRequestA",
        "HttpSendRequestW",
        "InternetCloseHandle",
        "InternetConnectA",
        "InternetConnectW",
        "InternetOpenA",
        "InternetOpenUrlA",
        "InternetOpenUrlW",
        "InternetOpenW",
        "InternetReadFile",
        "InternetReadFileExA",
        "InternetReadFileExW",
        "InternetWriteFile",
        "URLDownloadToFile",
        "URLDownloadToFileA",
        "WSAStartup",
        "accept",
        "bind",
        "closesocket",
        "connect",
        "gethostbyname",
        "gethostname",
        "htons",
        "inet_addr",
        "ioctlsocket",
        "listen",
        "recv",
        "send",
        "sendto",
        "setsockopt",
        "shutdown",
        "socket",
        "WSASocketA",
        "WSASocketW"
    ],
    "Anti-Debugging": [
        "CheckRemoteDebuggerPresent",
        "CreateToolhelp32Snapshot",
        "FindWindowA",
        "FindWindowExA",
        "FindWindowExW",
        "FindWindowW",
        "GetLocalTime",
        "GetSystemTime",
        "GetTickCount",
        "IsDebuggerPresent",
        "NtQueryInformationProcess",
        "OutputDebugStringA",
        "OutputDebugStringW",
        "QueryPerformanceCounter",
        "SetUnhandledExceptionFilter",
        "UnhandledExceptionFilter",
        "ZwQueryInformationProcess"
    ],
    "Ransomware": [
        "CryptAcquireContextA",
        "CryptAcquireContextW",
        "CryptDecrypt",
        "CryptEncrypt",
        "CryptExportKey",
        "CryptGenKey",
        "CryptImportKey"
    ],
    "Helper": [
        "AdjustTokenPrivileges",
        "CreateProcessA",
        "CreateProcessW",
        "CreateThread",
        "GetCurrentProcess",
        "GetCurrentProcessId",
        "GetCurrentThread",
        "GetCurrentThreadId",
        "GetExitCodeProcess",
        "GetLastError",
        "GetModuleBaseNameA",
        "GetModuleBaseNameW",
        "GetModuleFileNameExA",
        "GetModuleFileNameExW",
        "GetNativeSystemInfo",
        "GetStartupInfoA",
        "GetStartupInfoW",
        "GetSystemDefaultLangID",
        "GetSystemInfo",
        "GetThreadLocale",
        "GetUserDefaultLangID",
        "GetUserNameA",
        "GetUserNameW",
        "GetVersionExA",
        "GetVersionExW",
        "GlobalMemoryStatusEx",
        "LookupPrivilegeValueA",
        "LookupPrivilegeValueW",
        "Module32First",
        "Module32Next",
        "NtQuerySystemInformation",
        "OpenProcessToken",
        "OpenThread",
        "RtlGetVersion",
        "ShellExecuteA",
        "ShellExecuteExA",
        "ShellExecuteExW",
        "ShellExecuteW",
        "TerminateProcess",
        "TerminateThread",
        "WaitForSingleObject"
    ],
    "Other Malicious": [
        "DeviceIoControl",
        "DllCanUnloadNow",
        "DllGetClassObject",
        "DllInstall",
        "DllRegisterServer",
        "DllUnregisterServer",
        "EnumDisplayDevicesA",
        "EnumDisplayDevicesW",
        "GetAdaptersInfo",
        "GetComputerNameA",
        "GetComputerNameW",
        "GetKeyboardType",
        "GetSystemMetrics",
        "OleInitialize",
        "OleUninitialize",
        "SHGetFolderPathA",
        "SHGetFolderPathW",
        "SHGetSpecialFolderLocation",
        "SystemParametersInfoA",
        "SystemParametersInfoW"
    ],
    "Obfuscation/Encryption": [
        "CryptBinaryToStringA",
        "CryptDecodeObjectEx",
        "CryptHashData",
        "CryptStringToBinaryA",
        "RtlComputeCrc32",
        "RtlDecompressBuffer",
        "CryptCreateHash",
        "CryptDestroyHash",
        "CryptDestroyKey",
        "CryptGetHashParam",
        "CryptReleaseContext",
        "GetComputerNameExA",
        "GetComputerNameExW",
        "GetVolumeInformationA",
        "GetVolumeInformationW"
    ]
}


def normalize_api_name(name):
    return name.strip().lower()


def find_matches(user_apis):
    matches = {}

    # Create a reverse lookup dictionary for faster searching
    api_to_category = {}
    for category, apis in MALAPI_DB.items():
        for api in apis:
            api_to_category[normalize_api_name(api)] = (category, api)

    # Check each user API
    for user_api in user_apis:
        normalized = normalize_api_name(user_api)

        if normalized in api_to_category:
            category, original_api = api_to_category[normalized]

            if category not in matches:
                matches[category] = []

            matches[category].append({
                'user_input': user_api.strip(),
                'matched_api': original_api
            })

    return matches


def print_matches(matches):
    if not matches:
        print("\n[*] No matches found against MalAPI database.")
        return

    print("\n" + "="*70)
    print("POTENTIALLY MALICIOUS API CALLS DETECTED")
    print("="*70)

    total_matches = sum(len(apis) for apis in matches.values())
    print(f"\nTotal matches: {total_matches}")
    print()

    for category in sorted(matches.keys()):
        print(f"\n[!] Category: {category}")
        print("-" * 70)

        for match in matches[category]:
            if match['user_input'].lower() == match['matched_api'].lower():
                print(f"  * {match['matched_api']}")
            else:
                print(f"  * {match['user_input']} -> {match['matched_api']}")

    print("\n" + "="*70)


def main():
    import sys

    print("="*70)
    print("IAT MATCHER - MalAPI Database Checker")
    print("="*70)
    print("\nThis tool compares Import Address Table (IAT) calls against")
    print("a database of potentially malicious Windows API functions.")
    print("\nPaste your list of API calls below (one per line).")
    print("Press Enter twice (empty line) when done, or Ctrl+C to exit.")
    print("-" * 70)

    user_apis = []

    try:
        while True:
            line = input()
            if not line.strip():
                if user_apis:  # If we have at least one API, break
                    break
                else:  # If empty list, continue waiting
                    continue

            # Handle multiple APIs on one line (comma or tab separated)
            if ',' in line:
                apis = [api.strip() for api in line.split(',')]
                user_apis.extend(apis)
            elif '\t' in line:
                apis = [api.strip() for api in line.split('\t')]
                user_apis.extend(apis)
            else:
                user_apis.append(line.strip())

    except (KeyboardInterrupt, EOFError):
        if not user_apis:
            print("\n\n[*] Cancelled by user.")
            return

    # Remove empty strings and duplicates while preserving order
    seen = set()
    user_apis = [api for api in user_apis if api and not (normalize_api_name(api) in seen or seen.add(normalize_api_name(api)))]

    if not user_apis:
        print("\n[!] No API calls provided.")
        return

    print(f"\n[*] Analyzing {len(user_apis)} API call(s)...")

    # Find matches
    matches = find_matches(user_apis)

    # Print results
    print_matches(matches)

    # Show unmatched APIs if any
    matched_apis = set()
    for category_matches in matches.values():
        for match in category_matches:
            matched_apis.add(normalize_api_name(match['user_input']))

    unmatched = [api for api in user_apis if normalize_api_name(api) not in matched_apis]

    if unmatched:
        print(f"\n[i] {len(unmatched)} API call(s) not found in MalAPI database:")
        for api in unmatched:
            print(f"  * {api}")


if __name__ == "__main__":
    main()

 Date: February 4, 2026
 Tags:  content reference ai security

Previous:
⏪ GenAI Code Security Is a Context Problem, Not a Tooling Problem: Notes from a Pacific Hackers Presentation