exe脱壳
一直搜到的都是看雪论坛上用的lordPE和ImportREC进行脱壳和修复,感觉有点过时了.
记录一下x32/x64dbg的脱壳和IAT修复方法.
首先用esp定律等方法找到程序的入口点, 然后使用Scylla插件并填写其中的OEP地址.
然后用IAT Autosearch去找可能的IAT表,dump并fix pe文件即可.
DLL 脱壳
和exe脱壳不同, exe因为是第一个装载的模块,因此不需要进行重定位操作,它的base地址一定在0x400000. 但是DLL因为装载的冲突,会需要进行重定位,如果脱壳后重定位表缺失,重定位发生错误. 不过这个错误也不是一定会发生,万一DLL就装载到它原本预设的ImageBase处了,那么这时候没有reloc表也没有关系了(具体做法呢就是多重启几遍hh) .
当然这样重启很烦,所以DLL的脱壳会比exe多一步: 进行reloc表的修复.
基于《加密与解密》这本书的方法, 我们在壳代码中找到壳是怎么修复重定位表的.
首先看一下描述重定位表的结构体:
IMAGE_BASE_RELOCATION STRUCT
VirtualAddress dd 0
SizeOfBlock dd 0
Type1 dw 0 ; Bit15~Bit12 为type , Bit11~Bit0为Itemoffset
IMAGE_RELOCATION ENDS
VirtualAddress 是用来决定地址的Bit15~Bit12(它的后12位都是0), 而Type1决定了 Bit11~Bit0.
假设我们的dll文件内的ImageBase是0x560000, 但是真实的装载地址在0x7f0000, 那么rebase时就按照ImageBase+VirtualAddress+Itemoffset + (0x7f0000-0x560000)来计算重定位后的地址. 重定位表事实上就是由IMAGE_BASE_RELOCATION 结构体数组组成的. 这些数组中的VirtualAddress 值依次递增.
在脱壳时可以在需要重定位的地址下硬件断点,找到壳代码还原重定位表的地方:
然后在这段代码往上翻翻,能找到重定位表和结构体中VirtualAddress的值.
因此在上面的两张图中下断点,每次hit的时候第一张图打印edi, 第二张图打印eax的值, 分别可以获得IMAGE_BASE_RELOCATION 结构体中的Itemoffset 和 VirtualAddress.
重复运行以后,可以在x32dbg的日志中获得类似如下结构的文本:
eax:1000
591B57
591BD7
591BE4
...
eax:2000
592E00
592E16
...
eax:3000
593E68
593E6C
然后就可以写个python脚本修复一下啦!
import math
def find_pe_header(dll:bytearray):
return int.from_bytes(dll[0x3c:0x40],'little')
def find_base_reloction_tbl(dll,pe_header):
if(int.from_bytes(dll[pe_header+0x14:pe_header+0x16],'little') == 0xe0): #PE32
return pe_header+0xa0
elif (int.from_bytes(dll[pe_header+0x14:pe_header+0x16],'little') == 0xf0): #PE32+
return pe_header+0xb0
else:
raise RuntimeError
def find_section_reloc_header(dll:bytearray):
return dll.index(b'.reloc\x00\x00')
with open('DllSample_dump_SCY.dll','rb') as f:
dll = bytearray(f.read())
# find pe_header
pe_header = find_pe_header(dll)
#修改.reloc段中的PointertoLawData到文件最后我们新建的重定位表上
section_reloc_header = find_section_reloc_header(dll)
dll[section_reloc_header+20:section_reloc_header+24] = len(dll).to_bytes(4,'little')
with open('reloc.txt','r') as f:
data = f.read().split('\n')
base_relocation_map = {}
for x in data:
if 'eax' in x:
b = int(x[4:],16)
base_relocation_map[b]=[]
else:
x = int(x,16)
base_relocation_map[b].append(x&0x000fff|0x003000)
size = 0
for k,v in base_relocation_map.items():
blocksize = math.ceil((8+len(v)*2)/4)*4
size += blocksize
new_reloc = k.to_bytes(4,'little')+blocksize.to_bytes(4,'little')
for data in v:
new_reloc += data.to_bytes(2,'little')
new_reloc += b'\x00'*(blocksize-8-len(v)*2) #4字节对齐
dll += new_reloc
#file_alignment对齐
file_alignment = int.from_bytes(dll[pe_header+0x18+0x3c:pe_header+0x18+0x3c+4],'little')
dll += b'\x00'*(file_alignment-len(dll)%file_alignment)
# 修改重定位表大小
base_reloc_tbl = find_base_reloction_tbl(dll,pe_header)
dll[base_reloc_tbl+4:base_reloc_tbl+8] = size.to_bytes(4,'little')
# 修改SizeofRawData
dll[section_reloc_header+16:section_reloc_header+20] = (math.ceil(size/file_alignment)*file_alignment).to_bytes(4,'little')
with open('DllSample.dll','wb') as f:
f.write(dll)