Skip to content

Commit f605212

Browse files
authored
Merge pull request #1015 from bet4it/file_upx
Support file operations and upxed PE file on Windows
2 parents b5e1080 + d6472c2 commit f605212

File tree

8 files changed

+120
-36
lines changed

8 files changed

+120
-36
lines changed

examples/src/windows/file_upx.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <stdio.h>
2+
int main()
3+
{
4+
FILE *f;
5+
const char *filename = "test.bin";
6+
unsigned char buf[5] = {1, 2, 3, 4, 5};
7+
f = fopen(filename, "wb");
8+
fwrite(buf, 1, 5, f);
9+
fclose(f);
10+
f = fopen(filename, "rb");
11+
fread(buf, 1, 5, f);
12+
fclose(f);
13+
remove(filename);
14+
return 0;
15+
}

qiling/loader/pe.py

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
44
#
55

6-
import os, pefile, pickle, secrets, traceback
6+
import os, pefile, pickle, traceback
77
from typing import Any, MutableMapping, Optional, Mapping, Sequence
88

99
from qiling import Qiling
@@ -78,7 +78,8 @@ def load_dll(self, name: bytes, driver: bool = False) -> int:
7878
path = os.path.join(self.ql.rootfs, self.ql.dlls, dll_name)
7979

8080
if not os.path.exists(path):
81-
raise QlErrorFileNotFound("Cannot find dll in %s" % path)
81+
self.ql.log.error("Cannot find dll in %s" % path)
82+
return 0
8283

8384
# If the dll is already loaded
8485
if dll_name in self.dlls:
@@ -257,9 +258,13 @@ def init_peb(self):
257258

258259
# we must set an heap, will try to retrieve this value. Is ok to be all \x00
259260
process_heap = self.ql.os.heap.alloc(0x100)
260-
peb_data = PEB(self.ql, base=peb_addr, process_heap=process_heap,
261-
number_processors=self.ql.os.profile.getint("HARDWARE",
262-
"number_processors"))
261+
process_parameters = self.ql.os.heap.alloc(0x100)
262+
peb_data = PEB(
263+
self.ql,
264+
base=peb_addr,
265+
process_heap=process_heap,
266+
process_parameters=process_parameters,
267+
number_processors=self.ql.os.profile.getint("HARDWARE", "number_processors"))
263268
peb_data.LdrAddress = peb_addr + peb_data.size
264269
peb_data.write(peb_addr)
265270
self.structure_last_addr += peb_data.size
@@ -603,19 +608,6 @@ def load(self):
603608
self.pe.parse_data_directories()
604609
data = bytearray(self.pe.get_memory_mapped_image())
605610
self.ql.mem.write(self.pe_image_address, bytes(data))
606-
# setup IMAGE_LOAD_CONFIG_DIRECTORY
607-
if self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']].VirtualAddress != 0:
608-
SecurityCookie_rva = self.pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct.SecurityCookie - self.pe.OPTIONAL_HEADER.ImageBase
609-
SecurityCookie_value = default_security_cookie_value = self.ql.mem.read(self.pe_image_address+SecurityCookie_rva, self.ql.pointersize)
610-
while SecurityCookie_value == default_security_cookie_value:
611-
SecurityCookie_value = secrets.token_bytes(self.ql.pointersize)
612-
# rol rcx, 10h (rcx: cookie)
613-
# test cx, 0FFFFh
614-
SecurityCookie_value_array = bytearray(SecurityCookie_value)
615-
# Sanity question: We are always little endian, right?
616-
SecurityCookie_value_array[-2:] = b'\x00\x00'
617-
SecurityCookie_value = bytes(SecurityCookie_value_array)
618-
self.ql.mem.write(self.pe_image_address+SecurityCookie_rva, SecurityCookie_value)
619611

620612
# Add main PE to ldr_data_table
621613
mod_name = os.path.basename(self.path)

qiling/os/windows/dlls/kernel32/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from .winnt import *
1919
from .wow64apiset import *
2020
from .interlockedapi import *
21+
from .consoleapi import *
2122
from .consoleapi2 import *
2223
from .consoleapi3 import *
2324
from .debugapi import *
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
4+
#
5+
6+
from qiling import Qiling
7+
from qiling.os.windows.api import *
8+
from qiling.os.windows.fncc import *
9+
10+
# BOOL WINAPI GetConsoleMode(
11+
# _In_ HANDLE hConsoleHandle,
12+
# _Out_ LPDWORD lpMode
13+
# );
14+
@winsdkapi(cc=STDCALL, params={
15+
'hConsoleHandle' : HANDLE,
16+
'lpMode' : LPDWORD
17+
})
18+
def hook_GetConsoleMode(ql: Qiling, address: int, params):
19+
return 1

qiling/os/windows/dlls/kernel32/fileapi.py

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,12 @@ def hook_ReadFile(ql: Qiling, address: int, params):
171171
read_len = nNumberOfBytesToRead
172172

173173
ql.mem.write(lpBuffer, s)
174-
ql.mem.write(lpNumberOfBytesRead, ql.pack(read_len))
174+
ql.mem.write(lpNumberOfBytesRead, ql.pack32(read_len))
175175
else:
176176
f = ql.os.handle_manager.get(hFile).obj
177177
data = f.read(nNumberOfBytesToRead)
178178
ql.mem.write(lpBuffer, data)
179-
ql.mem.write(lpNumberOfBytesRead, ql.pack32(lpNumberOfBytesRead))
179+
ql.mem.write(lpNumberOfBytesRead, ql.pack32(len(data)))
180180

181181
return 1
182182

