본문 바로가기

HACK/Reversing

upx unpacker

https://ddddhkim.github.io/project/2017/03/20/Python-UPX_UNPACK.py.html

ddddhkim형과 같이 upx언패커를 만들었는데 아주 쉬울줄 알았는데 덤프가 가장 어려웠다

IAT rebuild때문에 조금 걸렸는데 이부분은 나중에 다시 좀 더 수정해야겠다.


(실수를 하나 했는데 모든 IAT가 idata섹션에만 있는거 아니라서 DataDirectory안에 있는 import table 관련해서 좀 더 수정해야할거 같다.)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
import pefile
from struct import *
from pydbg import *
from pydbg.defines import *
 
 
def signature(dbg):
    sig = dbg.read_process_memory(ep+62)
    if sig.encode('hex'== "8dbe":
        print "Packing with UPX!!"
    else:
        pass
 
 
def OEP(dbg):
    global num
    disasm = dbg.disasm(dbg.exception_address)
 
    if disasm.startswith("jmp"):
        num = True
        dbg.single_step(True)
    else:
        dbg.single_step(True)
    return DBG_CONTINUE
 
 
def entry_point(dbg):
    API(dbg)
    disasm = dbg.disasm(dbg.exception_address)
    esp = dbg.context.Esp
    dbg.bp_set_hw(esp, 4, HW_ACCESS, handler=OEP, restore=False)
    return DBG_CONTINUE
 
 
def single_step(dbg):
    global num
    disasm = dbg.disasm(dbg.exception_address)
 
    if 0x70000000 < int(dbg.exception_address):
        pass
    elif disasm.startswith("jmp"):
        num = True
        dbg.single_step(True)
    elif num:
        print "[*] Find OEP: " + hex(dbg.exception_address)
        dbg.bp_del_all()
        upx(dbg)
    else:
        dbg.single_step(True)
    return DBG_CONTINUE
 
 
def API(dbg):
    loadlibA = dbg.func_resolve("kernel32.dll","LoadLibraryA")
    getprocaddr = dbg.func_resolve("kernel32.dll","GetProcAddress")
    dbg.bp_set(loadlibA, handler=LoadLibraryA, restore=True)
    dbg.bp_set(getprocaddr, handler=GetProcessAddress, restore=True)
    return DBG_CONTINUE
 
 
def GetProcessAddress(dbg):
    global Switch_
    global API_Names
    global NumOfSection
 
    esp = dbg.context.Esp
    api_addr = unpack("<I", dbg.read_process_memory(esp+84))[0]
    api_name = dbg.get_ascii_string(dbg.read_process_memory(api_addr, 30))
    if Switch_ == True:
        API_Names.append([])
        Switch_ = False
 
    INDEX = len(API_Names) - 1  
 
    # NULL PADDING
    if len(api_name) % 2 == 0:
        API_Names[INDEX].append(api_name)
    else:
        API_Names[INDEX].append(api_name+'\x00')
    return DBG_CONTINUE
  
 
def LoadLibraryA(dbg):
    global DLL_Names
    global IAT_DLL_OFFSET
    global Switch_
 
    Switch_ = True
    esp = dbg.context.Esp
    ebx = dbg.context.Ebx
 
    dll_addr = unpack("<I", dbg.read_process_memory(esp+44))[0]
    dll_name = dbg.read_process_memory(dll_addr, 20)
    dll_name = dbg.get_ascii_string(dll_name)
    if dll_name.lower().endswith("dll"):
        INDEX = len(DLL_Names)
        DLL_Names.append([])
        DLL_Names[INDEX].append(dll_name+'\x00')
        DLL_Names[INDEX].append(ebx)
 
        if ebx > IAT_DLL_OFFSET[0]:
            IAT_DLL_OFFSET[0= ebx
            IAT_DLL_OFFSET[1= INDEX
    else:
        pass
    print INDEX
    API(dbg)
    return DBG_CONTINUE
 
 
def upx(dbg):
    global DLL_Names
    global API_Names
    global IAT_DLL_OFFSET
    global NumOfSection
 
    o = open("dump.exe""wb")
 
    ImageBase = pe.OPTIONAL_HEADER.ImageBase
    base = int(ImageBase + pe.sections[1].VirtualAddress)
 
    SizeOfDOSHeader = unpack("<I", dbg.read_process_memory(ImageBase+0x3c4))[0]
    DOS_Header = dbg.read_process_memory(ImageBase, SizeOfDOSHeader)
    text = dbg.read_process_memory(ImageBase, 0x300)
    add = text.encode('hex').find("50450000")
    DOS_Header = dbg.read_process_memory(ImageBase, (add/2))
    print "[*] DOS Header!!"
 
    base1 = int(ImageBase + pe.sections[1].VirtualAddress)
    length = int(pe.sections[1].Misc_VirtualSize)
    NT = dbg.read_process_memory(base1, length)
 
    add = NT.encode('hex').find("50450000")
    NT_Header = dbg.read_process_memory(base1+(add/2), 0xf8)
    NumOfSection = unpack("<H", NT_Header[6:8])[0]
    SizeOfPEHeader = unpack("<I", NT_Header[0x54:0x58])[0]
    print "[*] NT Header!!"
    Section = dbg.read_process_memory(base1+(add/2)+0xf80x28 * NumOfSection)
    padding = SizeOfPEHeader-(len(DOS_Header) + len(NT_Header) + len(Section))
    print "[*] Section Header!!"
 
    DataOfSection = ''
    for x in range(NumOfSection):
        index = x * 0x28
        VV_Addr = unpack("<I", Section[index+12:index+16])[0]
        V_Addr = ImageBase + VV_Addr
        V_Size = unpack("<I", Section[index+16:index+20])[0]
        Section_Name = Section[index:index+6]
        Section_Name = dbg.get_ascii_string(Section_Name)
        # IAT RECOVERY
        if Section_Name == ".idata":
 
            IAT = "\x00" * V_Size
            NumOfAPI = [len(x) for x in API_Names]
            NumOfDLL = len(DLL_Names)
 
            IAT_DLL_OFFSET = IAT_DLL_OFFSET[0- (V_Addr - (NumOfAPI[IAT_DLL_OFFSET[1]] + 1* 4)
 
 
            # IMPORT DLL Names
            IMPORT_DLL_Names = ""
            IMPORT_DLL_Names += "".join(["".join(DLL_Names[x][0]) for x in range(NumOfDLL)])
            IMPORT_DLL_Names += "\x00"
 
            # IMPORT Hints/Names
            IMPORT_Hints = "\x00\x00"
            IMPORT_Hints += "\x00\x00".join(["\x00\x00".join(y) for y in API_Names])
 
            # LenOfImport
            LenOfImport = len(IMPORT_DLL_Names) + len(IMPORT_Hints)
 
            # IAT Rebuilding
            IAT = IAT[:IAT_DLL_OFFSET] + IMPORT_DLL_Names + IMPORT_Hints + IAT[IAT_DLL_OFFSET+LenOfImport:]
            IAT_DLL_OFFSET += VV_Addr            
            # IMPORT_Directory_Talbe 
            for x in range(NumOfDLL):
                IAT = IAT[:(0x14*x)+0xc+ pack("<I", IAT_DLL_OFFSET) + pack("<I", DLL_Names[x][1]-ImageBase) + IAT[(0x14*x)+0x14:]
                IAT_DLL_OFFSET += len(DLL_Names[x][0])
 
            API_RVA = IAT_DLL_OFFSET + 1
 
            # IMPORT_Address_Table 
            for x in range(NumOfDLL): # Int
                IMPORT_Address_Table = ""
                for y in range(NumOfAPI[x]): # List   
                    IMPORT_Address_Table += pack("<I", API_RVA)
                    API_RVA += len(API_Names[x][y]) + 2
                INDEX = DLL_Names[x][1- V_Addr
                INDEX_ = INDEX + len(IMPORT_Address_Table) + 4
                IAT = IAT[:INDEX] + IMPORT_Address_Table + "\x00\x00\x00\x00" + IAT[INDEX_:]
            
            DataOfSection += IAT
 
        elif Section_Name == ".rsrc":
            for rsrc in pe.DIRECTORY_ENTRY_RESOURCE.entries:
                for entry in rsrc.directory.entries:
                    pass
            rva = (entry.directory.entries[0].data.struct.OffsetToData)
            # version
            size = (entry.directory.entries[0].data.struct.Size)
            # version size
            va = pe.sections[2].VirtualAddress
            # 0x56000
            address = rva-va+size
            rsrc_data = dbg.read_process_memory(V_Addr, V_Size)
            DataOfSection += dbg.read_process_memory(va+ImageBase, address) + rsrc_data[address:]
        else:
            DataOfSection += dbg.read_process_memory(V_Addr, V_Size)
    
    o.write(DOS_Header+NT_Header+Section+'\x00'*padding+DataOfSection)
    o.close()
    print "Success!!"
    return DBG_CONTINUE
 
 
Switch_ = True
DLL_Names = []
IAT_DLL_OFFSET = [00]
API_Names = []
num = False
 
target = raw_input("FileName: ")
pe = pefile.PE(target)
ep = pe.OPTIONAL_HEADER.AddressOfEntryPoint + pe.OPTIONAL_HEADER.ImageBase
dbg = pydbg()
dbg.set_callback(EXCEPTION_SINGLE_STEP, single_step)
dbg.load(target)
signature(dbg)
dbg.bp_set(ep+1, handler=entry_point, restore=False)
dbg.run()
cs


'HACK > Reversing' 카테고리의 다른 글

DLL 인젝션  (1) 2016.08.07
PE View path  (0) 2016.02.02
MSDN ANNOTATIONS  (3) 2016.01.11