Skip to content

Commit

Permalink
Merge pull request #231 from HongThatCong/master
Browse files Browse the repository at this point in the history
Add more API and some bugs fix
  • Loading branch information
williballenthin authored Mar 24, 2023
2 parents 5c0a507 + 528fc6f commit 078db94
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 54 deletions.
2 changes: 1 addition & 1 deletion speakeasy/windows/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
# Blank header used for a 64-bit PE header
EMPTY_PE_64 = DOS_HEADER + b'PE\x00\x00d\x86\x00\x00ABCD\x00\x00\x00\x00\x00\x00\x00\x00' \
b'\xf0\x00\x03\x10\x0b\x02\x08\x00\x04\x00\x00\x00\x00\x00\x00' \
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@' \
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00@' \
b'\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x06\x00' \
b'\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xb8' \
b'\x01\x00\x00\x00\x00\x00\x00AAAA\x02\x00\x00\x04\x00\x00\x10' \
Expand Down
10 changes: 6 additions & 4 deletions speakeasy/windows/win32.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,15 @@ def add_vectored_exception_handler(self, first, handler):
"""
Add a vectored exception handler that will be executed on an exception
"""
self.veh_handlers.append(handler)
if handler not in self.veh_handlers:
self.veh_handlers.append(handler)

def remove_vectored_exception_handler(self, handler):
"""
Remove a vectored exception handler
"""
self.veh_handlers.remove(handler)
if handler in self.veh_handlers:
self.veh_handlers.remove(handler)

def get_processes(self):
if len(self.processes) <= 1:
Expand Down Expand Up @@ -232,7 +234,7 @@ def load_module(self, path=None, data=None, first_time_setup=True):
pass

self.mem_map(pe.image_size, base=base,
tag='emu.module.%s' % (self.mod_name))
tag='emu.module.%s' % (self.mod_name))

self.modules.append((pe, ranges, emu_path))
self.mem_write(pe.base, pe.mapped_image)
Expand Down Expand Up @@ -392,7 +394,7 @@ def run_module(self, module, all_entrypoints=False, emulate_children=False):
child = self.child_processes.pop(0)

child.pe = self.load_module(data=child.pe_data,
first_time_setup=False)
first_time_setup=False)
self.prepare_module_for_emulation(child.pe, all_entrypoints)

self.command_line = child.cmdline
Expand Down
3 changes: 1 addition & 2 deletions speakeasy/windows/winemu.py
Original file line number Diff line number Diff line change
Expand Up @@ -1762,8 +1762,7 @@ def init_module(self, modconf={}, name='none', emu_path='', default_base=None):