@@ -204,7 +204,7 @@ def hook_WriteFile(ql: Qiling, address: int, params):
204204
s = ql.mem.read(lpBuffer, nNumberOfBytesToWrite)
205205
ql.os.stdout.write(s)
206206
ql.os.utils.string_appearance(s.decode())
207-
ql.mem.write(lpNumberOfBytesWritten, ql.pack(nNumberOfBytesToWrite))
207+
ql.mem.write(lpNumberOfBytesWritten, ql.pack32(nNumberOfBytesToWrite))
208208
else:
209209
f = ql.os.handle_manager.get(hFile)
210210

@@ -216,8 +216,8 @@ def hook_WriteFile(ql: Qiling, address: int, params):
216216
f = f.obj
217217

218218
buffer = ql.mem.read(lpBuffer, nNumberOfBytesToWrite)
219-
f.write(bytes(buffer))
220-
ql.mem.write(lpNumberOfBytesWritten, ql.pack32(nNumberOfBytesToWrite))
219+
nNumberOfBytesWritten = f.write(bytes(buffer))
220+
ql.mem.write(lpNumberOfBytesWritten, ql.pack32(nNumberOfBytesWritten))
221221

222222
return 1
223223

@@ -235,7 +235,7 @@ def _CreateFile(ql: Qiling, address: int, params):
235235
if dwDesiredAccess & GENERIC_WRITE:
236236
mode += "wb"
237237
else:
238-
mode += "r"
238+
mode += "rb"
239239

240240
try:
241241
f = ql.os.fs_mapper.open(s_lpFileName, mode)
@@ -663,3 +663,47 @@ def hook_SetFileAttributesA(ql: Qiling, address: int, params):
663663
})
664664
def hook_SetFileAttributesW(ql: Qiling, address: int, params):
665665
return 1
666+
667+
# BOOL AreFileApisANSI();
668+
@winsdkapi(cc=STDCALL, params={})
669+
def hook_AreFileApisANSI(ql: Qiling, address: int, params):
670+
# TODO make this coherent with SetFileApisToANSI/OEM calls
671+
return 1
672+
673+
# void SetFileApisToANSI();
674+
@winsdkapi(cc=STDCALL, params={})
675+
def hook_SetFileApisToANSI(ql: Qiling, address: int, params):
676+
pass
677+
678+
# void SetFileApisToOEM();
679+
@winsdkapi(cc=STDCALL, params={})
680+
def hook_SetFileApisToOEM(ql: Qiling, address: int, params):
681+
pass
682+
683+
# BOOL DeleteFileA(
684+
# LPCSTR lpFileName
685+
# );
686+
@winsdkapi(cc=STDCALL, params={
687+
'lpFileName' : LPCSTR
688+
})
689+
def hook_DeleteFileA(ql: Qiling, address: int, params):
690+
lpFileName = ql.os.path.transform_to_real_path(params["lpFileName"])
691+
try:
692+
os.remove(lpFileName)
693+
return 1
694+
except:
695+
return 0
696+
697+
# BOOL DeleteFileW(
698+
# LPCWSTR lpFileName
699+
# );
700+
@winsdkapi(cc=STDCALL, params={
701+
'lpFileName' : LPCWSTR
702+
})
703+
def hook_DeleteFileW(ql: Qiling, address: int, params):
704+
lpFileName = ql.os.path.transform_to_real_path(params["lpFileName"])
705+
try:
706+
os.remove(lpFileName)
707+
return 1
708+
except:
709+
return 0

qiling/os/windows/dlls/kernel32/libloaderapi.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ def hook_GetModuleHandleExW(ql: Qiling, address: int, params):
6363
res = _GetModuleHandle(ql, address, params)
6464
dst = params["phModule"]
6565

66-
ql.mem.write(dst, ql.pack32(res))
66+
ql.mem.write(dst, ql.pack(res))
6767

68-
return 1
68+
return res
6969

7070
# DWORD GetModuleFileNameA(
7171
# HMODULE hModule,

qiling/os/windows/dlls/kernel32/winbase.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -766,12 +766,3 @@ def hook_GetPrivateProfileStringA(ql: Qiling, address: int, params):
766766
})
767767
def hook_WritePrivateProfileStringA(ql: Qiling, address: int, params):
768768
pass
769-
770-
# BOOL DeleteFileA(
771-
# LPCSTR lpFileName
772-
# );
773-
@winsdkapi(cc=STDCALL, params={
774-
'lpFileName' : LPCSTR
775-
})
776-
def hook_DeleteFileA(ql: Qiling, address: int, params):
777-
return 1

tests/test_pe.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,28 @@ def _t():
8787
self.assertTrue(QLWinSingleTest(_t).run())
8888

8989

90+
def test_pe_win_x8664_file_upx(self):
91+
def _t():
92+
ql = Qiling(["../examples/rootfs/x8664_windows/bin/x8664_file_upx.exe"], "../examples/rootfs/x8664_windows",
93+
verbose=QL_VERBOSE.DEFAULT)
94+
ql.run()
95+
del ql
96+
return True
97+
98+
self.assertTrue(QLWinSingleTest(_t).run())
99+
100+
101+
def test_pe_win_x86_file_upx(self):
102+
def _t():
103+
ql = Qiling(["../examples/rootfs/x86_windows/bin/x86_file_upx.exe"], "../examples/rootfs/x86_windows",
104+
verbose=QL_VERBOSE.DEFAULT)
105+
ql.run()
106+
del ql
107+
return True
108+
109+
self.assertTrue(QLWinSingleTest(_t).run())
110+
111+
90112
def test_pe_win_x86_uselessdisk(self):
91113
def _t():
92114
if 'QL_FAST_TEST' in os.environ:
@@ -98,7 +120,7 @@ def read(self, size):
98120

99121
def write(self, bs):
100122
print(bs)
101-
return
123+
return len(bs)
102124

103125
def fstat(self):
104126
return -1

0 commit comments

Comments
 (0)