Skip to content

Commit

Permalink
Merge pull request mandiant#156 from jsherman212/justin
Browse files Browse the repository at this point in the history
Bug fixes, CryptDecrypt, CryptDeriveKey, SetWindowLong
  • Loading branch information
drewvis authored Jun 25, 2021
2 parents 7834129 + 79da526 commit 483283c
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 2 deletions.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ capstone
lznt1
unicorn==1.0.2
jsonschema
pycryptodome
2 changes: 1 addition & 1 deletion speakeasy/binemu.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ def read_mem_string(self, address, width=1, max_chars=0):
i = 0

if width == 1:
decode = 'utf-8'
decode = 'latin1'
elif width == 2:
decode = 'utf-16le'
else:
Expand Down
13 changes: 12 additions & 1 deletion speakeasy/windows/winemu.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ def map_pe(self, pe, mod_name='none', emu_path=''):
"""
Map the specified PE into the emulation space
"""
image_size = len(pe.mapped_image)
image_size = pe.image_size
base = pe.base
ranges = self.get_valid_ranges(image_size, addr=base)
base, size = ranges
Expand Down Expand Up @@ -1932,6 +1932,8 @@ def _dispatch_seh_x86(self, except_code):
"registers": regs,
})

# EBX clobber, -1 is what I observed inside a VM
self.reg_write(_arch.X86_REG_EBX, 0xffffffff)
self.set_pc(entry.Handler)
return True
return False
Expand All @@ -1954,6 +1956,15 @@ def _continue_seh_x86(self):
if seh.handler_ret_val is None:
seh.handler_ret_val = ret_val

ctx = seh.get_context()

if seh.context_address:
ctx = self.mem_cast(ctx, seh.context_address)

# Always restore thread context, is it correct to always
# do this?
self.load_thread_context(ctx)

for frame in seh.get_frames():
if not frame.searched:
seh.set_current_frame(frame)
Expand Down
96 changes: 96 additions & 0 deletions speakeasy/winenv/api/usermode/advapi32.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import speakeasy.windows.objman as objman

from .. import api
from Crypto.Cipher import ARC4
import hashlib

SERVICE_STATUS_HANDLE_BASE = 0x1000
Expand All @@ -35,6 +36,8 @@ def __init__(self, emu):
self.curr_handle = 0x2800
self.service_status_handle = SERVICE_STATUS_HANDLE_BASE

self.rc4 = None

super(AdvApi32, self).__get_hook_attrs__(self)

def get_handle(self):
Expand Down Expand Up @@ -1113,6 +1116,99 @@ def CryptDestroyHash(self, emu, argv, ctx={}):

return 1

@apihook('CryptDeriveKey', argc=5)
def CryptDeriveKey(self, emu, argv, ctx={}):
"""
BOOL CryptDeriveKey(
HCRYPTPROV hProv,
ALG_ID Algid,
HCRYPTHASH hBaseData,
DWORD dwFlags,
HCRYPTKEY *phKey
);
"""

hProv, Algid, hBaseData, dwFlags, phKey = argv

# Only RC4 supported right now
if Algid != 0x6801:
return 0

hnd = self.hash_objects.get(hBaseData, None)

if hnd is None:
emu.set_last_error(windefs.ERROR_INVALID_HANDLE)
return 0

# CryptDeriveKey zeroes out the last 11 bytes of the hash,
# so we gotta do the same before it is written to the
# phKey structure
fixed_digest = hnd.digest()[:5] + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

ptrsz = emu.get_ptr_size()

hKey = self.win.HCRYPTKEY(ptrsz)
hKey.Algid = Algid
hKey.keylen = hnd.digest_size
hKey.keyp = self.mem_alloc(hKey.keylen)

hKeyp = self.mem_alloc(hKey.sizeof())

self.mem_write(hKey.keyp, fixed_digest)

self.mem_write(hKeyp, hKey.get_bytes())
self.mem_write(phKey, hKeyp.to_bytes(ptrsz, "little"))

return 1

@apihook('CryptDecrypt', argc=6)
def CryptDecrypt(self, emu, argv, ctx={}):
"""
BOOL CryptDecrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash,
BOOL Final,
DWORD dwFlags,
BYTE *pbData,
DWORD *pdwDataLen
);
"""

hKey, hHash, Final, dwFlags, pbData, pdwDataLen = argv

# Hashing not supported
if hHash:
return 0

ptrsz = emu.get_ptr_size()

hKey = self.mem_cast(self.win.HCRYPTKEY(ptrsz), hKey)

# Only RC4 supported right now
if hKey.Algid != 0x6801:
return 0

encdatalen_b = self.mem_read(pdwDataLen, 4)
encdatalen = int.from_bytes(encdatalen_b, "little")

encdata = self.mem_read(pbData, encdatalen)

key = self.mem_read(hKey.keyp, hKey.keylen)

if self.rc4 is None:
self.rc4 = ARC4.new(key)

dec = self.rc4.decrypt(encdata)
declen = len(dec)

self.mem_write(pbData, dec)
self.mem_write(pdwDataLen, int.to_bytes(declen, 4, "little"))

if Final == True:
self.rc4 = None

return 1

@apihook('RegGetValue', argc=7, conv=_arch.CALL_CONV_STDCALL)
def RegGetValue(self, emu, argv, ctx={}):
'''
Expand Down
13 changes: 13 additions & 0 deletions speakeasy/winenv/api/usermode/user32.py
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,19 @@ def GetWindowLong(self, emu, argv, ctx={}):

return rv

@apihook('SetWindowLong', argc=3)
def SetWindowLong(self, emu, argv, ctx={}):
"""
LONG SetWindowLongA(
HWND hWnd,
int nIndex,
LONG dwNewLong
);
"""


return 1

@apihook('DialogBoxParam', argc=5)
def DialogBoxParam(self, emu, argv, ctx={}):
'''
Expand Down
8 changes: 8 additions & 0 deletions speakeasy/winenv/defs/windows/advapi32.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (C) 2020 FireEye, Inc. All Rights Reserved.

from speakeasy.struct import EmuStruct, Ptr
import ctypes as ct

NTE_BAD_ALGID = 0x80090008

Expand All @@ -15,6 +16,13 @@ def __init__(self, ptr_size):
self.lpServiceName = Ptr
self.lpServiceProc = Ptr

class HCRYPTKEY(EmuStruct):
def __init__(self, ptr_size):
super().__init__(ptr_size)
self.Algid = ct.c_uint32
self.keylen = ct.c_uint32
self.keyp = Ptr


def get_define_int(define, prefix=''):
for k, v in globals().items():
Expand Down

0 comments on commit 483283c

Please sign in to comment.