You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md
+92-1Lines changed: 92 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -169,7 +169,98 @@ If the files of a folder **shouldn't have been modified**, you can calculate the
169
169
170
170
When the information is saved in logs you can **check statistics like how many times each file of a web server was accessed as a web shell might be one of the most**.
Modern malware families heavily abuse Control-Flow Graph (CFG) obfuscation: instead of a direct jump/call they compute the destination at run-time and execute a `jmp rax` or `call rax`. A small *dispatcher* (typically nine instructions) sets the final target depending on the CPU `ZF`/`CF` flags, completely breaking static CFG recovery.
177
+
178
+
The technique – showcased by the SLOW#TEMPEST loader – can be defeated with a three-step workflow that only relies on IDAPython and the Unicorn CPU emulator.
179
+
180
+
### 1. Locate every indirect jump / call
181
+
182
+
```python
183
+
import idautils, idc
184
+
185
+
for ea in idautils.FunctionItems(idc.here()):
186
+
mnem = idc.print_insn_mnem(ea)
187
+
if mnem in ("jmp", "call") and idc.print_operand(ea, 0) =="rax":
188
+
print(f"[+] Dispatcher found @ {ea:X}")
189
+
```
190
+
191
+
### 2. Extract the dispatcher byte-code
192
+
193
+
```python
194
+
import idc
195
+
196
+
defget_dispatcher_start(jmp_ea, count=9):
197
+
s = jmp_ea
198
+
for _ inrange(count):
199
+
s = idc.prev_head(s, 0)
200
+
return s
201
+
202
+
start = get_dispatcher_start(jmp_ea)
203
+
size = jmp_ea + idc.get_item_size(jmp_ea) - start
204
+
code = idc.get_bytes(start, size)
205
+
open(f"{start:X}.bin", "wb").write(code)
206
+
```
207
+
208
+
### 3. Emulate it twice with Unicorn
209
+
210
+
```python
211
+
from unicorn import*
212
+
from unicorn.x86_const import*
213
+
import struct
214
+
215
+
defrun(code, zf=0, cf=0):
216
+
BASE=0x1000
217
+
mu = Uc(UC_ARCH_X86, UC_MODE_64)
218
+
mu.mem_map(BASE, 0x1000)
219
+
mu.mem_write(BASE, code)
220
+
mu.reg_write(UC_X86_REG_RFLAGS, (zf <<6) | cf)
221
+
mu.reg_write(UC_X86_REG_RAX, 0)
222
+
mu.emu_start(BASE, BASE+len(code))
223
+
return mu.reg_read(UC_X86_REG_RAX)
224
+
```
225
+
226
+
Run `run(code,0,0)` and `run(code,1,1)` to obtain the *false* and *true* branch targets.
227
+
228
+
### 4. Patch back a direct jump / call
229
+
230
+
```python
231
+
import struct, ida_bytes
232
+
233
+
defpatch_direct(ea, target, is_call=False):
234
+
op =0xE8if is_call else0xE9# CALL rel32 or JMP rel32
Once the real destination of every `call rax` is known you can tell IDA what it is so parameter types & variable names are recovered automatically:
249
+
250
+
```python
251
+
idc.set_callee_name(call_ea, resolved_addr, 0) # IDA 8.3+
252
+
```
253
+
254
+
### Practical benefits
255
+
256
+
* Restores the real CFG → decompilation goes from *10* lines to thousands.
257
+
* Enables string-cross-reference & xrefs, making behaviour reconstruction trivial.
258
+
* Scripts are reusable: drop them into any loader protected by the same trick.
259
+
260
+
---
173
261
262
+
## References
174
263
264
+
-[Unit42 – Evolving Tactics of SLOW#TEMPEST: A Deep Dive Into Advanced Malware Techniques](https://unit42.paloaltonetworks.com/slow-tempest-malware-obfuscation/)
0 commit comments