{
	"id": "88dc70df-5fda-4967-9b34-321e74f99416",
	"created_at": "2026-04-06T00:21:13.47666Z",
	"updated_at": "2026-04-10T03:23:52.224554Z",
	"deleted_at": null,
	"sha1_hash": "7cc4f50a2feddaa59d18e5a8d7f338f4dcbae15e",
	"title": "API-Hashing in the Sodinokibi/REvil Ransomware – Why and How? – nullteilerfrei",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 144792,
	"plain_text": "API-Hashing in the Sodinokibi/REvil Ransomware – Why and\r\nHow? – nullteilerfrei\r\nBy born\r\nPublished: 2019-11-09 · Archived: 2026-04-05 13:59:01 UTC\r\nThis post is written for aspiring reverse engineers and will talk about a technique called API hashing. The\r\ntechnique is used by malware authors to hinder reverse engineering. We will first discuss the reasons a malware\r\nauthor may even consider using API hashing. Then we will cover the necessary technical details around resolving\r\ndynamic imports at load-time and at runtime and finally, will described API hashing and show a Python script that\r\nemulates the hashing method used in Sodinokibi/REvil ransomware.\r\nTop Down vs. Bottom Up\r\nOne way to think about different approaches of reverse engineering is Top Down vs. Bottom Up. Top Down means\r\nthat you start with the entry point(s) of a binary and follow the execution flow. Bottom Up means that you start at\r\nan interesting location of the binary and try to understand, if and how the execution flow will reach it. Such a\r\nlocation may for example be an interesting string embedded in the binary ( http://example.com or s3cr3t for\r\nexample). So-called imports are also a good entry point for a Bottom Up analysis.\r\nOne strength of the Bottom Up approach is, that you can focus your analysis efforts with very little understanding\r\nof the malware or - to phrase it differently - at a very early point in time: You want to know where the juice crypto\r\nstuff is happening? Look for calls to the Windows API function BCryptOpenAlgorithmProvider . Want to know,\r\nwhere the payload, later dropped to %TEMP%\\evil.exe , comes from? Look for reference to the string \\evil.exe .\r\nSince this approach is so powerful, malware authors are highly motivated to make it harder for the reverse\r\nengineer to use it. One of these counter-measures is called \"dynamic API resolutions with name hashing\" and\r\ndescribed in this blog post.\r\nAPI Resolution at Load-Time\r\nUnder Windows, imports are listed in the import address table (IAT) of the Portable Executable (PE) file. The\r\ntable references functions that can be used during execution but are located in an external module. If a program\r\nwants to call PathCanonicalize in SHLWAPI.dll for example, the IAT of your executable contains an entry\r\nreferencing that function. Say, you are looking for the command \u0026 control (C2) server of the malware, then you\r\nmay want to look at the the functions referencing WININET.dll and find the InternetConnect entry. A string\r\ncontaining the C2 may be referenced in the the vacinity of calls to this function.\r\nAPI Resolution at Runtime\r\nAn obvious counter-measure against this reverse engineering approach is, to not use the function pointer from the\r\nIAT but resolve it at runtime. For this, the Windows API offers the functions LoadLibrary and GetProcAddress .\r\nhttps://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nPage 1 of 12\n\nThe LoadLibrary function accepts the name of a DLL and returns a handle to it. This handle can then be passed\r\nto GetProcAddress together with a function name to get a pointer to the corresponding function:\r\n#include \u003cstdio.h\u003e\r\n#include \u003cwindows.h\u003e\r\ntypedef BOOL(*funcType)(LPSTR, LPCSTR);\r\nint main() {\r\n char buf[MAX_PATH];\r\n HMODULE hModule = LoadLibrary(\"SHLWAPI.dll\");\r\n funcType PathCanonicalizeA = (funcType)GetProcAddress(hModule, \"PathCanonicalizeA\");\r\n if (! PathCanonicalizeA(buf, \"A:\\\\path\\\\.\\\\somewhere\\\\..\\\\file.dat\")) {\r\n return 1;\r\n }\r\n printf(\"%s\", buf);\r\n FreeLibrary(hModule);\r\n return 0;\r\n}\r\nLooking at references to PathCanonicalizeA in the IAT will not lead to the call seen in the above code listing,\r\ndefeating the technique described in Static Imports. For convenience reasons, one can now store the addresses\r\nretrieved by GetProcAddress in global variables: If these global variables are named like the actual API\r\nfunctions, one wouldn't even need to change old code.\r\nSwitching back to the perspective of a reverse engineer, one can look for the string PathCanonicalizeA now. It\r\nneeds to be passed to the GetProcAddress function at some point. This, of course, can then be defeated by\r\nobfuscating the strings in the binary. But we will explore a different avenue in this post: The below-described\r\nmethod avoids inclusion of function names altogether. Instead, only a fix-sized hash of the function name will be\r\npresent which has the additional benefit of saving on storage space. This is especially interesting when developing\r\nshellcode, where size restrictions may be tough and real.\r\nAPI Hashing\r\nLet's again put ourselfs into the shoes of a malware author and let us further assume, we have a list of all exported\r\nfunction names for a given DLL. We can now calculate a hash for each function name. The hash doesn't need be\r\ncryptographically secure or even evenly distributed on the codomain. It only needs to be relatively collision-free\r\non the set of all exported function names. It may become clearer soon, what that means exactly.\r\nThe following hashing function - which is used in a recent sample of the Sodinokibi/REvil ransomware -\r\ninitializes a state with 0x2b and then uses each character of the function name as argument for an arithmetic\r\noperation. It performs an arbitrary arithmetic operation in the end and finally returns the resulting state.\r\n#!/usr/bin/env python3\r\ndef calc_hash(function_name):\r\nhttps://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nPage 2 of 12\n\nret = 0x2b\r\n for c in function_name:\r\n ret = ret * 0x10f + ord(c)\r\n return (ret ^ 0xafb9) \u0026 0x1fffff\r\nThe table below lists the resulting hash for a few functions exported from WINHTTP.dll . One already may notice\r\nthat all the hashes are different. In fact, they are pairwise-different for all functions exported from WINHTTP.dll ,\r\nso if we would only consider exports of that DLL and only want to use these eleven functions, the hash can be\r\nused to uniquly identify the API function.\r\nAPI function name Hash\r\nWinHttpSendRequest 0x1ce2a7\r\nWinHttpSetOption 0x1a5e67\r\nWinHttpReadData 0x064450\r\nWinHttpCloseHandle 0x0fd1fe\r\nWinHttpOpen 0x0a459a\r\nWinHttpQueryDataAvailable 0x0b8299\r\nWinHttpReceiveResponse 0x128d32\r\nWinHttpConnect 0x105ed8\r\nWinHttpQueryHeaders 0x09d98e\r\nWinHttpOpenRequest 0x066635\r\nWinHttpCrackUrl 0x0c17e7\r\nYou might have guessed, what the overall goal now is: somehow get a list of all API function names together with\r\ntheir addresses, calculate the hashes for each of their names and only use the hash when referring to them. This\r\nway, we would avoid inclusion of any string - obfuscated or not - that refers to the API function by name. This\r\nworks as long as for each function we want to use, the hashing method is collision-free over all considered\r\nfunction names.\r\nRetrieving API function names\r\nBut how do we get a list of all exported functions, potentially for all kinds of DLLs? As already described in the\r\nStatic Imports section, every Portable Executable (PE) file, including DLLs, contain a table with all exported\r\nfunctions. And since all the data from a PE file is loaded into memory, these table is present in memory too. To be\r\nprecise, the table is the first entry of the DataDirectory array of the Optional Header of the PE. The Optional\r\nHeader is a structure of type IMAGE_OPTIONAL_HEADER and part of another structure called PE Header, which is\r\nlocated at offset 0x18 . The PE Header is of type IMAGE_NT_HEADERS and can be found when following the\r\nhttps://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nPage 3 of 12\n\naddress at the very end of the DOS Header (that is at offset 0x3c ). The DOS Header is of type\r\nIMAGE_DOS_HEADER . To summarize this rambling the other way around:\r\nThe DOS Header is located at the beginning of a PE file.\r\nThe PE Header is referenced at position 0x3c of the DOS header.\r\nThe Optional Header is part of the PE header at offset 0x18 .\r\nAll Data Directories are stored at offset 0x60 of the Optional Header.\r\nThe Export Directory is the first Data Directory.\r\nIf you prefere code over words, the following pseudo-C-snippet shows, how to retrieve the export directory\r\nstarting from a pointer to a PE in memory:\r\nIMAGE_DOS_HEADER pe_file = get_ptr_to_memory_containing_pe();\r\nIMAGE_NT_HEADERS pe_header = *(pe_file + 0x3c);\r\nIMAGE_OPTIONAL_HEADER optional_header = pe_header + 0x18;\r\nDATA_DIRECTORY data_directories[16] = optional_header + 0x60;\r\nIMAGE_EXPORT_DIRECTORY export_directory = data_directories[0];\r\n#lifehack: offsets 0x3c and 0x78 (which is the sum of the two offsets 0x18 + 0x60 from the listing above)\r\nappearing in assembly or decompiled code indicate that PE parsing is going on with the goal to retrieve exports\r\nfrom a PE file.\r\nListing exports of DLLs\r\nLet's get our hands dirty: In this section, we will briefly explain, how to easily collect API function names from\r\nWindows DLLs. This is useful independently of the malware family one analysis. In the section after, we will look\r\nat a concret sample of the Sodinokibi/REvil ransomware. With the help of a Python script, we will calculate API\r\nhashes for the collected function names and compare the resulting hashes with the content of a buffer embedded in\r\nthe malware.\r\nThe following Python script accepts a list of DLLs on the command line. Each line of its output is the name of the\r\nDLL followed by a space and the name of an export of the DLL.\r\n#!/usr/bin/env python3\r\nimport sys\r\nimport pefile\r\nimport glob\r\nfor arg in sys.argv[1:]:\r\n for file_name in glob.glob(arg):\r\n try:\r\n with open(file_name, 'rb') as fp:\r\n pe = pefile.PE(data=fp.read())\r\n except pefile.PEFormatError:\r\n continue\r\nhttps://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nPage 4 of 12\n\nif not hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'):\r\n continue\r\n export = pe.DIRECTORY_ENTRY_EXPORT\r\n dll_name = pe.get_string_at_rva(export.struct.Name)\r\n if not dll_name:\r\n continue\r\n if len(export.symbols) == 0:\r\n continue\r\n for pe_export in export.symbols:\r\n if not pe_export.name:\r\n continue\r\n print(dll_name.decode('utf-8'), pe_export.name.decode('utf-8'))\r\nYou can use the script to accumulate exports for as many DLLs as possible. We will collect the result in a file\r\ncalled exports.txt .\r\nAPI Hashing in Sodinokibi/REvil Ransomware\r\nLet's consider the sample with SHA-256 hash\r\n5f56d5748940e4039053f85978074bde16d64bd5ba97f6f0026ba8172cb29e93 . It belongs to the Sodinokibi/REvil\r\nransomware family and contains a build timestamp of 2019-06-10 15:29:32. Reverse engineering with Ghidra\r\nyields the buffer shown below in hex-encoded format.\r\nae91d3b60313b7aca7e29c9ff53a4b505bf85fc37c42ad39da426c2851aa6caeaad50b9f84573f3d142cbcc1c2dbbffc1a8fa8e55e0acfa\r\nRight after startup, the malware interprets it as an array of length 0x230 storing a DWORD in each entry. Each\r\nDWORD corresponds to an API function and Sodinokibi/REvil uses API hashing to resolve the corresponding\r\naddresses. The Python script listed in the Appendix emulates the behaviour of the malware: it reads all exports\r\nfrom exports.txt , calculate all hashes for all exports, and list each DWORD of the buffer stored in buffer.bin\r\ntogether with their API function name:\r\nDLL Name API Hash Function Name\r\nADVAPI32.dll 0x2b7d106b CheckTokenMembership\r\nADVAPI32.dll 0x40b57bbe FreeSid\r\nADVAPI32.dll 0x431d781e IsValidSid\r\nADVAPI32.dll 0x43e878e7 GetTokenInformation\r\nADVAPI32.dll 0x45357e2f GetUserNameW\r\nADVAPI32.dll 0x49d572d6 OpenProcessToken\r\nhttps://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nPage 5 of 12\n\nDLL Name API Hash Function Name\r\nADVAPI32.dll 0x5b2a6022 CryptAcquireContextW\r\nADVAPI32.dll 0x8012bb13 ImpersonateLoggedOnUser\r\nADVAPI32.dll 0x8c2cb729 RegCloseKey\r\nADVAPI32.dll 0x8f6ab47f RevertToSelf\r\nADVAPI32.dll 0x9c12a701 RegOpenKeyExW\r\nADVAPI32.dll 0x9d3ca625 RegSetValueExW\r\nADVAPI32.dll 0xc35ff85b RegQueryValueExW\r\nADVAPI32.dll 0xd48aef93 RegCreateKeyExW\r\nADVAPI32.dll 0xda1fe106 AllocateAndInitializeSid\r\nADVAPI32.dll 0xe46bdf69 CryptGenRandom\r\ncombase.dll 0x39ad427c CreateStreamOnHGlobal\r\nCRTDLL.dll 0x958c2a38 _snwprintf\r\nCRYPT32.dll 0x2dc78a5e CryptStringToBinaryW\r\nCRYPT32.dll 0xadcf0a5e CryptBinaryToStringW\r\nGDI32.dll 0x3371decc DeleteObject\r\nGDI32.dll 0x346dd9cc DeleteDC\r\nGDI32.dll 0x4bc6a666 SetTextColor\r\nGDI32.dll 0x5829b59a SetBkColor\r\nGDI32.dll 0x6e5983e6 SetPixel\r\nGDI32.dll 0x842f699b GetDeviceCaps\r\nGDI32.dll 0x8c936138 CreateFontW\r\nGDI32.dll 0xab3e468f GetDIBits\r\nGDI32.dll 0xc1bc2c14 GetObjectW\r\nGDI32.dll 0xd0803d2a SetBkMode\r\nGDI32.dll 0xeb6406c3 CreateCompatibleBitmap\r\nGDI32.dll 0xec0601b3 GetStockObject\r\nhttps://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nPage 6 of 12\n\nDLL Name API Hash Function Name\r\nGDI32.dll 0xedc4007f SelectObject\r\nGDI32.dll 0xf7bc1a03 CreateCompatibleDC\r\nKERNEL32.dll 0x08c76270 MapViewOfFile\r\nKERNEL32.dll 0x0924639c DeleteFileW\r\nKERNEL32.dll 0x0b6061c9 WaitForSingleObject\r\nKERNEL32.dll 0x0f5c65e6 GetNativeSystemInfo\r\nKERNEL32.dll 0x13dbba0b timeBeginPeriod\r\nKERNEL32.dll 0x15fc7f40 GetFileAttributesW\r\nKERNEL32.dll 0x1b4f71f8 Process32NextW\r\nKERNEL32.dll 0x1ce07649 GetVolumeInformationW\r\nKERNEL32.dll 0x286c42da CreateToolhelp32Snapshot\r\nKERNEL32.dll 0x2f324589 UnmapViewOfFile\r\nKERNEL32.dll 0x2ff4455d FindClose\r\nKERNEL32.dll 0x32bf580a GetCommandLineW\r\nKERNEL32.dll 0x35195fa1 GetFileSize\r\nKERNEL32.dll 0x3c89563a HeapCreate\r\nKERNEL32.dll 0x3d3f5784 OpenMutexW\r\nKERNEL32.dll 0x40d32a7d SetErrorMode\r\nKERNEL32.dll 0x427728cd FindNextFileW\r\nKERNEL32.dll 0x43f52945 CreateFileMappingW\r\nKERNEL32.dll 0x490d23af ExitProcess\r\nKERNEL32.dll 0x4d1e27a2 SystemTimeToFileTime\r\nKERNEL32.dll 0x4f3d2599 WriteFile\r\nKERNEL32.dll 0x504b3af5 PostQueuedCompletionStatus\r\nKERNEL32.dll 0x50733aca CompareFileTime\r\nKERNEL32.dll 0x588e3220 GetModuleFileNameW\r\nhttps://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nPage 7 of 12\n\nDLL Name API Hash Function Name\r\nKERNEL32.dll 0x5c6436d6 CreateMutexW\r\nKERNEL32.dll 0x5fcd3573 OpenProcess\r\nKERNEL32.dll 0x66d30c7b GetDiskFreeSpaceExW\r\nKERNEL32.dll 0x6a0800be GetUserDefaultUILanguage\r\nKERNEL32.dll 0x719e1b29 GetProcessHeap\r\nKERNEL32.dll 0x7f5b15e7 GetDriveTypeW\r\nKERNEL32.dll 0x8aabe016 FindFirstFileW\r\nKERNEL32.dll 0x8cabe614 SetFileAttributesW\r\nKERNEL32.dll 0x8cdbe673 MultiByteToWideChar\r\nKERNEL32.dll 0x90c6fa75 Sleep\r\nKERNEL32.dll 0x91f2fb5a ReleaseMutex\r\nKERNEL32.dll 0x93b3f91f GetComputerNameW\r\nKERNEL32.dll 0x9763fdd3 Process32FirstW\r\nKERNEL32.dll 0x9a9bf02c LocalAlloc\r\nKERNEL32.dll 0x9ad6f07d CreateFileW\r\nKERNEL32.dll 0x9c60f6ca GetSystemDefaultUILanguage\r\nKERNEL32.dll 0x9e07f4be GlobalAlloc\r\nKERNEL32.dll 0xa185cb2c CloseHandle\r\nKERNEL32.dll 0xa468cec4 SetFilePointerEx\r\nKERNEL32.dll 0xaf7fc5dd GetSystemDirectoryW\r\nKERNEL32.dll 0xb0b6da10 TerminateProcess\r\nKERNEL32.dll 0xb780dd38 GetCurrentProcess\r\nKERNEL32.dll 0xbf26d591 Wow64RevertWow64FsRedirection\r\nKERNEL32.dll 0xc1ddab7a GetProcAddress\r\nKERNEL32.dll 0xc610aca5 GetQueuedCompletionStatus\r\nKERNEL32.dll 0xc97fa3d5 LocalFree\r\nhttps://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nPage 8 of 12\n\nDLL Name API Hash Function Name\r\nKERNEL32.dll 0xca02a0b5 GetCurrentProcessId\r\nKERNEL32.dll 0xca0863c2 timeGetTime\r\nKERNEL32.dll 0xcb37a181 MulDiv\r\nKERNEL32.dll 0xcbe9a151 Wow64DisableWow64FsRedirection\r\nKERNEL32.dll 0xcc3aa698 CreateThread\r\nKERNEL32.dll 0xcc49a6fa GetTempPathW\r\nKERNEL32.dll 0xd0cdba63 GlobalFree\r\nKERNEL32.dll 0xdb88b122 GetFileSizeEx\r\nKERNEL32.dll 0xdd54b7ec VirtualAlloc\r\nKERNEL32.dll 0xe2e48854 ReadFile\r\nKERNEL32.dll 0xe36b89db WideCharToMultiByte\r\nKERNEL32.dll 0xe5a88f1a HeapDestroy\r\nKERNEL32.dll 0xe6c88c71 GetSystemInfo\r\nKERNEL32.dll 0xe96d83df GetFileAttributesExW\r\nKERNEL32.dll 0xeb7281db GetWindowsDirectoryW\r\nKERNEL32.dll 0xee8d8436 MoveFileW\r\nKERNEL32.dll 0xf1989b33 CreateIoCompletionPort\r\nKERNELBASE.dll 0x380572b8 PathFindExtensionW\r\nMPR.dll 0x7518713e WNetEnumResourceW\r\nMPR.dll 0xae6caa51 WNetCloseEnum\r\nMPR.dll 0xc258c662 WNetOpenEnumW\r\nntdll.dll 0x3822879e RtlFreeHeap\r\nntdll.dll 0x7697c934 RtlTimeToTimeFields\r\nntdll.dll 0x8fe93045 RtlDeleteCriticalSection\r\nntdll.dll 0x95e62a4e NtOpenFile\r\nntdll.dll 0xacb71303 RtlGetLastWin32Error\r\nhttps://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nPage 9 of 12\n\nDLL Name API Hash Function Name\r\nntdll.dll 0xb86307ce RtlInitializeCriticalSection\r\nntdll.dll 0xc09d7f3e NtClose\r\nntdll.dll 0xc97676c4 RtlEnterCriticalSection\r\nntdll.dll 0xd2ae6d17 RtlLeaveCriticalSection\r\nntdll.dll 0xd69d6931 RtlAllocateHeap\r\nntdll.dll 0xe4135ba8 NtQueryInformationFile\r\nntdll.dll 0xfac34566 RtlInitUnicodeString\r\nrtm.dll 0x6acc17e7 EnumOverTable\r\nSHCORE.dll 0x2b1ca591 CommandLineToArgvW\r\nSHCORE.dll 0x64472ee8 SHDeleteValueW\r\nSHCORE.dll 0x9f0bd5aa SHDeleteKeyW\r\nSHELL32.dll 0x9cbc123d ShellExecuteExW\r\nUSER32.dll 0x368a11e7 GetForegroundWindow\r\nUSER32.dll 0x9359b433 GetDC\r\nUSER32.dll 0xb6d391ae wsprintfW\r\nUSER32.dll 0xbda29ac3 GetKeyboardLayoutList\r\nUSER32.dll 0xd228f54c SystemParametersInfoW\r\nUSER32.dll 0xec21cb5b FillRect\r\nUSER32.dll 0xfb28dc52 DrawTextW\r\nUSER32.dll 0xfcbfdbc2 ReleaseDC\r\nWINHTTP.dll 0x1b146635 WinHttpOpenRequest\r\nWINHTTP.dll 0x235a5e67 WinHttpSetOption\r\nWINHTTP.dll 0x23ef5ed8 WinHttpConnect\r\nWINHTTP.dll 0x38b7459a WinHttpOpen\r\nWINHTTP.dll 0x39714450 WinHttpReadData\r\nWINHTTP.dll 0x9f9ce2a7 WinHttpSendRequest\r\nhttps://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nPage 10 of 12\n\nDLL Name API Hash Function Name\r\nWINHTTP.dll 0xa4a0d98e WinHttpQueryHeaders\r\nWINHTTP.dll 0xacd6d1fe WinHttpCloseHandle\r\nWINHTTP.dll 0xf0078d32 WinHttpReceiveResponse\r\nWINHTTP.dll 0xffb58299 WinHttpQueryDataAvailable\r\nThis data can now be used to deduce which functions are called and enable a Buttom Up approach again. Looking\r\nat the only references to WinHttpConnect for example will probably lead to a C2 server.\r\nAppendix\r\n#!/usr/bin/env python3.7\r\nfrom revil import calc_hash\r\ndef chunks(l, n):\r\n for i in range(0, len(l), n):\r\n yield l[i:i + n]\r\ndef main():\r\n exports = []\r\n with open('exports.txt', 'r') as fp:\r\n for line in fp:\r\n sp = line.strip().split(' ')\r\n if len(sp) != 2:\r\n continue\r\n exports.append(sp)\r\n with open('buffer.bin', 'rb') as fp:\r\n hash_buffer = fp.read()\r\n resolutions = {}\r\n for chunk in chunks(hash_buffer, 4):\r\n api_hash = int.from_bytes(chunk, byteorder='little')\r\n for dll_name, export_name in exports:\r\n calculated_hash = calc_hash(export_name)\r\n if calculated_hash == ( ( api_hash ^ 0x76c7 ) \u003c\u003c 0x10 ^ api_hash ) \u0026 0x1fffff:\r\n if dll_name not in resolutions.keys():\r\n resolutions[dll_name] = []\r\n resolutions[dll_name].append((api_hash, export_name))\r\n break\r\nhttps://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nPage 11 of 12\n\nfor dll_name, pairs in resolutions.items():\r\n for api_hash, export_name in pairs:\r\n print(F'{dll_name}\\t0x{api_hash:08x}\\t{export_name}')\r\nif __name__ == '__main__':\r\n main()\r\nUpdate (2019-11-22): No mention of the term \"static import\" now because it doesn't make sense. Instead of\r\n\"dynamic\" vs. \"static\" imports, the post now talks about imports resolved at \"load-time\" vs. those resolved at\r\n\"runtime\".\r\nSource: https://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nhttps://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/\r\nPage 12 of 12\n\nKERNEL32.dll KERNEL32.dll 0x13dbba0b 0x15fc7f40 timeBeginPeriod GetFileAttributesW\nKERNEL32.dll 0x1b4f71f8 Process32NextW\nKERNEL32.dll 0x1ce07649 GetVolumeInformationW\nKERNEL32.dll 0x286c42da CreateToolhelp32Snapshot\nKERNEL32.dll 0x2f324589 UnmapViewOfFile\nKERNEL32.dll 0x2ff4455d FindClose\nKERNEL32.dll 0x32bf580a GetCommandLineW\nKERNEL32.dll 0x35195fa1 GetFileSize\nKERNEL32.dll 0x3c89563a HeapCreate\nKERNEL32.dll 0x3d3f5784 OpenMutexW\nKERNEL32.dll 0x40d32a7d SetErrorMode\nKERNEL32.dll 0x427728cd FindNextFileW\nKERNEL32.dll 0x43f52945 CreateFileMappingW\nKERNEL32.dll 0x490d23af ExitProcess\nKERNEL32.dll 0x4d1e27a2 SystemTimeToFileTime\nKERNEL32.dll 0x4f3d2599 WriteFile\nKERNEL32.dll 0x504b3af5 PostQueuedCompletionStatus\nKERNEL32.dll 0x50733aca CompareFileTime\nKERNEL32.dll 0x588e3220 GetModuleFileNameW\n  Page 7 of 12\n\nKERNEL32.dll KERNEL32.dll 0x91f2fb5a 0x93b3f91f ReleaseMutex GetComputerNameW\nKERNEL32.dll 0x9763fdd3 Process32FirstW\nKERNEL32.dll 0x9a9bf02c LocalAlloc\nKERNEL32.dll 0x9ad6f07d CreateFileW\nKERNEL32.dll 0x9c60f6ca GetSystemDefaultUILanguage\nKERNEL32.dll 0x9e07f4be GlobalAlloc\nKERNEL32.dll 0xa185cb2c CloseHandle\nKERNEL32.dll 0xa468cec4 SetFilePointerEx\nKERNEL32.dll 0xaf7fc5dd GetSystemDirectoryW\nKERNEL32.dll 0xb0b6da10 TerminateProcess\nKERNEL32.dll 0xb780dd38 GetCurrentProcess\nKERNEL32.dll 0xbf26d591 Wow64RevertWow64FsRedirection\nKERNEL32.dll 0xc1ddab7a GetProcAddress\nKERNEL32.dll 0xc610aca5 GetQueuedCompletionStatus\nKERNEL32.dll 0xc97fa3d5 LocalFree\n  Page 8 of 12",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/"
	],
	"report_names": [
		"api-hashing-why-and-how"
	],
	"threat_actors": [
		{
			"id": "d90307b6-14a9-4d0b-9156-89e453d6eb13",
			"created_at": "2022-10-25T16:07:23.773944Z",
			"updated_at": "2026-04-10T02:00:04.746188Z",
			"deleted_at": null,
			"main_name": "Lead",
			"aliases": [
				"Casper",
				"TG-3279"
			],
			"source_name": "ETDA:Lead",
			"tools": [
				"Agentemis",
				"BleDoor",
				"Cobalt Strike",
				"CobaltStrike",
				"RbDoor",
				"RibDoor",
				"Winnti",
				"cobeacon"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434873,
	"ts_updated_at": 1775791432,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/7cc4f50a2feddaa59d18e5a8d7f338f4dcbae15e.pdf",
		"text": "https://archive.orkl.eu/7cc4f50a2feddaa59d18e5a8d7f338f4dcbae15e.txt",
		"img": "https://archive.orkl.eu/7cc4f50a2feddaa59d18e5a8d7f338f4dcbae15e.jpg"
	}
}