{
	"id": "777a4ebe-23e0-4fc6-a74e-441b6367a35a",
	"created_at": "2026-04-06T00:13:33.744375Z",
	"updated_at": "2026-04-10T03:24:44.540291Z",
	"deleted_at": null,
	"sha1_hash": "6eb19e94f90f0ab6421e5952e054b051013e98ad",
	"title": "Aura Stealer #2 beatin the obfuscation",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 522688,
	"plain_text": "Aura Stealer #2 beatin the obfuscation\r\nPublished: 2025-10-29 · Archived: 2026-04-05 18:02:04 UTC\r\nHello party people\r\ntoday we gonna deep dive into the topic of obfuscation in AuraStealer\r\njust kiddin this aint chatgpt, this is my bad english.\r\nProfile\r\nSo lets look into the obfuscation from Aura Stealer.\r\nIt uses a very similar technique like the one which is describe here https://cloud.google.com/blog/topics/threat-intelligence/lummac2-obfuscation-through-indirect-control-flow\r\nIt is called indirect control flow I’ll use the WinMain() here as a good example.\r\nwe directly can see that there are no direct function calls.\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 1 of 13\n\nlets take a look at the first call.\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 2 of 13\n\n.text:0042F6D7 mov eax, 30BCh\r\n.text:0042F6DC\r\n.text:0042F6DC loc_42F6DC:\r\n.text:0042F6DC add eax, off_49AEE4 ; dword ptr [0x49aee4]\r\n.text:0042F6E2 call eax\r\nso what is happening here? We can see mov eax, 30BCh that means the register holds the value 30BCh now after\r\nthat we add eax, off_49AEE4 off_49AEE4 lays in .data and points to loc_450461+3\r\nat loc_450461+3 the address would be 0x00450464 important we get the address not the data because of the\r\noff_49AEE4 which is the same like dword ptr [0x49aee4]\r\nwhat that means for the calculation of our functions address? we just have to calculate 0x30BC + 0x00450464 =\r\n0x00453520 so eax = 0x00453520\r\ncall 0x00453520 should be a function now lets see and boom we find a function here :3\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 3 of 13\n\nAs we can see we have many patterns like this with jmp/call variation\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 4 of 13\n\nSo what now?\r\nfirst i tried hardcoding every patttern…\r\nbut then I had the idea to just look for call/jmp register in the dissassembled code and trace back every\r\nregister which is important for the calculation of our final call address.\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 5 of 13\n\nTurns out I should have read the whole article “LummaC2: Obfuscation Through Indirect Control Flow”\r\nhttps://cloud.google.com/blog/topics/threat-intelligence/lummac2-obfuscation-through-indirect-control-flow\r\nHere it is described as symbolic backward slicing , but hey atleast i had the idea too, just need to fail for 3 days\r\nstraight wondering how many patterns are left.\r\n; earlier example from WinMain we calculated\r\n0x0042F6D7: mov eax, 30BCh\r\n0x0042F6DC: add eax, dword ptr [0x49AEE4]\r\n0x0042F6E2: call eax\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 6 of 13\n\nRegister Dependency Tracing\r\nCode Analysis\r\ntrace_register_dependencies\r\nidx=2, target_reg=eax\r\ntracked regs = eax\r\nWalk backwards\r\nidx=1: add eax,\r\ndword ptr [0x49AEE4]\r\nDisassemble x86\r\ninstructions\r\nanalyze_range\r\n0x0042F6D7, 0x0042F6E2\r\nLoop through\r\ninstructions\r\nFind call eax\r\nat 0x0042F6E2\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 7 of 13\n\nget_register_written\r\nreturns eax\r\nAdd to dependencies\r\nget_registers_read\r\nreturns eax, [0x49AEE4]\r\ntracked regs = eax\r\nidx=0: mov eax, 30BCh\r\nget_register_written\r\nreturns eax\r\nAdd to dependencies\r\nget_registers_read\r\nreturns empty\r\ntracked_regs = empty\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 8 of 13\n\nUnicorn Emulation\r\nemulate_dependencies\r\ndeps, target_reg=eax\r\nunicorn emulator\r\nExecute: mov eax, 0x30BC\r\neax = 0x30BC\r\nExecute: add eax,\r\ndword ptr [0x49AEE4]\r\nread_dword 0x49AEE4\r\nreturns 0x00450464\r\neax = 0x30BC + 0x00450464\r\n= 0x00453520\r\nDONE\r\nReturn dependencies\r\nmov + add\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 9 of 13\n\nReturn: 0x00453520\r\nReads a 4-byte integer from a virtual address by finding the correct section and unpacking the bytes.\r\ndef read_dword(self, va):\r\n for s in self.sections.values():\r\n if s['start'] \u003c= va \u003c s['end']:\r\n off = va - s['start']\r\n if off + 4 \u003c= len(s['data']):\r\n return struct.unpack('\u003cI', s['data'][off:off+4])[0]\r\n return None\r\nExtracts raw bytes from a specified virtual address range within the binary’s sections.\r\ndef get_code_range(self, start_va, end_va):\r\n for s in self.sections.values():\r\n if s['start'] \u003c= start_va \u003c s['end']:\r\n return bytes(s['data'][start_va - s['start']:end_va - s['start']])\r\n return None\r\nReturns the set of registers that are read by an instruction (excluding destination registers for mov/lea).\r\ndef get_registers_read(self, inst):\r\n regs = set()\r\n start = 1 if inst.mnemonic in ['mov', 'lea'] else 0\r\n for i, op in enumerate(inst.operands):\r\n if i \u003e= start or inst.mnemonic in ['cmp', 'test', 'sub', 'add', 'xor', 'or', 'and']:\r\n if op.type == X86_OP_REG:\r\n regs.add(op.reg)\r\n elif op.type == X86_OP_MEM:\r\n if op.mem.base: regs.add(op.mem.base)\r\n if op.mem.index: regs.add(op.mem.index)\r\n return regs\r\nWalks backward through instructions to find all instructions that contribute to computing the value of a target\r\nregister.\r\ndef trace_dependencies(self, instructions, target_idx, target_reg, max_lookback=100):\r\n deps, tracked, seen = [], {target_reg}, set()\r\n for idx in range(target_idx - 1, max(0, target_idx - max_lookback), -1):\r\n inst = instructions[idx]\r\n if inst.mnemonic in ['jmp', 'je', 'jne', 'jz', 'jnz', 'jg', 'jl', 'jge', 'jle', 'ja', 'jb', 'jae', 'jbe'\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 10 of 13\n\ncontinue\r\n if inst.operands and inst.operands[0].type == X86_OP_REG and inst.operands[0].reg in tracked and \\\r\n inst.mnemonic in ['mov', 'lea', 'add', 'sub', 'xor', 'or', 'and', 'shl', 'shr', 'sar', 'imul', 'mul'\r\n deps.insert(0, inst)\r\n seen.add(inst.address)\r\n tracked.remove(inst.operands[0].reg)\r\n tracked.update(self.get_registers_read(inst))\r\n if not tracked: break\r\n return deps\r\nUses Unicorn Engine to execute the dependency chain and resolve the final value of the target register.\r\ndef emulate_dependencies(self, deps, target_reg):\r\n if not deps: return None\r\n mu = Uc(UC_ARCH_X86, UC_MODE_32)\r\n code_base, stack_base = 0x1000000, 0x2000000\r\n mu.mem_map(code_base, 0x10000)\r\n mu.mem_map(stack_base, 0x10000)\r\n mu.reg_write(UC_X86_REG_ESP, stack_base + 0x8000)\r\n \r\n for s in self.sections.values():\r\n try:\r\n mu.mem_map(s['start'], ((len(s['data']) + 0xFFF) // 0x1000) * 0x1000)\r\n mu.mem_write(s['start'], bytes(s['data']))\r\n except: pass\r\nDisassembles a code range and finds all indirect calls/jumps, then traces and resolves their target addresses.\r\ndef analyze_range(self, start_va, end_va):\r\n code = self.get_code_range(start_va, end_va)\r\n if not code:\r\n print(f\"Failed to read code range 0x{start_va:08x} - 0x{end_va:08x}\")\r\n return\r\n instructions = list(self.cs.disasm(code, start_va))\r\n for idx, inst in enumerate(instructions):\r\n if inst.mnemonic in [\"call\", \"jmp\"] and inst.operands and inst.operands[0].type == X86_OP_REG:\r\n target_reg = self.cs.reg_name(inst.operands[0].reg)\r\n print(f\"\\n[+] Analyzing: 0x{inst.address:08x}: {inst.mnemonic} {target_reg}\")\r\n deps = self.trace_dependencies(instructions, idx, inst.operands[0].reg)\r\n target_value = self.emulate_dependencies(deps, inst.operands[0].reg)\r\n if target_value is not None:\r\n print(f\"\\n[*] Target resolved: 0x{target_value:08x}\\n\")\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 11 of 13\n\nOutput\r\nfull script can be found here\r\nhttps://github.com/01Xyris/RE-Malware/blob/main/AuraStealer/find_cff.py\r\nNow we just need to patch this in the binary. takes the last instruction before the call and patches it with # mov\r\nreg, calculated_address example\r\n0x0042F6D7: mov eax, 30BCh\r\n0x0042F6DC: add eax, dword ptr [0x49AEE4]\r\n0x0042F6E2: call eax\r\nafter\r\n0x0042F6D7: mov eax, 30BCh\r\n0x0042F6DC: mov eax, 0x00453520 ; calculated_address\r\n nop ; nop padding so size is the same\r\n0x0042F6E2: call eax\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 12 of 13\n\nThe full deobfuscation script can be found here\r\nhttps://github.com/01Xyris/RE-Malware/blob/main/AuraStealer/aura_patch_obf.py\r\nSource: https://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nhttps://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/\r\nPage 13 of 13",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://blog.xyris.mov/posts/aura-stealer-%232-beatin-the-obfuscation/"
	],
	"report_names": [
		"aura-stealer-%232-beatin-the-obfuscation"
	],
	"threat_actors": [
		{
			"id": "faa4a29b-254a-45bd-b412-9a1cbddbd5e3",
			"created_at": "2022-10-25T16:07:23.80111Z",
			"updated_at": "2026-04-10T02:00:04.753677Z",
			"deleted_at": null,
			"main_name": "LookBack",
			"aliases": [
				"FlowingFrog",
				"LookBack",
				"LookingFrog",
				"TA410",
				"Witchetty"
			],
			"source_name": "ETDA:LookBack",
			"tools": [
				"FlowCloud",
				"GUP Proxy Tool",
				"SodomMain",
				"SodomMain RAT",
				"SodomNormal"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434413,
	"ts_updated_at": 1775791484,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/6eb19e94f90f0ab6421e5952e054b051013e98ad.pdf",
		"text": "https://archive.orkl.eu/6eb19e94f90f0ab6421e5952e054b051013e98ad.txt",
		"img": "https://archive.orkl.eu/6eb19e94f90f0ab6421e5952e054b051013e98ad.jpg"
	}
}