mod.decoy_path = modconf.get('path', emu_path) or (name + '.dll')
# Reserve memory for the module
res, size = self.get_valid_ranges(mod.image_size,
base)
res, size = self.get_valid_ranges(mod.image_size, base)
mod.decoy_base = res
mod.name = modconf.get('name', name)
self.mem_reserve(size, base=res, tag='emu.module.%s' % (mod.name),
Expand Down
6 changes: 3 additions & 3 deletions speakeasy/winenv/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def mem_copy(self, dst, src, n):
return self.emu.mem_copy(dst, src, n)

def read_mem_string(self, addr, width, max_chars=0):
string = self.emu.read_mem_string(addr, width=width)
string = self.emu.read_mem_string(addr, width=width, max_chars=max_chars)
return string

def mem_string_len(self, addr, width):
Expand All @@ -165,14 +165,14 @@ def read_ansi_string(self, addr):
ans = ntos.STRING(self.emu.get_ptr_size())
ans = self.mem_cast(ans, addr)

string = self.emu.read_mem_string(ans.Buffer, width=1)
string = self.emu.read_mem_string(ans.Buffer, width=1, max_chars=ans.Length)
return string

def read_unicode_string(self, addr):
us = ntos.UNICODE_STRING(self.emu.get_ptr_size())
us = self.mem_cast(us, addr)

string = self.emu.read_mem_string(us.Buffer, width=2)
string = self.emu.read_mem_string(us.Buffer, width=2, max_chars=us.Length // 2)
return string

def read_wide_string(self, addr, max_chars=0):
Expand Down
6 changes: 3 additions & 3 deletions speakeasy/winenv/api/kernelmode/ntoskrnl.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def get_current_irql(self):

def set_current_irql(self, irql):
return self.emu.set_current_irql(irql)

def win_perms_to_emu_perms(self, win_perms):
"""
Maps Windows permissions to emulator engine permissions
Expand Down Expand Up @@ -600,14 +600,14 @@ def ZwQuerySystemInformation(self, emu, argv, ctx={}):
size = len(out)
self.mem_write(sysinfo, out)
nts = ddk.STATUS_SUCCESS

elif sysclass == ddk.SYSTEM_INFORMATION_CLASS.SystemCodeIntegrityInformation:
if sysinfo and syslen >= 8:
class_len = (8).to_bytes(4, "little")
flags = (1).to_bytes(4, "little")
self.mem_write(sysinfo, class_len + flags)
nts = ddk.STATUS_SUCCESS

elif sysclass == ddk.SYSTEM_INFORMATION_CLASS.SystemProcessInformation:
procs = emu.get_processes()
for proc in procs:
Expand Down
13 changes: 13 additions & 0 deletions speakeasy/winenv/api/usermode/advapi32.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,19 @@ def RegisterServiceCtrlHandler(self, emu, argv, ctx={}):

return self.service_status_handle

@apihook('RegisterServiceCtrlHandlerEx', argc=3)
def RegisterServiceCtrlHandlerEx(self, emu, argv, ctx={}):
'''
SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerExA(
LPCSTR lpServiceName,
LPHANDLER_FUNCTION_EX lpHandlerProc,
LPVOID lpContext
);
'''
lpServiceName, lpHandlerProc, lpContext = argv

return self.RegisterServiceCtrlHandler(self, emu, [lpServiceName, lpHandlerProc], ctx)

@apihook('SetServiceStatus', argc=2)
def SetServiceStatus(self, emu, argv, ctx={}):
'''
Expand Down
70 changes: 49 additions & 21 deletions speakeasy/winenv/api/usermode/kernel32.py
Original file line number Diff line number Diff line change
Expand Up @@ -1673,10 +1673,10 @@ def IsProcessorFeaturePresent(self, emu, argv, ctx={}):
);'''

rv = 1
'''
'''
Not all the features must return 1, because those can represent a feature which can be unavailable
for your processor. For example PF_FLOATING_POINT_PRECISION_ERRATA is a Pentium instructions
which doesn't exist on new ones, and malware developers are using it to see if they are in an
for your processor. For example PF_FLOATING_POINT_PRECISION_ERRATA is a Pentium instructions
which doesn't exist on new ones, and malware developers are using it to see if they are in an
emulated environment or not.
To get the correct value you just need write an app to check all the fatures, something like:
Expand Down Expand Up @@ -1730,10 +1730,10 @@ def IsProcessorFeaturePresent(self, emu, argv, ctx={}):
40:{"name":"PF_AVX2_INSTRUCTIONS_AVAILABLE","return":1},
41:{"name":"PF_AVX512F_INSTRUCTIONS_AVAILABLE","return":0},
}

rv = lookup[argv[0]]["return"]
argv[0] = lookup[argv[0]]["name"]

return rv

@apihook('lstrcmpi', argc=2)
Expand Down Expand Up @@ -3225,7 +3225,7 @@ def MapViewOfFile(self, emu, argv, ctx={}):

fname = ntpath.basename(f.get_path())
fname = fname.replace('.', '_')

# If the call to CreateFileMapping (done before calling this API)
# has beed done with SEC_IMAGE protection, the mapping is not
# done as a contigous stream of bytes, but it is mapped as
Expand All @@ -3237,7 +3237,7 @@ def MapViewOfFile(self, emu, argv, ctx={}):
base, size = emu.get_valid_ranges(pe.image_size)
while base and base & 0xFFF:
base, size = emu.get_valid_ranges(size)

emu.mem_map(pe.image_size, base=base,tag='%s.%s.0x%x' % (tag_prefix, fname, base))
mapping.add_view(base, full_offset, size, access)
self.mem_write(base, pe.mapped_image)
Expand Down Expand Up @@ -3411,9 +3411,9 @@ def RemoveDirectory(self, emu, argv, ctx={}):
if pn:
target = self.read_mem_string(pn, cw)
argv[0] = target

return True

@apihook('CopyFile', argc=3)
def CopyFile(self, emu, argv, ctx={}):
'''
Expand Down Expand Up @@ -3727,7 +3727,7 @@ def CloseHandle(self, emu, argv, ctx={}):
emu.dec_ref(obj)
return True
return False

@apihook('SetEndOfFile', argc=1)
def SetEndOfFile(self, emu, argv, ctx={}):
'''
Expand Down Expand Up @@ -5248,6 +5248,15 @@ def RtlZeroMemory(self, emu, argv, ctx={}):
buf = b'\x00' * length
self.mem_write(dest, buf)

@apihook('RtlMoveMemory', argc=3)
def RtlMoveMemory(self, emu, argv, ctx={}):
"""
void RtlMoveMemory(void* pvDest, const void *pSrc, size_t Length);
"""
dest, source, length = argv
buf = self.mem_read(source, length)
self.mem_write(dest, buf)

@apihook('QueryPerformanceFrequency', argc=1)
def QueryPerformanceFrequency(self, emu, argv, ctx={}):
"""
Expand Down Expand Up @@ -5766,7 +5775,7 @@ def GetModuleFileNameExA(self, emu, argv, ctx={}):
proc = self.get_object_from_handle(hProcess)

if proc == None:
return
return

filename = proc.get_process_path()

Expand Down Expand Up @@ -5832,6 +5841,16 @@ def AddVectoredExceptionHandler(self, emu, argv, ctx={}):

return Handler

@apihook('RemoveVectoredExceptionHandler', argc=1)
def RemoveVectoredExceptionHandler(self, emu, argv, ctx={}):
'''
ULONG RemoveVectoredExceptionHandler(
PVOID Handle);
'''
Handler = argv
emu.remove_vectored_exception_handler(Handler)
return 1

@apihook("GetSystemDefaultUILanguage", argc=0)
def GetSystemDefaultUILanguage(self, emu, argv, ctx={}):
'''
Expand Down Expand Up @@ -5942,21 +5961,21 @@ def _lclose(self, emu, argv, ctx={}):
def GetConsoleTitle(self, emu, argv, ctx={}):
'''
DWORD WINAPI GetConsoleTitle(
_Out_ LPTSTR lpConsoleTitle,
_In_  DWORD  nSize
);
'''
_Out_ LPTSTR lpConsoleTitle,
_In_ DWORD nSize
);
'''
lpConsoleTitle, nSize = argv
cw = self.get_char_width(ctx)
rv = False

# TODO: consider enumeration logic
temp_title = "explorer.exe"
if cw == 2:
temp_title = temp_title.encode('utf-16le') + b'\x00\x00'
else:
temp_title = temp_title.encode('utf-8') + b'\x00'

if cw == 2:
temp_title = temp_title.encode('utf-16le') + b'\x00\x00'
else:
temp_title = temp_title.encode('utf-8') + b'\x00'

argv[0] = temp_title
argv[1] = len(temp_title)
Expand Down Expand Up @@ -6036,3 +6055,12 @@ def GetPhysicallyInstalledSystemMemory(self, emu, argv, ctx={}):
@apihook('WTSGetActiveConsoleSessionId', argc=0)
def WTSGetActiveConsoleSessionId(self, emu, argv, ctx={}):
return emu.get_current_process().get_session_id()

@apihook('WaitForSingleObjectEx', argc=3)
def WaitForSingleObjectEx(self, emu, argv, ctx={}):
return 0 # = WAIT_OBJECT_0

@apihook('GetProfileInt', argc=3)
def GetProfileInt(self, emu, argv, ctx={}):
_, _, nDefault = argv
return nDefault
42 changes: 28 additions & 14 deletions speakeasy/winenv/api/usermode/ntdll.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.

import os
import binascii
import binascii

from .. import api

Expand Down Expand Up @@ -37,6 +37,11 @@ def RtlGetLastWin32Error(self, emu, argv, ctx={}):

return emu.get_last_error()

@apihook('RtlNtStatusToDosError', argc=1)
def RtlNtStatusToDosError(self, emu, argv, ctx={}):
'''ULONG RtlNtStatusToDosError(NTSTATUS Status);'''
return 0

@apihook('RtlFlushSecureMemoryCache', argc=2)
def RtlFlushSecureMemoryCache(self, emu, argv, ctx={}):
'''DWORD RtlFlushSecureMemoryCache(PVOID arg0, PVOID arg1);'''
Expand Down Expand Up @@ -146,7 +151,7 @@ def LdrGetProcedureAddress(self, emu, argv, ctx={}):
fn = ntos.STRING(emu.get_ptr_size())
fn = self.mem_cast(fn, proc_name)

proc = self.read_mem_string(fn.Buffer, 1)
proc = self.read_mem_string(fn.Buffer, 1, max_chars=fn.Length)
argv[1] = proc

elif ordinal:
Expand Down Expand Up @@ -175,6 +180,15 @@ def RtlZeroMemory(self, emu, argv, ctx={}):
buf = b'\x00' * length
self.mem_write(dest, buf)

@apihook('RtlMoveMemory', argc=3)
def RtlMoveMemory(self, emu, argv, ctx={}):
"""
void RtlMoveMemory(void* pvDest, const void *pSrc, size_t Length);
"""
dest, source, length = argv
buf = self.mem_read(source, length)
self.mem_write(dest, buf)

@apihook('NtSetInformationProcess', argc=4)
def NtSetInformationProcess(self, emu, argv, ctx={}):
"""
Expand Down Expand Up @@ -237,7 +251,7 @@ def NtWaitForSingleObject(self, emu, argv, ctx={}):
rv = ddk.STATUS_SUCCESS

return rv

@apihook('RtlComputeCrc32', argc=3)
def RtlComputeCrc32(self, emu, argv, ctx={}):
'''
Expand All @@ -258,9 +272,9 @@ def RtlComputeCrc32(self, emu, argv, ctx={}):
def LdrFindResource_U(self, emu, argv, ctx={}):
'''
pub unsafe extern "system" fn LdrFindResource_U(
DllHandle: PVOID,
ResourceInfo: PLDR_RESOURCE_INFO,
Level: ULONG,
DllHandle: PVOID,
ResourceInfo: PLDR_RESOURCE_INFO,
Level: ULONG,
ResourceDataEntry: *mut PIMAGE_RESOURCE_DATA_ENTRY
) -> NTSTATUS
Expand All @@ -280,7 +294,7 @@ def LdrFindResource_U(self, emu, argv, ctx={}):
'''
DllHandle, ResourceInfo, Level, ResourceDataEntry = argv

# Reusing some functions from kernel32 module that are used to
# Reusing some functions from kernel32 module that are used to
# handle the very similar function FindResourceA
k32 = emu.api.mods.get('kernel32')

Expand Down Expand Up @@ -313,15 +327,15 @@ def LdrFindResource_U(self, emu, argv, ctx={}):
self.mem_write(ptr_data_entry+4, resource['size'].to_bytes(4, 'little'))

return hnd

@apihook('LdrAccessResource', argc=4)
def LdrAccessResource(self, emu, argv, ctx={}):
'''
NTSTATUS NTAPI LdrAccessResource ( _In_ PVOID BaseAddress,
_In_ PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry,
_Out_opt_ PVOID * Resource,
_Out_opt_ PULONG Size
)
NTSTATUS NTAPI LdrAccessResource ( _In_ PVOID BaseAddress,
_In_ PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry,
_Out_opt_ PVOID * Resource,
_Out_opt_ PULONG Size
)
'''
BaseAddress, ResourceDataEntry, Resource, Size = argv

Expand All @@ -331,6 +345,6 @@ def LdrAccessResource(self, emu, argv, ctx={}):
# Fill in the Resource struct
self.mem_write(Size, size.to_bytes(4, 'little'))
self.mem_write(Resource, offset.to_bytes(4, 'little'))

return 0

Loading

0 comments on commit 078db94

Please sign in to comment.