本文为看雪论坛精华文章
看雪论坛作者ID:renzhexigua
# 场景
原始磁盘文件已删除,但进程尚在,需要想办法从进程中恢复原始程序以便后续分析。
关键词:样本应急、脱壳、PE 修复、IAT 重建
# 方法介绍
1. 创建 minidmp
Open TaskManager with admin privilege, select the target process, right click context-menu and select Create dump file. 任务管理器右键创建 dump 文件。
Use ProcessHacker. ProcessHacker 右键创建 dump 文件。
Use x64dbg plugin MiniDumpPlugin(https://github.com/mrexodia/MiniDumpPlugin)
Use pe-sieve with /minidmp option(需注意,pe-sieve 只会在检测到程序存在 path/hook 等 inconsistent 场景时才会 dump suspicious staff,在此前提下 /minidmp 才会起效。若程序本身较为简单,没检测到 suspicious,即使加了 /minidmp 也不会有任何输出)
...等
2. 从 dmp 中提取内存程序
# 查看当前加载模块
> lm
# 查看内存模块布局
> !address
# 将指定范围的内存数据保存至本地文件
> .writemem /path/to/dumpfile.dmp StartAddress Range
# 查看相关符号
> ln address
3. 修复 section 头
0:000> lmDvm112_http_stager
Browse full module list
start end module name
00400000 00409000 112_http_stager (no symbols)
Loaded symbol image file: 112_http_stager.exe
Image path: X:unpack112_http_stager.exe
Image name: 112_http_stager.exe
Browse all global symbols functions data
Timestamp: Tue Jun 9 08:17:15 2020 (5EDED50B)
CheckSum: 00010C4A
ImageSize: 00009000
Translations: 0000.04b0 0000.04e4 0409.04b0 0409.04e4
Information from resource tables:
.writemem /path/to/dump.dmp 00400000 L9000
3.1 pe_unmapper 工具法
Small tool to convert beteween the PE alignments (raw and virtual). Allows for easy PE unmapping: useful in recovering executables dumped from the memory.
pe_unmapper.exe /in dump.mem 00400000 /out 112_http_stager.exe
2022-09-13 10:40 PM 14,336 112_http_stager.exe <== 展开前/修复后的文件 1
2022-09-13 05:38 PM 52,110,299 112_http_stager.exe.dmp
2022-09-13 06:02 PM 36,864 dump.mem <== 展开后/修复前从 dmp 中提取的文件
pe_unmapper.exe /in dump.mem 00400000 /out 112_http_stager_mode_r.exe /mode r
2022-09-13 10:40 PM 14,336 112_http_stager.exe <== 修复后的文件 1
2022-09-13 05:38 PM 52,110,299 112_http_stager.exe.dmp
2022-09-13 10:54 PM 36,864 112_http_stager_mode_r.exe <== 修复后的文件 2
2022-09-13 06:02 PM 36,864 dump.mem <== 展开后/修复前从 dmp 中提取的文件
3.2 手工 realign 法
4. 修复 IAT
使用 pe_unmapper.exe /in NotePad.virtual.pe 0x400000 /out NotePad.realign.exe /mode r 得到 section realign 后的 PE 文件
4.1 手动修复
借助:IDAPython + dumpulator + lief
import idc
import ida_xref
import idautils
import ida_bytes
segm_start = 0x406000
segm_end = 0x407000
dInfos = dict()
for ea in range(segm_start, segm_end, 4):
val = ida_bytes.get_dword(ea)
for ref in idautils.XrefsTo(ea, ida_xref.XREF_DATA):
if ref.type in [idc.dr_R]:
entry = dInfos.setdefault(ea, {'val': val, 'refs': set()})
entry['refs'].add(ref.frm)
lInfos = []
for k, v in dInfos.items():
lInfos.append((k, v['val']))
print(dInfos)
{
IAT1_VA: {
'val': export_func_addr,
'refs': set(call_insn_addrs),
'name': export_func_name
},
IAT2_VA: {
...snip...
},
...snip...
}
{
4219616: {'refs': {4203561, 4203598}, 'val': 1993408960},
4219620: {'refs': {4203732, 4203654}, 'val': 1993402960}
...snip...
}
Dumpulator:是一个用以对 minidump 文件进行模拟执行的 Python 库
from dumpulator import Dumpulator
dp = Dumpulator('NotePad.exe.dmp', quiet=True)
lInfos = [(4219616, 1993408960), (4219620, 1993402960), ...snip...]
lInfoWithSymbols1 = []
for entry in lInfos:
iat_addr, func_addr = entry
try:
name = dp.exports[func_addr]
lInfoWithSymbols1.append((iat_addr, func_addr, name))
except:
# unfixed issue: https://github.com/mrexodia/dumpulator/issues/3
print(f'{iat_addr},{func_addr}, cannot parse address')
continue
print(lInfoWithSymbols)
iat_address, func_address, func_name
[(4219616, 1993408960, 'advapi32.dll:RegSetValueExA'),
...snip...
(4219628, 1993408992, 'advapi32.dll:RegOpenKeyA'),
(4219640, 1993104976, 'gdi32.dll:GetStockObject'),
(4219644, 1993096672, 'gdi32.dll:GetObjectA'),
(4219820, 2004124400, 'ntdll.dll:RtlMoveMemory'),
...snip...
(4219904, 1978456096, 'shell32.dll:DragQueryFileA'),
(4219908, 1978456080, 'shell32.dll:DragFinish'),
...snip...
(4220040, 1984116832, 'user32.dll:CharNextA'),
(4220044, 1984147904, 'user32.dll:IsDialogMessageA'),
...snip...
(4219816, 1994756464, 'kernel32.dll:GetProfileStringA'),
...snip...
(4219872, 1994792672, 'kernel32.dll:GetCommandLineA')]
# 方法 1
import lief
# PE
input_path = "NotePad.demo.exe"
artifact_path = "NotePad.demo.fixed.exe"
binary = lief.parse(input_path)
dInfos = {
4219616: {'refs': {4203561, 4203598}, 'val': 1993408960, 'name': 'advapi32.dll:RegSetValueExA'},
4219620: {'refs': {4203732, 4203654}, 'val': 1993402960, 'name': 'advapi32.dll:RegQueryValueExA'},
4219624: {'refs': {4204169}, 'val': 1993403168, 'name': 'advapi32.dll:RegCloseKey'},
...snip...
}
# Rebuild IAT entries
for info in dInfos.values():
symbol_name = info['name']
lib_name, entry_name = symbol_name.split(':')
lib = binary.get_import(lib_name)
# create new library if not exist
if not lib:
lib = binary.add_library(lib_name)
entry = lib.get_entry(entry_name)
# add new function entry if not exist
if not entry:
entry = lib.add_entry(entry_name)
# Fix IAT-related call reference
imagebase = binary.optional_header.imagebase
for info in dInfos.values():
symbol_name = info['name']
lib_name, entry_name = symbol_name.split(':')
# iat_addr_va: LIEF reassigned address
iat_addr_va = imagebase + binary.predict_function_rva(lib_name, entry_name)
for insn_va in info['refs']:
# print(f'{lib_name}!{entry_name} called by 0x{insn_va:x}: (insn) call 0x{iat_addr_va:x}')
# Actually, the mnemonics of insn calling IAT include: CALL, JMP, etc.
# Ref: https://github.com/volatilityfoundation/volatility/blob/master/volatility/plugins/malware/impscan.py#L197-L219
#
# FF 15 dd cc bb aa CALL 0xaabbccdd
binary.patch_address(insn_va + 2, iat_addr_va, 4, binary.VA_TYPES.VA)
# Modify the EP
binary.optional_header.addressof_entrypoint = 0x10CC
# Apply these changes
builder = lief.PE.Builder(binary)
builder.build_imports(True)
builder.patch_imports(True)
builder.build()
# Save this artifact
builder.write(artifact_path)
4.2 ApiScout 修复
借助:ApiScout + lief
apiscoutdb_builder> python DatabaseBuilder.py --auto
{
"aslr_offsets": true,
"crawled_paths": [
"C:\Windows\system32",
"C:\Windows\SysWOW64",
"C:\Windows\WinSxS",
"C:\Program Files\Common Files"
],
"dlls": {
"32_0.0.0.0_ActionCenter.dll_0x10000000": {
"aslr_offset": -1264844800,
"base_address": 268435456,
"bitness": 32,
"exports": [
{
"address": 73712,
"name": "DllCanUnloadNow",
"ordinal": 1
},
{
"address": 98864,
"name": "DllGetClassObject",
"ordinal": 2
}
],
"filepath": "C:\Windows\SysWOW64\ActionCenter.dll",
"version": "0.0.0.0"
},
"32_0.0.0.0_AudioSes.dll_0x10000000": {
"aslr_offset": -1283457024,
"base_address": 268435456,
"bitness": 32,
"exports": [
{
"address": 200912,
"name": "DllGetClassObject",
"ordinal": 9
},
...snip...
}
}
python scout.py "X:NotePad.demo.exe" 10.0_filtered.json
idx: offset ; VA ; IT?; #ref;DLL ; API
1: 0x000062e0; 0x76d101c0; err; 3; advapi32.dll_0x4c300000 (32bit) ; RegSetValueExA
2: 0x000062e4; 0x76d0ea50; err; 3; advapi32.dll_0x4c300000 (32bit) ; RegQueryValueExA
3: 0x000062e8; 0x76d0eb20; err; 2; advapi32.dll_0x4c300000 (32bit) ; RegCloseKey
4: 0x000062ec; 0x76d101e0; err; 2; advapi32.dll_0x4c300000 (32bit) ; RegOpenKeyA
5: 0x000062f0; 0x76d13190; err; 2; advapi32.dll_0x4c300000 (32bit) ; RegCreateKeyA
6: 0x000062f8; 0x76cc5e50; err; 2; gdi32.dll_0x4d500000 (32bit) ; GetStockObject
...snip...
155: 0x0000e35d; 0x72005000; err; 1; rasman.dll_0x10000000 (32bit) ; RasSignalMonitorThreadExit
---------------------------------------------------------------------------------------------------------------------------------
156: 0x0000e3d1; 0x72005000; err; 1; rasman.dll_0x10000000 (32bit) ; RasSignalMonitorThreadExit
DLLs: 8, APIs: 145, references: 389
WinApi1024 Vector Results:
Windows 10 (AMD64): 63 / 145 (43.45%) APIs covered in WinApi1024 vector.
Vector: A67EAAIAQA5BA8CAQEA8CAAQAAQACA5EAHA+A4HAMA3QAABAwA3IgCggIEAAEDABgEGKIOKgA3CIgEiJgEBg
Confidence: 89.97354108424373
idx: offset ; VA ; IT?; #ref;DLL ; API
1: 0x004062e0; 0x76d101c0; err; 3; advapi32.dll_0x4c300000 (32bit) ; RegSetValueExA
2: 0x004062e4; 0x76d0ea50; err; 3; advapi32.dll_0x4c300000 (32bit) ; RegQueryValueExA
3: 0x004062e8; 0x76d0eb20; err; 2; advapi32.dll_0x4c300000 (32bit) ; RegCloseKey
4: 0x004062ec; 0x76d101e0; err; 2; advapi32.dll_0x4c300000 (32bit) ; RegOpenKeyA
python scout.py "X:NotePad.demo.exe" 10.0_filtered.json -o NotePad.demo.apis.json
[
{
"offset": 25312,
"apiAddress": 1993408960,
"dll": "advapi32.dll(32 bit)",
"api": "RegSetValueExA",
"references": [
9257,
9294
]
},
{
"offset": 25444,
"apiAddress": 1995044640,
"dll": "kernel32.dll(32 bit)",
"api": "_lwrite",
"references": [
13005,
13092
]
},
{
"offset": 25448,
"apiAddress": 1994786592,
"dll": "kernel32.dll(32 bit)",
"api": "LocalUnlock",
"references": [
13022,
13174,
13699,
13794,
13906,
14657,
15018,
15062,
15083,
16643,
17155
]
},
...snip...
{
"offset": 57483,
"apiAddress": 1991430304,
"dll": "comdlg32.dll(32 bit)",
"api": "GetOpenFileNameA",
"references": []
},
{
"offset": 57491,
"apiAddress": 1993408960,
"dll": "advapi32.dll(32 bit)",
"api": "RegSetValueExA",
"references": []
},
{
"offset": 58205,
"apiAddress": 1912623104,
"dll": "rasman.dll(32 bit)",
"api": "RasSignalMonitorThreadExit",
"references": []
},
{
"offset": 58321,
"apiAddress": 1912623104,
"dll": "rasman.dll(32 bit)",
"api": "RasSignalMonitorThreadExit",
"references": []
}
]
该 json 输出中把我们在方法 1 中结合使用 IDAPython + dumpulator 所建立的映射关系直接呈现了出来。同时还把之前提到的实际未被引用的 API 也给标记了出来,如上图中 references 表示实际被应用的位置(即,call/jmp [iat_address] 指令的起始位置),可以看到,RasSignalMonitorThreadExit 等函数实际并未使用,references 列表为空。
# 方法 2
import json
import lief
# PE
input_path = "NotePad.demo.exe"
artifact_path = "NotePad.demo.fixed.exe"
binary = lief.parse(input_path)
api_path = "api.json"
dInfos = None
with open(api_path, 'r') as f:
dInfos = json.load(f)
# Rebuild IAT entries
for info in dInfos:
...snip...
# 方法 1
import lief
# PE
input_path = "NotePad.demo.exe"
artifact_path = "NotePad.demo.fixed.exe"
binary = lief.parse(input_path)
dInfos = {
4219616: {'refs': {4203561, 4203598}, 'val': 1993408960, 'name': 'advapi32.dll:RegSetValueExA'},
4219620: {'refs': {4203732, 4203654}, 'val': 1993402960, 'name': 'advapi32.dll:RegQueryValueExA'},
4219624: {'refs': {4204169}, 'val': 1993403168, 'name': 'advapi32.dll:RegCloseKey'},
4219628: {'refs': {4204221}, 'val': 1993408992, 'name': 'advapi32.dll:RegOpenKeyA'},
...snip...
}
# Rebuild IAT entries
for info in dInfos:
...snip...
# 总结
# 参考阅读
How would I go about rebuilding the IAT of a packed executable?
(https://security.stackexchange.com/questions/21540/how-would-i-go-about-rebuilding-the-iat-of-a-packed-executable/21563#21563)
Unpacking Smokeloader and Reconstructing PE Programatically using LIEF
(https://bbs.pediy.com/thread-274505.htm)
Dynamic Imports and Working Around Indirect Calls - Smokeloader Study Case
(https://bbs.pediy.com/thread-274505.htm)
@hasherezade
看雪ID:renzhexigua
https://bbs.pediy.com/user-home-752377.htm
2.5折门票限时抢购
峰会官网:https://meet.kanxue.com/kxmeet-6.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
原文始发于微信公众号(看雪学苑):进程 Dump & PE unpacking & IAT 修复 - Windows 篇
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论