{
	"id": "5fb0ac65-9782-4909-991e-73e27538615d",
	"created_at": "2026-04-06T00:16:55.057399Z",
	"updated_at": "2026-04-10T03:21:58.281089Z",
	"deleted_at": null,
	"sha1_hash": "35e5639a4f2b8732945112ea6fc5f2370cfafb03",
	"title": "Understanding API Hashing and build a rainbow table for LummaStealer",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 991489,
	"plain_text": "Understanding API Hashing and build a rainbow table for\r\nLummaStealer\r\nBy map[name:Alessandro Strino]\r\nPublished: 2024-03-24 · Archived: 2026-04-05 13:08:21 UTC\r\nUnderstanding PEB and Ldr structures represents a starting point when we are dealing with API hashing.\r\nHowever, before proceeding to analyze a sample it’s always necessary to recover obfuscated, encrypted or hashed\r\ndata. Because of that, through this blogpost I would like to continue what I have started in the previous post, using\r\nemulation to create a rainbow table for LummaStealer and then write a little resolver script that is going to use the\r\ninformation extracted to resolve all hashes.\r\n💡It’s worth mentioning that I’m trying to create self-contained posts. Of course, previous information\r\nwill give a more comprehensive understanding of the whole process, however, the goal for this post is to\r\nhave a guide that could be applied overtime even on different samples not related to LummaStealer.\r\nResolving Hashes\r\nStarting from where we left in the last post, we could explore the function routine that is in charge of collecting\r\nfunction names from a DLL and then perform a hashing algorithm to find a match with the input name.\r\nhttps://viuleeenz.github.io/posts/2024/03/understanding-api-hashing-and-build-a-rainbow-table-for-lummastealer/\r\nPage 1 of 8\n\nFigure 1: LummaStealer API Hashing overview\r\nAt the first glance, this function could be disorienting, however, understanding that ecx contains the module\r\nBaseAddress (explained in the previous article) it is possible to set a comment that is going to make the whole\r\nfunction easier to understand. Moreover, it has been also divided in three main parts( first two are going to be\r\ndetailed in the next sections):\r\n1. Collecting exported function within a PE file;\r\n2. Hashing routine;\r\n3. Compare hashing result until a match is found, otherwise return 0; (skipped because of a simple comparing\r\nroutine)\r\nCollecting exported function within a PE file\r\nhttps://viuleeenz.github.io/posts/2024/03/understanding-api-hashing-and-build-a-rainbow-table-for-lummastealer/\r\nPage 2 of 8\n\nThe first box starts with the instruction mov edi, ecx where ecx is a BaseAddress of a module that is going to be\r\nanalyzed. This is a fundamental instruction that gives us a chance to infere the subsequent value of edi and ebx. In\r\nfact, if we rename values associated to these registers, it should be clear that this code is going to collect exported\r\nfunctions names through AddressOfNames and AddressOfNameOrdinals pointers.\r\nFigure 2: Resolving structures names\r\nThose structures are very important in order to understand what is happening in the code. For now, you could\r\nthink about those structures as support structures that could be chained together in order to collect the actual\r\nfunction pointer (after a match is found!) within the Address of a Function structure.\r\n💡 At the end of this article I created a dedicated sections to explain those structures and their\r\nconnections.\r\nAnother step that could misleading is related to the following instruction:\r\nwhere ebx becomes a pointer for IMAGE_EXPORT_DIRECTORY.\r\nIn order to explain this instruction its useful to have a look at IMAGE_OPTIONAL_HEADERS documentation,\r\nwhere Microsoft states that DataDirectory is pointer to a dedicated structure called\r\nIMAGE_DATA_DIRECTORY that could be addressed through a number.\r\nWith that information let’s do some math unveiling the magic behind this assignment.\r\neax corresponds to the IMAGE_NT_HEADERS (because of its previous assignment)\r\nFrom there we have a 0x78 offset to sum. If we sum the first 18 bytes from eax, it’s possible to jump to the\r\nIMAGE_OPTIONAL_HEADER. Using the 60 bytes remaining to reach the next field within this structure, we\r\ncould see that we are directly pointing to DataDirectory.\r\nFrom here, we don’t have additional bytes to sum, it means that we are pointing to the first structure pointed by\r\nDataDirectory, that is, according to the documentation the IMAGE_DIRECTORY_ENTRY_EXPORT also\r\nknown as Export Directory.\r\n💡 See Reference section to find out a more clear image about the whole PE structure\r\nRetrieve the function pointer\r\nhttps://viuleeenz.github.io/posts/2024/03/understanding-api-hashing-and-build-a-rainbow-table-for-lummastealer/\r\nPage 3 of 8\n\nOnce the code in charge to collect and compare exported functions has been completed, and a match is found, it’s\r\ntime to retrieve the actual function pointer using some of the structures mentioned above. In fact, as you can see\r\nfrom the code related to the third box (that has been renamed accordingly), once the match if found, the structure\r\nAddressOfNameOrdinals it’s used to retrieve the functions number that is going to address the structure\r\nAddressOfFunctions that contains the actual function pointers.\r\nFigure 3: Collect the actual function pointer\r\n💡I don’t want to bother you with so much details at this point, since we have already analyzed\r\nthroughly some structures and we still have additional contents to discuss. However, the image above\r\nhas been thought to be self-contained. That said, to not get lost please remember that edi represents the\r\nLdr_Module.BaseAddress\r\nAnalyze the hashing routine\r\nThrough the information collected so far, this code should be childishly simple.\r\necx contains the hash name extracted from the export table that is going to forward as input to the hash function\r\n(identified, in this case, as murmur2). The function itself is quite long but does not take too much time to be\r\nunderstood and reimplemented. However, the purpose of this article is to emulate this code in order to find out the\r\nvalues of all hardcoded hashes.\r\nhttps://viuleeenz.github.io/posts/2024/03/understanding-api-hashing-and-build-a-rainbow-table-for-lummastealer/\r\nPage 4 of 8\n\nFigure 4: MurMur2\r\nhashing routine\r\nAs we have already done, we could select the function opcodes (without the return instruction) and put them in\r\nour code emulator routine. It’s worth mentioning that, ecx contains the function name that is going to be used as\r\nargument for hashing routine, because of that, it’s important to assign that register properly.\r\nLet’s take a test. Using the LoadLibraryW name, we get back 0xab87776c. If we explore a little bit our code, we\r\nwill find almost immediately this value! it is called each time a new hash needs to be resolved.\r\nFigure 5: LoadLibraryW Hash\r\nThis behavior is a clear indication that before proceeding to extract exported functions, we need to load the\r\nassociated library (DLL) in memory. With that information we could be sure that our emulator works fine.\r\nhttps://viuleeenz.github.io/posts/2024/03/understanding-api-hashing-and-build-a-rainbow-table-for-lummastealer/\r\nPage 5 of 8\n\nBuild a rainbow table\r\nBuilding a rainbow table can be done in a few lines of code:\r\nThe code presented above should be pretty clear, however, I would like to point out the role of the filter variable.\r\nEmulation brings a lot of advantages to reverse engineering, nevertheless, it also has a drawback related to\r\nperformance. In fact, code that contains an emulation routine could be tremendously slow, and if you don’t pay\r\nattention it could take forever. Using a filter variable keeps our code more flexible, resolving tailored functions\r\nnames without wasting time.\r\n💡Of course, in this case we could look for libraries names used within the code. However, we could\r\nnot be so lucky in the future. Because of that, I prefer to show a way that could be used in multiple\r\nsituations.\r\nAutomation\r\nNow that we have built almost all fundamental components, it’s time to combine everything in a single and\r\neffective script file. What we are still missing is a regular expression that is going to look for hashes and forward\r\nthem to the MurMur2 emulator.\r\nObserving the code, an easy pattern to follow involves a register and an immediate values:\r\nImplementing this strategy and filtering results only on kernel32.dll, we are able to extract all referenced hashes:\r\nFigure 6: Some hashes related to Kernel32.dll\r\nConclusion\r\nAs always, going deep in each section requires an entire course and at the moment it’s an impossible challenge.\r\nHowever, through this blog post I tried to scratch the surface giving some essential concepts (that could be applied\r\nstraightaway) to make reversing time a lot more fun.\r\nAnother important thing to highlight here, is related to combine emulation and scripting techniques. Emulation is\r\ngreat, however, writing a script that contains some emulated routine could be a challenging task if we think about\r\nefficiency. Writing a single script for a single sample its not a big deal and it won’t have a great impact in a single\r\nanalysis, however, doing it a scale is a different kettle of fish.\r\nThat said, it’s time to conclude, otherwise, even reading this post could be a challenging task! :)\r\nhttps://viuleeenz.github.io/posts/2024/03/understanding-api-hashing-and-build-a-rainbow-table-for-lummastealer/\r\nPage 6 of 8\n\nHave fun and keep reversing!\r\nReferences:\r\nWindows PE file format:\r\nPE file overview\r\nLummaStealer Hash resolver:\r\nlummaStealer_v2_hash_resolver.py\r\nLummaSteler sample:\r\nMalwareBazaar\r\nBonus\r\nIn order to understand how API Hashing works it’s very useful to make your hand dirty on low level components.\r\nHowever, once you have some experience, it is also very helpful to have some tools that speed up your analysis.\r\nAn amazing project is HashDB maintained by OALabs. It is a simple and effective plugin for IDA and Binary\r\nNinja that is going to resolve hashes, if the routine is implemented. If you want to try out this plugin for this\r\nLummaStealer sample, my pull request has already been merged ;)\r\nAppendix 1 - AddressOfNames\r\nThe algorithm to retrieve the RVA associated to a function is quite straightforward:\r\n1. Iterate over the AddressOfNames structures.\r\n2. Once you find a match with a specific function, suppose at i position, the loader is going to use index i to\r\naddress the structure AddressOfNamesOrdinals.\r\n1. k = AddressOfNamesOrdinals[i]\r\n3. After collecting the value stored in AddressOfNamesOrdinals (2.a) we could use that value to address\r\nAddressOfFunctions, collecting the actual RVA of the function we were looking for.\r\n1. function_rva = AddressOfFunctions[k]\r\nhttps://viuleeenz.github.io/posts/2024/03/understanding-api-hashing-and-build-a-rainbow-table-for-lummastealer/\r\nPage 7 of 8\n\nFigure 7: How to retrieve functions names and pointers\r\n💡If you want to experiment a little bit more with this concept, I suggest to take the kernel32.dll library\r\nand follows this algorithm using PE-Bear\r\nSource: https://viuleeenz.github.io/posts/2024/03/understanding-api-hashing-and-build-a-rainbow-table-for-lummastealer/\r\nhttps://viuleeenz.github.io/posts/2024/03/understanding-api-hashing-and-build-a-rainbow-table-for-lummastealer/\r\nPage 8 of 8",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://viuleeenz.github.io/posts/2024/03/understanding-api-hashing-and-build-a-rainbow-table-for-lummastealer/"
	],
	"report_names": [
		"understanding-api-hashing-and-build-a-rainbow-table-for-lummastealer"
	],
	"threat_actors": [],
	"ts_created_at": 1775434615,
	"ts_updated_at": 1775791318,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/35e5639a4f2b8732945112ea6fc5f2370cfafb03.pdf",
		"text": "https://archive.orkl.eu/35e5639a4f2b8732945112ea6fc5f2370cfafb03.txt",
		"img": "https://archive.orkl.eu/35e5639a4f2b8732945112ea6fc5f2370cfafb03.jpg"
	}
}