{
	"id": "b0148a67-56ec-4bf7-913d-bb1523f78e6b",
	"created_at": "2026-04-06T00:15:57.279881Z",
	"updated_at": "2026-04-12T02:21:56.47337Z",
	"deleted_at": null,
	"sha1_hash": "eab23627e86e7dcbc936ce022343f61a5568fd5f",
	"title": "Understanding PEB and LDR Structures using IDA and LummaStealer",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 760241,
	"plain_text": "Understanding PEB and LDR Structures using IDA and\r\nLummaStealer\r\nBy map[name:Alessandro Strino]\r\nPublished: 2024-02-04 · Archived: 2026-04-05 15:37:14 UTC\r\nIn this post I’m going to explain how Process Environment Block (PEB) is parsed by malware devs and how that\r\nstructure is abused. Instead of going too deep into a lot of details, I would like to follow an easier approach pairing\r\nthe theory with a practical real example using IDA and LummaStealer, without overwhelming the reader with a lot\r\nof technical details trying to simplify the data structure involved in the process. At the end of the theory part, I’m\r\ngoing to apply PEB and all related structures in IDA, inspecting malware parsing capabilities that are going to\r\nbe applied for resolving hashed APIs.\r\nLet’s start.\r\nPEB Structure\r\nThe PEB is a crucial data structure that contains various information about a running process. Unlike other\r\nWindows structure (e.g., EPROCESS, ETHREAD, etc..), it exists in the user address space and is available for\r\nevery process at a fixed address in memory (PEB can be found at fs:[0x30] in the Thread Environment Block\r\n(TEB) for x86 processes as well as at gs:[0x60] for x64 processes). Some of documented fields that it’s worth\r\nknowing are:\r\nBeingDebugged: Whether the process is being debugged;\r\nLdr: A pointer to a PEB_LDR_DATA structure providing information about loaded modules;\r\nProcessParameters: A pointer to a RTL_USER_PROCESS_PARAMETERS structure providing information about\r\nprocess startup parameters;\r\nPostProcessInitRoutine: A pointer to a callback function called after DLL initialization but before the main\r\nexecutable code is invoked\r\nImage Loader aka Ldr\r\nWhen a process is started on the system, the kernel creates a process object to represent it and performs various\r\nkernel-related initialization tasks. However, these tasks do not result in the execution of the application, but in the\r\npreparation of its context and environment. This work is performed by the image loader (Ldr).\r\nThe loader is responsible for several main tasks, including:\r\nParsing the import address table (IAT) of the application to look for all DLLs that it requires (and then\r\nrecursively parsing the IAT of each DLL), followed by parsing the export table of the DLLs to make sure\r\nthe function is actually present.\r\nhttps://viuleeenz.github.io/posts/2024/02/understanding-peb-and-ldr-structures-using-ida-and-lummastealer/\r\nPage 1 of 6\n\nLoading and unloading DLLs at runtime, as well as on demand, and maintaining a list of all loaded\r\nmodules (the module database).\r\nFigure 1: PEB, LDR_DATA and LDR_MODULE interactions\r\nAt first glance, these structures might seem a little bit confusing. However, let’s simplify them to make them more\r\nunderstandable. We could think about them as a list where the structure PEB_LDR_DATA is the head of the\r\nlist and each module information is accessed through a double linked list (InOrderLoaderModuleList in this\r\ncase) that points to LDR_MODULE.\r\nHow those structures are abused\r\nMost of the times when we see PEB and LDR_MODULE structure parsing we are dealing with malwares that\r\nare potentially using API Hashing technique. Shellcode will typically walk through those structures in order to\r\nfind the base address of loaded dlls and extract all their exported functions, collecting names and pointers to the\r\nfunctions that are intended to call, avoiding to leave direct reference of them within the malware file.\r\nThis is a simple trick that tries to evade some basic protections mechanism that could arise when we see clear\r\nreferences to malware-related functions such as: VirtualAlloc, VirtualProtect, CreateProcessInterW,\r\nResumeThread, etc…\r\nAPI Hashing\r\nBy employing API hashing, malware creators can ensure that specific Windows APIs remain hidden from casual\r\nobservation. Through this approach, malware developers try to add an extra layer of complexity by concealing\r\nsuspicious Windows API calls within the Import Address Table (IAT) of PE.\r\nAPI hashing technique is pretty straightforward and it could be divided in three main steps:\r\n1. Malware developers prepare a set of hashes corresponding to WINAPI functions.\r\n2. When an API needs to be called, it looks for loaded modules through the PEB.Ldr structure.\r\n3. Then, when a module is find, it goes through all the functions performing the hash function until the result\r\nmatches with the given input.\r\nhttps://viuleeenz.github.io/posts/2024/02/understanding-peb-and-ldr-structures-using-ida-and-lummastealer/\r\nPage 2 of 6\n\nFigure 2: API Hashing Overview\r\nNow that we have a more understanding of the basic concepts related to API hashing, PEB and Ldr structures,\r\nlet’s try to put them in practice using LummaStealer as an example.\r\nParsing PEB and LDR with LummaStealer\r\nOpening up the sample in IDA and scrolling a little after the main function it is possible to bump into very\r\ninteresting functions that perform some actions on a couple of parameters that are quite interesting and correlated\r\nto explanation so far.\r\nFigure 3: Wrapper function for hash resolving routine in LummaStealer\r\nBefore function call sub_4082D3 (highlighted) we could see some mov operation of two values:\r\nThose parameters are quite interesting because:\r\nThe former represents an interesting dll that contains some useful functions such as LoadLibrary,\r\nVirtualAlloc, etc..\r\nThe latter appears to be a hash (maybe correlated to the previous string).\r\nhttps://viuleeenz.github.io/posts/2024/02/understanding-peb-and-ldr-structures-using-ida-and-lummastealer/\r\nPage 3 of 6\n\nIf we would like to make an educated guess, it is possible that this function is going to find a function (within\r\nkernel32.dll) whose hash corresponds to the input hash. However, let’s try to understand if and how those\r\nparameters are manipulated in the function call, validating also our idea.\r\nFigure 4: Parsing PEB and LDR_MODULE for API hash routine.\r\nThrough Figure 6, you can see the exact same code, before (left side) and after (right side) renaming structures.\r\nExamining the code a little bit we should be able to recall the concepts already explained in the previous sections.\r\nLet’s examine the first block of code. Starting from the top of the code we could spot the instruction mov eax,\r\n(large)fs:30h that is going to collect the PEB pointer, storing its value in eax. Then, right after this instruction\r\nwe could see eax used with an offset(0xC). In order to understand what is going on, its possible to collect the\r\nPEB structure and look for the 0xC offset. Doing that, it’s clear that eax is going to collect the Ldr pointer. The\r\nlast instruction of the first block is mov edi, [eax+10h] . This is a crucial instruction that needs a dedicated\r\nexplanation:\r\nhttps://viuleeenz.github.io/posts/2024/02/understanding-peb-and-ldr-structures-using-ida-and-lummastealer/\r\nPage 4 of 6\n\nIf you are going to look at PEB_LDR_DATA you will see that 0x10 offset (for x64 bit architecture) points to\r\nInLoadOrderModuleList (that contains, according to its description, pointers to previous and next\r\nLDR_MODULE in initialization order ). Through this instruction, malware is going to take a LDR_MODULE\r\nstructure (as explained in Figure 3), settling all the requirements to parse it.\r\nWithout going too deep in the code containing the loop (this could be left as an exercise), it is possible to see that\r\nthe next three blocks are going to find the kernel32.dll iterating over the LDR_MODULE structure parameters.\r\nAt the very end of the code, we could see the last block calling a function using the dll pointers retrieved through\r\nthe loop, using another hash value. This behavior give us another chance for a couple of insight:\r\nThis code is a candidate to settle all parameters that are going to be used for API hash resolving routine (as\r\nillustrated in the API Hashing section), since that its output will be used as a function call.\r\nThe string kernel32.dll gave us some hints about possible candidate functions (e.g., LoadLibraryA,\r\nVirtualAlloc, etc..).\r\nWith this last consideration, it’s time to conclude this post avoiding adding more layers of complexity, losing our\r\nfocus on PEB and related structures.\r\nFunction recap\r\nBefore concluding, let’s try to sum up, what we have seen so far, in order to make the analysis even more clear:\r\n1. The function 4082D3 takes two parameters that are a hash value and a string containing a dll library.\r\n2. Iterating over the loaded modules, it looks for the module name containing the hardcoded kernel32.dll.\r\n3. Once the module is found, it invokes another function ( 40832A ), passing a pointer to the base address of\r\nthe module and a hash value.\r\n4. The function returns a pointer to a function that takes as an argument the dll name passed to 4082D3. This\r\nbehavior suggests that some sort of LoadLibrary has been resolved on point 3.\r\n5. As a final step, the function 40832A is called once again, using the hash value passed as a parameter in the\r\nfunction 4082D3 and a base address retrieved from the point 4.\r\nFollowing all the steps it’s easy to spot that the 40832A function is the actual API hash resolving routine and the\r\nfunction 4082D3 has been used to settle all the required variables.\r\nConclusion\r\nThrough this blog post I tried to explain a little bit better how the PEB and related structures are parsed and\r\nabused by malwares. However, I also tried to show how malware analysis could be carried out examining the code\r\nand renaming structures accordingly. This brief introduction will be also used as a starting point for the next\r\narticle where I would like to take the same sample and emulate the API hashing routine in order to resolve all\r\nhashes, making this sample ready to be analyzed.\r\nNote about simplification\r\nhttps://viuleeenz.github.io/posts/2024/02/understanding-peb-and-ldr-structures-using-ida-and-lummastealer/\r\nPage 5 of 6\n\nIt’s worth mentioning that to make those steps easier, there has been a simplification. In fact, PEB_LDR_DATA\r\ncontains three different structures that could be used to navigate modules, but for this blogpost, their use could be\r\nignored. Another structure that is worth mentioning it’s LDR_DATA_TABLE_ENTRY that could be considered a\r\ncorresponding to the LDR_MODULE structure.\r\nReferences:\r\nUndocumented Windows Internal Structures:\r\nThe Undocumented Functions\r\nPEB_LDR_DATA\r\nLummaSteler sample:\r\nMalwareBazaar\r\nSource: https://viuleeenz.github.io/posts/2024/02/understanding-peb-and-ldr-structures-using-ida-and-lummastealer/\r\nhttps://viuleeenz.github.io/posts/2024/02/understanding-peb-and-ldr-structures-using-ida-and-lummastealer/\r\nPage 6 of 6",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://viuleeenz.github.io/posts/2024/02/understanding-peb-and-ldr-structures-using-ida-and-lummastealer/"
	],
	"report_names": [
		"understanding-peb-and-ldr-structures-using-ida-and-lummastealer"
	],
	"threat_actors": [],
	"ts_created_at": 1775434557,
	"ts_updated_at": 1775960516,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/eab23627e86e7dcbc936ce022343f61a5568fd5f.pdf",
		"text": "https://archive.orkl.eu/eab23627e86e7dcbc936ce022343f61a5568fd5f.txt",
		"img": "https://archive.orkl.eu/eab23627e86e7dcbc936ce022343f61a5568fd5f.jpg"
	}
}