{
	"id": "5962ccf1-0796-421c-a7d2-c80bee289935",
	"created_at": "2026-04-06T01:31:10.324314Z",
	"updated_at": "2026-04-10T03:21:42.884032Z",
	"deleted_at": null,
	"sha1_hash": "9ab59d42167eadb9ed93867b6a969aef64e19974",
	"title": "DRIDEX: Analysing API Obfuscation Through VEH | 0ffset",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 701066,
	"plain_text": "DRIDEX: Analysing API Obfuscation Through VEH | 0ffset\r\nBy Chuong Dong\r\nPublished: 2021-10-26 · Archived: 2026-04-06 00:32:25 UTC\r\nDRIDEX is one of the most famous and prevalent banking Trojans that dates back to around late 2014.\r\nThroughout its improvement and variations, DRIDEX has been successful in targeting the financial services sector\r\nto steal banking information and crucial user credentials. Typically, DRIDEX samples are delivered through\r\nphishing in the form of Word and Excel documents containing malicious VBA macros.\r\nIn this post particularly, we will dive into the theory behind DRIDEX’s anti-analysis method of obfuscating\r\nWindows API calls using string hashing and Vectored Exception Handling.\r\nTo follow along, you can grab the sample on MalwareBazaar!\r\nSha256: ad86dbdd7edd170f44aac99eebf972193535a9989b06dccc613d065a931222e7\r\nStep 1: API Resolving Function\r\nUpon performing some basic static analysis on the sample, we can quickly see that the DRIDEX DLL has two\r\nfunctions, OutputDebugStringA and Sleep, in its import address table. Considering how DRIDEX is a large\r\npiece of malware with many complex functionalities, the lack of imports hints to us that the malware resolves\r\nmost of its API dynamically.\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/\r\nPage 1 of 11\n\nWhen entering the DLL’s entry point, we can immediately see a function called with two hashes as parameters.\r\nThis function is called twice from the entry point function, both times with the same value for the first parameters.\r\nFor the second call, the return value is called as a function, so we know that sub_6815C0 must be dynamically\r\nresolving API through the hashes from its parameters. Furthermore, since both calls share the same value for the\r\nfirst parameter but different values for the second one, we can assume that the first hash corresponds to a library\r\nname, and the second one corresponds to the name of the target API in that library.\r\nWe can further examine sub_6815C0 to confirm this. The subroutine first starts with passing the DLL hash to the\r\nfunctions sub_686C50 and sub_687564. The return value and the API hash are then passed into sub_6867C8 as\r\nparameters. From this, we can assume the first two functions retrieve the base of the DLL corresponding to the\r\nDLL hash, and this base address is passed to the last function with the API hash to resolve the API.\r\nWhen diving into sub_687564, we can see that DRIDEX accesses the loader data table from the Process\r\nEnvironment Block (PEB), which contains a doubly linked list of loader data table entries. Each of these entries\r\ncontains information about a loaded library in memory, so by iterating through the table, the malware extracts the\r\nname of each library, converts it to lowercase, and finally hashes it with sub_69D620 and XORs it with\r\n0x38BA5C7B. Each library’s hash is compared against the target hash, and the base address of the target library is\r\nreturned if found. This confirms that sub_687564 retrieves the base of the DLL corresponding to the given DLL\r\nhash.\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/\r\nPage 2 of 11\n\nSimilarly, in sub_6867C8, DRIDEX uses the base address of the target library to access its export table and iterate\r\nthrough the list containing the address of exports’ names. Since API names are stored as UNICODE strings in the\r\nexport table, the malware converts each API’s name to ASCII and hashes it using the same hashing function\r\nsub_69D620. The target API hash is XOR-ed with 0x38BA5C7B before being compared to the hash of each API\r\nname. This confirms to us that sub_6867C8 dynamically retrieves an API from the target library using a given\r\nhash.\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/\r\nPage 3 of 11\n\nStep 2: Identifying API Hashing Algorithm\r\nAt this point, we know that sub_69D620 is the hashing algorithm, and the final hash is produced by XOR-ing the\r\nfunction’s return value with 0x38BA5C7B. The core functionality of this function contains SSE data transfer\r\ninstructions to deal with XMM registers.\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/\r\nPage 4 of 11\n\nTypically, it’s not worth the time to analyze the assembly instructions in these cryptographic functions. For most\r\ncases, we can depend on constant values being loaded or used in the program to pick out the correct algorithm,\r\nand tools like Mandiant’s capa are awesome in helping us automate this process. Unfortunately, capa fails to\r\nidentify this specific algorithm, so we have to analyze the constants on our own.\r\nFortunately, among the three constants being used in this function, one stands out with the repetition of the value\r\n0x0EDB8832, which is typically used in the CRC32 hashing algorithm. As a result, we can assume that\r\nsub_69D620 is a function to generate a CRC32 hash from a given string, and the API hashing algorithm of\r\nDRIDEX boils down to XOR-ing the CRC32 hash of API/DLL names with 0x38BA5C7B.\r\nTo quickly check if this hashing algorithm is correct, we can use OALabs’s hashdb plugin for IDA to test\r\nresolving the API resolved in the DLL’s entry point function. First, since DRIDEX’s hashes have an additional\r\nlayer of XOR, we must set 0x38BA5C7B as hashdb’s XOR key before looking the hashes up using CRC32.\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/\r\nPage 5 of 11\n\nFinally, we can use hashdb to look up the hashes in the sample. Here, we can see that the hash 0x1DAACBB7\r\ncorresponds correctly to the ExitProcess API, which confirms to us that our assumption about the hashing\r\nalgorithm is correct.\r\nStep 3: Vectored Exception Handler\r\nUnlike most malware, DRIDEX does not use the call instruction to call APIs. Instead, the malware uses a\r\ncombination of int3 and retn instructions to call its Windows APIs after dynamically resolving them.\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/\r\nPage 6 of 11\n\nThis feature is a great anti-analysis trick because it makes both static and dynamic analysis harder. Due to the retn\r\ninstruction, IDA treats every API call as the end of the parent function. This makes all instructions behind it\r\nunreachable and breaks up the control flow of the function’s decompiled code.\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/\r\nPage 7 of 11\n\nThe int3 instruction also slows down dynamic analysis since debuggers like x64dbg register the interrupt as an\r\nexception instead of swallowing it as a normal breakpoint to avoid debugger detection. This requires the analyst to\r\nmanually skip over the int3 instruction or pass it to the system’s exception handlers while debugging.\r\nTo properly call an API, DRIDEX resolves it from hashes dynamically, stores the API’s address in eax, pushes the\r\nAPI’s parameters on the stack, and executes int3 as shown above. However, instead of using the system’s\r\nexception handlers to handle this interrupt, the malware registers its own custom handler by calling sub_687980 at\r\nthe beginning of the DLL entry point function.\r\nThe function sub_687980 dynamically resolves RtlAddVectoredExceptionHandler and calls it to register\r\nsub_687D40 as a vectored exception handler. This means that when the program encounters an int3 instruction,\r\nsub_687D40 is invoked by the kernel to handle the interrupt and transfer control to the API stored in eax.\r\nThe handler code first checks the exception information structure to see the exception type it’s handling. If the\r\ntype is EXCEPTION_ACCESS_VIOLATION, EXCEPTION_STACK_OVERFLOW,\r\nSTATUS_HEAP_CORRUPTION, DRIDEX resolves the TerminateProcess API and calls it to terminate itself\r\nby interrupting with int3.\r\nFor Vectored Exception Handling, system handlers and user-registered handlers are placed in a vector or chain. An\r\nexception is passed through handlers on this chain until one properly handles it and returns control to the point at\r\nwhich it occurred. In DRIDEX’s handler, if the type is anything else but EXCEPTION_BREAKPOINT (which\r\nis invoked by int3), the handler returns 0 (EXCEPTION_CONTINUE_SEARCH) to pass the exception along to\r\nanother handler.\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/\r\nPage 8 of 11\n\nFinally, if the exception type is EXCEPTION_BREAKPOINT, the handler sets up the API in eax to be called.\r\nWhen an exception occurs, the system transfers control from the user thread that triggers the exception to a kernel\r\nthread to execute the exception handlers. As context switching happens, the system saves all registers from the\r\nuser thread in memory before executing the kernel thread, in order to properly restore them when the handlers\r\nfinish. For Vectored Exception Handling, the context record of the user thread containing its registers is stored in\r\nthe EXCEPTION_POINTERS structure that is passed as a parameter to handlers.\r\nUsing this structure, DRIDEX’s handler accesses the context record and increments the eip value to have it points\r\nto the retn instruction after int3. Because eip is restored from context record after handlers finish, this sets the\r\nuser thread to begin executing at the retn instruction after exception handling. Next, the address of the instruction\r\nafter retn and the address of the API from eax are consecutively pushed on the stack.\r\nBelow is what the current user stack looks like at the end of this handler.\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/\r\nPage 9 of 11\n\nAfter the handler returns the EXCEPTION_CONTINUE_EXECUTION code, the user thread’s context is\r\nrestored and the malware begins executing at the retn instruction. Because the retn instruction pops the value at\r\nthe top of the stack and jumps to it, the malware will jump to the address of the resolved API. This becomes a\r\nnormal stack frame for a function call with esp pointing to the return address and parameters being properly set up\r\non the stack. When the API returns, DRIDEX continues executing at the address after the retn instruction.\r\nStep 4: Writing Script To Patch DLL\r\nAfter understanding how DRIDEX uses VEH to call APIs, we can programmatically patch the sample to bypass\r\nthis anti-analysis feature in IDA and debuggers by modifying all “int3, retn” sequences (0xCCC3) to call eax\r\ninstructions (0xFFD0) in the sample .text section. This should make IDA’s decompilation work nicely while\r\npreventing the execution from being interrupted in our debuggers!\r\nimport pefile\r\n \r\nfile_path = '\u003cDRIDEX SAMPLE PATH\u003e'\r\n \r\nfile = open(file_path, 'rb')\r\nfile_buffer = file.read()\r\nfile.close()\r\n \r\ndridex_pe = pefile.PE(data=file_buffer)\r\n \r\ntext_sect_start = 0\r\ntext_sect_size = 0\r\nfor section in dridex_pe.sections:\r\n if section.Name.decode('utf-8').startswith('.text'):\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/\r\nPage 10 of 11\n\ntext_sect_start = dridex_pe.get_offset_from_rva(section.VirtualAddress)\r\n text_sect_size = section.SizeOfRawData\r\n \r\npatched_text_sect = file_buffer[text_sect_start:text_sect_start+text_sect_size].replace(b'\\xCC\\xC3', b'\\xFF\\xD0'\r\n \r\nfile_buffer = file_buffer[0:text_sect_start] + patched_text_sect + file_buffer[text_sect_start+text_sect_size:]\r\n \r\nout_path = '\u003cOUTPUT PATH\u003e'\r\nout_file = open(out_path, 'wb')\r\nout_file.write(file_buffer)\r\nout_file.close()\r\nIf you have any questions regarding the analysis, feel free to reach out to me via Twitter.\r\nSource: https://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/\r\nhttps://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/\r\nPage 11 of 11",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://www.0ffset.net/reverse-engineering/malware-analysis/dridex-veh-api-obfuscation/"
	],
	"report_names": [
		"dridex-veh-api-obfuscation"
	],
	"threat_actors": [],
	"ts_created_at": 1775439070,
	"ts_updated_at": 1775791302,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/9ab59d42167eadb9ed93867b6a969aef64e19974.pdf",
		"text": "https://archive.orkl.eu/9ab59d42167eadb9ed93867b6a969aef64e19974.txt",
		"img": "https://archive.orkl.eu/9ab59d42167eadb9ed93867b6a969aef64e19974.jpg"
	}
}