diff options
Diffstat (limited to 'src/miasm/os_dep')
| -rw-r--r-- | src/miasm/os_dep/__init__.py | 1 | ||||
| -rw-r--r-- | src/miasm/os_dep/common.py | 169 | ||||
| -rw-r--r-- | src/miasm/os_dep/linux/__init__.py | 1 | ||||
| -rw-r--r-- | src/miasm/os_dep/linux/environment.py | 993 | ||||
| -rw-r--r-- | src/miasm/os_dep/linux/syscall.py | 1129 | ||||
| -rw-r--r-- | src/miasm/os_dep/linux_stdlib.py | 213 | ||||
| -rw-r--r-- | src/miasm/os_dep/win_32_structs.py | 231 | ||||
| -rw-r--r-- | src/miasm/os_dep/win_api_x86_32.py | 3595 | ||||
| -rw-r--r-- | src/miasm/os_dep/win_api_x86_32_seh.py | 705 |
9 files changed, 7037 insertions, 0 deletions
diff --git a/src/miasm/os_dep/__init__.py b/src/miasm/os_dep/__init__.py new file mode 100644 index 00000000..6aa660d8 --- /dev/null +++ b/src/miasm/os_dep/__init__.py @@ -0,0 +1 @@ +"Operating System specific methods" diff --git a/src/miasm/os_dep/common.py b/src/miasm/os_dep/common.py new file mode 100644 index 00000000..74100817 --- /dev/null +++ b/src/miasm/os_dep/common.py @@ -0,0 +1,169 @@ +import os + +from future.utils import viewitems + +from miasm.core.utils import force_bytes, force_str +from miasm.jitter.csts import PAGE_READ, PAGE_WRITE +from miasm.core.utils import get_caller_name +from miasm.core.utils import pck64, upck64 + +BASE_SB_PATH = "file_sb" +WIN_CODEPAGE = "cp1252" + +def get_win_str_a(jitter, ad_str, max_char=None): + l = 0 + tmp = ad_str + while ((max_char is None or l < max_char) and + jitter.vm.get_mem(tmp, 1) != b"\x00"): + tmp += 1 + l += 1 + data = jitter.vm.get_mem(ad_str, l) + ret = data.decode(WIN_CODEPAGE) + return ret + + +def get_win_str_w(jitter, ad_str, max_char=None): + l = 0 + tmp = ad_str + while ((max_char is None or l < max_char) and + jitter.vm.get_mem(tmp, 2) != b"\x00\x00"): + tmp += 2 + l += 2 + s = jitter.vm.get_mem(ad_str, l) + s = s.decode("utf-16le") + return s + +def encode_win_str_a(value): + value = value.encode(WIN_CODEPAGE) + b"\x00" + return value + +def encode_win_str_w(value): + value = value.encode("utf-16le") + b'\x00' * 2 + return value + + +def set_win_str_a(jitter, addr, value): + value = encode_win_str_a(value) + jitter.vm.set_mem(addr, value) + + +def set_win_str_w(jitter, addr, value): + value = encode_win_str_w(value) + jitter.vm.set_mem(addr, value) + + +class heap(object): + + "Light heap simulation" + + addr = 0x20000000 + align = 0x1000 + size = 32 + mask = (1 << size) - 1 + + def next_addr(self, size): + """ + @size: the size to allocate + return the future checnk address + """ + ret = self.addr + self.addr = (self.addr + size + self.align - 1) + self.addr &= self.mask ^ (self.align - 1) + return ret + + def alloc(self, jitter, size, perm=PAGE_READ | PAGE_WRITE, cmt=""): + """ + @jitter: a jitter instance + @size: the size to allocate + @perm: permission flags (see vm_alloc doc) + """ + return self.vm_alloc(jitter.vm, size, perm=perm, cmt=cmt) + + def vm_alloc(self, vm, size, perm=PAGE_READ | PAGE_WRITE, cmt=""): + """ + @vm: a VmMngr instance + @size: the size to allocate + @perm: permission flags (PAGE_READ, PAGE_WRITE, PAGE_EXEC or any `|` + combination of them); default is PAGE_READ|PAGE_WRITE + """ + addr = self.next_addr(size) + vm.add_memory_page( + addr, + perm, + b"\x00" * (size), + "Heap alloc by %s %s" % (get_caller_name(2), cmt) + ) + return addr + + def get_size(self, vm, ptr): + """ + @vm: a VmMngr instance + @size: ptr to get the size of the associated allocation. + + `ptr` can be the base address of a previous allocation, or an address + within the allocated range. The size of the whole allocation is always + returned, regardless ptr is the base address or not. + """ + assert vm.is_mapped(ptr, 1) + data = vm.get_all_memory() + ptr_page = data.get(ptr, None) + if ptr_page is None: + for address, page_info in viewitems(data): + if address <= ptr < address + page_info["size"]: + ptr_page = page_info + break + else: + raise RuntimeError("Must never happen (unmapped but mark as mapped by API)") + return ptr_page["size"] + + +def windows_to_sbpath(path): + """Convert a Windows path to a valid filename within the sandbox + base directory. + + """ + path = [elt for elt in path.lower().replace('/', '_').split('\\') if elt] + return os.path.join(BASE_SB_PATH, *path) + + +def unix_to_sbpath(path): + """Convert a POSIX path to a valid filename within the sandbox + base directory. + + """ + path = [elt for elt in path.split('/') if elt] + return os.path.join(BASE_SB_PATH, *path) + +def get_fmt_args(fmt, cur_arg, get_str, get_arg_n): + idx = 0 + fmt = get_str(fmt) + chars_format = '%cdfsuxX' + char_percent = '%' + char_string = 's' + output = "" + + while True: + if idx == len(fmt): + break + char = fmt[idx:idx+1] + idx += 1 + if char == char_percent: + token = char_percent + while True: + char = fmt[idx:idx+1] + idx += 1 + token += char + if char in chars_format: + break + if char == char_percent: + output += char + continue + if token.endswith(char_string): + addr = get_arg_n(cur_arg) + arg = get_str(addr) + else: + arg = get_arg_n(cur_arg) + char = token % arg + cur_arg += 1 + output += char + return output diff --git a/src/miasm/os_dep/linux/__init__.py b/src/miasm/os_dep/linux/__init__.py new file mode 100644 index 00000000..4434ce50 --- /dev/null +++ b/src/miasm/os_dep/linux/__init__.py @@ -0,0 +1 @@ +# Linux emulation diff --git a/src/miasm/os_dep/linux/environment.py b/src/miasm/os_dep/linux/environment.py new file mode 100644 index 00000000..3ba4382f --- /dev/null +++ b/src/miasm/os_dep/linux/environment.py @@ -0,0 +1,993 @@ +from __future__ import print_function +from collections import namedtuple +import functools +import logging +import os +import re +import struct +import termios + +from future.utils import viewitems + +from miasm.core.interval import interval +from miasm.jitter.csts import PAGE_READ, PAGE_WRITE + + +REGEXP_T = type(re.compile(r'')) + +StatInfo = namedtuple("StatInfo", [ + "st_dev", "st_ino", "st_nlink", "st_mode", "st_uid", "st_gid", "st_rdev", + "st_size", "st_blksize", "st_blocks", "st_atime", "st_atimensec", + "st_mtime", "st_mtimensec", "st_ctime", "st_ctimensec" +]) +StatFSInfo = namedtuple("StatFSInfo", [ + "f_type", "f_bsize", "f_blocks", "f_bfree", "f_bavail", "f_files", + "f_ffree", "f_fsid", "f_namelen", "f_frsize", "f_flags", "f_spare", +]) + +log = logging.getLogger("environment") +console_handler = logging.StreamHandler() +console_handler.setFormatter(logging.Formatter("[%(levelname)-8s]: %(message)s")) +log.addHandler(console_handler) +log.setLevel(logging.WARNING) + +class FileDescriptor(object): + """Stand for a file descriptor on a system + + According to inode(7), following types are possibles: + - socket + - symbolic link + - regular file + - block device + - directory + - character device + - FIFO + """ + + # st_mode's file type + file_type = None + # st_mode's file mode (9 least bits are file permission bits) + file_mode = 0o0777 + # st_dev / st_rdev + cont_device_id = None + device_id = 0 + # inode number (st_ino) + inode = None + # Number of hardlink (st_nlink) + nlink = 0 + # Owner / group + uid = None + gid = None + # Size (st_size / st_blksize / st_blocks) + size = 0 + blksize = 0 + blocks = 0 + # Times + atime = 0 + atimensec = 0 + mtime = 0 + mtimensec = 0 + ctime = 0 + ctimensec = 0 + + def __init__(self, number): + self.number = number + self.is_closed = False + + def stat(self): + mode = self.file_type | self.file_mode + return StatInfo( + st_dev=self.cont_device_id, st_ino=self.inode, + st_nlink=self.nlink, st_mode=mode, + st_uid=self.uid, st_gid=self.gid, + st_rdev=self.device_id, st_size=self.size, + st_blksize=self.blksize, st_blocks=self.blocks, + st_atime=self.atime, st_atimensec=self.atimensec, + st_mtime=self.mtime, st_mtimensec=self.mtimensec, + st_ctime=self.ctime, st_ctimensec=self.ctimensec + ) + + def close(self): + self.is_closed = True + + +class FileDescriptorCharDevice(FileDescriptor): + file_type = 0o0020000 # S_IFCHR + file_mode = 0o0620 + cont_device_id = 1 + device_id = 1 + + +class FileDescriptorSTDIN(FileDescriptorCharDevice): + """Special file descriptor standinf for STDIN""" + inode = 0 + + def read(self, count): + raise RuntimeError("Not implemented") + + +class FileDescriptorSTDOUT(FileDescriptorCharDevice): + """Special file descriptor standinf for STDOUT""" + inode = 1 + + def write(self, data): + print("[STDOUT] %s" % data.rstrip()) + + +class FileDescriptorSTDERR(FileDescriptorCharDevice): + """Special file descriptor standinf for STDERR""" + inode = 2 + + def write(self, data): + print("[STDERR] %s" % data.rstrip()) + + +class FileDescriptorDirectory(FileDescriptor): + """FileDescription designing a directory""" + + file_type = 0o0040000 # S_IFDIR + + def __init__(self, number, flags, filesystem, real_path): + super(FileDescriptorDirectory, self).__init__(number) + self.filesystem = filesystem + self.real_path = real_path + self.cur_listdir = None + self.flags = flags + + def listdir(self): + if self.cur_listdir is None: + self.cur_listdir = os.listdir(self.real_path) + while self.cur_listdir: + yield self.cur_listdir.pop() + + +class FileDescriptorRegularFile(FileDescriptor): + """FileDescriptor designing a regular file""" + + file_type = 0o0100000 # S_IFREG + + def __init__(self, number, flags, filesystem, real_fd): + super(FileDescriptorRegularFile, self).__init__(number) + self.flags = flags + self.filesystem = filesystem + self.real_fd = real_fd + + def write(self, data): + raise RuntimeError("Not implemented") + + def read(self, count): + return os.read(self.real_fd, count) + + def close(self): + super(FileDescriptorRegularFile, self).close() + return os.close(self.real_fd) + + def lseek(self, offset, whence): + return os.lseek(self.real_fd, offset, whence) # SEEK_SET + + def tell(self): + return self.lseek(0, 1) # SEEK_CUR + + def seek(self, offset): + return self.lseek(offset, 0) # SEEK_SET + + +class FileDescriptorSocket(FileDescriptor): + """FileDescription standing for a socket""" + + file_type = 0o0140000 # S_IFSOCK + + def __init__(self, number, family, type_, protocol): + super(FileDescriptorSocket, self).__init__(number) + self.family = family + self.type_ = type_ + self.protocol = protocol + + +class FileSystem(object): + """File system abstraction + Provides standard operations on the filesystem, (a bit like FUSE) + + API using FileSystem only used sandbox-side path. FileSystem should be the + only object able to interact with real path, outside the sandbox. + + Thus, if `resolve_path` is correctly implemented and used, it should not be + possible to modify files outside the sandboxed path + """ + + device_id = 0x1234 # ID of device containing file (stat.st_dev) + blocksize = 0x1000 # Size of block on this filesystem + f_type = 0xef53 # (Type of filesystem) EXT4_SUPER_MAGIC + nb_total_block = 0x1000 + nb_free_block = 0x100 + nb_avail_block = nb_free_block # Available to unprivileged user + nb_total_fnode = 100 # Total file nodes in filesystem + nb_free_fnode = 50 + max_filename_len = 256 + fragment_size = 0 + mount_flags = 0 + + def __init__(self, base_path, linux_env): + self.base_path = base_path + self.linux_env = linux_env + self.passthrough = [] + self.path_to_inode = {} # Real path (post-resolution) -> inode number + + def resolve_path(self, path, follow_link=True): + """Resolve @path to the corresponding sandboxed path""" + + # path_bytes is used for Python 2 / Python 3 compatibility + path_bytes = not isinstance(path, str) + path_sep = os.path.sep.encode() if path_bytes else os.path.sep + + if path_bytes: + def _convert(subpath): + if not isinstance(subpath, str): + return subpath + return subpath.encode() + def _convert_re(expr): + if isinstance(expr.pattern, str): + try: + return re.compile( + expr.pattern.encode(), + flags=expr.flags & ~re.UNICODE + ) + except UnicodeEncodeError: + # Will never match + log.warning( + 'Cannot convert regexp to bytes %r %r', + expr.pattern, + expr.flags, + exc_info=True, + ) + return re.compile(b'$X') + return expr + else: + def _convert(subpath): + if not isinstance(subpath, str): + return subpath.decode() + return subpath + def _convert_re(expr): + if not isinstance(expr.pattern, str): + try: + return re.compile( + expr.pattern.decode(), + flags=expr.flags & re.UNICODE + ) + except UnicodeDecodeError: + # Will never match + log.warning( + 'Cannot convert regexp to str %r %r', + expr.pattern, + expr.flags, + exc_info=True, + ) + return re.compile(r'$X') + return expr + + # Remove '../', etc. + path = os.path.normpath(path) + + # Passthrough + for passthrough in self.passthrough: + if isinstance(passthrough, REGEXP_T): + if _convert_re(passthrough).match(path): + return path + elif _convert(passthrough) == path: + return path + + # Remove leading '/' if any + path = path.lstrip(path_sep) + + base_path = os.path.abspath(_convert(self.base_path)) + out_path = os.path.join(base_path, path) + assert out_path.startswith(base_path + path_sep) + if os.path.islink(out_path): + link_target = os.readlink(out_path) + # Link can be absolute or relative -> absolute + link = os.path.normpath(os.path.join(os.path.dirname(path), link_target)) + if follow_link: + out_path = self.resolve_path(link) + else: + out_path = link + return out_path + + def get_path_inode(self, real_path): + inode = self.path_to_inode.setdefault(real_path, len(self.path_to_inode)) + return inode + + def exists(self, path): + sb_path = self.resolve_path(path) + return os.path.exists(sb_path) + + def readlink(self, path): + sb_path = self.resolve_path(path, follow_link=False) + if not os.path.islink(sb_path): + return None + return os.readlink(sb_path) + + def statfs(self): + return StatFSInfo( + f_type=self.f_type, f_bsize=self.blocksize, + f_blocks=self.nb_total_block, f_bfree=self.nb_free_block, + f_bavail=self.nb_avail_block, f_files=self.nb_total_fnode, + f_ffree=self.nb_free_fnode, f_fsid=self.device_id, + f_namelen=self.max_filename_len, + f_frsize=self.fragment_size, f_flags=self.mount_flags, f_spare=0) + + def getattr_(self, path, follow_link=True): + sb_path = self.resolve_path(path, follow_link=follow_link) + flags = self.linux_env.O_RDONLY + if os.path.isdir(sb_path): + flags |= self.linux_env.O_DIRECTORY + + fd = self.open_(path, flags, follow_link=follow_link) + info = self.linux_env.fstat(fd) + self.linux_env.close(fd) + return info + + def open_(self, path, flags, follow_link=True): + path = self.resolve_path(path, follow_link=follow_link) + if not os.path.exists(path): + # ENOENT (No such file or directory) + return -1 + fd = self.linux_env.next_fd() + acc_mode = flags & self.linux_env.O_ACCMODE + + if os.path.isdir(path): + assert flags & self.linux_env.O_DIRECTORY == self.linux_env.O_DIRECTORY + if acc_mode == self.linux_env.O_RDONLY: + fdesc = FileDescriptorDirectory(fd, flags, self, path) + else: + raise RuntimeError("Not implemented") + elif os.path.isfile(path): + if acc_mode == os.O_RDONLY: + # Read only + real_fd = os.open(path, os.O_RDONLY) + else: + raise RuntimeError("Not implemented") + fdesc = FileDescriptorRegularFile(fd, flags, self, real_fd) + + elif os.path.islink(path): + raise RuntimeError("Not implemented") + else: + raise RuntimeError("Unknown file type for %r" % path) + + self.linux_env.file_descriptors[fd] = fdesc + # Set stat info + fdesc.cont_device_id = self.device_id + fdesc.inode = self.get_path_inode(path) + fdesc.uid = self.linux_env.user_uid + fdesc.gid = self.linux_env.user_gid + size = os.path.getsize(path) + fdesc.size = size + fdesc.blksize = self.blocksize + fdesc.blocks = (size + ((512 - (size % 512)) % 512)) // 512 + return fd + + +class Networking(object): + """Network abstraction""" + + def __init__(self, linux_env): + self.linux_env = linux_env + + def socket(self, family, type_, protocol): + fd = self.linux_env.next_fd() + fdesc = FileDescriptorSocket(fd, family, type_, protocol) + self.linux_env.file_descriptors[fd] = fdesc + return fd + + +class LinuxEnvironment(object): + """A LinuxEnvironment regroups information to simulate a Linux-like + environment""" + + # To be overridden + platform_arch = None + + # User information + user_uid = 1000 + user_euid = 1000 + user_gid = 1000 + user_egid = 1000 + user_name = b"user" + + # Memory mapping information + brk_current = 0x74000000 + mmap_current = 0x75000000 + + # System information + sys_sysname = b"Linux" + sys_nodename = b"user-pc" + sys_release = b"4.13.0-19-generic" + sys_version = b"#22-Ubuntu" + sys_machine = None + + # Filesystem + filesystem_base = "file_sb" + file_descriptors = None + + # Current process + process_tid = 1000 + process_pid = 1000 + + # Syscall restrictions + ioctl_allowed = None # list of (fd, cmd), None value for wildcard + ioctl_disallowed = None # list of (fd, cmd), None value for wildcard + + # Time + base_time = 1531900000 + + # Arch specific constant + O_ACCMODE = None + O_CLOEXEC = None + O_DIRECTORY = None + O_LARGEFILE = None + O_NONBLOCK = None + O_RDONLY = None + + def __init__(self): + stdin = FileDescriptorSTDIN(0) + stdout = FileDescriptorSTDOUT(1) + stderr = FileDescriptorSTDERR(2) + for std in [stdin, stdout, stderr]: + std.uid = self.user_uid + std.gid = self.user_gid + self.file_descriptors = { + 0: stdin, + 1: stdout, + 2: stderr, + } + self.ioctl_allowed = [ + (0, termios.TCGETS), + (0, termios.TIOCGWINSZ), + (0, termios.TIOCSWINSZ), + (1, termios.TCGETS), + (1, termios.TIOCGWINSZ), + (1, termios.TIOCSWINSZ), + ] + self.ioctl_disallowed = [ + (2, termios.TCGETS), + (0, termios.TCSETSW), + ] + self.filesystem = FileSystem(self.filesystem_base, self) + self.network = Networking(self) + + def next_fd(self): + return len(self.file_descriptors) + + def clock_gettime(self): + out = self.base_time + self.base_time += 1 + return out + + def open_(self, path, flags, follow_link=True): + """Stub for 'open' syscall""" + return self.filesystem.open_(path, flags, follow_link=follow_link) + + def socket(self, family, type_, protocol): + """Stub for 'socket' syscall""" + return self.network.socket(family, type_, protocol) + + def fstat(self, fd): + """Get file status through fd""" + fdesc = self.file_descriptors.get(fd) + if fdesc is None: + return None + return fdesc.stat() + + def stat(self, path): + """Get file status through path""" + return self.filesystem.getattr_(path) + + def lstat(self, path): + """Get file status through path (not following links)""" + return self.filesystem.getattr_(path, follow_link=False) + + def close(self, fd): + """Stub for 'close' syscall""" + fdesc = self.file_descriptors.get(fd) + if fdesc is None: + return None + return fdesc.close() + + def write(self, fd, data): + """Stub for 'write' syscall""" + fdesc = self.file_descriptors.get(fd) + if fdesc is None: + return None + fdesc.write(data) + return len(data) + + def read(self, fd, count): + """Stub for 'read' syscall""" + fdesc = self.file_descriptors.get(fd) + if fdesc is None: + return None + return fdesc.read(count) + + def getdents(self, fd, count, packing_callback): + """Stub for 'getdents' syscall + + 'getdents64' must be handled by caller (only the structure layout is + modified) + + @fd: getdents' fd argument + @count: getdents' count argument + @packing_callback(cur_len, d_ino, d_type, name) -> entry + """ + fdesc = self.file_descriptors[fd] + if not isinstance(fdesc, FileDescriptorDirectory): + raise RuntimeError("Not implemented") + + out = b"" + # fdesc.listdir continues from where it stopped + for name in fdesc.listdir(): + d_ino = 1 # Not the real one + d_type = 0 # DT_UNKNOWN (getdents(2) "All applications must properly + # handle a return of DT_UNKNOWN.") + entry = packing_callback(len(out), d_ino, d_type, name) + + if len(out) + len(entry) > count: + # Report to a further call + fdesc.cur_listdir.append(name) + break + out = out + entry + return out + + def ioctl(self, fd, cmd, arg): + """Stub for 'ioctl' syscall + Return the list of element to pack back depending on target ioctl + If the ioctl is disallowed, return False + """ + allowed = False + disallowed = False + for test in [(fd, cmd), (None, cmd), (fd, None)]: + if test in self.ioctl_allowed: + allowed = True + if test in self.ioctl_disallowed: + disallowed = True + + if allowed and disallowed: + raise ValueError("fd: %x, cmd: %x is allowed and disallowed" % (fd, cmd)) + + if allowed: + if cmd == termios.TCGETS: + return 0, 0, 0, 0 + elif cmd == termios.TIOCGWINSZ: + # struct winsize + # { + # unsigned short ws_row; /* rows, in characters */ + # unsigned short ws_col; /* columns, in characters */ + # unsigned short ws_xpixel; /* horizontal size, pixels */ + # unsigned short ws_ypixel; /* vertical size, pixels */ + # }; + return 1000, 360, 1000, 1000 + elif cmd == termios.TIOCSWINSZ: + # Ignore it + return + else: + raise RuntimeError("Not implemented") + + elif disallowed: + return False + + else: + raise KeyError("Unknown ioctl fd:%x cmd:%x" % (fd, cmd)) + + def mmap(self, addr, len_, prot, flags, fd, off, vmmngr): + """Stub for 'mmap' syscall + + 'mmap2' must be implemented by calling this function with off * 4096 + """ + if addr == 0: + addr = self.mmap_current + self.mmap_current += (len_ + 0x1000) & ~0xfff + + all_mem = vmmngr.get_all_memory() + mapped = interval( + [ + (start, start + info["size"] - 1) + for start, info in viewitems(all_mem) + ] + ) + + MAP_FIXED = 0x10 + if flags & MAP_FIXED: + # Alloc missing and override + missing = interval([(addr, addr + len_ - 1)]) - mapped + for start, stop in missing: + vmmngr.add_memory_page( + start, + PAGE_READ|PAGE_WRITE, + b"\x00" * (stop - start + 1), + "mmap allocated" + ) + else: + # Find first candidate segment nearby addr + for start, stop in mapped: + if stop < addr: + continue + rounded = (stop + 1 + 0x1000) & ~0xfff + if (interval([(rounded, rounded + len_)]) & mapped).empty: + addr = rounded + break + else: + assert (interval([(addr, addr + len_)]) & mapped).empty + + vmmngr.add_memory_page( + addr, + PAGE_READ|PAGE_WRITE, + b"\x00" * len_, + "mmap allocated" + ) + + if fd == 0xffffffff: + MAP_ANONYMOUS = 0x20 # mman.h + # fd and offset are ignored if MAP_ANONYMOUS flag is present + if not(flags & MAP_ANONYMOUS) and off != 0: + raise RuntimeError("Not implemented") + data = b"\x00" * len_ + else: + fdesc = self.file_descriptors[fd] + cur_pos = fdesc.tell() + fdesc.seek(off) + data = fdesc.read(len_) + fdesc.seek(cur_pos) + + vmmngr.set_mem(addr, data) + return addr + + def brk(self, addr, vmmngr): + """Stub for 'brk' syscall""" + if addr == 0: + addr = self.brk_current + else: + all_mem = vmmngr.get_all_memory() + mapped = interval( + [ + (start, start + info["size"] - 1) + for start, info in viewitems(all_mem) + ] + ) + + # Alloc missing and override + missing = interval([(self.brk_current, addr)]) - mapped + for start, stop in missing: + vmmngr.add_memory_page( + start, + PAGE_READ|PAGE_WRITE, + b"\x00" * (stop - start + 1), + "BRK" + ) + + self.brk_current = addr + return addr + + +class LinuxEnvironment_x86_32(LinuxEnvironment): + platform_arch = b"x86_32" + sys_machine = b"x86_32" + + # TODO FIXME + ## O_ACCMODE = 0x3 + ## O_CLOEXEC = 0x80000 + ## O_DIRECTORY = 0x10000 + ## O_LARGEFILE = 0x8000 + ## O_NONBLOCK = 0x800 + ## O_RDONLY = 0 + + +class LinuxEnvironment_x86_64(LinuxEnvironment): + platform_arch = b"x86_64" + sys_machine = b"x86_64" + + O_ACCMODE = 0x3 + O_CLOEXEC = 0x80000 + O_DIRECTORY = 0x10000 + O_LARGEFILE = 0x8000 + O_NONBLOCK = 0x800 + O_RDONLY = 0 + + +class LinuxEnvironment_arml(LinuxEnvironment): + platform_arch = b"arml" + sys_machine = b"arml" + + O_ACCMODE = 0x3 + O_CLOEXEC = 0x80000 + O_DIRECTORY = 0x4000 + O_LARGEFILE = 0x20000 + O_NONBLOCK = 0x800 + O_RDONLY = 0 + + # ARM specific + tls = 0 + # get_tls: __kuser_helper_version >= 1 + # cmpxchg: __kuser_helper_version >= 2 + # memory_barrier: __kuser_helper_version >= 3 + kuser_helper_version = 3 + + +class LinuxEnvironment_mips32b(LinuxEnvironment): + platform_arch = b"mips32b" + sys_machine = b"mips32b" + + +class AuxVec(object): + """Auxiliary vector abstraction, filled with default values + (mainly based on https://lwn.net/Articles/519085) + + # Standard usage + >>> auxv = AuxVec(elf_base_addr, cont_target.entry_point, linux_env) + + # Enable AT_SECURE + >>> auxv = AuxVec(..., AuxVec.AT_SECURE=1) + # Modify AT_RANDOM + >>> auxv = AuxVec(..., AuxVec.AT_RANDOM="\x00"*0x10) + + # Using AuxVec instance for stack preparation + # First, fill memory with vectors data + >>> for AT_number, data in auxv.data_to_map(): + dest_ptr = ... + copy_to_dest(data, dest_ptr) + auxv.ptrs[AT_number] = dest_ptr + # Then, get the key: value (with value being sometime a pointer) + >>> for auxid, auxval in auxv.iteritems(): + ... + """ + + AT_PHDR = 3 + AT_PHNUM = 5 + AT_PAGESZ = 6 + AT_ENTRY = 9 + AT_UID = 11 + AT_EUID = 12 + AT_GID = 13 + AT_EGID = 14 + AT_PLATFORM = 15 + AT_HWCAP = 16 + AT_SECURE = 23 + AT_RANDOM = 25 + AT_SYSINFO_EHDR = 33 + + def __init__(self, elf_phdr_vaddr, entry_point, linux_env, **kwargs): + """Instantiate an AuxVec, with required elements: + - elf_phdr_vaddr: virtual address of the ELF's PHDR in memory + - entry_point: virtual address of the ELF entry point + - linux_env: LinuxEnvironment instance, used to provides some of the + option values + + Others options can be overridden by named arguments + + """ + self.info = { + self.AT_PHDR: elf_phdr_vaddr, + self.AT_PHNUM: 9, + self.AT_PAGESZ: 0x1000, + self.AT_ENTRY: entry_point, + self.AT_UID: linux_env.user_uid, + self.AT_EUID: linux_env.user_euid, + self.AT_GID: linux_env.user_gid, + self.AT_EGID: linux_env.user_egid, + self.AT_PLATFORM: linux_env.platform_arch, + self.AT_HWCAP: 0, + self.AT_SECURE: 0, + self.AT_RANDOM: b"\x00" * 0x10, + # vDSO is not mandatory + self.AT_SYSINFO_EHDR: None, + } + self.info.update(kwargs) + self.ptrs = {} # info key -> corresponding virtual address + + def data_to_map(self): + """Iterator on (AT_number, data) + Once the data has been mapped, the corresponding ptr must be set in + 'self.ptrs[AT_number]' + """ + for AT_number in [self.AT_PLATFORM, self.AT_RANDOM]: + yield (AT_number, self.info[AT_number]) + + def iteritems(self): + """Iterator on auxiliary vector id and values""" + for AT_number, value in viewitems(self.info): + if AT_number in self.ptrs: + value = self.ptrs[AT_number] + if value is None: + # AT to ignore + continue + yield (AT_number, value) + + items = iteritems + +def prepare_loader_x86_64(jitter, argv, envp, auxv, linux_env, + hlt_address=0x13371acc): + """Fill the environment with enough information to run a linux loader + + @jitter: Jitter instance + @argv: list of strings + @envp: dict of environment variables names to their values + @auxv: AuxVec instance + @hlt_address (default to 0x13371acc): stopping address + + Example of use: + >>> jitter = machine.jitter() + >>> jitter.init_stack() + >>> linux_env = LinuxEnvironment_x86_64() + >>> argv = ["/bin/ls", "-lah"] + >>> envp = {"PATH": "/usr/local/bin", "USER": linux_env.user_name} + >>> auxv = AuxVec(elf_base_addr, entry_point, linux_env) + >>> prepare_loader_x86_64(jitter, argv, envp, auxv, linux_env) + # One may want to enable syscall handling here + # The program can now run from the loader + >>> jitter.init_run(ld_entry_point) + >>> jitter.continue_run() + """ + # Stack layout looks like + # [data] + # - auxv values + # - envp name=value + # - argv arguments + # [auxiliary vector] + # [environment pointer] + # [argument vector] + + for AT_number, data in auxv.data_to_map(): + data += b"\x00" + jitter.cpu.RSP -= len(data) + ptr = jitter.cpu.RSP + jitter.vm.set_mem(ptr, data) + auxv.ptrs[AT_number] = ptr + + env_ptrs = [] + for name, value in viewitems(envp): + env = b"%s=%s\x00" % (name, value) + jitter.cpu.RSP -= len(env) + ptr = jitter.cpu.RSP + jitter.vm.set_mem(ptr, env) + env_ptrs.append(ptr) + + argv_ptrs = [] + for arg in argv: + arg += b"\x00" + jitter.cpu.RSP -= len(arg) + ptr = jitter.cpu.RSP + jitter.vm.set_mem(ptr, arg) + argv_ptrs.append(ptr) + + jitter.push_uint64_t(hlt_address) + jitter.push_uint64_t(0) + jitter.push_uint64_t(0) + for auxid, auxval in viewitems(auxv): + jitter.push_uint64_t(auxval) + jitter.push_uint64_t(auxid) + jitter.push_uint64_t(0) + for ptr in reversed(env_ptrs): + jitter.push_uint64_t(ptr) + jitter.push_uint64_t(0) + for ptr in reversed(argv_ptrs): + jitter.push_uint64_t(ptr) + jitter.push_uint64_t(len(argv)) + + + +def _arml__kuser_get_tls(linux_env, jitter): + # __kuser_get_tls + jitter.pc = jitter.cpu.LR + jitter.cpu.R0 = linux_env.tls + return True + +def _arml__kuser_cmpxchg(jitter): + oldval = jitter.cpu.R0 + newval = jitter.cpu.R1 + ptr = jitter.cpu.R2 + + value = struct.unpack("<I", jitter.vm.get_mem(ptr, 4))[0] + if value == oldval: + jitter.vm.set_mem(ptr, struct.pack("<I", newval)) + jitter.cpu.R0 = 0 + jitter.cpu.cf = 1 + else: + jitter.cpu.R0 = -1 + jitter.cpu.cf = 0 + + jitter.pc = jitter.cpu.LR + return True + +def _arml__kuser_memory_barrier(jitter): + # __kuser_memory_barrier + jitter.pc = jitter.cpu.LR + return True + +def _arml__kuser_helper_version(linux_env, jitter): + jitter.pc = jitter.cpu.LR + jitter.cpu.R0 = linux_env.kuser_helper_version + return True + + +def prepare_loader_arml(jitter, argv, envp, auxv, linux_env, + hlt_address=0x13371acc): + """Fill the environment with enough information to run a linux loader + + @jitter: Jitter instance + @argv: list of strings + @envp: dict of environment variables names to their values + @auxv: AuxVec instance + @hlt_address (default to 0x13371acc): stopping address + + Example of use: + >>> jitter = machine.jitter() + >>> jitter.init_stack() + >>> linux_env = LinuxEnvironment_arml() + >>> argv = ["/bin/ls", "-lah"] + >>> envp = {"PATH": "/usr/local/bin", "USER": linux_env.user_name} + >>> auxv = AuxVec(elf_base_addr, entry_point, linux_env) + >>> prepare_loader_arml(jitter, argv, envp, auxv, linux_env) + # One may want to enable syscall handling here + # The program can now run from the loader + >>> jitter.init_run(ld_entry_point) + >>> jitter.continue_run() + """ + # Stack layout looks like + # [data] + # - auxv values + # - envp name=value + # - argv arguments + # [auxiliary vector] + # [environment pointer] + # [argument vector] + + for AT_number, data in auxv.data_to_map(): + data += b"\x00" + jitter.cpu.SP -= len(data) + ptr = jitter.cpu.SP + jitter.vm.set_mem(ptr, data) + auxv.ptrs[AT_number] = ptr + + env_ptrs = [] + for name, value in viewitems(envp): + env = b"%s=%s\x00" % (name, value) + jitter.cpu.SP -= len(env) + ptr = jitter.cpu.SP + jitter.vm.set_mem(ptr, env) + env_ptrs.append(ptr) + + argv_ptrs = [] + for arg in argv: + arg += b"\x00" + jitter.cpu.SP -= len(arg) + ptr = jitter.cpu.SP + jitter.vm.set_mem(ptr, arg) + argv_ptrs.append(ptr) + + jitter.push_uint32_t(hlt_address) + jitter.push_uint32_t(0) + jitter.push_uint32_t(0) + for auxid, auxval in viewitems(auxv): + jitter.push_uint32_t(auxval) + jitter.push_uint32_t(auxid) + jitter.push_uint32_t(0) + for ptr in reversed(env_ptrs): + jitter.push_uint32_t(ptr) + jitter.push_uint32_t(0) + for ptr in reversed(argv_ptrs): + jitter.push_uint32_t(ptr) + jitter.push_uint32_t(len(argv)) + + # Add kernel user helpers + # from Documentation/arm/kernel_user_helpers.txt + + if linux_env.kuser_helper_version >= 1: + jitter.add_breakpoint( + 0xFFFF0FE0, + functools.partial(_arml__kuser_get_tls, linux_env) + ) + + if linux_env.kuser_helper_version >= 2: + jitter.add_breakpoint(0XFFFF0FC0, _arml__kuser_cmpxchg) + + if linux_env.kuser_helper_version >= 3: + jitter.add_breakpoint(0xFFFF0FA0, _arml__kuser_memory_barrier) + + jitter.add_breakpoint(0xffff0ffc, _arml__kuser_helper_version) diff --git a/src/miasm/os_dep/linux/syscall.py b/src/miasm/os_dep/linux/syscall.py new file mode 100644 index 00000000..bbaae1bc --- /dev/null +++ b/src/miasm/os_dep/linux/syscall.py @@ -0,0 +1,1129 @@ +from builtins import range +import fcntl +import functools +import logging +import struct +import termios + +from miasm.jitter.csts import EXCEPT_INT_XX, EXCEPT_SYSCALL +from miasm.core.utils import pck64 + +log = logging.getLogger('syscalls') +hnd = logging.StreamHandler() +hnd.setFormatter(logging.Formatter("[%(levelname)-8s]: %(message)s")) +log.addHandler(hnd) +log.setLevel(logging.WARNING) + + +def _dump_struct_stat_x86_64(info): + data = struct.pack( + "QQQIIIIQQQQQQQQQQQQQ", + info.st_dev, + info.st_ino, + info.st_nlink, + info.st_mode, + info.st_uid, + info.st_gid, + 0, # 32 bit padding + info.st_rdev, + info.st_size, + info.st_blksize, + info.st_blocks, + info.st_atime, + info.st_atimensec, + info.st_mtime, + info.st_mtimensec, + info.st_ctime, + info.st_ctimensec, + 0, # unused + 0, # unused + 0, # unused + ) + return data + + +def _dump_struct_stat_arml(info): + data = struct.pack( + "QIIIIIIIIIIIIIIIIII", + info.st_dev, + 0, # pad + info.st_ino, + info.st_mode, + info.st_nlink, + info.st_uid, + info.st_gid, + info.st_rdev, + info.st_size, + info.st_blksize, + info.st_blocks, + info.st_atime, + info.st_atimensec, + info.st_mtime, + info.st_mtimensec, + info.st_ctime, + info.st_ctimensec, + 0, # unused + 0, # unused + ) + return data + + +def sys_x86_64_rt_sigaction(jitter, linux_env): + # Parse arguments + sig, act, oact, sigsetsize = jitter.syscall_args_systemv(4) + log.debug("sys_rt_sigaction(%x, %x, %x, %x)", sig, act, oact, sigsetsize) + + # Stub + if oact != 0: + # Return an empty old action + jitter.vm.set_mem(oact, b"\x00" * sigsetsize) + jitter.syscall_ret_systemv(0) + + +def sys_generic_brk(jitter, linux_env): + # Parse arguments + addr, = jitter.syscall_args_systemv(1) + log.debug("sys_brk(%d)", addr) + + # Stub + jitter.syscall_ret_systemv(linux_env.brk(addr, jitter.vm)) + + +def sys_x86_32_newuname(jitter, linux_env): + # struct utsname { + # char sysname[]; /* Operating system name (e.g., "Linux") */ + # char nodename[]; /* Name within "some implementation-defined + # network" */ + # char release[]; /* Operating system release (e.g., "2.6.28") */ + # char version[]; /* Operating system version */ + # char machine[]; /* Hardware identifier */ + # } + + # Parse arguments + nameptr, = jitter.syscall_args_systemv(1) + log.debug("sys_newuname(%x)", nameptr) + + # Stub + info = [ + linux_env.sys_sysname, + linux_env.sys_nodename, + linux_env.sys_release, + linux_env.sys_version, + linux_env.sys_machine + ] + # TODO: Elements start at 0x41 multiples on my tests... + output = b"" + for elem in info: + output += elem + output += b"\x00" * (0x41 - len(elem)) + jitter.vm.set_mem(nameptr, output) + jitter.syscall_ret_systemv(0) + + +def sys_x86_64_newuname(jitter, linux_env): + # struct utsname { + # char sysname[]; /* Operating system name (e.g., "Linux") */ + # char nodename[]; /* Name within "some implementation-defined + # network" */ + # char release[]; /* Operating system release (e.g., "2.6.28") */ + # char version[]; /* Operating system version */ + # char machine[]; /* Hardware identifier */ + # } + + # Parse arguments + nameptr, = jitter.syscall_args_systemv(1) + log.debug("sys_newuname(%x)", nameptr) + + # Stub + info = [ + linux_env.sys_sysname, + linux_env.sys_nodename, + linux_env.sys_release, + linux_env.sys_version, + linux_env.sys_machine + ] + # TODO: Elements start at 0x41 multiples on my tests... + output = b"" + for elem in info: + output += elem + output += b"\x00" * (0x41 - len(elem)) + jitter.vm.set_mem(nameptr, output) + jitter.syscall_ret_systemv(0) + + +def sys_arml_newuname(jitter, linux_env): + # struct utsname { + # char sysname[]; /* Operating system name (e.g., "Linux") */ + # char nodename[]; /* Name within "some implementation-defined + # network" */ + # char release[]; /* Operating system release (e.g., "2.6.28") */ + # char version[]; /* Operating system version */ + # char machine[]; /* Hardware identifier */ + # } + + # Parse arguments + nameptr, = jitter.syscall_args_systemv(1) + log.debug("sys_newuname(%x)", nameptr) + + # Stub + info = [ + linux_env.sys_sysname, + linux_env.sys_nodename, + linux_env.sys_release, + linux_env.sys_version, + linux_env.sys_machine + ] + # TODO: Elements start at 0x41 multiples on my tests... + output = b"" + for elem in info: + output += elem + output += b"\x00" * (0x41 - len(elem)) + jitter.vm.set_mem(nameptr, output) + jitter.syscall_ret_systemv(0) + + +def sys_generic_access(jitter, linux_env): + # Parse arguments + pathname, mode = jitter.syscall_args_systemv(2) + rpathname = jitter.get_c_str(pathname) + rmode = mode + if mode == 1: + rmode = "F_OK" + elif mode == 2: + rmode = "R_OK" + log.debug("sys_access(%s, %s)", rpathname, rmode) + + # Stub + # Do not check the mode + if linux_env.filesystem.exists(rpathname): + jitter.syscall_ret_systemv(0) + else: + jitter.syscall_ret_systemv(-1) + + +def sys_x86_64_openat(jitter, linux_env): + # Parse arguments + dfd, filename, flags, mode = jitter.syscall_args_systemv(4) + rpathname = jitter.get_c_str(filename) + log.debug("sys_openat(%x, %r, %x, %x)", dfd, rpathname, flags, mode) + + # Stub + # flags, openat particularity over 'open' are ignored + jitter.syscall_ret_systemv(linux_env.open_(rpathname, flags)) + + +def sys_x86_64_newstat(jitter, linux_env): + # Parse arguments + filename, statbuf = jitter.syscall_args_systemv(2) + rpathname = jitter.get_c_str(filename) + log.debug("sys_newstat(%r, %x)", rpathname, statbuf) + + # Stub + if linux_env.filesystem.exists(rpathname): + info = linux_env.stat(rpathname) + data = _dump_struct_stat_x86_64(info) + jitter.vm.set_mem(statbuf, data) + jitter.syscall_ret_systemv(0) + else: + # ENOENT (No such file or directory) + jitter.syscall_ret_systemv(-1) + + +def sys_arml_stat64(jitter, linux_env): + # Parse arguments + filename, statbuf = jitter.syscall_args_systemv(2) + rpathname = jitter.get_c_str(filename) + log.debug("sys_newstat(%r, %x)", rpathname, statbuf) + + # Stub + if linux_env.filesystem.exists(rpathname): + info = linux_env.stat(rpathname) + data = _dump_struct_stat_arml(info) + jitter.vm.set_mem(statbuf, data) + jitter.syscall_ret_systemv(0) + else: + # ENOENT (No such file or directory) + jitter.syscall_ret_systemv(-1) + + +def sys_x86_64_writev(jitter, linux_env): + # Parse arguments + fd, vec, vlen = jitter.syscall_args_systemv(3) + log.debug("sys_writev(%d, %d, %x)", fd, vec, vlen) + + # Stub + fdesc = linux_env.file_descriptors[fd] + for iovec_num in range(vlen): + # struct iovec { + # void *iov_base; /* Starting address */ + # size_t iov_len; /* Number of bytes to transfer */ + # }; + iovec = jitter.vm.get_mem(vec + iovec_num * 8 * 2, 8*2) + iov_base, iov_len = struct.unpack("QQ", iovec) + fdesc.write(jitter.get_c_str(iov_base)[:iov_len]) + + jitter.syscall_ret_systemv(vlen) + + +def sys_arml_writev(jitter, linux_env): + # Parse arguments + fd, vec, vlen = jitter.syscall_args_systemv(3) + log.debug("sys_writev(%d, %d, %x)", fd, vec, vlen) + + # Stub + fdesc = linux_env.file_descriptors[fd] + for iovec_num in range(vlen): + # struct iovec { + # void *iov_base; /* Starting address */ + # size_t iov_len; /* Number of bytes to transfer */ + # }; + iovec = jitter.vm.get_mem(vec + iovec_num * 4 * 2, 4*2) + iov_base, iov_len = struct.unpack("II", iovec) + fdesc.write(jitter.get_c_str(iov_base)[:iov_len]) + + jitter.syscall_ret_systemv(vlen) + + +def sys_generic_exit_group(jitter, linux_env): + # Parse arguments + status, = jitter.syscall_args_systemv(1) + log.debug("sys_exit_group(%d)", status) + + # Stub + log.debug("Exit with status code %d", status) + jitter.running = False + + +def sys_generic_read(jitter, linux_env): + # Parse arguments + fd, buf, count = jitter.syscall_args_systemv(3) + log.debug("sys_read(%d, %x, %x)", fd, buf, count) + + # Stub + data = linux_env.read(fd, count) + jitter.vm.set_mem(buf, data) + jitter.syscall_ret_systemv(len(data)) + + +def sys_x86_64_fstat(jitter, linux_env): + # Parse arguments + fd, statbuf = jitter.syscall_args_systemv(2) + log.debug("sys_fstat(%d, %x)", fd, statbuf) + + # Stub + info = linux_env.fstat(fd) + data = _dump_struct_stat_x86_64(info) + jitter.vm.set_mem(statbuf, data) + jitter.syscall_ret_systemv(0) + + +def sys_arml_fstat64(jitter, linux_env): + # Parse arguments + fd, statbuf = jitter.syscall_args_systemv(2) + log.debug("sys_fstat(%d, %x)", fd, statbuf) + + # Stub + info = linux_env.fstat(fd) + data = _dump_struct_stat_arml(info) + jitter.vm.set_mem(statbuf, data) + jitter.syscall_ret_systemv(0) + + +def sys_generic_mmap(jitter, linux_env): + # Parse arguments + addr, len_, prot, flags, fd, off = jitter.syscall_args_systemv(6) + log.debug("sys_mmap(%x, %x, %x, %x, %x, %x)", addr, len_, prot, flags, fd, off) + + # Stub + addr = linux_env.mmap(addr, len_, prot & 0xFFFFFFFF, flags & 0xFFFFFFFF, + fd & 0xFFFFFFFF, off, jitter.vm) + jitter.syscall_ret_systemv(addr) + + +def sys_generic_mmap2(jitter, linux_env): + # Parse arguments + addr, len_, prot, flags, fd, off = jitter.syscall_args_systemv(6) + log.debug("sys_mmap2(%x, %x, %x, %x, %x, %x)", addr, len_, prot, flags, fd, off) + off = off * 4096 + + # Stub + addr = linux_env.mmap(addr, len_, prot & 0xFFFFFFFF, flags & 0xFFFFFFFF, + fd & 0xFFFFFFFF, off, jitter.vm) + jitter.syscall_ret_systemv(addr) + + +def sys_generic_mprotect(jitter, linux_env): + # Parse arguments + start, len_, prot = jitter.syscall_args_systemv(3) + assert jitter.vm.is_mapped(start, len_) + log.debug("sys_mprotect(%x, %x, %x)", start, len_, prot) + + # Do nothing + jitter.syscall_ret_systemv(0) + + +def sys_generic_close(jitter, linux_env): + # Parse arguments + fd, = jitter.syscall_args_systemv(1) + log.debug("sys_close(%x)", fd) + + # Stub + linux_env.close(fd) + jitter.syscall_ret_systemv(0) + + +def sys_x86_64_arch_prctl(jitter, linux_env): + # Parse arguments + code_name = { + 0x1001: "ARCH_SET_GS", + 0x1002: "ARCH_SET_FS", + 0x1003: "ARCH_GET_FS", + 0x1004: "ARCH_GET_GS", + 0x1011: "ARCH_GET_CPUID", + 0x1012: "ARCH_SET_CPUID", + 0x2001: "ARCH_MAP_VDSO_X32", + 0x2002: "ARCH_MAP_VDSO_32", + 0x2003: "ARCH_MAP_VDSO_64", + 0x3001: "ARCH_CET_STATUS", + 0x3002: "ARCH_CET_DISABLE", + 0x3003: "ARCH_CET_LOCK", + 0x3004: "ARCH_CET_EXEC", + 0x3005: "ARCH_CET_ALLOC_SHSTK", + 0x3006: "ARCH_CET_PUSH_SHSTK", + 0x3007: "ARCH_CET_LEGACY_BITMAP", + } + code = jitter.cpu.RDI + rcode = code_name[code] + addr = jitter.cpu.RSI + log.debug("sys_arch_prctl(%s, %x)", rcode, addr) + + if code == 0x1002: + jitter.cpu.set_segm_base(jitter.cpu.FS, addr) + elif code == 0x3001: + # CET status (disabled) + jitter.vm.set_mem(addr, pck64(0)) + else: + raise RuntimeError("Not implemented") + jitter.cpu.RAX = 0 + + +def sys_x86_64_set_tid_address(jitter, linux_env): + # Parse arguments + tidptr = jitter.cpu.RDI + # clear_child_tid = tidptr + log.debug("sys_set_tid_address(%x)", tidptr) + + jitter.cpu.RAX = linux_env.process_tid + + +def sys_x86_64_set_robust_list(jitter, linux_env): + # Parse arguments + head = jitter.cpu.RDI + len_ = jitter.cpu.RSI + # robust_list = head + log.debug("sys_set_robust_list(%x, %x)", head, len_) + jitter.cpu.RAX = 0 + +def sys_x86_64_rt_sigprocmask(jitter, linux_env): + # Parse arguments + how = jitter.cpu.RDI + nset = jitter.cpu.RSI + oset = jitter.cpu.RDX + sigsetsize = jitter.cpu.R10 + log.debug("sys_rt_sigprocmask(%x, %x, %x, %x)", how, nset, oset, sigsetsize) + if oset != 0: + raise RuntimeError("Not implemented") + jitter.cpu.RAX = 0 + + +def sys_x86_64_prlimit64(jitter, linux_env): + # Parse arguments + pid = jitter.cpu.RDI + resource = jitter.cpu.RSI + new_rlim = jitter.cpu.RDX + if new_rlim != 0: + raise RuntimeError("Not implemented") + old_rlim = jitter.cpu.R10 + log.debug("sys_prlimit64(%x, %x, %x, %x)", pid, resource, new_rlim, + old_rlim) + + # Stub + if resource == 3: + # RLIMIT_STACK + jitter.vm.set_mem(old_rlim, + struct.pack("QQ", + 0x100000, + 0x7fffffffffffffff, # RLIM64_INFINITY + )) + else: + raise RuntimeError("Not implemented") + jitter.cpu.RAX = 0 + + +def sys_x86_64_statfs(jitter, linux_env): + # Parse arguments + pathname = jitter.cpu.RDI + buf = jitter.cpu.RSI + rpathname = jitter.get_c_str(pathname) + log.debug("sys_statfs(%r, %x)", rpathname, buf) + + # Stub + if not linux_env.filesystem.exists(rpathname): + jitter.cpu.RAX = -1 + else: + info = linux_env.filesystem.statfs() + raise RuntimeError("Not implemented") + + +def sys_x86_64_ioctl(jitter, linux_env): + # Parse arguments + fd, cmd, arg = jitter.syscall_args_systemv(3) + log.debug("sys_ioctl(%x, %x, %x)", fd, cmd, arg) + + info = linux_env.ioctl(fd, cmd, arg) + if info is False: + jitter.syscall_ret_systemv(-1) + else: + if cmd == termios.TCGETS: + data = struct.pack("BBBB", *info) + jitter.vm.set_mem(arg, data) + elif cmd == termios.TIOCGWINSZ: + data = struct.pack("HHHH", *info) + jitter.vm.set_mem(arg, data) + else: + assert data is None + jitter.syscall_ret_systemv(0) + + +def sys_arml_ioctl(jitter, linux_env): + # Parse arguments + fd, cmd, arg = jitter.syscall_args_systemv(3) + log.debug("sys_ioctl(%x, %x, %x)", fd, cmd, arg) + + info = linux_env.ioctl(fd, cmd, arg) + if info is False: + jitter.syscall_ret_systemv(-1) + else: + if cmd == termios.TCGETS: + data = struct.pack("BBBB", *info) + jitter.vm.set_mem(arg, data) + elif cmd == termios.TIOCGWINSZ: + data = struct.pack("HHHH", *info) + jitter.vm.set_mem(arg, data) + else: + assert data is None + jitter.syscall_ret_systemv(0) + +def sys_generic_open(jitter, linux_env): + # Parse arguments + filename, flags, mode = jitter.syscall_args_systemv(3) + rpathname = jitter.get_c_str(filename) + log.debug("sys_open(%r, %x, %x)", rpathname, flags, mode) + # Stub + # 'mode' is ignored + jitter.syscall_ret_systemv(linux_env.open_(rpathname, flags)) + + +def sys_generic_write(jitter, linux_env): + # Parse arguments + fd, buf, count = jitter.syscall_args_systemv(3) + log.debug("sys_write(%d, %x, %x)", fd, buf, count) + + # Stub + data = jitter.vm.get_mem(buf, count) + jitter.syscall_ret_systemv(linux_env.write(fd, data)) + + +def sys_x86_64_getdents(jitter, linux_env): + # Parse arguments + fd = jitter.cpu.RDI + dirent = jitter.cpu.RSI + count = jitter.cpu.RDX + log.debug("sys_getdents(%x, %x, %x)", fd, dirent, count) + + # Stub + def packing_callback(cur_len, d_ino, d_type, name): + # struct linux_dirent { + # unsigned long d_ino; /* Inode number */ + # unsigned long d_off; /* Offset to next linux_dirent */ + # unsigned short d_reclen; /* Length of this linux_dirent */ + # char d_name[]; /* Filename (null-terminated) */ + # /* length is actually (d_reclen - 2 - + # offsetof(struct linux_dirent, d_name)) */ + # /* + # char pad; // Zero padding byte + # char d_type; // File type (only since Linux + # // 2.6.4); offset is (d_reclen - 1) + # */ + # } + d_reclen = 8 * 2 + 2 + 1 + len(name) + 1 + d_off = cur_len + d_reclen + entry = struct.pack("QqH", d_ino, d_off, d_reclen) + \ + name.encode("utf8") + b"\x00" + struct.pack("B", d_type) + assert len(entry) == d_reclen + return entry + + out = linux_env.getdents(fd, count, packing_callback) + jitter.vm.set_mem(dirent, out) + jitter.cpu.RAX = len(out) + + +def sys_arml_getdents64(jitter, linux_env): + # Parse arguments + fd = jitter.cpu.R0 + dirent = jitter.cpu.R1 + count = jitter.cpu.R2 + log.debug("sys_getdents64(%x, %x, %x)", fd, dirent, count) + + # Stub + def packing_callback(cur_len, d_ino, d_type, name): + # struct linux_dirent64 { + # ino64_t d_ino; /* 64-bit inode number */ + # off64_t d_off; /* 64-bit offset to next structure */ + # unsigned short d_reclen; /* Size of this dirent */ + # unsigned char d_type; /* File type */ + # char d_name[]; /* Filename (null-terminated) */ + # }; + d_reclen = 8 * 2 + 2 + 1 + len(name) + 1 + d_off = cur_len + d_reclen + entry = struct.pack("QqHB", d_ino, d_off, d_reclen, d_type) + \ + name + b"\x00" + assert len(entry) == d_reclen + return entry + + out = linux_env.getdents(fd, count, packing_callback) + jitter.vm.set_mem(dirent, out) + jitter.cpu.R0 = len(out) + + +def sys_x86_64_newlstat(jitter, linux_env): + # Parse arguments + filename = jitter.cpu.RDI + statbuf = jitter.cpu.RSI + rpathname = jitter.get_c_str(filename) + log.debug("sys_newlstat(%s, %x)", rpathname, statbuf) + + # Stub + if not linux_env.filesystem.exists(rpathname): + # ENOENT (No such file or directory) + jitter.cpu.RAX = -1 + else: + info = linux_env.lstat(rpathname) + data = _dump_struct_stat_x86_64(info) + jitter.vm.set_mem(statbuf, data) + jitter.cpu.RAX = 0 + + +def sys_arml_lstat64(jitter, linux_env): + # Parse arguments + filename = jitter.cpu.R0 + statbuf = jitter.cpu.R1 + rpathname = jitter.get_c_str(filename) + log.debug("sys_newlstat(%s, %x)", rpathname, statbuf) + + # Stub + if not linux_env.filesystem.exists(rpathname): + # ENOENT (No such file or directory) + jitter.cpu.R0 = -1 + else: + info = linux_env.lstat(rpathname) + data = _dump_struct_stat_arml(info) + jitter.vm.set_mem(statbuf, data) + jitter.cpu.R0 = 0 + + +def sys_x86_64_lgetxattr(jitter, linux_env): + # Parse arguments + pathname = jitter.cpu.RDI + name = jitter.cpu.RSI + value = jitter.cpu.RDX + size = jitter.cpu.R10 + rpathname = jitter.get_c_str(pathname) + rname = jitter.get_c_str(name) + log.debug("sys_lgetxattr(%r, %r, %x, %x)", rpathname, rname, value, size) + + # Stub + jitter.vm.set_mem(value, b"\x00" * size) + jitter.cpu.RAX = 0 + + +def sys_x86_64_getxattr(jitter, linux_env): + # Parse arguments + pathname = jitter.cpu.RDI + name = jitter.cpu.RSI + value = jitter.cpu.RDX + size = jitter.cpu.R10 + rpathname = jitter.get_c_str(pathname) + rname = jitter.get_c_str(name) + log.debug("sys_getxattr(%r, %r, %x, %x)", rpathname, rname, value, size) + + # Stub + jitter.vm.set_mem(value, b"\x00" * size) + jitter.cpu.RAX = 0 + + +def sys_x86_64_socket(jitter, linux_env): + # Parse arguments + family = jitter.cpu.RDI + type_ = jitter.cpu.RSI + protocol = jitter.cpu.RDX + log.debug("sys_socket(%x, %x, %x)", family, type_, protocol) + + jitter.cpu.RAX = linux_env.socket(family, type_, protocol) + + +def sys_x86_64_connect(jitter, linux_env): + # Parse arguments + fd = jitter.cpu.RDI + uservaddr = jitter.cpu.RSI + addrlen = jitter.cpu.RDX + raddr = jitter.get_c_str(uservaddr + 2) + log.debug("sys_connect(%x, %r, %x)", fd, raddr, addrlen) + + # Stub + # Always refuse the connection + jitter.cpu.RAX = -1 + + +def sys_x86_64_clock_gettime(jitter, linux_env): + # Parse arguments + which_clock = jitter.cpu.RDI + tp = jitter.cpu.RSI + log.debug("sys_clock_gettime(%x, %x)", which_clock, tp) + + # Stub + value = linux_env.clock_gettime() + jitter.vm.set_mem(tp, struct.pack("Q", value)) + jitter.cpu.RAX = 0 + + +def sys_x86_64_lseek(jitter, linux_env): + # Parse arguments + fd = jitter.cpu.RDI + offset = jitter.cpu.RSI + whence = jitter.cpu.RDX + log.debug("sys_lseek(%d, %x, %x)", fd, offset, whence) + + # Stub + fdesc = linux_env.file_descriptors[fd] + mask = (1 << 64) - 1 + if offset > (1 << 63): + offset = - ((offset ^ mask) + 1) + + new_offset = fdesc.lseek(offset, whence) + jitter.cpu.RAX = new_offset + + +def sys_x86_64_munmap(jitter, linux_env): + # Parse arguments + addr = jitter.cpu.RDI + len_ = jitter.cpu.RSI + log.debug("sys_munmap(%x, %x)", addr, len_) + + # Do nothing + jitter.cpu.RAX = 0 + + +def sys_x86_64_readlink(jitter, linux_env): + # Parse arguments + path = jitter.cpu.RDI + buf = jitter.cpu.RSI + bufsize = jitter.cpu.RDX + rpath = jitter.get_c_str(path) + log.debug("sys_readlink(%r, %x, %x)", rpath, buf, bufsize) + + # Stub + link = linux_env.filesystem.readlink(rpath) + if link is None: + # Not a link + jitter.cpu.RAX = -1 + else: + data = link[:bufsize - 1] + b"\x00" + jitter.vm.set_mem(buf, data) + jitter.cpu.RAX = len(data) - 1 + +def sys_x86_64_getpid(jitter, linux_env): + # Parse arguments + log.debug("sys_getpid()") + + # Stub + jitter.cpu.RAX = linux_env.process_pid + + +def sys_x86_64_sysinfo(jitter, linux_env): + # Parse arguments + info = jitter.cpu.RDI + log.debug("sys_sysinfo(%x)", info) + + # Stub + data = struct.pack("QQQQQQQQQQHQQI", + 0x1234, # uptime + 0x2000, # loads (1 min) + 0x2000, # loads (5 min) + 0x2000, # loads (15 min) + 0x10000000, # total ram + 0x10000000, # free ram + 0x10000000, # shared memory + 0x0, # memory used by buffers + 0x0, # total swap + 0x0, # free swap + 0x1, # nb current processes + 0x0, # total high mem + 0x0, # available high mem + 0x1, # memory unit size + ) + jitter.vm.set_mem(info, data) + jitter.cpu.RAX = 0 + + +def sys_generic_geteuid(jitter, linux_env): + # Parse arguments + log.debug("sys_geteuid()") + + # Stub + jitter.syscall_ret_systemv(linux_env.user_euid) + + +def sys_generic_getegid(jitter, linux_env): + # Parse arguments + log.debug("sys_getegid()") + + # Stub + jitter.syscall_ret_systemv(linux_env.user_egid) + + +def sys_generic_getuid(jitter, linux_env): + # Parse arguments + log.debug("sys_getuid()") + + # Stub + jitter.syscall_ret_systemv(linux_env.user_uid) + + +def sys_generic_getgid(jitter, linux_env): + # Parse arguments + log.debug("sys_getgid()") + + # Stub + jitter.syscall_ret_systemv(linux_env.user_gid) + + +def sys_generic_setgid(jitter, linux_env): + # Parse arguments + gid, = jitter.syscall_args_systemv(1) + log.debug("sys_setgid(%x)", gid) + + # Stub + # Denied if different + if gid != linux_env.user_gid: + jitter.syscall_ret_systemv(-1) + else: + jitter.syscall_ret_systemv(0) + + +def sys_generic_setuid(jitter, linux_env): + # Parse arguments + uid, = jitter.syscall_args_systemv(1) + log.debug("sys_setuid(%x)", uid) + + # Stub + # Denied if different + if uid != linux_env.user_uid: + jitter.syscall_ret_systemv(-1) + else: + jitter.syscall_ret_systemv(0) + + +def sys_arml_set_tls(jitter, linux_env): + # Parse arguments + ptr = jitter.cpu.R0 + log.debug("sys_set_tls(%x)", ptr) + + # Stub + linux_env.tls = ptr + jitter.cpu.R0 = 0 + + +def sys_generic_fcntl64(jitter, linux_env): + # Parse arguments + fd, cmd, arg = jitter.syscall_args_systemv(3) + log.debug("sys_fcntl(%x, %x, %x)", fd, cmd, arg) + + # Stub + fdesc = linux_env.file_descriptors[fd] + if cmd == fcntl.F_GETFL: + jitter.syscall_ret_systemv(fdesc.flags) + elif cmd == fcntl.F_SETFL: + # Ignore flag change + jitter.syscall_ret_systemv(0) + elif cmd == fcntl.F_GETFD: + jitter.syscall_ret_systemv(fdesc.flags) + elif cmd == fcntl.F_SETFD: + # Ignore flag change + jitter.syscall_ret_systemv(0) + else: + raise RuntimeError("Not implemented") + + +def sys_x86_64_pread64(jitter, linux_env): + # Parse arguments + fd = jitter.cpu.RDI + buf = jitter.cpu.RSI + count = jitter.cpu.RDX + pos = jitter.cpu.R10 + log.debug("sys_pread64(%x, %x, %x, %x)", fd, buf, count, pos) + + # Stub + fdesc = linux_env.file_descriptors[fd] + cur_pos = fdesc.tell() + fdesc.seek(pos) + data = fdesc.read(count) + jitter.vm.set_mem(buf, data) + fdesc.seek(cur_pos) + jitter.cpu.RAX = len(data) + + +def sys_arml_gettimeofday(jitter, linux_env): + # Parse arguments + tv = jitter.cpu.R0 + tz = jitter.cpu.R1 + log.debug("sys_gettimeofday(%x, %x)", tv, tz) + + # Stub + value = linux_env.clock_gettime() + if tv: + jitter.vm.set_mem(tv, struct.pack("II", value, 0)) + if tz: + jitter.vm.set_mem(tz, struct.pack("II", 0, 0)) + jitter.cpu.R0 = 0 + + +def sys_mips32b_socket(jitter, linux_env): + # Parse arguments + family, type_, protocol = jitter.syscall_args_systemv(3) + log.debug("sys_socket(%x, %x, %x)", family, type_, protocol) + + ret1 = linux_env.socket(family, type_, protocol) + jitter.syscall_ret_systemv(ret1, 0, 0) + + +syscall_callbacks_x86_32 = { + 0x7A: sys_x86_32_newuname, +} + + +syscall_callbacks_x86_64 = { + 0x0: sys_generic_read, + 0x1: sys_generic_write, + 0x2: sys_generic_open, + 0x3: sys_generic_close, + 0x4: sys_x86_64_newstat, + 0x5: sys_x86_64_fstat, + 0x6: sys_x86_64_newlstat, + 0x8: sys_x86_64_lseek, + 0x9: sys_generic_mmap, + 0x10: sys_x86_64_ioctl, + 0xA: sys_generic_mprotect, + 0xB: sys_x86_64_munmap, + 0xC: sys_generic_brk, + 0xD: sys_x86_64_rt_sigaction, + 0xE: sys_x86_64_rt_sigprocmask, + 0x11: sys_x86_64_pread64, + 0x14: sys_x86_64_writev, + 0x15: sys_generic_access, + 0x27: sys_x86_64_getpid, + 0x29: sys_x86_64_socket, + 0x2A: sys_x86_64_connect, + 0x3F: sys_x86_64_newuname, + 0x48: sys_generic_fcntl64, + 0x4E: sys_x86_64_getdents, + 0x59: sys_x86_64_readlink, + 0x63: sys_x86_64_sysinfo, + 0x66: sys_generic_getuid, + 0x68: sys_generic_getgid, + 0x6B: sys_generic_geteuid, + 0x6C: sys_generic_getegid, + 0xE4: sys_x86_64_clock_gettime, + 0x89: sys_x86_64_statfs, + 0x9E: sys_x86_64_arch_prctl, + 0xBF: sys_x86_64_getxattr, + 0xC0: sys_x86_64_lgetxattr, + 0xDA: sys_x86_64_set_tid_address, + 0xE7: sys_generic_exit_group, + 0x101: sys_x86_64_openat, + 0x111: sys_x86_64_set_robust_list, + 0x12E: sys_x86_64_prlimit64, +} + + +syscall_callbacks_arml = { + + 0x3: sys_generic_read, + 0x4: sys_generic_write, + 0x5: sys_generic_open, + 0x6: sys_generic_close, + 0x2d: sys_generic_brk, + 0x21: sys_generic_access, + 0x36: sys_arml_ioctl, + 0x7a: sys_arml_newuname, + 0x7d: sys_generic_mprotect, + 0x92: sys_arml_writev, + 0xc0: sys_generic_mmap2, + 0xc3: sys_arml_stat64, + 0xc4: sys_arml_lstat64, + 0xc5: sys_arml_fstat64, + 0xc7: sys_generic_getuid, + 0xc8: sys_generic_getgid, + 0xc9: sys_generic_geteuid, + 0xcA: sys_generic_getegid, + 0x4e: sys_arml_gettimeofday, + 0xd5: sys_generic_setuid, + 0xd6: sys_generic_setgid, + 0xd9: sys_arml_getdents64, + 0xdd: sys_generic_fcntl64, + 0xf8: sys_generic_exit_group, + + # ARM-specific ARM_NR_BASE == 0x0f0000 + 0xf0005: sys_arml_set_tls, +} + + +syscall_callbacks_mips32b = { + 0x1057: sys_mips32b_socket, +} + +def syscall_x86_64_exception_handler(linux_env, syscall_callbacks, jitter): + """Call to actually handle an EXCEPT_SYSCALL exception + In the case of an error raised by a SYSCALL, call the corresponding + syscall_callbacks + @linux_env: LinuxEnvironment_x86_64 instance + @syscall_callbacks: syscall number -> func(jitter, linux_env) + """ + + # Dispatch to SYSCALL stub + syscall_number = jitter.cpu.RAX + callback = syscall_callbacks.get(syscall_number) + if callback is None: + raise KeyError( + "No callback found for syscall number 0x%x" % syscall_number + ) + callback(jitter, linux_env) + log.debug("-> %x", jitter.cpu.RAX) + + # Clean exception and move pc to the next instruction, to let the jitter + # continue + jitter.cpu.set_exception(jitter.cpu.get_exception() ^ EXCEPT_SYSCALL) + return True + + + +def syscall_x86_32_exception_handler(linux_env, syscall_callbacks, jitter): + """Call to actually handle an EXCEPT_INT_XX exception + In the case of an error raised by a SYSCALL, call the corresponding + syscall_callbacks + @linux_env: LinuxEnvironment_x86_32 instance + @syscall_callbacks: syscall number -> func(jitter, linux_env) + """ + # Ensure the jitter has break on a SYSCALL + if jitter.cpu.interrupt_num != 0x80: + return True + + # Dispatch to SYSCALL stub + syscall_number = jitter.cpu.EAX + callback = syscall_callbacks.get(syscall_number) + if callback is None: + raise KeyError( + "No callback found for syscall number 0x%x" % syscall_number + ) + callback(jitter, linux_env) + log.debug("-> %x", jitter.cpu.EAX) + + # Clean exception and move pc to the next instruction, to let the jitter + # continue + jitter.cpu.set_exception(jitter.cpu.get_exception() ^ EXCEPT_INT_XX) + return True + + + +def syscall_arml_exception_handler(linux_env, syscall_callbacks, jitter): + """Call to actually handle an EXCEPT_PRIV_INSN exception + In the case of an error raised by a SYSCALL, call the corresponding + syscall_callbacks + @linux_env: LinuxEnvironment_arml instance + @syscall_callbacks: syscall number -> func(jitter, linux_env) + """ + # Ensure the jitter has break on a SYSCALL + if jitter.cpu.interrupt_num != 0x0: + return True + + # Dispatch to SYSCALL stub + syscall_number = jitter.cpu.R7 + callback = syscall_callbacks.get(syscall_number) + if callback is None: + raise KeyError( + "No callback found for syscall number 0x%x" % syscall_number + ) + callback(jitter, linux_env) + log.debug("-> %x", jitter.cpu.R0) + + # Clean exception and move pc to the next instruction, to let the jitter + # continue + jitter.cpu.set_exception(jitter.cpu.get_exception() ^ EXCEPT_INT_XX) + return True + + + +def syscall_mips32b_exception_handler(linux_env, syscall_callbacks, jitter): + """Call to actually handle an EXCEPT_SYSCALL exception + In the case of an error raised by a SYSCALL, call the corresponding + syscall_callbacks + @linux_env: LinuxEnvironment_mips32b instance + @syscall_callbacks: syscall number -> func(jitter, linux_env) + """ + + # Dispatch to SYSCALL stub + syscall_number = jitter.cpu.V0 + callback = syscall_callbacks.get(syscall_number) + if callback is None: + raise KeyError( + "No callback found for syscall number 0x%x" % syscall_number + ) + callback(jitter, linux_env) + log.debug("-> %x", jitter.cpu.V0) + + # Clean exception and move pc to the next instruction, to let the jitter + # continue + jitter.cpu.set_exception(jitter.cpu.get_exception() ^ EXCEPT_SYSCALL) + return True + + + +def enable_syscall_handling(jitter, linux_env, syscall_callbacks): + """Activate handling of syscall for the current jitter instance. + Syscall handlers are provided by @syscall_callbacks + @linux_env: LinuxEnvironment instance + @syscall_callbacks: syscall number -> func(jitter, linux_env) + + Example of use: + >>> linux_env = LinuxEnvironment_x86_64() + >>> enable_syscall_handling(jitter, linux_env, syscall_callbacks_x86_64) + """ + arch_name = jitter.jit.arch_name + if arch_name == "x8664": + handler = syscall_x86_64_exception_handler + handler = functools.partial(handler, linux_env, syscall_callbacks) + jitter.add_exception_handler(EXCEPT_SYSCALL, handler) + elif arch_name == "x8632": + handler = syscall_x86_32_exception_handler + handler = functools.partial(handler, linux_env, syscall_callbacks) + jitter.add_exception_handler(EXCEPT_INT_XX, handler) + elif arch_name == "arml": + handler = syscall_arml_exception_handler + handler = functools.partial(handler, linux_env, syscall_callbacks) + jitter.add_exception_handler(EXCEPT_INT_XX, handler) + elif arch_name == "mips32b": + handler = syscall_mips32b_exception_handler + handler = functools.partial(handler, linux_env, syscall_callbacks) + jitter.add_exception_handler(EXCEPT_SYSCALL, handler) + else: + raise ValueError("No syscall handler implemented for %s" % arch_name) diff --git a/src/miasm/os_dep/linux_stdlib.py b/src/miasm/os_dep/linux_stdlib.py new file mode 100644 index 00000000..f0c708ba --- /dev/null +++ b/src/miasm/os_dep/linux_stdlib.py @@ -0,0 +1,213 @@ +#-*- coding:utf-8 -*- + +from __future__ import print_function +import struct +from sys import stdout + +try: + # Python3 binary stdout + stdout = stdout.buffer +except AttributeError: + pass + +from miasm.core.utils import int_to_byte, cmp_elts +from miasm.os_dep.common import heap +from miasm.os_dep.common import get_fmt_args as _get_fmt_args + + +class c_linobjs(object): + + base_addr = 0x20000000 + align_addr = 0x1000 + def __init__(self): + self.alloc_ad = self.base_addr + self.alloc_align = self.align_addr + self.heap = heap() + +linobjs = c_linobjs() + +ABORT_ADDR = 0x1337beef + +def xxx___libc_start_main(jitter): + """Basic implementation of __libc_start_main + + int __libc_start_main(int *(main) (int, char * *, char * *), int argc, + char * * ubp_av, void (*init) (void), + void (*fini) (void), void (*rtld_fini) (void), + void (* stack_end)); + + Note: + - init, fini, rtld_fini are ignored + - return address is forced to ABORT_ADDR, to avoid calling abort/hlt/... + - in powerpc, signature is: + + int __libc_start_main (int argc, char **argv, char **ev, ElfW (auxv_t) * + auxvec, void (*rtld_fini) (void), struct startup_info + *stinfo, char **stack_on_entry) + + """ + global ABORT_ADDR + if jitter.arch.name == "ppc32": + ret_ad, args = jitter.func_args_systemv( + ["argc", "argv", "ev", "aux_vec", "rtld_fini", "st_info", + "stack_on_entry"] + ) + + # Mimic glibc implementation + if args.stack_on_entry != 0: + argc = struct.unpack(">I", + jitter.vm.get_mem(args.stack_on_entry, 4))[0] + argv = args.stack_on_entry + 4 + envp = argv + ((argc + 1) * 4) + else: + argc = args.argc + argv = args.argv + envp = args.ev + # sda_base, main, init, fini + _, main, _, _ = struct.unpack(">IIII", + jitter.vm.get_mem(args.st_info, 4 * 4)) + + else: + ret_ad, args = jitter.func_args_systemv( + ["main", "argc", "ubp_av", "init", "fini", "rtld_fini", "stack_end"] + ) + + main = args.main + # done by __libc_init_first + size = jitter.lifter.pc.size // 8 + argc = args.argc + argv = args.ubp_av + envp = argv + (args.argc + 1) * size + + + # Call int main(int argc, char** argv, char** envp) + jitter.func_ret_systemv(main) + ret_ad = ABORT_ADDR + jitter.func_prepare_systemv(ret_ad, argc, argv, envp) + return True + + +def xxx_isprint(jitter): + ''' + #include <ctype.h> + int isprint(int c); + + checks for any printable character including space. + ''' + ret_addr, args = jitter.func_args_systemv(['c']) + ret = 1 if 0x20 <= args.c & 0xFF < 0x7f else 0 + return jitter.func_ret_systemv(ret_addr, ret) + + +def xxx_memcpy(jitter): + ''' + #include <string.h> + void *memcpy(void *dest, const void *src, size_t n); + + copies n bytes from memory area src to memory area dest. + ''' + ret_addr, args = jitter.func_args_systemv(['dest', 'src', 'n']) + jitter.vm.set_mem(args.dest, jitter.vm.get_mem(args.src, args.n)) + return jitter.func_ret_systemv(ret_addr, args.dest) + + +def xxx_memset(jitter): + ''' + #include <string.h> + void *memset(void *s, int c, size_t n); + + fills the first n bytes of the memory area pointed to by s with the constant + byte c.''' + + ret_addr, args = jitter.func_args_systemv(['dest', 'c', 'n']) + jitter.vm.set_mem(args.dest, int_to_byte(args.c & 0xFF) * args.n) + return jitter.func_ret_systemv(ret_addr, args.dest) + + +def xxx_puts(jitter): + ''' + #include <stdio.h> + int puts(const char *s); + + writes the string s and a trailing newline to stdout. + ''' + ret_addr, args = jitter.func_args_systemv(['s']) + index = args.s + char = jitter.vm.get_mem(index, 1) + while char != b'\x00': + stdout.write(char) + index += 1 + char = jitter.vm.get_mem(index, 1) + stdout.write(b'\n') + return jitter.func_ret_systemv(ret_addr, 1) + + +def get_fmt_args(jitter, fmt, cur_arg): + return _get_fmt_args(fmt, cur_arg, jitter.get_c_str, jitter.get_arg_n_systemv) + + +def xxx_snprintf(jitter): + ret_addr, args = jitter.func_args_systemv(['string', 'size', 'fmt']) + cur_arg, fmt = 3, args.fmt + size = args.size if args.size else 1 + output = get_fmt_args(jitter, fmt, cur_arg) + output = output[:size - 1] + ret = len(output) + jitter.set_c_str(args.string, output) + return jitter.func_ret_systemv(ret_addr, ret) + + +def xxx_sprintf(jitter): + ret_addr, args = jitter.func_args_systemv(['string', 'fmt']) + cur_arg, fmt = 2, args.fmt + output = get_fmt_args(jitter, fmt, cur_arg) + ret = len(output) + jitter.set_c_str(args.string, output) + return jitter.func_ret_systemv(ret_addr, ret) + + +def xxx_printf(jitter): + ret_addr, args = jitter.func_args_systemv(['fmt']) + cur_arg, fmt = 1, args.fmt + output = get_fmt_args(jitter, fmt, cur_arg) + ret = len(output) + stdout.write(output.encode('utf8')) + return jitter.func_ret_systemv(ret_addr, ret) + + +def xxx_strcpy(jitter): + ret_ad, args = jitter.func_args_systemv(["dst", "src"]) + str_src = jitter.get_c_str(args.src) + jitter.set_c_str(args.dst, str_src) + jitter.func_ret_systemv(ret_ad, args.dst) + + +def xxx_strlen(jitter): + ret_ad, args = jitter.func_args_systemv(["src"]) + str_src = jitter.get_c_str(args.src) + jitter.func_ret_systemv(ret_ad, len(str_src)) + + +def xxx_malloc(jitter): + ret_ad, args = jitter.func_args_systemv(["msize"]) + addr = linobjs.heap.alloc(jitter, args.msize) + jitter.func_ret_systemv(ret_ad, addr) + + +def xxx_free(jitter): + ret_ad, args = jitter.func_args_systemv(["ptr"]) + jitter.func_ret_systemv(ret_ad, 0) + + +def xxx_strcmp(jitter): + ret_ad, args = jitter.func_args_systemv(["ptr_str1", "ptr_str2"]) + s1 = jitter.get_c_str(args.ptr_str1) + s2 = jitter.get_c_str(args.ptr_str2) + jitter.func_ret_systemv(ret_ad, cmp_elts(s1, s2)) + + +def xxx_strncmp(jitter): + ret_ad, args = jitter.func_args_systemv(["ptr_str1", "ptr_str2", "size"]) + s1 = jitter.get_c_str(args.ptr_str1, args.size) + s2 = jitter.get_c_str(args.ptr_str2, args.size) + jitter.func_ret_systemv(ret_ad, cmp_elts(s1, s2)) diff --git a/src/miasm/os_dep/win_32_structs.py b/src/miasm/os_dep/win_32_structs.py new file mode 100644 index 00000000..fc9c62ea --- /dev/null +++ b/src/miasm/os_dep/win_32_structs.py @@ -0,0 +1,231 @@ +from miasm.core.types import MemStruct, Num, Ptr, Str, \ + Array, RawStruct, Union, \ + BitField, Self, Void, Bits, \ + set_allocator, MemUnion, Struct + + +class UnicodeString(MemStruct): + fields = [ + ("length", Num("H")), + ("maxlength", Num("H")), + ("data", Ptr("<I", Str("utf16"))), + ] + + +class ListEntry(MemStruct): + fields = [ + ("flink", Ptr("<I", Void())), + ("blink", Ptr("<I", Void())), + ] + + +class LdrDataEntry(MemStruct): + + """ + +0x000 InLoadOrderLinks : _LIST_ENTRY + +0x008 InMemoryOrderLinks : _LIST_ENTRY + +0x010 InInitializationOrderLinks : _LIST_ENTRY + +0x018 DllBase : Ptr32 Void + +0x01c EntryPoint : Ptr32 Void + +0x020 SizeOfImage : Uint4B + +0x024 FullDllName : _UNICODE_STRING + +0x02c BaseDllName : _UNICODE_STRING + +0x034 Flags : Uint4B + +0x038 LoadCount : Uint2B + +0x03a TlsIndex : Uint2B + +0x03c HashLinks : _LIST_ENTRY + +0x03c SectionPointer : Ptr32 Void + +0x040 CheckSum : Uint4B + +0x044 TimeDateStamp : Uint4B + +0x044 LoadedImports : Ptr32 Void + +0x048 EntryPointActivationContext : Ptr32 Void + +0x04c PatchInformation : Ptr32 Void + """ + + fields = [ + ("InLoadOrderLinks", ListEntry), + ("InMemoryOrderLinks", ListEntry), + ("InInitializationOrderLinks", ListEntry), + ("DllBase", Ptr("<I", Void())), + ("EntryPoint", Ptr("<I", Void())), + ("SizeOfImage", Num("<I")), + ("FullDllName", UnicodeString), + ("BaseDllName", UnicodeString), + ("Flags", Array(Num("B"), 4)), + ("LoadCount", Num("H")), + ("TlsIndex", Num("H")), + ("union1", Union([ + ("HashLinks", Ptr("<I", Void())), + ("SectionPointer", Ptr("<I", Void())), + ])), + ("CheckSum", Num("<I")), + ("union2", Union([ + ("TimeDateStamp", Num("<I")), + ("LoadedImports", Ptr("<I", Void())), + ])), + ("EntryPointActivationContext", Ptr("<I", Void())), + ("PatchInformation", Ptr("<I", Void())), + + ] + + +class PEB_LDR_DATA(MemStruct): + + """ + +0x000 Length : Uint4B + +0x004 Initialized : UChar + +0x008 SsHandle : Ptr32 Void + +0x00c InLoadOrderModuleList : _LIST_ENTRY + +0x014 InMemoryOrderModuleList : _LIST_ENTRY + +0x01C InInitializationOrderModuleList : _LIST_ENTRY + """ + + fields = [ + ("Length", Num("<I")), + ("Initialized", Num("<I")), + ("SsHandle", Ptr("<I", Void())), + ("InLoadOrderModuleList", ListEntry), + ("InMemoryOrderModuleList", ListEntry), + ("InInitializationOrderModuleList", ListEntry) + ] + + +class PEB(MemStruct): + + """ + +0x000 InheritedAddressSpace : UChar + +0x001 ReadImageFileExecOptions : UChar + +0x002 BeingDebugged : UChar + +0x003 SpareBool : UChar + +0x004 Mutant : Ptr32 Void + +0x008 ImageBaseAddress : Ptr32 Void + +0x00c Ldr : Ptr32 _PEB_LDR_DATA + +0x010 processparameter + """ + + fields = [ + ("InheritedAddressSpace", Num("B")), + ("ReadImageFileExecOptions", Num("B")), + ("BeingDebugged", Num("B")), + ("SpareBool", Num("B")), + ("Mutant", Ptr("<I", Void())), + ("ImageBaseAddress", Num("<I")), + ("Ldr", Ptr("<I", PEB_LDR_DATA)), + ] + + +class EXCEPTION_REGISTRATION_RECORD(MemStruct): + """ + +0x00 Next : struct _EXCEPTION_REGISTRATION_RECORD * + +0x04 Handler : Ptr32 Void + """ + + fields = [ + ("Next", Ptr("<I", Self())), + ("Handler", Ptr("<I", Void())), + ] + + +class EXCEPTION_RECORD(MemStruct): + """ + DWORD ExceptionCode; + DWORD ExceptionFlags; + struct _EXCEPTION_RECORD *ExceptionRecord; + PVOID ExceptionAddress; + DWORD NumberParameters; + ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; + """ + EXCEPTION_MAXIMUM_PARAMETERS = 15 + + fields = [ + ("ExceptionCode", Num("<I")), + ("ExceptionFlags", Num("<I")), + ("ExceptionRecord", Ptr("<I", Self())), + ("ExceptionAddress", Ptr("<I", Void())), + ("NumberParameters", Num("<I")), + ("ExceptionInformation", Ptr("<I", Void())), + ] + + +class NT_TIB(MemStruct): + + """ + +00 struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList + +04 void *StackBase + +08 void *StackLimit + +0c void *SubSystemTib + +10 void *FiberData + +10 uint32 Version + +14 void *ArbitraryUserPointer + +18 struct _NT_TIB *Self + """ + + fields = [ + ("ExceptionList", Ptr("<I", EXCEPTION_REGISTRATION_RECORD)), + ("StackBase", Ptr("<I", Void())), + ("StackLimit", Ptr("<I", Void())), + ("SubSystemTib", Ptr("<I", Void())), + (None, Union([ + ("FiberData", Ptr("<I", Void())), + ("Version", Num("<I")) + ])), + ("ArbitraryUserPointer", Ptr("<I", Void())), + ("Self", Ptr("<I", Self())), + ] + + +class TEB(MemStruct): + + """ + +0x000 NtTib : _NT_TIB + +0x01c EnvironmentPointer : Ptr32 Void + +0x020 ClientId : _CLIENT_ID + +0x028 ActiveRpcHandle : Ptr32 Void + +0x02c ThreadLocalStoragePointer : Ptr32 Void + +0x030 ProcessEnvironmentBlock : Ptr32 _PEB + +0x034 LastErrorValue : Uint4B + ... + """ + + fields = [ + ("NtTib", NT_TIB), + ("EnvironmentPointer", Ptr("<I", Void())), + ("ClientId", Array(Num("B"), 0x8)), + ("ActiveRpcHandle", Ptr("<I", Void())), + ("ThreadLocalStoragePointer", Ptr("<I", Void())), + ("ProcessEnvironmentBlock", Ptr("<I", PEB)), + ("LastErrorValue", Num("<I")), + ] + + +class ContextException(MemStruct): + fields = [ + ("ContextFlags", Num("<I")), + ("dr0", Num("<I")), + ("dr1", Num("<I")), + ("dr2", Num("<I")), + ("dr3", Num("<I")), + ("dr4", Num("<I")), + ("dr5", Num("<I")), + + ("Float", Array(Num("B"), 112)), + + ("gs", Num("<I")), + ("fs", Num("<I")), + ("es", Num("<I")), + ("ds", Num("<I")), + + ("edi", Num("<I")), + ("esi", Num("<I")), + ("ebx", Num("<I")), + ("edx", Num("<I")), + ("ecx", Num("<I")), + ("eax", Num("<I")), + ("ebp", Num("<I")), + ("eip", Num("<I")), + + ("cs", Num("<I")), + ("eflags", Num("<I")), + ("esp", Num("<I")), + ("ss", Num("<I")), + ] diff --git a/src/miasm/os_dep/win_api_x86_32.py b/src/miasm/os_dep/win_api_x86_32.py new file mode 100644 index 00000000..6e568abb --- /dev/null +++ b/src/miasm/os_dep/win_api_x86_32.py @@ -0,0 +1,3595 @@ +from __future__ import print_function +# +# Copyright (C) 2011 EADS France, Fabrice Desclaux <fabrice.desclaux@eads.net> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +from past.builtins import cmp +import struct +import os +import stat +import time +import string +import logging +from zlib import crc32 +from io import StringIO +import time +import datetime + +from future.utils import PY3, viewitems, viewvalues + +try: + from Crypto.Hash import MD5, SHA +except ImportError: + print("cannot find crypto, skipping") + +from miasm.jitter.csts import PAGE_READ, PAGE_WRITE, PAGE_EXEC +from miasm.core.utils import pck16, pck32, hexdump, whoami, int_to_byte +from miasm.os_dep.common import heap, windows_to_sbpath +from miasm.os_dep.common import set_win_str_w, set_win_str_a +from miasm.os_dep.common import get_fmt_args as _get_fmt_args +from miasm.os_dep.common import get_win_str_a, get_win_str_w +from miasm.os_dep.common import encode_win_str_a, encode_win_str_w +from miasm.os_dep.win_api_x86_32_seh import tib_address + +log = logging.getLogger("win_api_x86_32") +console_handler = logging.StreamHandler() +console_handler.setFormatter(logging.Formatter("[%(levelname)-8s]: %(message)s")) +log.addHandler(console_handler) +log.setLevel(logging.WARN) + +DATE_1601_TO_1970 = 116444736000000000 + +MAX_PATH = 260 + + +""" +typedef struct tagPROCESSENTRY32 { + DWORD dwSize; + DWORD cntUsage; + DWORD th32ProcessID; + ULONG_PTR th32DefaultHeapID; + DWORD th32ModuleID; + DWORD cntThreads; + DWORD th32ParentProcessID; + LONG pcPriClassBase; + DWORD dwFlags; + TCHAR szExeFile[MAX_PATH]; +} PROCESSENTRY32, *PPROCESSENTRY32; +""" + + +ACCESS_DICT = {0x0: 0, + 0x1: 0, + 0x2: PAGE_READ, + 0x4: PAGE_READ | PAGE_WRITE, + 0x10: PAGE_EXEC, + 0x20: PAGE_EXEC | PAGE_READ, + 0x40: PAGE_EXEC | PAGE_READ | PAGE_WRITE, + 0x80: PAGE_EXEC | PAGE_READ | PAGE_WRITE, + # 0x80: PAGE_EXECUTE_WRITECOPY + 0x100: 0 + } + +ACCESS_DICT_INV = dict((x[1], x[0]) for x in viewitems(ACCESS_DICT)) + + +class whandle(object): + + def __init__(self, name, info): + self.name = name + self.info = info + + def __repr__(self): + return '<%r %r %r>' % (self.__class__.__name__, self.name, self.info) + + +class handle_generator(object): + + def __init__(self): + self.offset = 600 + self.all_handles = {} + + def add(self, name, info=None): + self.offset += 1 + h = whandle(name, info) + self.all_handles[self.offset] = h + + log.debug(repr(self)) + return self.offset + + def __repr__(self): + out = '<%r\n' % self.__class__.__name__ + ks = list(self.all_handles) + ks.sort() + + for k in ks: + out += " %r %r\n" % (k, self.all_handles[k]) + out += '>' + return out + + def __contains__(self, e): + return e in self.all_handles + + def __getitem__(self, item): + return self.all_handles.__getitem__(item) + + def __delitem__(self, item): + self.all_handles.__delitem__(item) + + +class c_winobjs(object): + + def __init__(self): + self.alloc_ad = 0x20000000 + self.alloc_align = 0x1000 + self.heap = heap() + self.handle_toolhelpsnapshot = 0xaaaa00 + self.toolhelpsnapshot_info = {} + self.handle_curprocess = 0xaaaa01 + self.dbg_present = 0 + self.tickcount = 0 + self.dw_pid_dummy1 = 0x111 + self.dw_pid_explorer = 0x222 + self.dw_pid_dummy2 = 0x333 + self.dw_pid_cur = 0x444 + self.module_fname_nux = None + self.module_name = "test.exe" + self.module_path = "c:\\mydir\\" + self.module_name + self.hcurmodule = None + self.module_filesize = None + self.getversion = 0x0A280105 + self.getforegroundwindow = 0x333333 + self.cryptcontext_hwnd = 0x44400 + self.cryptcontext_bnum = 0x44000 + self.cryptcontext_num = 0 + self.cryptcontext = {} + self.phhash_crypt_md5 = 0x55555 + # key used by EncodePointer and DecodePointer + # (kernel32) + self.ptr_encode_key = 0xabababab + self.files_hwnd = {} + self.windowlong_dw = 0x77700 + self.module_cur_hwnd = 0x88800 + self.module_file_nul = 0x999000 + self.runtime_dll = None + self.current_pe = None + self.tls_index = 0xf + self.tls_values = {} + self.handle_pool = handle_generator() + self.handle_mapped = {} + self.hkey_handles = { + 0x80000001: b"hkey_current_user", + 0x80000002: b"hkey_local_machine" + } + self.cur_dir = "c:\\tmp" + + self.nt_mdl = {} + self.nt_mdl_ad = None + self.nt_mdl_cur = 0 + self.win_event_num = 0x13370 + self.cryptdll_md5_h = {} + + self.lastwin32error = 0 + self.mutex = {} + self.env_variables = {} + self.events_pool = {} + self.find_data = None + + self.allocated_pages = {} + self.current_datetime = datetime.datetime( + year=2017, month=8, day=21, + hour=13, minute=37, + second=11, microsecond=123456 + ) + +winobjs = c_winobjs() + + +process_list = [ + [ + 0x40, # DWORD dwSize; + 0, # DWORD cntUsage; + winobjs.dw_pid_dummy1, # DWORD th32ProcessID; + 0x11111111, # ULONG_PTR th32DefaultHeapID; + 0x11111112, # DWORD th32ModuleID; + 1, # DWORD cntThreads; + winobjs.dw_pid_explorer, # DWORD th32ParentProcessID; + 0xbeef, # LONG pcPriClassBase; + 0x0, # DWORD dwFlags; + "dummy1.exe" # TCHAR szExeFile[MAX_PATH]; + ], + [ + 0x40, # DWORD dwSize; + 0, # DWORD cntUsage; + winobjs.dw_pid_explorer, # DWORD th32ProcessID; + 0x11111111, # ULONG_PTR th32DefaultHeapID; + 0x11111112, # DWORD th32ModuleID; + 1, # DWORD cntThreads; + 4, # DWORD th32ParentProcessID; + 0xbeef, # LONG pcPriClassBase; + 0x0, # DWORD dwFlags; + "explorer.exe" # TCHAR szExeFile[MAX_PATH]; + ], + + [ + 0x40, # DWORD dwSize; + 0, # DWORD cntUsage; + winobjs.dw_pid_dummy2, # DWORD th32ProcessID; + 0x11111111, # ULONG_PTR th32DefaultHeapID; + 0x11111112, # DWORD th32ModuleID; + 1, # DWORD cntThreads; + winobjs.dw_pid_explorer, # DWORD th32ParentProcessID; + 0xbeef, # LONG pcPriClassBase; + 0x0, # DWORD dwFlags; + "dummy2.exe" # TCHAR szExeFile[MAX_PATH]; + ], + + [ + 0x40, # DWORD dwSize; + 0, # DWORD cntUsage; + winobjs.dw_pid_cur, # DWORD th32ProcessID; + 0x11111111, # ULONG_PTR th32DefaultHeapID; + 0x11111112, # DWORD th32ModuleID; + 1, # DWORD cntThreads; + winobjs.dw_pid_explorer, # DWORD th32ParentProcessID; + 0xbeef, # LONG pcPriClassBase; + 0x0, # DWORD dwFlags; + winobjs.module_name # TCHAR szExeFile[MAX_PATH]; + ], + + +] + + +class hobj(object): + pass + + +class mdl(object): + + def __init__(self, ad, l): + self.ad = ad + self.l = l + + def __bytes__(self): + return struct.pack('LL', self.ad, self.l) + + def __str__(self): + if PY3: + return repr(self) + return self.__bytes__() + + +def kernel32_HeapAlloc(jitter): + ret_ad, args = jitter.func_args_stdcall(["heap", "flags", "size"]) + alloc_addr = winobjs.heap.alloc(jitter, args.size, cmt=hex(ret_ad)) + jitter.func_ret_stdcall(ret_ad, alloc_addr) + + +def kernel32_HeapFree(jitter): + ret_ad, _ = jitter.func_args_stdcall(["heap", "flags", "pmem"]) + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_GlobalAlloc(jitter): + ret_ad, args = jitter.func_args_stdcall(["uflags", "msize"]) + alloc_addr = winobjs.heap.alloc(jitter, args.msize) + jitter.func_ret_stdcall(ret_ad, alloc_addr) + + +def kernel32_LocalFree(jitter): + ret_ad, _ = jitter.func_args_stdcall(["lpvoid"]) + jitter.func_ret_stdcall(ret_ad, 0) + + +def kernel32_LocalAlloc(jitter): + ret_ad, args = jitter.func_args_stdcall(["uflags", "msize"]) + alloc_addr = winobjs.heap.alloc(jitter, args.msize) + jitter.func_ret_stdcall(ret_ad, alloc_addr) + +def msvcrt_new(jitter): + ret_ad, args = jitter.func_args_cdecl(["size"]) + alloc_addr = winobjs.heap.alloc(jitter, args.size) + jitter.func_ret_cdecl(ret_ad, alloc_addr) + +globals()['msvcrt_??2@YAPAXI@Z'] = msvcrt_new + +def msvcrt_delete(jitter): + ret_ad, args = jitter.func_args_cdecl(["ptr"]) + jitter.func_ret_cdecl(ret_ad, 0) + +globals()['msvcrt_??3@YAXPAX@Z'] = msvcrt_delete + +def kernel32_GlobalFree(jitter): + ret_ad, _ = jitter.func_args_stdcall(["addr"]) + jitter.func_ret_stdcall(ret_ad, 0) + + +def kernel32_IsDebuggerPresent(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + jitter.func_ret_stdcall(ret_ad, winobjs.dbg_present) + + +def kernel32_CreateToolhelp32Snapshot(jitter): + ret_ad, _ = jitter.func_args_stdcall(["dwflags", "th32processid"]) + jitter.func_ret_stdcall(ret_ad, winobjs.handle_toolhelpsnapshot) + + +def kernel32_GetCurrentProcess(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + jitter.func_ret_stdcall(ret_ad, winobjs.handle_curprocess) + + +def kernel32_GetCurrentProcessId(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + jitter.func_ret_stdcall(ret_ad, winobjs.dw_pid_cur) + + +def kernel32_Process32First(jitter): + ret_ad, args = jitter.func_args_stdcall(["s_handle", "ad_pentry"]) + + pentry = struct.pack( + 'IIIIIIIII', *process_list[0][:-1] + ) + (process_list[0][-1] + '\x00').encode('utf8') + jitter.vm.set_mem(args.ad_pentry, pentry) + winobjs.toolhelpsnapshot_info[args.s_handle] = 0 + + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_Process32Next(jitter): + ret_ad, args = jitter.func_args_stdcall(["s_handle", "ad_pentry"]) + + winobjs.toolhelpsnapshot_info[args.s_handle] += 1 + if winobjs.toolhelpsnapshot_info[args.s_handle] >= len(process_list): + ret = 0 + else: + ret = 1 + n = winobjs.toolhelpsnapshot_info[args.s_handle] + pentry = struct.pack( + 'IIIIIIIII', *process_list[n][:-1]) + (process_list[n][-1]+ '\x00').encode('utf8') + jitter.vm.set_mem(args.ad_pentry, pentry) + jitter.func_ret_stdcall(ret_ad, ret) + + +def kernel32_GetTickCount(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + winobjs.tickcount += 1 + jitter.func_ret_stdcall(ret_ad, winobjs.tickcount) + + +def kernel32_GetVersion(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + jitter.func_ret_stdcall(ret_ad, winobjs.getversion) + + +def kernel32_GetVersionEx(jitter, str_size, encode_str): + ret_ad, args = jitter.func_args_stdcall(["ptr_struct"]) + + size = jitter.vm.get_u32(args.ptr_struct) + if size in [0x14+str_size, 0x1c+str_size]: + tmp = struct.pack( + "IIIII%dsHHHBB" % str_size, + 0x114, # struct size + 0x5, # maj vers + 0x2, # min vers + 0xa28, # build nbr + 0x2, # platform id + encode_str("Service pack 4"), + 3, # wServicePackMajor + 0, # wServicePackMinor + 0x100, # wSuiteMask + 1, # wProductType + 0 # wReserved + ) + tmp = tmp[:size] + jitter.vm.set_mem(args.ptr_struct, tmp) + ret = 1 + else: + ret = 0 + jitter.func_ret_stdcall(ret_ad, ret) + + +kernel32_GetVersionExA = lambda jitter: kernel32_GetVersionEx(jitter, 128, + encode_win_str_a) +kernel32_GetVersionExW = lambda jitter: kernel32_GetVersionEx(jitter, 256, + encode_win_str_w) + + +def kernel32_GetPriorityClass(jitter): + ret_ad, _ = jitter.func_args_stdcall(["hwnd"]) + jitter.func_ret_stdcall(ret_ad, 0) + + +def kernel32_SetPriorityClass(jitter): + ret_ad, _ = jitter.func_args_stdcall(["hwnd", "dwpclass"]) + jitter.func_ret_stdcall(ret_ad, 0) + + +def kernel32_CloseHandle(jitter): + ret_ad, _ = jitter.func_args_stdcall(["hwnd"]) + jitter.func_ret_stdcall(ret_ad, 1) + +def kernel32_EncodePointer(jitter): + """ + PVOID EncodePointer( + _In_ PVOID Ptr + ); + + Encoding globally available pointers helps protect them from being + exploited. The EncodePointer function obfuscates the pointer value + with a secret so that it cannot be predicted by an external agent. + The secret used by EncodePointer is different for each process. + + A pointer must be decoded before it can be used. + + """ + ret, args = jitter.func_args_stdcall(1) + jitter.func_ret_stdcall(ret, args[0] ^ winobjs.ptr_encode_key) + return True + +def kernel32_DecodePointer(jitter): + """ + PVOID DecodePointer( + PVOID Ptr + ); + + The function returns the decoded pointer. + + """ + ret, args = jitter.func_args_stdcall(1) + jitter.func_ret_stdcall(ret, args[0] ^ winobjs.ptr_encode_key) + return True + +def user32_GetForegroundWindow(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + jitter.func_ret_stdcall(ret_ad, winobjs.getforegroundwindow) + + +def user32_FindWindowA(jitter): + ret_ad, args = jitter.func_args_stdcall(["pclassname", "pwindowname"]) + if args.pclassname: + classname = get_win_str_a(jitter, args.pclassname) + log.info("FindWindowA classname %s", classname) + if args.pwindowname: + windowname = get_win_str_a(jitter, args.pwindowname) + log.info("FindWindowA windowname %s", windowname) + jitter.func_ret_stdcall(ret_ad, 0) + + +def user32_GetTopWindow(jitter): + ret_ad, _ = jitter.func_args_stdcall(["hwnd"]) + jitter.func_ret_stdcall(ret_ad, 0) + + +def user32_BlockInput(jitter): + ret_ad, _ = jitter.func_args_stdcall(["blockit"]) + jitter.func_ret_stdcall(ret_ad, 1) + + +def advapi32_CryptAcquireContext(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(["phprov", "pszcontainer", + "pszprovider", "dwprovtype", + "dwflags"]) + prov = get_str(args.pszprovider) if args.pszprovider else "NONE" + log.debug('prov: %r', prov) + jitter.vm.set_u32(args.phprov, winobjs.cryptcontext_hwnd) + jitter.func_ret_stdcall(ret_ad, 1) + + +def advapi32_CryptAcquireContextA(jitter): + advapi32_CryptAcquireContext(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def advapi32_CryptAcquireContextW(jitter): + advapi32_CryptAcquireContext(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def advapi32_CryptCreateHash(jitter): + ret_ad, args = jitter.func_args_stdcall(["hprov", "algid", "hkey", + "dwflags", "phhash"]) + + winobjs.cryptcontext_num += 1 + + if args.algid == 0x00008003: + log.debug('algo is MD5') + jitter.vm.set_u32( + args.phhash, + winobjs.cryptcontext_bnum + winobjs.cryptcontext_num + ) + winobjs.cryptcontext[ + winobjs.cryptcontext_bnum + winobjs.cryptcontext_num] = hobj() + winobjs.cryptcontext[ + winobjs.cryptcontext_bnum + winobjs.cryptcontext_num].h = MD5.new() + elif args.algid == 0x00008004: + log.debug('algo is SHA1') + jitter.vm.set_u32( + args.phhash, + winobjs.cryptcontext_bnum + winobjs.cryptcontext_num + ) + winobjs.cryptcontext[ + winobjs.cryptcontext_bnum + winobjs.cryptcontext_num] = hobj() + winobjs.cryptcontext[ + winobjs.cryptcontext_bnum + winobjs.cryptcontext_num].h = SHA.new() + else: + raise ValueError('un impl algo1') + jitter.func_ret_stdcall(ret_ad, 1) + + +def advapi32_CryptHashData(jitter): + ret_ad, args = jitter.func_args_stdcall(["hhash", "pbdata", "dwdatalen", + "dwflags"]) + + if not args.hhash in winobjs.cryptcontext: + raise ValueError("unknown crypt context") + + data = jitter.vm.get_mem(args.pbdata, args.dwdatalen) + log.debug('will hash %X', args.dwdatalen) + log.debug(repr(data[:0x10]) + "...") + winobjs.cryptcontext[args.hhash].h.update(data) + jitter.func_ret_stdcall(ret_ad, 1) + + +def advapi32_CryptGetHashParam(jitter): + ret_ad, args = jitter.func_args_stdcall(["hhash", "param", "pbdata", + "dwdatalen", "dwflags"]) + + if not args.hhash in winobjs.cryptcontext: + raise ValueError("unknown crypt context") + + if args.param == 2: + # HP_HASHVAL + # XXX todo: save h state? + h = winobjs.cryptcontext[args.hhash].h.digest() + jitter.vm.set_mem(args.pbdata, h) + jitter.vm.set_u32(args.dwdatalen, len(h)) + elif args.param == 4: + # HP_HASHSIZE + ret = winobjs.cryptcontext[args.hhash].h.digest_size + jitter.vm.set_u32(args.pbdata, ret) + jitter.vm.set_u32(args.dwdatalen, 4) + else: + raise ValueError('not impl', args.param) + + jitter.func_ret_stdcall(ret_ad, 1) + + +def advapi32_CryptReleaseContext(jitter): + ret_ad, _ = jitter.func_args_stdcall(["hhash", "flags"]) + jitter.func_ret_stdcall(ret_ad, 0) + + +def advapi32_CryptDeriveKey(jitter): + ret_ad, args = jitter.func_args_stdcall(["hprov", "algid", "hbasedata", + "dwflags", "phkey"]) + + if args.algid == 0x6801: + log.debug('using DES') + else: + raise ValueError('un impl algo2') + h = winobjs.cryptcontext[args.hbasedata].h.digest() + log.debug('hash %r', h) + winobjs.cryptcontext[args.hbasedata].h_result = h + jitter.vm.set_u32(args.phkey, args.hbasedata) + jitter.func_ret_stdcall(ret_ad, 1) + + +def advapi32_CryptDestroyHash(jitter): + ret_ad, _ = jitter.func_args_stdcall(["hhash"]) + jitter.func_ret_stdcall(ret_ad, 1) + + +def advapi32_CryptDecrypt(jitter): + # ret_ad, _ = jitter.func_args_stdcall(["hkey", "hhash", "final", + # "dwflags", "pbdata", + # "pdwdatalen"]) + raise ValueError("Not implemented") + # jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_CreateFile(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(["lpfilename", "access", + "dwsharedmode", + "lpsecurityattr", + "dwcreationdisposition", + "dwflagsandattr", + "htemplatefile"]) + if args.lpfilename == 0: + jitter.func_ret_stdcall(ret_ad, 0xffffffff) + return + + fname = get_str(args.lpfilename) + log.info('CreateFile fname %s', fname) + ret = 0xffffffff + + log.debug("%r %r", fname.lower(), winobjs.module_path.lower()) + is_original_file = fname.lower() == winobjs.module_path.lower() + + if fname.upper() in [r"\\.\SICE", r"\\.\NTICE", r"\\.\SIWVID", r'\\.\SIWDEBUG']: + pass + elif fname.upper() in ['NUL']: + ret = winobjs.module_cur_hwnd + else: + # sandbox path + sb_fname = windows_to_sbpath(fname) + if args.access & 0x80000000 or args.access == 1: + # read and maybe write + if args.dwcreationdisposition == 2: + # create_always + if os.access(sb_fname, os.R_OK): + # but file exist + pass + else: + raise NotImplementedError("Untested case") # to test + # h = open(sb_fname, 'rb+') + elif args.dwcreationdisposition == 3: + # open_existing + if os.access(sb_fname, os.R_OK): + s = os.stat(sb_fname) + if stat.S_ISDIR(s.st_mode): + ret = winobjs.handle_pool.add(sb_fname, 0x1337) + else: + open_mode = 'rb' + if (args.access & 0x40000000) or args.access == 2: + open_mode = 'r+b' + h = open(sb_fname, open_mode) + ret = winobjs.handle_pool.add(sb_fname, h) + else: + log.warning("FILE %r (%s) DOES NOT EXIST!", fname, sb_fname) + elif args.dwcreationdisposition == 1: + # create new + if os.access(sb_fname, os.R_OK): + # file exist + # ret = 80 + winobjs.lastwin32error = 80 + else: + # first create an empty file + open(sb_fname, 'wb').close() + # then open + h = open(sb_fname, 'r+b') + ret = winobjs.handle_pool.add(sb_fname, h) + elif args.dwcreationdisposition == 4: + # open_always + if os.access(sb_fname, os.R_OK): + s = os.stat(sb_fname) + if stat.S_ISDIR(s.st_mode): + ret = winobjs.handle_pool.add(sb_fname, 0x1337) + else: + h = open(sb_fname, 'r+b') + ret = winobjs.handle_pool.add(sb_fname, h) + else: + raise NotImplementedError("Untested case") + else: + raise NotImplementedError("Untested case") + elif (args.access & 0x40000000) or args.access == 2: + # write but not read + if args.dwcreationdisposition == 3: + # open existing + if is_original_file: + # cannot open self in write mode! + pass + elif os.access(sb_fname, os.R_OK): + s = os.stat(sb_fname) + if stat.S_ISDIR(s.st_mode): + # open dir + ret = winobjs.handle_pool.add(sb_fname, 0x1337) + else: + h = open(sb_fname, 'wb') + ret = winobjs.handle_pool.add(sb_fname, h) + else: + raise NotImplementedError("Untested case") # to test + elif args.dwcreationdisposition == 5: + # truncate_existing + if is_original_file: + pass + else: + raise NotImplementedError("Untested case") # to test + else: + # raise NotImplementedError("Untested case") # to test + h = open(sb_fname, 'wb') + ret = winobjs.handle_pool.add(sb_fname, h) + else: + raise NotImplementedError("Untested case") + + # h = open(sb_fname, 'rb+') + # ret = winobjs.handle_pool.add(sb_fname, h) + log.debug('CreateFile ret %x', ret) + jitter.func_ret_stdcall(ret_ad, ret) + + +def kernel32_CreateFileA(jitter): + kernel32_CreateFile(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def kernel32_CreateFileW(jitter): + kernel32_CreateFile(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def kernel32_ReadFile(jitter): + ret_ad, args = jitter.func_args_stdcall(["hwnd", "lpbuffer", + "nnumberofbytestoread", + "lpnumberofbytesread", + "lpoverlapped"]) + if args.hwnd == winobjs.module_cur_hwnd: + pass + elif args.hwnd in winobjs.handle_pool: + pass + else: + raise ValueError('unknown hwnd!') + + data = None + if args.hwnd in winobjs.files_hwnd: + data = winobjs.files_hwnd[ + winobjs.module_cur_hwnd].read(args.nnumberofbytestoread) + elif args.hwnd in winobjs.handle_pool: + wh = winobjs.handle_pool[args.hwnd] + data = wh.info.read(args.nnumberofbytestoread) + else: + raise ValueError('unknown filename') + + if data is not None: + if (args.lpnumberofbytesread): + jitter.vm.set_u32(args.lpnumberofbytesread, len(data)) + jitter.vm.set_mem(args.lpbuffer, data) + + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_GetFileSize(jitter): + ret_ad, args = jitter.func_args_stdcall(["hwnd", "lpfilesizehight"]) + + if args.hwnd == winobjs.module_cur_hwnd: + ret = len(open(winobjs.module_fname_nux, "rb").read()) + elif args.hwnd in winobjs.handle_pool: + wh = winobjs.handle_pool[args.hwnd] + ret = len(open(wh.name, "rb").read()) + else: + raise ValueError('unknown hwnd!') + + if args.lpfilesizehight != 0: + jitter.vm.set_u32(args.lpfilesizehight, ret) + jitter.func_ret_stdcall(ret_ad, ret) + + +def kernel32_GetFileSizeEx(jitter): + ret_ad, args = jitter.func_args_stdcall(["hwnd", "lpfilesizehight"]) + + if args.hwnd == winobjs.module_cur_hwnd: + l = len(open(winobjs.module_fname_nux, "rb").read()) + elif args.hwnd in winobjs.handle_pool: + wh = winobjs.handle_pool[args.hwnd] + l = len(open(wh.name, "rb").read()) + else: + raise ValueError('unknown hwnd!') + + if args.lpfilesizehight == 0: + raise NotImplementedError("Untested case") + jitter.vm.set_mem(args.lpfilesizehight, pck32( + l & 0xffffffff) + pck32((l >> 32) & 0xffffffff)) + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_FlushInstructionCache(jitter): + ret_ad, _ = jitter.func_args_stdcall(["hprocess", "lpbasead", "dwsize"]) + jitter.func_ret_stdcall(ret_ad, 0x1337) + + +def kernel32_VirtualProtect(jitter): + ret_ad, args = jitter.func_args_stdcall(['lpvoid', 'dwsize', + 'flnewprotect', + 'lpfloldprotect']) + # XXX mask hpart + flnewprotect = args.flnewprotect & 0xFFF + if not flnewprotect in ACCESS_DICT: + raise ValueError('unknown access dw!') + + if args.lpfloldprotect: + old = jitter.vm.get_mem_access(args.lpvoid) + jitter.vm.set_u32(args.lpfloldprotect, ACCESS_DICT_INV[old]) + + paddr = args.lpvoid - (args.lpvoid % winobjs.alloc_align) + paddr_max = (args.lpvoid + args.dwsize + winobjs.alloc_align - 1) + paddr_max_round = paddr_max - (paddr_max % winobjs.alloc_align) + psize = paddr_max_round - paddr + for addr, items in list(winobjs.allocated_pages.items()): + alloc_addr, alloc_size = items + if (paddr + psize <= alloc_addr or + paddr > alloc_addr + alloc_size): + continue + size = jitter.vm.get_all_memory()[addr]["size"] + # Page is included in Protect area + if (paddr <= addr < addr + size <= paddr + psize): + log.warn("set page %x %x", addr, ACCESS_DICT[flnewprotect]) + jitter.vm.set_mem_access(addr, ACCESS_DICT[flnewprotect]) + continue + + # Page is partly in Protect area: splitting is needed + if (addr <= paddr < addr + size or + addr <= paddr + psize < addr + size): + + old_access = jitter.vm.get_mem_access(addr) + + # splits = [ + # addr -> max(paddr, addr) + # max(paddr, addr) -> min(addr + size, paddr + psize) + # min(addr + size, paddr + psize) -> addr + size + # ] + splits = [ + (addr, old_access, + jitter.vm.get_mem(addr, max(paddr, addr) - addr)), + (max(paddr, addr), ACCESS_DICT[flnewprotect], + jitter.vm.get_mem( + max(paddr, addr), + min(addr + size, paddr + psize) - max(paddr, addr))), + (min(addr + size, paddr + psize), old_access, + jitter.vm.get_mem( + min(addr + size, paddr + psize), + addr + size - min(addr + size, paddr + psize))) + ] + + jitter.vm.remove_memory_page(addr) + for split_addr, split_access, split_data in splits: + if not split_data: + continue + log.warn("create page %x %x", split_addr, + ACCESS_DICT[flnewprotect]) + jitter.vm.add_memory_page( + split_addr, split_access, split_data, + "VirtualProtect split ret 0x%X" % ret_ad) + winobjs.allocated_pages[split_addr] = (alloc_addr, alloc_size) + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_VirtualAlloc(jitter): + ret_ad, args = jitter.func_args_stdcall(['lpvoid', 'dwsize', + 'alloc_type', 'flprotect']) + + + if not args.flprotect in ACCESS_DICT: + raise ValueError('unknown access dw!') + + if args.lpvoid == 0: + alloc_addr = winobjs.heap.next_addr(args.dwsize) + winobjs.allocated_pages[alloc_addr] = (alloc_addr, args.dwsize) + jitter.vm.add_memory_page( + alloc_addr, ACCESS_DICT[args.flprotect], b"\x00" * args.dwsize, + "Alloc in %s ret 0x%X" % (whoami(), ret_ad)) + else: + all_mem = jitter.vm.get_all_memory() + if args.lpvoid in all_mem: + alloc_addr = args.lpvoid + jitter.vm.set_mem_access(args.lpvoid, ACCESS_DICT[args.flprotect]) + else: + alloc_addr = winobjs.heap.next_addr(args.dwsize) + winobjs.allocated_pages[alloc_addr] = (alloc_addr, args.dwsize) + # alloc_addr = args.lpvoid + jitter.vm.add_memory_page( + alloc_addr, ACCESS_DICT[args.flprotect], b"\x00" * args.dwsize, + "Alloc in %s ret 0x%X" % (whoami(), ret_ad)) + + log.info('VirtualAlloc addr: 0x%x', alloc_addr) + jitter.func_ret_stdcall(ret_ad, alloc_addr) + + +def kernel32_VirtualFree(jitter): + ret_ad, _ = jitter.func_args_stdcall(["lpvoid", "dwsize", "alloc_type"]) + jitter.func_ret_stdcall(ret_ad, 0) + + +def user32_GetWindowLongA(jitter): + ret_ad, _ = jitter.func_args_stdcall(["hwnd", "nindex"]) + jitter.func_ret_stdcall(ret_ad, winobjs.windowlong_dw) + + +def user32_SetWindowLongA(jitter): + ret_ad, _ = jitter.func_args_stdcall(["hwnd", "nindex", "newlong"]) + jitter.func_ret_stdcall(ret_ad, winobjs.windowlong_dw) + + +def kernel32_GetModuleFileName(jitter, funcname, set_str): + ret_ad, args = jitter.func_args_stdcall(["hmodule", "lpfilename", "nsize"]) + + if args.hmodule in [0, winobjs.hcurmodule]: + p = winobjs.module_path[:] + elif (winobjs.runtime_dll and + args.hmodule in viewvalues(winobjs.runtime_dll.name2off)): + name_inv = dict( + [ + (x[1], x[0]) + for x in viewitems(winobjs.runtime_dll.name2off) + ] + ) + p = name_inv[args.hmodule] + else: + log.warning(('Unknown module 0x%x.' + + 'Set winobjs.hcurmodule and retry'), args.hmodule) + p = None + + if p is None: + l = 0 + elif args.nsize < len(p): + p = p[:args.nsize] + l = len(p) + else: + l = len(p) + + if p: + set_str(args.lpfilename, p) + + jitter.func_ret_stdcall(ret_ad, l) + + +def kernel32_GetModuleFileNameA(jitter): + kernel32_GetModuleFileName(jitter, whoami(), lambda addr,value: set_win_str_a(jitter, addr, value)) + + +def kernel32_GetModuleFileNameW(jitter): + kernel32_GetModuleFileName(jitter, whoami(), lambda addr,value: set_win_str_w(jitter, addr, value)) + + +def kernel32_CreateMutex(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(["mutexattr", "initowner", + "lpname"]) + + if args.lpname: + name = get_str(args.lpname) + log.info("CreateMutex %r", name) + else: + name = None + if args.initowner: + if name in winobjs.mutex: + raise NotImplementedError("Untested case") + # ret = 0 + else: + winobjs.mutex[name] = id(name) & 0xFFFFFFFF + ret = winobjs.mutex[name] + else: + if name in winobjs.mutex: + raise NotImplementedError("Untested case") + # ret = 0 + else: + winobjs.mutex[name] = id(name) & 0xFFFFFFFF + ret = winobjs.mutex[name] + jitter.func_ret_stdcall(ret_ad, ret) + + +def kernel32_CreateMutexA(jitter): + kernel32_CreateMutex(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def kernel32_CreateMutexW(jitter): + kernel32_CreateMutex(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def shell32_SHGetSpecialFolderLocation(jitter): + ret_ad, args = jitter.func_args_stdcall(["hwndowner", "nfolder", "ppidl"]) + jitter.vm.set_u32(args.ppidl, args.nfolder) + jitter.func_ret_stdcall(ret_ad, 0) + + +def kernel32_SHGetPathFromIDList(jitter, funcname, set_str): + ret_ad, args = jitter.func_args_stdcall(["pidl", "ppath"]) + + if args.pidl == 7: # CSIDL_STARTUP: + s = "c:\\doc\\user\\startmenu\\programs\\startup" + set_str(args.ppath, s) + else: + raise ValueError('pidl not implemented', args.pidl) + jitter.func_ret_stdcall(ret_ad, 1) + + +def shell32_SHGetPathFromIDListW(jitter): + kernel32_SHGetPathFromIDList(jitter, whoami(), lambda addr,value: set_win_str_w(jitter, addr, value)) + + +def shell32_SHGetPathFromIDListA(jitter): + kernel32_SHGetPathFromIDList(jitter, whoami(), lambda addr,value: set_win_str_a(jitter, addr, value)) + + +def kernel32_GetLastError(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + jitter.func_ret_stdcall(ret_ad, winobjs.lastwin32error) + + +def kernel32_SetLastError(jitter): + ret_ad, args = jitter.func_args_stdcall(["errcode"]) + # lasterr addr + # ad = tib_address + 0x34 + # jitter.vm.set_mem(ad, pck32(args.errcode)) + winobjs.lastwin32error = args.errcode + jitter.func_ret_stdcall(ret_ad, 0) + + +def kernel32_RestoreLastError(jitter): + kernel32_SetLastError(jitter) + + +def kernel32_LoadLibrary(jitter, get_str): + ret_ad, args = jitter.func_args_stdcall(["dllname"]) + + libname = get_str(args.dllname, 0x100) + ret = winobjs.runtime_dll.lib_get_add_base(libname) + log.info("Loading %r ret 0x%x", libname, ret) + jitter.func_ret_stdcall(ret_ad, ret) + + +def kernel32_LoadLibraryA(jitter): + kernel32_LoadLibrary(jitter, lambda addr, max_char=None:get_win_str_a(jitter, addr, max_char)) + + +def kernel32_LoadLibraryW(jitter): + kernel32_LoadLibrary(jitter, lambda addr, max_char=None:get_win_str_w(jitter, addr, max_char)) + + +def kernel32_LoadLibraryEx(jitter, get_str): + ret_ad, args = jitter.func_args_stdcall(["dllname", "hfile", "flags"]) + + if args.hfile != 0: + raise NotImplementedError("Untested case") + libname = get_str(args.dllname, 0x100) + ret = winobjs.runtime_dll.lib_get_add_base(libname) + log.info("Loading %r ret 0x%x", libname, ret) + jitter.func_ret_stdcall(ret_ad, ret) + + +def kernel32_LoadLibraryExA(jitter): + kernel32_LoadLibraryEx(jitter, lambda addr, max_char=None:get_win_str_a(jitter, addr, max_char)) + + +def kernel32_LoadLibraryExW(jitter): + kernel32_LoadLibraryEx(jitter, lambda addr, max_char=None:get_win_str_w(jitter, addr, max_char)) + + +def kernel32_GetProcAddress(jitter): + ret_ad, args = jitter.func_args_stdcall(["libbase", "fname"]) + fname = args.fname + if fname >= 0x10000: + fname = jitter.get_c_str(fname, 0x100) + if not fname: + fname = None + if fname is not None: + ad = winobjs.runtime_dll.lib_get_add_func(args.libbase, fname) + else: + ad = 0 + log.info("GetProcAddress %r %r ret 0x%x", args.libbase, fname, ad) + jitter.add_breakpoint(ad, jitter.handle_lib) + jitter.func_ret_stdcall(ret_ad, ad) + + +def kernel32_GetModuleHandle(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(["dllname"]) + + if args.dllname: + libname = get_str(args.dllname) + if libname: + ret = winobjs.runtime_dll.lib_get_add_base(libname) + else: + log.warning('unknown module!') + ret = 0 + log.info("GetModuleHandle %r ret 0x%x", libname, ret) + else: + ret = winobjs.current_pe.NThdr.ImageBase + log.info("GetModuleHandle default ret 0x%x", ret) + jitter.func_ret_stdcall(ret_ad, ret) + + +def kernel32_GetModuleHandleA(jitter): + kernel32_GetModuleHandle(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def kernel32_GetModuleHandleW(jitter): + kernel32_GetModuleHandle(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def kernel32_VirtualLock(jitter): + ret_ad, _ = jitter.func_args_stdcall(["lpaddress", "dwsize"]) + jitter.func_ret_stdcall(ret_ad, 1) + + +class systeminfo(object): + oemId = 0 + dwPageSize = 0x1000 + lpMinimumApplicationAddress = 0x10000 + lpMaximumApplicationAddress = 0x7ffeffff + dwActiveProcessorMask = 0x1 + numberOfProcessors = 0x1 + ProcessorsType = 586 + dwAllocationgranularity = 0x10000 + wProcessorLevel = 0x6 + ProcessorRevision = 0xf0b + + def pack(self): + return struct.pack('IIIIIIIIHH', + self.oemId, + self.dwPageSize, + self.lpMinimumApplicationAddress, + self.lpMaximumApplicationAddress, + self.dwActiveProcessorMask, + self.numberOfProcessors, + self.ProcessorsType, + self.dwAllocationgranularity, + self.wProcessorLevel, + self.ProcessorRevision) + + +def kernel32_GetSystemInfo(jitter): + ret_ad, args = jitter.func_args_stdcall(["sys_ptr"]) + sysinfo = systeminfo() + jitter.vm.set_mem(args.sys_ptr, sysinfo.pack()) + jitter.func_ret_stdcall(ret_ad, 0) + + +def kernel32_IsWow64Process(jitter): + ret_ad, args = jitter.func_args_stdcall(["process", "bool_ptr"]) + jitter.vm.set_u32(args.bool_ptr, 0) + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_GetCommandLine(jitter, set_str): + ret_ad, _ = jitter.func_args_stdcall(0) + alloc_addr = winobjs.heap.alloc(jitter, 0x1000) + set_str(alloc_addr, '"%s"' % winobjs.module_path) + jitter.func_ret_stdcall(ret_ad, alloc_addr) + + +def kernel32_GetCommandLineA(jitter): + kernel32_GetCommandLine(jitter, lambda addr, value: set_win_str_a(jitter, addr, value)) + + +def kernel32_GetCommandLineW(jitter): + kernel32_GetCommandLine(jitter, lambda addr, value: set_win_str_w(jitter, addr, value)) + + +def shell32_CommandLineToArgvW(jitter): + ret_ad, args = jitter.func_args_stdcall(["pcmd", "pnumargs"]) + cmd = get_win_str_w(jitter, args.pcmd) + if cmd.startswith('"') and cmd.endswith('"'): + cmd = cmd[1:-1] + log.info("CommandLineToArgv %r", cmd) + tks = cmd.split(' ') + addr = winobjs.heap.alloc(jitter, len(cmd) * 2 + 4 * len(tks)) + addr_ret = winobjs.heap.alloc(jitter, 4 * (len(tks) + 1)) + o = 0 + for i, t in enumerate(tks): + set_win_str_w(jitter, addr + o, t) + jitter.vm.set_u32(addr_ret + 4 * i, addr + o) + o += len(t)*2 + 2 + + jitter.vm.set_u32(addr_ret + 4 * (i+1), 0) + jitter.vm.set_u32(args.pnumargs, len(tks)) + jitter.func_ret_stdcall(ret_ad, addr_ret) + +def cryptdll_MD5Init(jitter): + ret_ad, args = jitter.func_args_stdcall(["ad_ctx"]) + index = len(winobjs.cryptdll_md5_h) + h = MD5.new() + winobjs.cryptdll_md5_h[index] = h + + jitter.vm.set_u32(args.ad_ctx, index) + jitter.func_ret_stdcall(ret_ad, 0) + + +def cryptdll_MD5Update(jitter): + ret_ad, args = jitter.func_args_stdcall(["ad_ctx", "ad_input", "inlen"]) + + index = jitter.vm.get_u32(args.ad_ctx) + if not index in winobjs.cryptdll_md5_h: + raise ValueError('unknown h context', index) + + data = jitter.vm.get_mem(args.ad_input, args.inlen) + winobjs.cryptdll_md5_h[index].update(data) + log.debug(hexdump(data)) + + jitter.func_ret_stdcall(ret_ad, 0) + + +def cryptdll_MD5Final(jitter): + ret_ad, args = jitter.func_args_stdcall(["ad_ctx"]) + + index = jitter.vm.get_u32(args.ad_ctx) + if not index in winobjs.cryptdll_md5_h: + raise ValueError('unknown h context', index) + h = winobjs.cryptdll_md5_h[index].digest() + jitter.vm.set_mem(args.ad_ctx + 88, h) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntdll_RtlInitAnsiString(jitter): + ret_ad, args = jitter.func_args_stdcall(["ad_ctx", "ad_str"]) + + s = get_win_str_a(jitter, args.ad_str) + l = len(s) + jitter.vm.set_mem(args.ad_ctx, + pck16(l) + pck16(l + 1) + pck32(args.ad_str)) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntdll_RtlHashUnicodeString(jitter): + ret_ad, args = jitter.func_args_stdcall(["ad_ctxu", "case_i", "h_id", + "phout"]) + + if args.h_id != 1: + raise ValueError('unk hash unicode', args.h_id) + + l1, l2, ptra = struct.unpack('HHL', jitter.vm.get_mem(args.ad_ctxu, 8)) + s = jitter.vm.get_mem(ptra, l1) + s = s[:-1] + hv = 0 + + if args.case_i: + s = s.lower() + for c in s: + hv = ((65599 * hv) + ord(c)) & 0xffffffff + jitter.vm.set_u32(args.phout, hv) + jitter.func_ret_stdcall(ret_ad, 0) + + +def kernel32_RtlMoveMemory(jitter): + ret_ad, args = jitter.func_args_stdcall(["ad_dst", "ad_src", "m_len"]) + data = jitter.vm.get_mem(args.ad_src, args.m_len) + jitter.vm.set_mem(args.ad_dst, data) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntdll_RtlAnsiCharToUnicodeChar(jitter): + ret_ad, args = jitter.func_args_stdcall(['ad_ad_ch']) + ad_ch = jitter.vm.get_u32(args.ad_ad_ch) + ch = ord(jitter.vm.get_mem(ad_ch, 1)) + jitter.vm.set_u32(args.ad_ad_ch, ad_ch + 1) + jitter.func_ret_stdcall(ret_ad, ch) + + +def ntdll_RtlFindCharInUnicodeString(jitter): + ret_ad, args = jitter.func_args_stdcall(["flags", "main_str_ad", + "search_chars_ad", "pos_ad"]) + + if args.flags != 0: + raise ValueError('unk flags') + + ml1, ml2, mptra = struct.unpack('HHL', + jitter.vm.get_mem(args.main_str_ad, 8)) + sl1, sl2, sptra = struct.unpack( + 'HHL', jitter.vm.get_mem(args.search_chars_ad, 8)) + main_data = jitter.vm.get_mem(mptra, ml1)[:-1] + search_data = jitter.vm.get_mem(sptra, sl1)[:-1] + + pos = None + for i, c in enumerate(main_data): + for s in search_data: + if s == c: + pos = i + break + if pos: + break + if pos is None: + ret = 0xC0000225 + jitter.vm.set_u32(args.pos_ad, 0) + else: + ret = 0 + jitter.vm.set_u32(args.pos_ad, pos) + + jitter.func_ret_stdcall(ret_ad, ret) + + +def ntdll_RtlComputeCrc32(jitter): + ret_ad, args = jitter.func_args_stdcall(["dwinit", "pdata", "ilen"]) + data = jitter.vm.get_mem(args.pdata, args.ilen) + crc_r = crc32(data, args.dwinit) + jitter.func_ret_stdcall(ret_ad, crc_r) + + +def ntdll_RtlExtendedIntegerMultiply(jitter): + ret_ad, args = jitter.func_args_stdcall(['multiplicand_low', + 'multiplicand_high', + 'multiplier']) + a = (args.multiplicand_high << 32) + args.multiplicand_low + a = a * args.multiplier + jitter.func_ret_stdcall(ret_ad, a & 0xffffffff, (a >> 32) & 0xffffffff) + + +def ntdll_RtlLargeIntegerAdd(jitter): + ret_ad, args = jitter.func_args_stdcall(['a_low', 'a_high', + 'b_low', 'b_high']) + a = (args.a_high << 32) + args.a_low + (args.b_high << 32) + args.b_low + jitter.func_ret_stdcall(ret_ad, a & 0xffffffff, (a >> 32) & 0xffffffff) + + +def ntdll_RtlLargeIntegerShiftRight(jitter): + ret_ad, args = jitter.func_args_stdcall(['a_low', 'a_high', 's_count']) + a = ((args.a_high << 32) + args.a_low) >> args.s_count + jitter.func_ret_stdcall(ret_ad, a & 0xffffffff, (a >> 32) & 0xffffffff) + + +def ntdll_RtlEnlargedUnsignedMultiply(jitter): + ret_ad, args = jitter.func_args_stdcall(['a', 'b']) + a = args.a * args.b + jitter.func_ret_stdcall(ret_ad, a & 0xffffffff, (a >> 32) & 0xffffffff) + + +def ntdll_RtlLargeIntegerSubtract(jitter): + ret_ad, args = jitter.func_args_stdcall(['a_low', 'a_high', + 'b_low', 'b_high']) + a = (args.a_high << 32) + args.a_low - (args.b_high << 32) + args.b_low + jitter.func_ret_stdcall(ret_ad, a & 0xffffffff, (a >> 32) & 0xffffffff) + + +def ntdll_RtlCompareMemory(jitter): + ret_ad, args = jitter.func_args_stdcall(['ad1', 'ad2', 'm_len']) + data1 = jitter.vm.get_mem(args.ad1, args.m_len) + data2 = jitter.vm.get_mem(args.ad2, args.m_len) + + i = 0 + while data1[i] == data2[i]: + i += 1 + if i >= args.m_len: + break + + jitter.func_ret_stdcall(ret_ad, i) + + +def user32_GetMessagePos(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + jitter.func_ret_stdcall(ret_ad, 0x00110022) + + +def kernel32_Sleep(jitter): + ret_ad, _ = jitter.func_args_stdcall(['t']) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntdll_ZwUnmapViewOfSection(jitter): + ret_ad, _ = jitter.func_args_stdcall(['h', 'ad']) + jitter.func_ret_stdcall(ret_ad, 0) + + +def kernel32_IsBadReadPtr(jitter): + ret_ad, _ = jitter.func_args_stdcall(['lp', 'ucb']) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntoskrnl_KeInitializeEvent(jitter): + ret_ad, args = jitter.func_args_stdcall(['my_event', 'my_type', + 'my_state']) + jitter.vm.set_u32(args.my_event, winobjs.win_event_num) + winobjs.win_event_num += 1 + + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntoskrnl_RtlGetVersion(jitter): + ret_ad, args = jitter.func_args_stdcall(['ptr_version']) + + s = struct.pack("IIIII", + 0x114, # struct size + 0x5, # maj vers + 0x2, # min vers + 0x666, # build nbr + 0x2, # platform id + ) + encode_win_str_w("Service pack 4") + + jitter.vm.set_mem(args.ptr_version, s) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntoskrnl_RtlVerifyVersionInfo(jitter): + ret_ad, args = jitter.func_args_stdcall(['ptr_version']) + + s = jitter.vm.get_mem(args.ptr_version, 0x5 * 4) + s_size, s_majv, s_minv, s_buildn, s_platform = struct.unpack('IIIII', s) + raise NotImplementedError("Untested case") + # jitter.vm.set_mem(args.ptr_version, s) + # jitter.func_ret_stdcall(ret_ad, 0) + + +def hal_ExAcquireFastMutex(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + jitter.func_ret_stdcall(ret_ad, 0) + + +def mdl2ad(n): + return winobjs.nt_mdl_ad + 0x10 * n + + +def ad2mdl(ad): + return ((ad - winobjs.nt_mdl_ad) & 0xFFFFFFFF) // 0x10 + + +def ntoskrnl_IoAllocateMdl(jitter): + ret_ad, args = jitter.func_args_stdcall(["v_addr", "l", "second_buf", + "chargequota", "pirp"]) + m = mdl(args.v_addr, args.l) + winobjs.nt_mdl[winobjs.nt_mdl_cur] = m + jitter.vm.set_mem(mdl2ad(winobjs.nt_mdl_cur), bytes(m)) + jitter.func_ret_stdcall(ret_ad, mdl2ad(winobjs.nt_mdl_cur)) + winobjs.nt_mdl_cur += 1 + + +def ntoskrnl_MmProbeAndLockPages(jitter): + ret_ad, args = jitter.func_args_stdcall(["p_mdl", "access_mode", "op"]) + + if not ad2mdl(args.p_mdl) in winobjs.nt_mdl: + raise ValueError('unk mdl', hex(args.p_mdl)) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntoskrnl_MmMapLockedPagesSpecifyCache(jitter): + ret_ad, args = jitter.func_args_stdcall(["p_mdl", "access_mode", + "cache_type", "base_ad", + "bugcheckonfailure", + "priority"]) + if not ad2mdl(args.p_mdl) in winobjs.nt_mdl: + raise ValueError('unk mdl', hex(args.p_mdl)) + + jitter.func_ret_stdcall(ret_ad, winobjs.nt_mdl[ad2mdl(args.p_mdl)].ad) + + +def ntoskrnl_MmProtectMdlSystemAddress(jitter): + ret_ad, args = jitter.func_args_stdcall(["p_mdl", "prot"]) + if not ad2mdl(args.p_mdl) in winobjs.nt_mdl: + raise ValueError('unk mdl', hex(args.p_mdl)) + + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntoskrnl_MmUnlockPages(jitter): + ret_ad, args = jitter.func_args_stdcall(['p_mdl']) + if not ad2mdl(args.p_mdl) in winobjs.nt_mdl: + raise ValueError('unk mdl', hex(args.p_mdl)) + + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntoskrnl_IoFreeMdl(jitter): + ret_ad, args = jitter.func_args_stdcall(['p_mdl']) + if not ad2mdl(args.p_mdl) in winobjs.nt_mdl: + raise ValueError('unk mdl', hex(args.p_mdl)) + del(winobjs.nt_mdl[ad2mdl(args.p_mdl)]) + jitter.func_ret_stdcall(ret_ad, 0) + + +def hal_ExReleaseFastMutex(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntoskrnl_RtlQueryRegistryValues(jitter): + ret_ad, args = jitter.func_args_stdcall(["relativeto", "path", + "querytable", + "context", + "environ"]) + # path = get_win_str_w(jitter, args.path) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntoskrnl_ExAllocatePoolWithTagPriority(jitter): + ret_ad, args = jitter.func_args_stdcall(["pool_type", + "nbr_of_bytes", + "tag", "priority"]) + alloc_addr = winobjs.heap.next_addr(args.nbr_of_bytes) + jitter.vm.add_memory_page( + alloc_addr, PAGE_READ | PAGE_WRITE, b"\x00" * args.nbr_of_bytes, + "Alloc in %s ret 0x%X" % (whoami(), ret_ad)) + + jitter.func_ret_stdcall(ret_ad, alloc_addr) + + +def my_lstrcmp(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(["ptr_str1", "ptr_str2"]) + s1 = get_str(args.ptr_str1) + s2 = get_str(args.ptr_str2) + log.info("Compare %r with %r", s1, s2) + jitter.func_ret_stdcall(ret_ad, cmp(s1, s2)) + +def msvcrt_wcscmp(jitter): + ret_ad, args = jitter.func_args_cdecl(["ptr_str1", "ptr_str2"]) + s1 = get_win_str_w(jitter, args.ptr_str1) + s2 = get_win_str_w(jitter, args.ptr_str2) + log.debug("%s('%s','%s')" % (whoami(), s1, s2)) + jitter.func_ret_cdecl(ret_ad, cmp(s1, s2)) + +def msvcrt__wcsicmp(jitter): + ret_ad, args = jitter.func_args_cdecl(["ptr_str1", "ptr_str2"]) + s1 = get_win_str_w(jitter, args.ptr_str1) + s2 = get_win_str_w(jitter, args.ptr_str2) + log.debug("%s('%s','%s')" % (whoami(), s1, s2)) + jitter.func_ret_cdecl(ret_ad, cmp(s1.lower(), s2.lower())) + +def msvcrt__wcsnicmp(jitter): + ret_ad, args = jitter.func_args_cdecl(["ptr_str1", "ptr_str2", "count"]) + s1 = get_win_str_w(jitter, args.ptr_str1) + s2 = get_win_str_w(jitter, args.ptr_str2) + log.debug("%s('%s','%s',%d)" % (whoami(), s1, s2, args.count)) + jitter.func_ret_cdecl(ret_ad, cmp(s1.lower()[:args.count], s2.lower()[:args.count])) + +def msvcrt_wcsncpy(jitter): + ret_ad, args = jitter.func_args_cdecl(["dst", "src", "n"]) + src = get_win_str_w(jitter, args.src) + dst = src[:args.n] + jitter.vm.set_mem(args.dst, b"\x00\x00" * args.n) + jitter.vm.set_mem(args.dst, dst.encode("utf-16le")) + jitter.func_ret_cdecl(ret_ad, args.dst) + +def kernel32_lstrcmpA(jitter): + my_lstrcmp(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def kernel32_lstrcmpiA(jitter): + my_lstrcmp(jitter, whoami(), lambda x: get_win_str_a(jitter, x).lower()) + + +def kernel32_lstrcmpW(jitter): + my_lstrcmp(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def kernel32_lstrcmpiW(jitter): + my_lstrcmp(jitter, whoami(), lambda x: get_win_str_w(jitter, x).lower()) + + +def kernel32_lstrcmpi(jitter): + my_lstrcmp(jitter, whoami(), lambda x: get_win_str_a(jitter, x).lower()) + + +def my_strcpy(jitter, funcname, get_str, set_str): + ret_ad, args = jitter.func_args_stdcall(["ptr_str1", "ptr_str2"]) + s2 = get_str(args.ptr_str2) + set_str(args.ptr_str1, s2) + log.info("Copy '%r'", s2) + jitter.func_ret_stdcall(ret_ad, args.ptr_str1) + + +def kernel32_lstrcpyW(jitter): + my_strcpy(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr), lambda addr,value: set_win_str_w(jitter, addr, value)) + + +def kernel32_lstrcpyA(jitter): + my_strcpy(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr), lambda addr,value: set_win_str_a(jitter, addr, value)) + + +def kernel32_lstrcpy(jitter): + my_strcpy(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr), lambda addr,value: set_win_str_a(jitter, addr, value)) + +def msvcrt__mbscpy(jitter): + ret_ad, args = jitter.func_args_cdecl(["ptr_str1", "ptr_str2"]) + s2 = get_win_str_w(jitter, args.ptr_str2) + set_win_str_w(jitter, args.ptr_str1, s2) + jitter.func_ret_cdecl(ret_ad, args.ptr_str1) + +def msvcrt_wcscpy(jitter): + return msvcrt__mbscpy(jitter) + + +def kernel32_lstrcpyn(jitter): + ret_ad, args = jitter.func_args_stdcall(["ptr_str1", "ptr_str2", + "mlen"]) + s2 = get_win_str_a(jitter, args.ptr_str2) + if len(s2) >= args.mlen: + s2 = s2[:args.mlen - 1] + log.info("Copy '%r'", s2) + set_win_str_a(jitter, args.ptr_str1, s2) + jitter.func_ret_stdcall(ret_ad, args.ptr_str1) + + +def my_strlen(jitter, funcname, get_str, mylen): + ret_ad, args = jitter.func_args_stdcall(["src"]) + src = get_str(args.src) + length = mylen(src) + log.info("Len of '%r' -> 0x%x", src, length) + jitter.func_ret_stdcall(ret_ad, length) + + +def kernel32_lstrlenA(jitter): + my_strlen(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr), len) + + +def kernel32_lstrlenW(jitter): + my_strlen(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr), len) + + +def kernel32_lstrlen(jitter): + my_strlen(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr), len) + + +def my_lstrcat(jitter, funcname, get_str, set_str): + ret_ad, args = jitter.func_args_stdcall(['ptr_str1', 'ptr_str2']) + s1 = get_str(args.ptr_str1) + s2 = get_str(args.ptr_str2) + set_str(args.ptr_str1, s1 + s2) + jitter.func_ret_stdcall(ret_ad, args.ptr_str1) + + +def kernel32_lstrcatA(jitter): + my_lstrcat(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr), lambda addr,value: set_win_str_a(jitter, addr, value)) + + +def kernel32_lstrcatW(jitter): + my_lstrcat(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr), lambda addr,value: set_win_str_w(jitter, addr, value)) + + +def kernel32_GetUserGeoID(jitter): + ret_ad, args = jitter.func_args_stdcall(["geoclass"]) + if args.geoclass == 14: + ret = 12345678 + elif args.geoclass == 16: + ret = 55667788 + else: + raise ValueError('unknown geolcass') + jitter.func_ret_stdcall(ret_ad, ret) + + +def my_GetVolumeInformation(jitter, funcname, get_str, set_str): + ret_ad, args = jitter.func_args_stdcall(["lprootpathname", + "lpvolumenamebuffer", + "nvolumenamesize", + "lpvolumeserialnumber", + "lpmaximumcomponentlength", + "lpfilesystemflags", + "lpfilesystemnamebuffer", + "nfilesystemnamesize"]) + if args.lprootpathname: + s = get_str(args.lprootpathname) + log.info('GetVolumeInformation %r', s) + + + if args.lpvolumenamebuffer: + s = "volumename" + s = s[:args.nvolumenamesize] + set_str(args.lpvolumenamebuffer, s) + + if args.lpvolumeserialnumber: + jitter.vm.set_u32(args.lpvolumeserialnumber, 11111111) + if args.lpmaximumcomponentlength: + jitter.vm.set_u32(args.lpmaximumcomponentlength, 0xff) + if args.lpfilesystemflags: + jitter.vm.set_u32(args.lpfilesystemflags, 22222222) + + if args.lpfilesystemnamebuffer: + s = "filesystemname" + s = s[:args.nfilesystemnamesize] + set_str(args.lpfilesystemnamebuffer, s) + + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_GetVolumeInformationA(jitter): + my_GetVolumeInformation( + jitter, whoami(), lambda addr:get_win_str_a(jitter, addr), lambda addr,value: set_win_str_a(jitter, addr, value)) + + +def kernel32_GetVolumeInformationW(jitter): + my_GetVolumeInformation(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr), lambda addr,value: set_win_str_w(jitter, addr, value)) + + +def kernel32_MultiByteToWideChar(jitter): + MB_ERR_INVALID_CHARS = 0x8 + CP_ACP = 0x000 + CP_1252 = 0x4e4 + + ret_ad, args = jitter.func_args_stdcall(["codepage", "dwflags", + "lpmultibytestr", + "cbmultibyte", + "lpwidecharstr", + "cchwidechar"]) + if args.codepage != CP_ACP and args.codepage != CP_1252: + raise NotImplementedError + # according to MSDN: + # "Note that, if cbMultiByte is 0, the function fails." + if args.cbmultibyte == 0: + raise ValueError + # according to MSDN: + # "Alternatively, this parameter can be set to -1 if the string is + # null-terminated." + if args.cbmultibyte == 0xffffffff: + src_len = 0 + while jitter.vm.get_mem(args.lpmultibytestr + src_len, 1) != b'\0': + src_len += 1 + src = jitter.vm.get_mem(args.lpmultibytestr, src_len) + else: + src = jitter.vm.get_mem(args.lpmultibytestr, args.cbmultibyte) + if args.dwflags & MB_ERR_INVALID_CHARS: + # will raise an exception if decoding fails + s = src.decode("cp1252", errors="replace").encode("utf-16le") + else: + # silently replace undecodable chars with U+FFFD + s = src.decode("cp1252", errors="replace").encode("utf-16le") + if args.cchwidechar > 0: + # return value is number of bytes written + retval = min(args.cchwidechar, len(s)) + jitter.vm.set_mem(args.lpwidecharstr, s[:retval]) + else: + # return value is number of bytes to write + # i.e., size of dest. buffer to allocate + retval = len(s) + jitter.func_ret_stdcall(ret_ad, retval) + + +def kernel32_WideCharToMultiByte(jitter): + """ + int WideCharToMultiByte( + UINT CodePage, + DWORD dwFlags, + _In_NLS_string_(cchWideChar)LPCWCH lpWideCharStr, + int cchWideChar, + LPSTR lpMultiByteStr, + int cbMultiByte, + LPCCH lpDefaultChar, + LPBOOL lpUsedDefaultChar + ); + + """ + CP_ACP = 0x000 + CP_1252 = 0x4e4 + + ret, args = jitter.func_args_stdcall([ + 'CodePage', 'dwFlags', 'lpWideCharStr', 'cchWideChar', + 'lpMultiByteStr', 'cbMultiByte', 'lpDefaultChar', 'lpUsedDefaultChar', + ]) + if args.CodePage != CP_ACP and args.CodePage != CP_1252: + raise NotImplementedError + cchWideChar = args.cchWideChar + if cchWideChar == 0xffffffff: + cchWideChar = len(get_win_str_w(jitter, args.lpWideCharStr)) + 1 + src = jitter.vm.get_mem(args.lpWideCharStr, cchWideChar * 2) + dst = src.decode("utf-16le").encode("cp1252", errors="replace") + if args.cbMultiByte > 0: + # return value is the number of bytes written + retval = min(args.cbMultiByte, len(dst)) + jitter.vm.set_mem(args.lpMultiByteStr, dst[:retval]) + else: + # return value is the size of the buffer to allocate + # to get the multibyte string + retval = len(dst) + jitter.func_ret_stdcall(ret, retval) + + +def my_GetEnvironmentVariable(jitter, funcname, get_str, set_str, mylen): + ret_ad, args = jitter.func_args_stdcall(["lpname", "lpbuffer", + "nsize"]) + + s = get_str(args.lpname) + log.info('GetEnvironmentVariable %r', s) + if s in winobjs.env_variables: + v = winobjs.env_variables[s] + else: + log.warning('WARNING unknown env variable %r', s) + v = "" + set_str(args.lpbuffer, v) + jitter.func_ret_stdcall(ret_ad, mylen(v)) + + +def kernel32_GetEnvironmentVariableA(jitter): + my_GetEnvironmentVariable(jitter, whoami(), + lambda addr:get_win_str_a(jitter, addr), + lambda addr,value: set_win_str_a(jitter, addr, value), + len) + + +def kernel32_GetEnvironmentVariableW(jitter): + my_GetEnvironmentVariable(jitter, whoami(), + lambda addr:get_win_str_w(jitter, addr), + lambda addr,value: set_win_str_w(jitter, addr, value), + len) + + +def my_GetSystemDirectory(jitter, funcname, set_str): + ret_ad, args = jitter.func_args_stdcall(["lpbuffer", "usize"]) + s = "c:\\windows\\system32" + l = len(s) + set_str(args.lpbuffer, s) + jitter.func_ret_stdcall(ret_ad, l) + + + +def kernel32_GetSystemDirectoryA(jitter): + my_GetSystemDirectory(jitter, whoami(), lambda addr,value: set_win_str_a(jitter, addr, value)) + + +def kernel32_GetSystemDirectoryW(jitter): + my_GetSystemDirectory(jitter, whoami(), lambda addr,value: set_win_str_w(jitter, addr, value)) + + +def my_CreateDirectory(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(['lppath', 'secattrib']) + # path = get_str(jitter, args.lppath) + jitter.func_ret_stdcall(ret_ad, 0x1337) + + +def kernel32_CreateDirectoryW(jitter): + my_CreateDirectory(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def kernel32_CreateDirectoryA(jitter): + my_CreateDirectory(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + + +def my_CreateEvent(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(["lpeventattributes", + "bmanualreset", + "binitialstate", + "lpname"]) + s = get_str(args.lpname) if args.lpname else None + if not s in winobjs.events_pool: + winobjs.events_pool[s] = (args.bmanualreset, args.binitialstate) + else: + log.warning('WARNING: known event') + jitter.func_ret_stdcall(ret_ad, id(s) & 0xFFFFFFFF) + + +def kernel32_CreateEventA(jitter): + my_CreateEvent(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def kernel32_CreateEventW(jitter): + my_CreateEvent(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def kernel32_WaitForSingleObject(jitter): + ret_ad, args = jitter.func_args_stdcall(['handle', 'dwms']) + + t_start = time.time() * 1000 + found = False + while True: + if args.dwms and args.dwms + t_start > time.time() * 1000: + ret = 0x102 + break + for key, value in viewitems(winobjs.events_pool): + if key != args.handle: + continue + found = True + if value[1] == 1: + ret = 0 + break + if not found: + log.warning('unknown handle') + ret = 0xffffffff + break + time.sleep(0.1) + jitter.func_ret_stdcall(ret_ad, ret) + + +def kernel32_SetFileAttributesA(jitter): + ret_ad, args = jitter.func_args_stdcall(["lpfilename", + "dwfileattributes"]) + if args.lpfilename: + # fname = get_win_str_a(jitter, args.lpfilename) + ret = 1 + else: + ret = 0 + jitter.vm.set_u32(tib_address + 0x34, 3) + + jitter.func_ret_stdcall(ret_ad, ret) + + +def ntdll_RtlMoveMemory(jitter): + ret_ad, args = jitter.func_args_stdcall(["dst", "src", "l"]) + s = jitter.vm.get_mem(args.src, args.l) + jitter.vm.set_mem(args.dst, s) + jitter.func_ret_stdcall(ret_ad, 1) + + +def ntdll_ZwQuerySystemInformation(jitter): + ret_ad, args = jitter.func_args_stdcall(["systeminformationclass", + "systeminformation", + "systeminformationl", + "returnl"]) + if args.systeminformationclass == 2: + # SYSTEM_PERFORMANCE_INFORMATION + o = struct.pack('II', 0x22222222, 0x33333333) + o += b"\x00" * args.systeminformationl + o = o[:args.systeminformationl] + jitter.vm.set_mem(args.systeminformation, o) + else: + raise ValueError('unknown sysinfo class', + args.systeminformationclass) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntdll_ZwProtectVirtualMemory(jitter): + ret_ad, args = jitter.func_args_stdcall(["handle", "lppvoid", + "pdwsize", + "flnewprotect", + "lpfloldprotect"]) + + ad = jitter.vm.get_u32(args.lppvoid) + # dwsize = upck32(jitter.vm.get_mem(args.pdwsize, 4)) + # XXX mask hpart + flnewprotect = args.flnewprotect & 0xFFF + + if not flnewprotect in ACCESS_DICT: + raise ValueError('unknown access dw!') + jitter.vm.set_mem_access(ad, ACCESS_DICT[flnewprotect]) + + # XXX todo real old protect + jitter.vm.set_u32(args.lpfloldprotect, 0x40) + + jitter.func_ret_stdcall(ret_ad, 1) + + +def ntdll_ZwAllocateVirtualMemory(jitter): + ret_ad, args = jitter.func_args_stdcall(["handle", "lppvoid", + "zerobits", "pdwsize", + "alloc_type", + "flprotect"]) + + # ad = upck32(jitter.vm.get_mem(args.lppvoid, 4)) + dwsize = jitter.vm.get_u32(args.pdwsize) + + if not args.flprotect in ACCESS_DICT: + raise ValueError('unknown access dw!') + + alloc_addr = winobjs.heap.next_addr(dwsize) + jitter.vm.add_memory_page( + alloc_addr, ACCESS_DICT[args.flprotect], b"\x00" * dwsize, + "Alloc in %s ret 0x%X" % (whoami(), ret_ad)) + jitter.vm.set_u32(args.lppvoid, alloc_addr) + + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntdll_ZwFreeVirtualMemory(jitter): + ret_ad, args = jitter.func_args_stdcall(["handle", "lppvoid", + "pdwsize", "alloc_type"]) + # ad = upck32(jitter.vm.get_mem(args.lppvoid, 4)) + # dwsize = upck32(jitter.vm.get_mem(args.pdwsize, 4)) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntdll_RtlInitString(jitter): + ret_ad, args = jitter.func_args_stdcall(["pstring", "source"]) + s = get_win_str_a(jitter, args.source) + l = len(s) + 1 + o = struct.pack('HHI', l, l, args.source) + jitter.vm.set_mem(args.pstring, o) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntdll_RtlAnsiStringToUnicodeString(jitter): + ret_ad, args = jitter.func_args_stdcall(["dst", "src", "alloc_str"]) + + l1, l2, p_src = struct.unpack('HHI', jitter.vm.get_mem(args.src, 0x8)) + s = get_win_str_a(jitter, p_src) + l = (len(s) + 1) * 2 + if args.alloc_str: + alloc_addr = winobjs.heap.next_addr(l) + jitter.vm.add_memory_page( + alloc_addr, PAGE_READ | PAGE_WRITE, b"\x00" * l, + "Alloc in %s ret 0x%X" % (whoami(), ret_ad)) + else: + alloc_addr = p_src + set_win_str_w(jitter, alloc_addr, s) + o = struct.pack('HHI', l, l, alloc_addr) + jitter.vm.set_mem(args.dst, o) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntdll_LdrLoadDll(jitter): + ret_ad, args = jitter.func_args_stdcall(["path", "flags", + "modname", "modhandle"]) + + l1, l2, p_src = struct.unpack('HHI', + jitter.vm.get_mem(args.modname, 0x8)) + s = get_win_str_w(jitter, p_src) + libname = s.lower() + + ad = winobjs.runtime_dll.lib_get_add_base(libname) + log.info("Loading %r ret 0x%x", s, ad) + jitter.vm.set_u32(args.modhandle, ad) + + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntdll_RtlFreeUnicodeString(jitter): + ret_ad, args = jitter.func_args_stdcall(['src']) + # l1, l2, p_src = struct.unpack('HHI', jitter.vm.get_mem(args.src, 0x8)) + # s = get_win_str_w(jitter, p_src) + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntdll_LdrGetProcedureAddress(jitter): + ret_ad, args = jitter.func_args_stdcall(["libbase", "pfname", + "opt", "p_ad"]) + + l1, l2, p_src = struct.unpack('HHI', jitter.vm.get_mem(args.pfname, 0x8)) + fname = get_win_str_a(jitter, p_src) + + ad = winobjs.runtime_dll.lib_get_add_func(args.libbase, fname) + jitter.add_breakpoint(ad, jitter.handle_lib) + + jitter.vm.set_u32(args.p_ad, ad) + + jitter.func_ret_stdcall(ret_ad, 0) + + +def ntdll_memset(jitter): + ret_ad, args = jitter.func_args_cdecl(['addr', 'c', 'size']) + jitter.vm.set_mem(args.addr, int_to_byte(args.c) * args.size) + jitter.func_ret_cdecl(ret_ad, args.addr) + + +def msvcrt_memset(jitter): + ret_ad, args = jitter.func_args_cdecl(['addr', 'c', 'size']) + jitter.vm.set_mem(args.addr, int_to_byte(args.c) * args.size) + jitter.func_ret_cdecl(ret_ad, args.addr) + +def msvcrt_strrchr(jitter): + ret_ad, args = jitter.func_args_cdecl(['pstr','c']) + s = get_win_str_a(jitter, args.pstr) + c = int_to_byte(args.c).decode() + ret = args.pstr + s.rfind(c) + log.info("strrchr(%x '%s','%s') = %x" % (args.pstr,s,c,ret)) + jitter.func_ret_cdecl(ret_ad, ret) + +def msvcrt_wcsrchr(jitter): + ret_ad, args = jitter.func_args_cdecl(['pstr','c']) + s = get_win_str_w(jitter, args.pstr) + c = int_to_byte(args.c).decode() + ret = args.pstr + (s.rfind(c)*2) + log.info("wcsrchr(%x '%s',%s) = %x" % (args.pstr,s,c,ret)) + jitter.func_ret_cdecl(ret_ad, ret) + +def msvcrt_memcpy(jitter): + ret_ad, args = jitter.func_args_cdecl(['dst', 'src', 'size']) + s = jitter.vm.get_mem(args.src, args.size) + jitter.vm.set_mem(args.dst, s) + jitter.func_ret_cdecl(ret_ad, args.dst) + +def msvcrt_realloc(jitter): + ret_ad,args = jitter.func_args_cdecl(['ptr','new_size']) + if args.ptr == 0: + addr = winobjs.heap.alloc(jitter, args.new_size) + else: + addr = winobjs.heap.alloc(jitter, args.new_size) + size = winobjs.heap.get_size(jitter.vm, args.ptr) + data = jitter.vm.get_mem(args.ptr, size) + jitter.vm.set_mem(addr, data) + jitter.func_ret_cdecl(ret_ad, addr) + +def msvcrt_memcmp(jitter): + ret_ad, args = jitter.func_args_cdecl(['ps1', 'ps2', 'size']) + s1 = jitter.vm.get_mem(args.ps1, args.size) + s2 = jitter.vm.get_mem(args.ps2, args.size) + ret = cmp(s1, s2) + jitter.func_ret_cdecl(ret_ad, ret) + + +def shlwapi_PathFindExtensionA(jitter): + ret_ad, args = jitter.func_args_stdcall(['path_ad']) + path = get_win_str_a(jitter, args.path_ad) + i = path.rfind('.') + if i == -1: + i = args.path_ad + len(path) + else: + i = args.path_ad + i + jitter.func_ret_stdcall(ret_ad, i) + + +def shlwapi_PathRemoveFileSpecW(jitter): + ret_ad, args = jitter.func_args_stdcall(['path_ad']) + path = get_win_str_w(jitter, args.path_ad) + i = path.rfind('\\') + if i == -1: + i = 0 + jitter.vm.set_mem(args.path_ad + i * 2, b"\x00\x00") + path = get_win_str_w(jitter, args.path_ad) + jitter.func_ret_stdcall(ret_ad, 1) + + +def shlwapi_PathIsPrefixW(jitter): + ret_ad, args = jitter.func_args_stdcall(['ptr_prefix', 'ptr_path']) + prefix = get_win_str_w(jitter, args.ptr_prefix) + path = get_win_str_w(jitter, args.ptr_path) + + if path.startswith(prefix): + ret = 1 + else: + ret = 0 + jitter.func_ret_stdcall(ret_ad, ret) + + +def shlwapi_PathIsDirectoryW(jitter): + ret_ad, args = jitter.func_args_stdcall(['ptr_path']) + fname = get_win_str_w(jitter, args.ptr_path) + + sb_fname = windows_to_sbpath(fname) + + s = os.stat(sb_fname) + ret = 0 + if stat.S_ISDIR(s.st_mode): + ret = 1 + + jitter.func_ret_cdecl(ret_ad, ret) + + +def shlwapi_PathIsFileSpec(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(['path_ad']) + path = get_str(args.path_ad) + if path.find(':') != -1 and path.find('\\') != -1: + ret = 0 + else: + ret = 1 + + jitter.func_ret_stdcall(ret_ad, ret) + + +def shlwapi_PathGetDriveNumber(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(['path_ad']) + path = get_str(args.path_ad) + l = ord(path[0].upper()) - ord('A') + if 0 <= l <= 25: + ret = l + else: + ret = -1 + + jitter.func_ret_stdcall(ret_ad, ret) + + +def shlwapi_PathGetDriveNumberA(jitter): + shlwapi_PathGetDriveNumber(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def shlwapi_PathGetDriveNumberW(jitter): + shlwapi_PathGetDriveNumber(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def shlwapi_PathIsFileSpecA(jitter): + shlwapi_PathIsFileSpec(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def shlwapi_PathIsFileSpecW(jitter): + shlwapi_PathIsFileSpec(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def shlwapi_StrToIntA(jitter): + ret_ad, args = jitter.func_args_stdcall(['i_str_ad']) + i_str = get_win_str_a(jitter, args.i_str_ad) + try: + i = int(i_str) + except: + log.warning('WARNING cannot convert int') + i = 0 + + jitter.func_ret_stdcall(ret_ad, i) + + +def shlwapi_StrToInt64Ex(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(['pstr', 'flags', 'pret']) + i_str = get_str(args.pstr) + + if args.flags == 0: + r = int(i_str) + elif args.flags == 1: + r = int(i_str, 16) + else: + raise ValueError('cannot decode int') + + jitter.vm.set_mem(args.pret, struct.pack('q', r)) + jitter.func_ret_stdcall(ret_ad, 1) + + +def shlwapi_StrToInt64ExA(jitter): + shlwapi_StrToInt64Ex(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def shlwapi_StrToInt64ExW(jitter): + shlwapi_StrToInt64Ex(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def user32_IsCharAlpha(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(["c"]) + try: + c = int_to_byte(args.c) + except: + log.error('bad char %r', args.c) + c = "\x00" + if c.isalpha(jitter): + ret = 1 + else: + ret = 0 + jitter.func_ret_stdcall(ret_ad, ret) + + +def user32_IsCharAlphaA(jitter): + user32_IsCharAlpha(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def user32_IsCharAlphaW(jitter): + user32_IsCharAlpha(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def user32_IsCharAlphaNumericA(jitter): + ret_ad, args = jitter.func_args_stdcall(["c"]) + c = int_to_byte(args.c) + if c.isalnum(jitter): + ret = 1 + else: + ret = 0 + jitter.func_ret_stdcall(ret_ad, ret) + +def get_fmt_args(jitter, fmt, cur_arg, get_str): + return _get_fmt_args(fmt, cur_arg, get_str, jitter.get_arg_n_cdecl) + +def msvcrt_sprintf_str(jitter, get_str): + ret_ad, args = jitter.func_args_cdecl(['string', 'fmt']) + cur_arg, fmt = 2, args.fmt + return ret_ad, args, get_fmt_args(jitter, fmt, cur_arg, get_str) + +def msvcrt_sprintf(jitter): + ret_ad, args, output = msvcrt_sprintf_str(jitter, lambda addr:get_win_str_a(jitter, addr)) + ret = len(output) + log.info("sprintf() = '%s'" % (output)) + jitter.vm.set_mem(args.string, (output + '\x00').encode('utf8')) + return jitter.func_ret_cdecl(ret_ad, ret) + +def msvcrt_swprintf(jitter): + ret_ad, args = jitter.func_args_cdecl(['string', 'fmt']) + cur_arg, fmt = 2, args.fmt + output = get_fmt_args(jitter, fmt, cur_arg, lambda addr:get_win_str_w(jitter, addr)) + ret = len(output) + log.info("swprintf('%s') = '%s'" % (get_win_str_w(jitter, args.fmt), output)) + jitter.vm.set_mem(args.string, output.encode("utf-16le") + b'\x00\x00') + return jitter.func_ret_cdecl(ret_ad, ret) + +def msvcrt_fprintf(jitter): + ret_addr, args = jitter.func_args_cdecl(['file', 'fmt']) + cur_arg, fmt = 2, args.fmt + output = get_fmt_args(jitter, fmt, cur_arg, lambda addr:get_win_str_a(jitter, addr)) + ret = len(output) + log.info("fprintf(%x, '%s') = '%s'" % (args.file, lambda addr:get_win_str_a(jitter, addr)(args.fmt), output)) + + fd = jitter.vm.get_u32(args.file + 0x10) + if not fd in winobjs.handle_pool: + raise NotImplementedError("Untested case") + winobjs.handle_pool[fd].info.write(output) + + return jitter.func_ret_cdecl(ret_addr, ret) + +def shlwapi_StrCmpNIA(jitter): + ret_ad, args = jitter.func_args_stdcall(["ptr_str1", "ptr_str2", + "nchar"]) + s1 = get_win_str_a(jitter, args.ptr_str1).lower() + s2 = get_win_str_a(jitter, args.ptr_str2).lower() + s1 = s1[:args.nchar] + s2 = s2[:args.nchar] + jitter.func_ret_stdcall(ret_ad, cmp(s1, s2)) + + +def advapi32_RegCreateKeyW(jitter): + ret_ad, args = jitter.func_args_stdcall(["hkey", "subkey", + "phandle"]) + s_subkey = get_win_str_w(jitter, args.subkey).lower() if args.subkey else "" + + ret_hkey = 0 + ret = 2 + if args.hkey in winobjs.hkey_handles: + ret = 0 + if s_subkey: + ret_hkey = hash(s_subkey) & 0xffffffff + winobjs.hkey_handles[ret_hkey] = s_subkey + else: + ret_hkey = args.hkey + + log.info("RegCreateKeyW(%x, '%s') = (%x,%d)" % (args.hkey, s_subkey, ret_hkey, ret)) + jitter.vm.set_u32(args.phandle, ret_hkey) + + jitter.func_ret_stdcall(ret_ad, ret) + +def kernel32_GetCurrentDirectoryA(jitter): + ret_ad, args = jitter.func_args_stdcall(["size","buf"]) + dir_ = winobjs.cur_dir + log.debug("GetCurrentDirectory() = '%s'" % dir_) + set_win_str_a(jitter, args.buf, dir_[:args.size-1]) + ret = len(dir_) + if args.size <= len(dir_): + ret += 1 + jitter.func_ret_stdcall(ret_ad, ret) + +def advapi32_RegOpenKeyEx(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(["hkey", "subkey", + "reserved", "access", + "phandle"]) + s_subkey = get_str(args.subkey).lower() if args.subkey else "" + + ret_hkey = 0 + ret = 2 + if args.hkey in winobjs.hkey_handles: + if s_subkey: + h = hash(s_subkey) & 0xffffffff + if h in winobjs.hkey_handles: + ret_hkey = h + ret = 0 + else: + log.error('unknown skey') + + jitter.vm.set_u32(args.phandle, ret_hkey) + + jitter.func_ret_stdcall(ret_ad, ret) + + +def advapi32_RegOpenKeyExA(jitter): + advapi32_RegOpenKeyEx(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def advapi32_RegOpenKeyExW(jitter): + advapi32_RegOpenKeyEx(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def advapi32_RegSetValue(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(["hkey", "psubkey", + "valuetype", "pvalue", + "vlen"]) + if args.psubkey: + log.info("Subkey %s", get_str(args.psubkey)) + if args.pvalue: + log.info("Value %s", get_str(args.pvalue)) + jitter.func_ret_stdcall(ret_ad, 0) + +def advapi32_RegSetValueEx(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(["hkey", "lpvaluename", + "reserved", "dwtype", + "lpdata", "cbData"]) + hkey = winobjs.hkey_handles.get(args.hkey, "unknown HKEY") + value_name = get_str(args.lpvaluename) if args.lpvaluename else "" + data = get_str(args.lpdata) if args.lpdata else "" + log.info("%s('%s','%s'='%s',%x)" % (funcname, hkey, value_name, data, args.dwtype)) + jitter.func_ret_stdcall(ret_ad, 0) + +def advapi32_RegCloseKey(jitter): + ret_ad, args = jitter.func_args_stdcall(["hkey"]) + del winobjs.hkey_handles[args.hkey] + log.info("RegCloseKey(%x)" % args.hkey) + jitter.func_ret_stdcall(ret_ad, 0) + +def advapi32_RegSetValueExA(jitter): + advapi32_RegSetValueEx(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def advapi32_RegSetValueExW(jitter): + advapi32_RegOpenKeyEx(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def advapi32_RegSetValueA(jitter): + advapi32_RegSetValue(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def advapi32_RegSetValueW(jitter): + advapi32_RegSetValue(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def kernel32_GetThreadLocale(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + jitter.func_ret_stdcall(ret_ad, 0x40c) + +def kernel32_SetCurrentDirectory(jitter, get_str): + ret_ad, args = jitter.func_args_stdcall(['dir']) + dir_ = get_str(args.dir) + log.debug("SetCurrentDirectory('%s') = 1" % dir_) + winobjs.cur_dir = dir_ + jitter.func_ret_stdcall(ret_ad, 1) + +def kernel32_SetCurrentDirectoryW(jitter): + return kernel32_SetCurrentDirectory(jitter, lambda addr:get_win_str_w(jitter, addr)) + +def kernel32_SetCurrentDirectoryA(jitter): + return kernel32_SetCurrentDirectory(jitter, lambda addr:get_win_str_a(jitter, addr)) + +def msvcrt_wcscat(jitter): + ret_ad, args = jitter.func_args_cdecl(['ptr_str1', 'ptr_str2']) + s1 = get_win_str_w(jitter, args.ptr_str1) + s2 = get_win_str_w(jitter, args.ptr_str2) + log.info("strcat('%s','%s')" % (s1,s2)) + set_win_str_w(jitter, args.ptr_str1, s1 + s2) + jitter.func_ret_cdecl(ret_ad, args.ptr_str1) + + +def kernel32_GetLocaleInfo(jitter, funcname, set_str): + ret_ad, args = jitter.func_args_stdcall(["localeid", "lctype", + "lplcdata", "cchdata"]) + + buf = None + ret = 0 + if args.localeid == 0x40c: + if args.lctype == 0x3: + buf = "ENGLISH" + buf = buf[:args.cchdata - 1] + set_str(args.lplcdata, buf) + ret = len(buf) + else: + raise ValueError('unimpl localeid') + + jitter.func_ret_stdcall(ret_ad, ret) + + +def kernel32_GetLocaleInfoA(jitter): + kernel32_GetLocaleInfo(jitter, whoami(), lambda addr,value: set_win_str_a(jitter, addr, value)) + + +def kernel32_GetLocaleInfoW(jitter): + kernel32_GetLocaleInfo(jitter, whoami(), lambda addr,value: set_win_str_w(jitter, addr, value)) + + +def kernel32_TlsAlloc(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + winobjs.tls_index += 1 + jitter.func_ret_stdcall(ret_ad, winobjs.tls_index) + + +def kernel32_TlsFree(jitter): + ret_ad, _ = jitter.func_args_stdcall(["tlsindex"]) + jitter.func_ret_stdcall(ret_ad, 0) + + +def kernel32_TlsSetValue(jitter): + ret_ad, args = jitter.func_args_stdcall(["tlsindex", "tlsvalue"]) + winobjs.tls_values[args.tlsindex] = args.tlsvalue + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_TlsGetValue(jitter): + ret_ad, args = jitter.func_args_stdcall(["tlsindex"]) + if not args.tlsindex in winobjs.tls_values: + raise ValueError("unknown tls val", repr(args.tlsindex)) + jitter.func_ret_stdcall(ret_ad, winobjs.tls_values[args.tlsindex]) + + +def user32_GetKeyboardType(jitter): + ret_ad, args = jitter.func_args_stdcall(["typeflag"]) + + ret = 0 + if args.typeflag == 0: + ret = 4 + else: + raise ValueError('unimpl keyboard type') + + jitter.func_ret_stdcall(ret_ad, ret) + + +class startupinfo(object): + """ + typedef struct _STARTUPINFOA { + /* 00000000 */ DWORD cb; + /* 00000004 */ LPSTR lpReserved; + /* 00000008 */ LPSTR lpDesktop; + /* 0000000C */ LPSTR lpTitle; + /* 00000010 */ DWORD dwX; + /* 00000014 */ DWORD dwY; + /* 00000018 */ DWORD dwXSize; + /* 0000001C */ DWORD dwYSize; + /* 00000020 */ DWORD dwXCountChars; + /* 00000024 */ DWORD dwYCountChars; + /* 00000028 */ DWORD dwFillAttribute; + /* 0000002C */ DWORD dwFlags; + /* 00000030 */ WORD wShowWindow; + /* 00000032 */ WORD cbReserved2; + /* 00000034 */ LPBYTE lpReserved2; + /* 00000038 */ HANDLE hStdInput; + /* 0000003C */ HANDLE hStdOutput; + /* 00000040 */ HANDLE hStdError; + } STARTUPINFOA, *LPSTARTUPINFOA; + + """ + # TODO: fill with relevant values + # for now, struct is just a placeholder + cb = 0x0 + lpReserved = 0x0 + lpDesktop = 0x0 + lpTitle = 0x0 + dwX = 0x0 + dwY = 0x0 + dwXSize = 0x0 + dwYSize = 0x0 + dwXCountChars = 0x0 + dwYCountChars = 0x0 + dwFillAttribute = 0x0 + dwFlags = 0x0 + wShowWindow = 0x0 + cbReserved2 = 0x0 + lpReserved2 = 0x0 + hStdInput = 0x0 + hStdOutput = 0x0 + hStdError = 0x0 + + def pack(self): + return struct.pack('IIIIIIIIIIIIHHIIII', + self.cb, + self.lpReserved, + self.lpDesktop, + self.lpTitle, + self.dwX, + self.dwY, + self.dwXSize, + self.dwYSize, + self.dwXCountChars, + self.dwYCountChars, + self.dwFillAttribute, + self.dwFlags, + self.wShowWindow, + self.cbReserved2, + self.lpReserved2, + self.hStdInput, + self.hStdOutput, + self.hStdError) + + +def kernel32_GetStartupInfo(jitter, funcname, set_str): + """ + void GetStartupInfo( + LPSTARTUPINFOW lpStartupInfo + ); + + Retrieves the contents of the STARTUPINFO structure that was specified + when the calling process was created. + + https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getstartupinfow + + """ + ret_ad, args = jitter.func_args_stdcall(["ptr"]) + jitter.vm.set_mem(args.ptr, startupinfo().pack()) + jitter.func_ret_stdcall(ret_ad, args.ptr) + + +def kernel32_GetStartupInfoA(jitter): + kernel32_GetStartupInfo(jitter, whoami(), lambda addr,value: set_win_str_a(jitter, addr, value)) + + +def kernel32_GetStartupInfoW(jitter): + kernel32_GetStartupInfo(jitter, whoami(), lambda addr,value: set_win_str_w(jitter, addr, value)) + + +def kernel32_GetCurrentThreadId(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + jitter.func_ret_stdcall(ret_ad, 0x113377) + + +def kernel32_InitializeCriticalSection(jitter): + ret_ad, _ = jitter.func_args_stdcall(["lpcritic"]) + jitter.func_ret_stdcall(ret_ad, 0) + + +def user32_GetSystemMetrics(jitter): + ret_ad, args = jitter.func_args_stdcall(["nindex"]) + + ret = 0 + if args.nindex in [0x2a, 0x4a]: + ret = 0 + else: + raise ValueError('unimpl index') + jitter.func_ret_stdcall(ret_ad, ret) + + +def wsock32_WSAStartup(jitter): + ret_ad, args = jitter.func_args_stdcall(["version", "pwsadata"]) + jitter.vm.set_mem(args.pwsadata, b"\x01\x01\x02\x02WinSock 2.0\x00") + jitter.func_ret_stdcall(ret_ad, 0) + + +def get_current_filetime(): + """ + Get current filetime + https://msdn.microsoft.com/en-us/library/ms724228 + """ + curtime = winobjs.current_datetime + unixtime = int(time.mktime(curtime.timetuple())) + filetime = (int(unixtime * 1000000 + curtime.microsecond) * 10 + + DATE_1601_TO_1970) + return filetime + + +def unixtime_to_filetime(unixtime): + """ + Convert unixtime to filetime + https://msdn.microsoft.com/en-us/library/ms724228 + """ + return (unixtime * 10000000) + DATE_1601_TO_1970 + + +def filetime_to_unixtime(filetime): + """ + Convert filetime to unixtime + # https://msdn.microsoft.com/en-us/library/ms724228 + """ + return int((filetime - DATE_1601_TO_1970) // 10000000) + + +def datetime_to_systemtime(curtime): + + s = struct.pack('HHHHHHHH', + curtime.year, # year + curtime.month, # month + curtime.weekday(), # dayofweek + curtime.day, # day + curtime.hour, # hour + curtime.minute , # minutes + curtime.second, # seconds + int(curtime.microsecond // 1000), # millisec + ) + return s + + +def kernel32_GetSystemTimeAsFileTime(jitter): + ret_ad, args = jitter.func_args_stdcall(["lpSystemTimeAsFileTime"]) + + current_filetime = get_current_filetime() + filetime = struct.pack('II', + current_filetime & 0xffffffff, + (current_filetime>>32) & 0xffffffff) + + jitter.vm.set_mem(args.lpSystemTimeAsFileTime, filetime) + jitter.func_ret_stdcall(ret_ad, 0) + + +def kernel32_GetLocalTime(jitter): + ret_ad, args = jitter.func_args_stdcall(["lpsystemtime"]) + systemtime = datetime_to_systemtime(winobjs.current_datetime) + jitter.vm.set_mem(args.lpsystemtime, systemtime) + jitter.func_ret_stdcall(ret_ad, args.lpsystemtime) + + +def kernel32_GetSystemTime(jitter): + ret_ad, args = jitter.func_args_stdcall(["lpsystemtime"]) + systemtime = datetime_to_systemtime(winobjs.current_datetime) + jitter.vm.set_mem(args.lpsystemtime, systemtime) + jitter.func_ret_stdcall(ret_ad, args.lpsystemtime) + + +def kernel32_CreateFileMapping(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(["hfile", "lpattr", "flprotect", + "dwmaximumsizehigh", + "dwmaximumsizelow", "lpname"]) + + if args.hfile == 0xffffffff: + # Create null mapping + if args.dwmaximumsizehigh: + raise NotImplementedError("Untested case") + hmap = StringIO("\x00" * args.dwmaximumsizelow) + hmap_handle = winobjs.handle_pool.add('filemem', hmap) + + ret = winobjs.handle_pool.add('filemapping', hmap_handle) + else: + if not args.hfile in winobjs.handle_pool: + raise ValueError('unknown handle') + ret = winobjs.handle_pool.add('filemapping', args.hfile) + jitter.func_ret_stdcall(ret_ad, ret) + + +def kernel32_CreateFileMappingA(jitter): + kernel32_CreateFileMapping(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def kernel32_CreateFileMappingW(jitter): + kernel32_CreateFileMapping(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def kernel32_MapViewOfFile(jitter): + ret_ad, args = jitter.func_args_stdcall(["hfile", "flprotect", + "dwfileoffsethigh", + "dwfileoffsetlow", + "length"]) + + if not args.hfile in winobjs.handle_pool: + raise ValueError('unknown handle') + hmap = winobjs.handle_pool[args.hfile] + if not hmap.info in winobjs.handle_pool: + raise ValueError('unknown file handle') + + hfile_o = winobjs.handle_pool[hmap.info] + fd = hfile_o.info + fd.seek((args.dwfileoffsethigh << 32) | args.dwfileoffsetlow) + data = fd.read(args.length) if args.length else fd.read() + length = len(data) + + log.debug('MapViewOfFile len: %x', len(data)) + + if not args.flprotect in ACCESS_DICT: + raise ValueError('unknown access dw!') + + alloc_addr = winobjs.heap.alloc(jitter, len(data)) + jitter.vm.set_mem(alloc_addr, data) + + winobjs.handle_mapped[alloc_addr] = (hfile_o, args.dwfileoffsethigh, + args.dwfileoffsetlow, length) + + jitter.func_ret_stdcall(ret_ad, alloc_addr) + + +def kernel32_UnmapViewOfFile(jitter): + ret_ad, args = jitter.func_args_stdcall(['ad']) + + if not args.ad in winobjs.handle_mapped: + raise NotImplementedError("Untested case") + """ + hfile_o, dwfileoffsethigh, dwfileoffsetlow, length = winobjs.handle_mapped[ad] + off = (dwfileoffsethigh<<32) | dwfileoffsetlow + s = jitter.vm.get_mem(ad, length) + hfile_o.info.seek(off) + hfile_o.info.write(s) + hfile_o.info.close() + """ + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_GetDriveType(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(['pathname']) + + p = get_str(args.pathname) + p = p.upper() + + log.debug('Drive: %r', p) + + ret = 0 + if p[0] == "C": + ret = 3 + + jitter.func_ret_stdcall(ret_ad, ret) + + +def kernel32_GetDriveTypeA(jitter): + kernel32_GetDriveType(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def kernel32_GetDriveTypeW(jitter): + kernel32_GetDriveType(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def kernel32_GetDiskFreeSpace(jitter, funcname, get_str): + ret_ad, args = jitter.func_args_stdcall(["lprootpathname", + "lpsectorpercluster", + "lpbytespersector", + "lpnumberoffreeclusters", + "lptotalnumberofclusters"]) + jitter.vm.set_u32(args.lpsectorpercluster, 8) + jitter.vm.set_u32(args.lpbytespersector, 0x200) + jitter.vm.set_u32(args.lpnumberoffreeclusters, 0x222222) + jitter.vm.set_u32(args.lptotalnumberofclusters, 0x333333) + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_GetDiskFreeSpaceA(jitter): + kernel32_GetDiskFreeSpace(jitter, whoami(), lambda addr:get_win_str_a(jitter, addr)) + + +def kernel32_GetDiskFreeSpaceW(jitter): + kernel32_GetDiskFreeSpace(jitter, whoami(), lambda addr:get_win_str_w(jitter, addr)) + + +def kernel32_VirtualQuery(jitter): + ret_ad, args = jitter.func_args_stdcall(["ad", "lpbuffer", "dwl"]) + + all_mem = jitter.vm.get_all_memory() + found = None + for basead, m in viewitems(all_mem): + if basead <= args.ad < basead + m['size']: + found = args.ad, m + break + if not found: + raise ValueError('cannot find mem', hex(args.ad)) + + if args.dwl != 0x1c: + raise ValueError('strange mem len', hex(args.dwl)) + s = struct.pack('IIIIIII', + args.ad, + basead, + ACCESS_DICT_INV[m['access']], + m['size'], + 0x1000, + ACCESS_DICT_INV[m['access']], + 0x01000000) + jitter.vm.set_mem(args.lpbuffer, s) + jitter.func_ret_stdcall(ret_ad, args.dwl) + + +def kernel32_GetProcessAffinityMask(jitter): + ret_ad, args = jitter.func_args_stdcall(["hprocess", + "procaffmask", + "systemaffmask"]) + jitter.vm.set_u32(args.procaffmask, 1) + jitter.vm.set_u32(args.systemaffmask, 1) + jitter.func_ret_stdcall(ret_ad, 1) + + +def msvcrt_rand(jitter): + ret_ad, _ = jitter.func_args_cdecl(0) + jitter.func_ret_stdcall(ret_ad, 0x666) + +def msvcrt_srand(jitter): + ret_ad, _ = jitter.func_args_cdecl(['seed']) + jitter.func_ret_stdcall(ret_ad, 0) + +def msvcrt_wcslen(jitter): + ret_ad, args = jitter.func_args_cdecl(["pwstr"]) + s = get_win_str_w(jitter, args.pwstr) + jitter.func_ret_cdecl(ret_ad, len(s)) + +def kernel32_SetFilePointer(jitter): + ret_ad, args = jitter.func_args_stdcall(["hwnd", "dinstance", + "p_dinstance_high", + "movemethod"]) + + if args.hwnd == winobjs.module_cur_hwnd: + pass + elif args.hwnd in winobjs.handle_pool: + pass + else: + raise ValueError('unknown hwnd!') + + # data = None + if args.hwnd in winobjs.files_hwnd: + winobjs.files_hwnd[winobjs.module_cur_hwnd].seek(args.dinstance, args.movemethod) + elif args.hwnd in winobjs.handle_pool: + wh = winobjs.handle_pool[args.hwnd] + wh.info.seek(args.dinstance, args.movemethod) + else: + raise ValueError('unknown filename') + jitter.func_ret_stdcall(ret_ad, args.dinstance) + + +def kernel32_SetFilePointerEx(jitter): + ret_ad, args = jitter.func_args_stdcall(["hwnd", "dinstance_l", + "dinstance_h", + "pnewfileptr", + "movemethod"]) + dinstance = args.dinstance_l | (args.dinstance_h << 32) + if dinstance: + raise ValueError('Not implemented') + if args.pnewfileptr: + raise ValueError('Not implemented') + if args.hwnd == winobjs.module_cur_hwnd: + pass + elif args.hwnd in winobjs.handle_pool: + pass + else: + raise ValueError('unknown hwnd!') + + # data = None + if args.hwnd in winobjs.files_hwnd: + winobjs.files_hwnd[winobjs.module_cur_hwnd].seek(dinstance, args.movemethod) + elif args.hwnd in winobjs.handle_pool: + wh = winobjs.handle_pool[args.hwnd] + wh.info.seek(dinstance, args.movemethod) + else: + raise ValueError('unknown filename') + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_SetEndOfFile(jitter): + ret_ad, args = jitter.func_args_stdcall(['hwnd']) + if args.hwnd in winobjs.handle_pool: + wh = winobjs.handle_pool[args.hwnd] + wh.info.seek(0, 2) + else: + raise ValueError('unknown filename') + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_FlushFileBuffers(jitter): + ret_ad, args = jitter.func_args_stdcall(['hwnd']) + if args.hwnd in winobjs.handle_pool: + pass + else: + raise ValueError('unknown filename') + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_WriteFile(jitter): + ret_ad, args = jitter.func_args_stdcall(["hwnd", "lpbuffer", + "nnumberofbytestowrite", + "lpnumberofbyteswrite", + "lpoverlapped"]) + data = jitter.vm.get_mem(args.lpbuffer, args.nnumberofbytestowrite) + + if args.hwnd == winobjs.module_cur_hwnd: + pass + elif args.hwnd in winobjs.handle_pool: + pass + else: + raise ValueError('unknown hwnd!') + + if args.hwnd in winobjs.files_hwnd: + winobjs.files_hwnd[winobjs.module_cur_hwnd].write(data) + elif args.hwnd in winobjs.handle_pool: + wh = winobjs.handle_pool[args.hwnd] + wh.info.write(data) + else: + raise ValueError('unknown filename') + + if (args.lpnumberofbyteswrite): + jitter.vm.set_u32(args.lpnumberofbyteswrite, len(data)) + + jitter.func_ret_stdcall(ret_ad, 1) + + +def user32_IsCharUpperA(jitter): + ret_ad, args = jitter.func_args_stdcall(["c"]) + ret = 0 if args.c & 0x20 else 1 + jitter.func_ret_stdcall(ret_ad, ret) + + +def user32_IsCharLowerA(jitter): + ret_ad, args = jitter.func_args_stdcall(["c"]) + ret = 1 if args.c & 0x20 else 0 + jitter.func_ret_stdcall(ret_ad, ret) + + +def kernel32_GetSystemDefaultLangID(jitter): + ret_ad, _ = jitter.func_args_stdcall(0) + jitter.func_ret_stdcall(ret_ad, 0x409) # encglish + + +def msvcrt_malloc(jitter): + ret_ad, args = jitter.func_args_cdecl(["msize"]) + addr = winobjs.heap.alloc(jitter, args.msize) + jitter.func_ret_cdecl(ret_ad, addr) + + +def msvcrt_free(jitter): + ret_ad, _ = jitter.func_args_cdecl(["ptr"]) + jitter.func_ret_cdecl(ret_ad, 0) + + +def msvcrt_fseek(jitter): + ret_ad, args = jitter.func_args_cdecl(['stream', 'offset', 'orig']) + fd = jitter.vm.get_u32(args.stream + 0x10) + + if not fd in winobjs.handle_pool: + raise NotImplementedError("Untested case") + o = winobjs.handle_pool[fd] + o.info.seek(args.offset, args.orig) + jitter.func_ret_cdecl(ret_ad, 0) + + +def msvcrt_ftell(jitter): + ret_ad, args = jitter.func_args_cdecl(["stream"]) + fd = jitter.vm.get_u32(args.stream + 0x10) + + if not fd in winobjs.handle_pool: + raise NotImplementedError("Untested case") + o = winobjs.handle_pool[fd] + off = o.info.tell() + jitter.func_ret_cdecl(ret_ad, off) + + +def msvcrt_rewind(jitter): + ret_ad, args = jitter.func_args_cdecl(["stream"]) + fd = jitter.vm.get_u32(args.stream + 0x10) + if not fd in winobjs.handle_pool: + raise NotImplementedError("Untested case") + o = winobjs.handle_pool[fd] + # off = o.info.seek(0, 0) + jitter.func_ret_cdecl(ret_ad, 0) + + +def msvcrt_fread(jitter): + ret_ad, args = jitter.func_args_cdecl(["buf", "size", "nmemb", "stream"]) + fd = jitter.vm.get_u32(args.stream + 0x10) + if not fd in winobjs.handle_pool: + raise NotImplementedError("Untested case") + + data = winobjs.handle_pool[fd].info.read(args.size * args.nmemb) + jitter.vm.set_mem(args.buf, data) + jitter.func_ret_cdecl(ret_ad, args.nmemb) + + +def msvcrt_fwrite(jitter): + ret_ad, args = jitter.func_args_cdecl(["buf", "size", "nmemb", "stream"]) + fd = jitter.vm.get_u32(args.stream + 0x10) + if not fd in winobjs.handle_pool: + raise NotImplementedError("Unknown file handle!") + + data = jitter.vm.get_mem(args.buf, args.size*args.nmemb) + winobjs.handle_pool[fd].info.write(data) + jitter.func_ret_cdecl(ret_ad, args.nmemb) + + +def msvcrt_fclose(jitter): + ret_ad, args = jitter.func_args_cdecl(['stream']) + fd = jitter.vm.get_u32(args.stream + 0x10) + + if not fd in winobjs.handle_pool: + raise NotImplementedError("Untested case") + o = winobjs.handle_pool[fd] + # off = o.info.close() + jitter.func_ret_cdecl(ret_ad, 0) + + +def msvcrt_atexit(jitter): + ret_ad, _ = jitter.func_args_cdecl(["func"]) + jitter.func_ret_cdecl(ret_ad, 0) + + +def user32_MessageBoxA(jitter): + ret_ad, args = jitter.func_args_stdcall(["hwnd", "lptext", + "lpcaption", "utype"]) + + text = get_win_str_a(jitter, args.lptext) + caption = get_win_str_a(jitter, args.lpcaption) + + log.info('Caption: %r Text: %r', caption, text) + + jitter.func_ret_stdcall(ret_ad, 0) + + +def kernel32_myGetTempPath(jitter, set_str): + ret_ad, args = jitter.func_args_stdcall(["l", "buf"]) + l = 'c:\\temp\\' + if len(l) < args.l: + set_str(args.buf, l) + jitter.func_ret_stdcall(ret_ad, len(l)) + + +def kernel32_GetTempPathA(jitter): + kernel32_myGetTempPath(jitter, lambda addr,value: set_win_str_a(jitter, addr, value)) + + +def kernel32_GetTempPathW(jitter): + kernel32_myGetTempPath(jitter, lambda addr,value: set_win_str_w(jitter, addr, value)) + + +temp_num = 0 + + +def kernel32_GetTempFileNameA(jitter): + global temp_num + ret_ad, args = jitter.func_args_stdcall(["path", "ext", "unique", "buf"]) + + temp_num += 1 + ext = get_win_str_a(jitter, args.ext) if args.ext else 'tmp' + path = get_win_str_a(jitter, args.path) if args.path else "xxx" + fname = path + "\\" + "temp%.4d" % temp_num + "." + ext + jitter.vm.set_mem(args.buf, fname.encode('utf-8')) + + jitter.func_ret_stdcall(ret_ad, 0) + + +class win32_find_data(object): + fileattrib = 0 + creationtime = 0 + lastaccesstime = 0 + lastwritetime = 0 + filesizehigh = 0 + filesizelow = 0 + dwreserved0 = 0 + dwreserved1 = 0x1337beef + cfilename = "" + alternamefilename = "" + + def __init__(self, **kargs): + for k, v in viewitems(kargs): + setattr(self, k, v) + + def toStruct(self, encode_str=encode_win_str_w): + s = struct.pack('=IQQQIIII', + self.fileattrib, + self.creationtime, + self.lastaccesstime, + self.lastwritetime, + self.filesizehigh, + self.filesizelow, + self.dwreserved0, + self.dwreserved1) + fname = encode_str(self.cfilename) + b'\x00' * MAX_PATH + fname = fname[:MAX_PATH] + s += fname + fname = encode_str(self.alternamefilename) + b'\x00' * 14 + fname = fname[:14] + s += fname + return s + + +class find_data_mngr(object): + + def __init__(self): + self.patterns = {} + self.flist = [] + # handle number -> (flist index, current index in list) + self.handles = {} + + def add_list(self, pattern, flist): + index = len(self.flist) + self.flist.append(flist) + + self.patterns[pattern] = index + + def findfirst(self, pattern): + assert(pattern in self.patterns) + findex = self.patterns[pattern] + h = len(self.handles) + 1 + self.handles[h] = [findex, 0] + return h + + def findnext(self, h): + assert(h in self.handles) + findex, index = self.handles[h] + if index >= len(self.flist[findex]): + return None + fname = self.flist[findex][index] + self.handles[h][1] += 1 + + return fname + +def my_FindFirstFile(jitter, pfilepattern, pfindfiledata, get_win_str, encode_str): + filepattern = get_win_str(jitter, pfilepattern) + h = winobjs.find_data.findfirst(filepattern) + + fname = winobjs.find_data.findnext(h) + fdata = win32_find_data(cfilename=fname) + + jitter.vm.set_mem(pfindfiledata, fdata.toStruct(encode_str=encode_str)) + return h + +def kernel32_FindFirstFileA(jitter): + ret_ad, args = jitter.func_args_stdcall(["pfilepattern", "pfindfiledata"]) + h = my_FindFirstFile(jitter, args.pfilepattern, args.pfindfiledata, + get_win_str_a, encode_win_str_a) + jitter.func_ret_stdcall(ret_ad, h) + +def kernel32_FindFirstFileW(jitter): + ret_ad, args = jitter.func_args_stdcall(["pfilepattern", "pfindfiledata"]) + h = my_FindFirstFile(jitter, args.pfilepattern, args.pfindfiledata, + get_win_str_w, encode_win_str_w) + jitter.func_ret_stdcall(ret_ad, h) + +def kernel32_FindFirstFileExA(jitter): + ret_ad, args = jitter.func_args_stdcall([ + "lpFileName", + "fInfoLevelId", + "lpFindFileData", + "fSearchOp", + "lpSearchFilter", + "dwAdditionalFlags"]) + h = my_FindFirstFile(jitter, args.lpFileName, args.lpFindFileData, + get_win_str_a, encode_win_str_a) + jitter.func_ret_stdcall(ret_ad, h) + +def kernel32_FindFirstFileExW(jitter): + ret_ad, args = jitter.func_args_stdcall([ + "lpFileName", + "fInfoLevelId", + "lpFindFileData", + "fSearchOp", + "lpSearchFilter", + "dwAdditionalFlags"]) + h = my_FindFirstFile(jitter, args.lpFileName, args.lpFindFileData, + get_win_str_w, encode_win_str_w) + jitter.func_ret_stdcall(ret_ad, h) + +def my_FindNextFile(jitter, encode_str): + ret_ad, args = jitter.func_args_stdcall(["handle", "pfindfiledata"]) + fname = winobjs.find_data.findnext(args.handle) + if fname is None: + winobjs.lastwin32error = 0x12 # ERROR_NO_MORE_FILES + ret = 0 + else: + ret = 1 + fdata = win32_find_data(cfilename=fname) + jitter.vm.set_mem(args.pfindfiledata, fdata.toStruct(encode_str=encode_str)) + jitter.func_ret_stdcall(ret_ad, ret) + +kernel32_FindNextFileA = lambda jitter: my_FindNextFile(jitter, encode_win_str_a) +kernel32_FindNextFileW = lambda jitter: my_FindNextFile(jitter, encode_win_str_w) + +def kernel32_GetNativeSystemInfo(jitter): + ret_ad, args = jitter.func_args_stdcall(["sys_ptr"]) + sysinfo = systeminfo() + jitter.vm.set_mem(args.sys_ptr, sysinfo.pack()) + jitter.func_ret_stdcall(ret_ad, 0) + + +def raw2guid(r): + o = struct.unpack('IHHHBBBBBB', r) + return '{%.8X-%.4X-%.4X-%.4X-%.2X%.2X%.2X%.2X%.2X%.2X}' % o + + +digs = string.digits + string.ascii_lowercase + + +def int2base(x, base): + if x < 0: + sign = -1 + elif x == 0: + return '0' + else: + sign = 1 + x *= sign + digits = [] + while x: + digits.append(digs[x % base]) + x /= base + if sign < 0: + digits.append('-') + digits.reverse() + return ''.join(digits) + + +def msvcrt__ultow(jitter): + ret_ad, args = jitter.func_args_cdecl(["value", "p", "radix"]) + + value = args.value & 0xFFFFFFFF + if not args.radix in [10, 16, 20]: + raise ValueError("Not tested") + s = int2base(value, args.radix) + set_win_str_w(jitter, args.p, s) + jitter.func_ret_cdecl(ret_ad, args.p) + + +def msvcrt_myfopen(jitter, get_str): + ret_ad, args = jitter.func_args_cdecl(["pfname", "pmode"]) + + fname = get_str(args.pfname) + rw = get_str(args.pmode) + log.info("fopen %r, %r", fname, rw) + + if rw in ['r', 'rb', 'wb+','wb','wt']: + sb_fname = windows_to_sbpath(fname) + h = open(sb_fname, rw) + eax = winobjs.handle_pool.add(sb_fname, h) + dwsize = 0x20 + alloc_addr = winobjs.heap.alloc(jitter, dwsize) + pp = pck32(0x11112222) + pck32(0) + pck32(0) + pck32(0) + pck32(eax) + jitter.vm.set_mem(alloc_addr, pp) + + else: + raise ValueError('unknown access mode %s' % rw) + + jitter.func_ret_cdecl(ret_ad, alloc_addr) + + +def msvcrt__wfopen(jitter): + msvcrt_myfopen(jitter, lambda addr:get_win_str_w(jitter, addr)) + + +def msvcrt_fopen(jitter): + msvcrt_myfopen(jitter, lambda addr:get_win_str_a(jitter, addr)) + + +def msvcrt_strlen(jitter): + ret_ad, args = jitter.func_args_cdecl(["src"]) + + s = get_win_str_a(jitter, args.src) + jitter.func_ret_cdecl(ret_ad, len(s)) + + +def kernel32_QueryPerformanceCounter(jitter): + ret_ad, args = jitter.func_args_stdcall(["lpPerformanceCount"]) + jitter.vm.set_mem(args.lpPerformanceCount, struct.pack('<Q', 0x1)) + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_InitializeCriticalSectionEx(jitter): + ''' + LPCRITICAL_SECTION lpCriticalSection, + DWORD dwSpinCount, + DWORD Flags + ''' + ret_ad, args = jitter.func_args_stdcall(["lpCriticalSection", "dwSpinCount", "Flags"]) + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_EnterCriticalSection(jitter): + ''' + void EnterCriticalSection( + LPCRITICAL_SECTION lpCriticalSection + ); + ''' + ret_ad, args = jitter.func_args_stdcall(["lpCriticalSection"]) + jitter.func_ret_stdcall(ret_ad, 0x0) + + +def kernel32_LeaveCriticalSection(jitter): + ''' + void LeaveCriticalSection( + LPCRITICAL_SECTION lpCriticalSection + ); + ''' + ret_ad, args = jitter.func_args_stdcall(["lpCriticalSection"]) + jitter.func_ret_stdcall(ret_ad, 0x0) + + +class FLS(object): + def __init__(self): + self.slots = [] + + def kernel32_FlsAlloc(self, jitter): + ''' + DWORD FlsAlloc( + PFLS_CALLBACK_FUNCTION lpCallback + ); + ''' + ret_ad, args = jitter.func_args_stdcall(["lpCallback"]) + index = len(self.slots) + self.slots.append(0x0) + jitter.func_ret_stdcall(ret_ad, index) + + def kernel32_FlsSetValue(self, jitter): + ''' + BOOL FlsSetValue( + DWORD dwFlsIndex, + PVOID lpFlsData + ); + ''' + ret_ad, args = jitter.func_args_stdcall(["dwFlsIndex", "lpFlsData"]) + self.slots[args.dwFlsIndex] = args.lpFlsData + jitter.func_ret_stdcall(ret_ad, 1) + + def kernel32_FlsGetValue(self, jitter): + ''' + PVOID FlsGetValue( + DWORD dwFlsIndex + ); + ''' + ret_ad, args = jitter.func_args_stdcall(["dwFlsIndex"]) + jitter.func_ret_stdcall(ret_ad, self.slots[args.dwFlsIndex]) + +fls = FLS() + + +def kernel32_GetProcessHeap(jitter): + ''' + HANDLE GetProcessHeap(); + ''' + ret_ad, args = jitter.func_args_stdcall([]) + hHeap = 0x67676767 + jitter.func_ret_stdcall(ret_ad, hHeap) + + +STD_INPUT_HANDLE = 0xfffffff6 +STD_OUTPUT_HANDLE = 0xfffffff5 +STD_ERROR_HANDLE = 0xfffffff4 + + +def kernel32_GetStdHandle(jitter): + ''' + HANDLE WINAPI GetStdHandle( + _In_ DWORD nStdHandle + ); + + STD_INPUT_HANDLE (DWORD)-10 + The standard input device. Initially, this is the console input buffer, CONIN$. + + STD_OUTPUT_HANDLE (DWORD)-11 + The standard output device. Initially, this is the active console screen buffer, CONOUT$. + + STD_ERROR_HANDLE (DWORD)-12 + The standard error device. Initially, this is the active console screen buffer, CONOUT$. + ''' + ret_ad, args = jitter.func_args_stdcall(["nStdHandle"]) + jitter.func_ret_stdcall(ret_ad, { + STD_OUTPUT_HANDLE: 1, + STD_ERROR_HANDLE: 2, + STD_INPUT_HANDLE: 3, + }[args.nStdHandle]) + + +FILE_TYPE_UNKNOWN = 0x0000 +FILE_TYPE_CHAR = 0x0002 + + +def kernel32_GetFileType(jitter): + ''' + DWORD GetFileType( + HANDLE hFile + ); + ''' + ret_ad, args = jitter.func_args_stdcall(["hFile"]) + jitter.func_ret_stdcall(ret_ad, { + # STD_OUTPUT_HANDLE + 1: FILE_TYPE_CHAR, + # STD_ERROR_HANDLE + 2: FILE_TYPE_CHAR, + # STD_INPUT_HANDLE + 3: FILE_TYPE_CHAR, + }.get(args.hFile, FILE_TYPE_UNKNOWN)) + + +def kernel32_IsProcessorFeaturePresent(jitter): + ''' + BOOL IsProcessorFeaturePresent( + DWORD ProcessorFeature + ); + ''' + ret_ad, args = jitter.func_args_stdcall(["ProcessorFeature"]) + jitter.func_ret_stdcall(ret_ad, { + # PF_ARM_64BIT_LOADSTORE_ATOMIC + 25: False, + # PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE + 24: False, + # PF_ARM_EXTERNAL_CACHE_AVAILABLE + 26: False, + # PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE + 27: False, + # PF_ARM_VFP_32_REGISTERS_AVAILABLE + 18: False, + # PF_3DNOW_INSTRUCTIONS_AVAILABLE + 7: False, + # PF_CHANNELS_ENABLED + 16: True, + # PF_COMPARE_EXCHANGE_DOUBLE + 2: False, + # PF_COMPARE_EXCHANGE128 + 14: False, + # PF_COMPARE64_EXCHANGE128 + 15: False, + # PF_FASTFAIL_AVAILABLE + 23: False, + # PF_FLOATING_POINT_EMULATED + 1: False, + # PF_FLOATING_POINT_PRECISION_ERRATA + 0: True, + # PF_MMX_INSTRUCTIONS_AVAILABLE + 3: True, + # PF_NX_ENABLED + 12: True, + # PF_PAE_ENABLED + 9: True, + # PF_RDTSC_INSTRUCTION_AVAILABLE + 8: True, + # PF_RDWRFSGSBASE_AVAILABLE + 22: True, + # PF_SECOND_LEVEL_ADDRESS_TRANSLATION + 20: True, + # PF_SSE3_INSTRUCTIONS_AVAILABLE + 13: True, + # PF_VIRT_FIRMWARE_ENABLED + 21: False, + # PF_XMMI_INSTRUCTIONS_AVAILABLE + 6: True, + # PF_XMMI64_INSTRUCTIONS_AVAILABLE + 10: True, + # PF_XSAVE_ENABLED + 17: False, + }[args.ProcessorFeature]) + + +def kernel32_GetACP(jitter): + ''' + UINT GetACP(); + ''' + ret_ad, args = jitter.func_args_stdcall([]) + # Windows-1252: Latin 1 / Western European Superset of ISO-8859-1 (without C1 controls). + jitter.func_ret_stdcall(ret_ad, 1252) + + +# ref: https://docs.microsoft.com/en-us/windows/win32/intl/code-page-identifiers +VALID_CODE_PAGES = { + 37,437,500,708,709,710,720,737,775,850,852,855,857,858,860,861,862,863,864,865,866,869,870,874,875, + 932,936,949,950,1026,1047,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1200,1201,1250,1251,1252, + 1253,1254,1255,1256,1257,1258,1361,10000,10001,10002,10003,10004,10005,10006,10007,10008,10010,10017, + 10021,10029,10079,10081,10082,12000,12001,20000,20001,20002,20003,20004,20005,20105,20106,20107,20108, + 20127,20261,20269,20273,20277,20278,20280,20284,20285,20290,20297,20420,20423,20424,20833,20838,20866, + 20871,20880,20905,20924,20932,20936,20949,21025,21027,21866,28591,28592,28593,28594,28595,28596,28597, + 28598,28599,28603,28605,29001,38598,50220,50221,50222,50225,50227,50229,50930,50931,50933,50935,50936, + 50937,50939,51932,51936,51949,51950,52936,54936,57002,57003,57004,57005,57006,57007,57008,57009,57010, + 57011,65000,65001 +} + + +def kernel32_IsValidCodePage(jitter): + ''' + BOOL IsValidCodePage( + UINT CodePage + ); + ''' + ret_ad, args = jitter.func_args_stdcall(["CodePage"]) + jitter.func_ret_stdcall(ret_ad, args.CodePage in VALID_CODE_PAGES) + + +def kernel32_GetCPInfo(jitter): + ''' + BOOL GetCPInfo( + UINT CodePage, + LPCPINFO lpCPInfo + ); + ''' + ret_ad, args = jitter.func_args_stdcall(["CodePage", "lpCPInfo"]) + assert args.CodePage == 1252 + # ref: http://www.rensselaer.org/dept/cis/software/g77-mingw32/include/winnls.h + #define MAX_LEADBYTES 12 + #define MAX_DEFAULTCHAR 2 + jitter.vm.set_mem(args.lpCPInfo, struct.pack('<I', 0x1) + b'??' + b'\x00' * 12) + jitter.func_ret_stdcall(ret_ad, 1) + + +def kernel32_GetStringTypeW(jitter): + """ + BOOL GetStringTypeW( + DWORD dwInfoType, + _In_NLS_string_(cchSrc)LPCWCH lpSrcStr, + int cchSrc, + LPWORD lpCharType + ); + + Retrieves character type information for the characters in the specified + Unicode source string. For each character in the string, the function + sets one or more bits in the corresponding 16-bit element of the output + array. Each bit identifies a given character type, for example, letter, + digit, or neither. + + """ + # These types support ANSI C and POSIX (LC_CTYPE) character typing + # functions.A bitwise-OR of these values is retrieved in the array in the + # output buffer when dwInfoType is set to CT_CTYPE1. For DBCS locales, the + # type attributes apply to both narrow characters and wide characters. The + # Japanese hiragana and katakana characters, and the kanji ideograph + # characters all have the C1_ALPHA attribute. + CT_TYPE1 = 0x01 + # TODO handle other types of information + # (CT_TYPE2, CT_TYPE3) + # for now, they raise NotImplemented + CT_TYPE2 = 0x02 + CT_TYPE3 = 0x03 + + C1_UPPER = 0x0001 # Uppercase + C1_LOWER = 0x0002 # Lowercase + C1_DIGIT = 0x0004 # Decimal digits + C1_SPACE = 0x0008 # Space characters + C1_PUNCT = 0x0010 # Punctuation + C1_CNTRL = 0x0020 # Control characters + C1_BLANK = 0x0040 # Blank characters + C1_XDIGIT = 0x0080 # Hexadecimal digits + C1_ALPHA = 0x0100 # Any linguistic character: alphabetical, syllabary, or ideographic + C1_DEFINED = 0x0200 # A defined character, but not one of the other C1_* types + + # the following sets have been generated from the Linux python library curses + # e.g., C1_PUNCT_SET = [chr(i) for i in range(256) if curses.ascii.ispunct(chr(i))] + C1_PUNCT_SET = ['!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', + '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', + '^', '_', '`', '{', '|', '}', '~'] + C1_CNTRL_SET = ['\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', + '\x07', '\x08', '\t', '\n', '\x0b', '\x0c', '\r', '\x0e', '\x0f', + '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', + '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', + '\x7f'] + C1_BLANK_SET = ['\t', ' '] + C1_XDIGIT_SET = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', + 'B', 'C', 'D', 'E', 'F', 'a', 'b', 'c', 'd', 'e', 'f'] + + ret, args = jitter.func_args_stdcall(['dwInfoType', 'lpSrcStr', 'cchSrc', + 'lpCharType']) + s = jitter.vm.get_mem(args.lpSrcStr, args.cchSrc).decode("utf-16") + if args.dwInfoType == CT_TYPE1: + # iterate over characters from the decoded W string + for i, c in enumerate(s): + # TODO handle non-ascii characters + if not c.isascii(): + continue + val = 0 + if c.isupper(): + val |= C1_UPPER + if c.islower(): + val |= C1_LOWER + if c.isdigit(): + val |= C1_DIGIT + if c.isspace(): + val |= C1_SPACE + if c in C1_PUNCT_SET: + val |= C1_PUNCT + if c in C1_CNTRL_SET: + val |= C1_CNTRL + if c in C1_BLANK_SET: + val |= C1_BLANK + if c in C1_XDIGIT_SET: + val |= C1_XDIGIT + if c.isalpha(): + val |= C1_ALPHA + if val == 0: + val = C1_DEFINED + jitter.vm.set_u16(args.lpCharType + i * 2, val) + elif args.dwInfoType == CT_TYPE2: + raise NotImplemented + elif args.dwInfoType == CT_TYPE3: + raise NotImplemented + else: + raise ValueError("CT_TYPE unknown: %i" % args.dwInfoType) + jitter.func_ret_stdcall(ret, 1) + return True diff --git a/src/miasm/os_dep/win_api_x86_32_seh.py b/src/miasm/os_dep/win_api_x86_32_seh.py new file mode 100644 index 00000000..57416477 --- /dev/null +++ b/src/miasm/os_dep/win_api_x86_32_seh.py @@ -0,0 +1,705 @@ +#-*- coding:utf-8 -*- + +# +# Copyright (C) 2011 EADS France, Fabrice Desclaux <fabrice.desclaux@eads.net> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +import logging +import os +import struct + +from future.utils import viewitems + +from miasm.loader import pe_init + +from miasm.jitter.csts import PAGE_READ, PAGE_WRITE +from miasm.core.utils import pck32 +import miasm.arch.x86.regs as x86_regs + +from miasm.os_dep.win_32_structs import LdrDataEntry, ListEntry, \ + TEB, NT_TIB, PEB, PEB_LDR_DATA, ContextException, \ + EXCEPTION_REGISTRATION_RECORD, EXCEPTION_RECORD + +# Constants Windows +EXCEPTION_BREAKPOINT = 0x80000003 +EXCEPTION_SINGLE_STEP = 0x80000004 +EXCEPTION_ACCESS_VIOLATION = 0xc0000005 +EXCEPTION_INT_DIVIDE_BY_ZERO = 0xc0000094 +EXCEPTION_PRIV_INSTRUCTION = 0xc0000096 +EXCEPTION_ILLEGAL_INSTRUCTION = 0xc000001d + + +log = logging.getLogger("seh_helper") +console_handler = logging.StreamHandler() +console_handler.setFormatter(logging.Formatter("[%(levelname)-8s]: %(message)s")) +log.addHandler(console_handler) +log.setLevel(logging.INFO) + +# fs:[0] Page (TIB) +tib_address = 0x7ff70000 +PEB_AD = 0x7ffdf000 +LDR_AD = 0x340000 +DEFAULT_SEH = 0x7ffff000 + +MAX_MODULES = 0x40 + +peb_address = PEB_AD +peb_ldr_data_offset = 0x1ea0 +peb_ldr_data_address = LDR_AD + peb_ldr_data_offset + + +modules_list_offset = 0x1f00 + +InInitializationOrderModuleList_offset = 0x1ee0 +InInitializationOrderModuleList_address = LDR_AD + \ + InInitializationOrderModuleList_offset + +InLoadOrderModuleList_offset = 0x1ee0 + \ + MAX_MODULES * 0x1000 +InLoadOrderModuleList_address = LDR_AD + \ + InLoadOrderModuleList_offset + + +process_environment_address = 0x10000 +process_parameters_address = 0x200000 + +return_from_exception = 0x6eadbeef + + +name2module = [] +main_pe = None +main_pe_name = "c:\\xxx\\toto.exe" + +MAX_SEH = 5 + + +def build_teb(jitter, teb_address): + """ + Build TEB information using following structure: + + @jitter: jitter instance + @teb_address: the TEB address + """ + + # Only allocate space for ExceptionList/ProcessEnvironmentBlock/Self + jitter.vm.add_memory_page( + teb_address, + PAGE_READ | PAGE_WRITE, + b"\x00" * NT_TIB.get_offset("StackBase"), + "TEB.NtTib.ExceptionList" + ) + jitter.vm.add_memory_page( + teb_address + NT_TIB.get_offset("Self"), + PAGE_READ | PAGE_WRITE, + b"\x00" * (NT_TIB.sizeof() - NT_TIB.get_offset("Self")), + "TEB.NtTib.Self" + ) + jitter.vm.add_memory_page( + teb_address + TEB.get_offset("ProcessEnvironmentBlock"), + PAGE_READ | PAGE_WRITE, + b"\x00" * ( + TEB.get_offset("LastErrorValue") - + TEB.get_offset("ProcessEnvironmentBlock") + ), + "TEB.ProcessEnvironmentBlock" + ) + Teb = TEB(jitter.vm, teb_address) + Teb.NtTib.ExceptionList = DEFAULT_SEH + Teb.NtTib.Self = teb_address + Teb.ProcessEnvironmentBlock = peb_address + +def build_peb(jitter, peb_address): + """ + Build PEB information using following structure: + + @jitter: jitter instance + @peb_address: the PEB address + """ + + if main_pe: + offset, length = 8, 4 + else: + offset, length = 0xC, 0 + length += 4 + + jitter.vm.add_memory_page( + peb_address + offset, + PAGE_READ | PAGE_WRITE, + b"\x00" * length, + "PEB + 0x%x" % offset + ) + + Peb = PEB(jitter.vm, peb_address) + if main_pe: + Peb.ImageBaseAddress = main_pe.NThdr.ImageBase + Peb.Ldr = peb_ldr_data_address + + +def build_ldr_data(jitter, modules_info): + """ + Build Loader information using following structure: + + +0x000 Length : Uint4B + +0x004 Initialized : UChar + +0x008 SsHandle : Ptr32 Void + +0x00c InLoadOrderModuleList : _LIST_ENTRY + +0x014 InMemoryOrderModuleList : _LIST_ENTRY + +0x01C InInitializationOrderModuleList : _LIST_ENTRY + # dummy dll base + +0x024 DllBase : Ptr32 Void + + @jitter: jitter instance + @modules_info: LoadedModules instance + + """ + # ldr offset pad + offset = 0xC + addr = LDR_AD + peb_ldr_data_offset + ldrdata = PEB_LDR_DATA(jitter.vm, addr) + + main_pe = modules_info.name2module.get(main_pe_name, None) + ntdll_pe = modules_info.name2module.get("ntdll.dll", None) + + + size = 0 + if main_pe: + size += ListEntry.sizeof() * 2 + main_addr_entry = modules_info.module2entry[main_pe] + if ntdll_pe: + size += ListEntry.sizeof() + ntdll_addr_entry = modules_info.module2entry[ntdll_pe] + + jitter.vm.add_memory_page( + addr + offset, + PAGE_READ | PAGE_WRITE, + b"\x00" * size, + "Loader struct" + ) # (ldrdata.get_size() - offset)) + + last_module = modules_info.module2entry[ + modules_info.modules[-1]] + + if main_pe: + ldrdata.InLoadOrderModuleList.flink = main_addr_entry + ldrdata.InLoadOrderModuleList.blink = last_module + + + ldrdata.InMemoryOrderModuleList.flink = main_addr_entry + \ + LdrDataEntry.get_type().get_offset("InMemoryOrderLinks") + ldrdata.InMemoryOrderModuleList.blink = last_module + \ + LdrDataEntry.get_type().get_offset("InMemoryOrderLinks") + if ntdll_pe: + ldrdata.InInitializationOrderModuleList.flink = ntdll_addr_entry + \ + LdrDataEntry.get_type().get_offset("InInitializationOrderLinks") + ldrdata.InInitializationOrderModuleList.blink = last_module + \ + LdrDataEntry.get_type().get_offset("InInitializationOrderLinks") + + # Add dummy dll base + jitter.vm.add_memory_page(peb_ldr_data_address + 0x24, + PAGE_READ | PAGE_WRITE, pck32(0), + "Loader struct dummy dllbase") + + +class LoadedModules(object): + + """Class representing modules in memory""" + + def __init__(self): + self.modules = [] + self.name2module = {} + self.module2entry = {} + self.module2name = {} + + def add(self, name, module, module_entry): + """Track a new module + @name: module name (with extension) + @module: module object + @module_entry: address of the module entry + """ + + self.modules.append(module) + self.name2module[name] = module + self.module2entry[module] = module_entry + self.module2name[module] = name + + def __repr__(self): + return "\n".join(str(x) for x in viewitems(self.name2module)) + + +def create_modules_chain(jitter, name2module): + """ + Create the modules entries. Those modules are not linked in this function. + + @jitter: jitter instance + @name2module: dict containing association between name and its pe instance + """ + + modules_info = LoadedModules() + base_addr = LDR_AD + modules_list_offset # XXXX + offset_name = 0x500 + offset_path = 0x600 + + out = "" + for i, (fname, pe_obj) in enumerate(viewitems(name2module), 1): + if pe_obj is None: + log.warning("Unknown module: omitted from link list (%r)", + fname) + continue + addr = base_addr + i * 0x1000 + bpath = fname.replace('/', '\\') + bname_str = os.path.split(fname)[1].lower() + bname_unicode = bname_str.encode("utf-16le") + log.info("Add module %x %r", pe_obj.NThdr.ImageBase, bname_str) + + modules_info.add(bname_str, pe_obj, addr) + + # Allocate a partial LdrDataEntry (0-Flags) + jitter.vm.add_memory_page( + addr, + PAGE_READ | PAGE_WRITE, + b"\x00" * LdrDataEntry.get_offset("Flags"), + "Module info %r" % bname_str + ) + + LdrEntry = LdrDataEntry(jitter.vm, addr) + + LdrEntry.DllBase = pe_obj.NThdr.ImageBase + LdrEntry.EntryPoint = pe_obj.Opthdr.AddressOfEntryPoint + LdrEntry.SizeOfImage = pe_obj.NThdr.sizeofimage + LdrEntry.FullDllName.length = len(bname_unicode) + LdrEntry.FullDllName.maxlength = len(bname_unicode) + 2 + LdrEntry.FullDllName.data = addr + offset_path + LdrEntry.BaseDllName.length = len(bname_unicode) + LdrEntry.BaseDllName.maxlength = len(bname_unicode) + 2 + LdrEntry.BaseDllName.data = addr + offset_name + + jitter.vm.add_memory_page( + addr + offset_name, + PAGE_READ | PAGE_WRITE, + bname_unicode + b"\x00" * 2, + "Module name %r" % bname_str + ) + + if isinstance(bpath, bytes): + bpath = bpath.decode('utf8') + bpath_unicode = bpath.encode('utf-16le') + jitter.vm.add_memory_page( + addr + offset_path, + PAGE_READ | PAGE_WRITE, + bpath_unicode + b"\x00" * 2, + "Module path %r" % bname_str + ) + + return modules_info + + +def set_link_list_entry(jitter, loaded_modules, modules_info, offset): + for i, module in enumerate(loaded_modules): + cur_module_entry = modules_info.module2entry[module] + prev_module = loaded_modules[(i - 1) % len(loaded_modules)] + next_module = loaded_modules[(i + 1) % len(loaded_modules)] + prev_module_entry = modules_info.module2entry[prev_module] + next_module_entry = modules_info.module2entry[next_module] + if i == 0: + prev_module_entry = peb_ldr_data_address + 0xC + if i == len(loaded_modules) - 1: + next_module_entry = peb_ldr_data_address + 0xC + + list_entry = ListEntry(jitter.vm, cur_module_entry + offset) + list_entry.flink = next_module_entry + offset + list_entry.blink = prev_module_entry + offset + + + +def fix_InLoadOrderModuleList(jitter, modules_info): + """Fix InLoadOrderModuleList double link list. First module is the main pe, + then ntdll, kernel32. + + @jitter: the jitter instance + @modules_info: the LoadedModules instance + """ + + log.debug("Fix InLoadOrderModuleList") + main_pe = modules_info.name2module.get(main_pe_name, None) + kernel32_pe = modules_info.name2module.get("kernel32.dll", None) + ntdll_pe = modules_info.name2module.get("ntdll.dll", None) + special_modules = [main_pe, kernel32_pe, ntdll_pe] + if not all(special_modules): + log.warn( + 'No main pe, ldr data will be unconsistant %r', special_modules) + loaded_modules = modules_info.modules + else: + loaded_modules = [module for module in modules_info.modules + if module not in special_modules] + loaded_modules[0:0] = [main_pe] + loaded_modules[1:1] = [ntdll_pe] + loaded_modules[2:2] = [kernel32_pe] + + set_link_list_entry(jitter, loaded_modules, modules_info, 0x0) + + +def fix_InMemoryOrderModuleList(jitter, modules_info): + """Fix InMemoryOrderLinks double link list. First module is the main pe, + then ntdll, kernel32. + + @jitter: the jitter instance + @modules_info: the LoadedModules instance + """ + + log.debug("Fix InMemoryOrderModuleList") + main_pe = modules_info.name2module.get(main_pe_name, None) + kernel32_pe = modules_info.name2module.get("kernel32.dll", None) + ntdll_pe = modules_info.name2module.get("ntdll.dll", None) + special_modules = [main_pe, kernel32_pe, ntdll_pe] + if not all(special_modules): + log.warn('No main pe, ldr data will be unconsistant') + loaded_modules = modules_info.modules + else: + loaded_modules = [module for module in modules_info.modules + if module not in special_modules] + loaded_modules[0:0] = [main_pe] + loaded_modules[1:1] = [ntdll_pe] + loaded_modules[2:2] = [kernel32_pe] + + set_link_list_entry(jitter, loaded_modules, modules_info, 0x8) + + +def fix_InInitializationOrderModuleList(jitter, modules_info): + """Fix InInitializationOrderModuleList double link list. First module is the + ntdll, then kernel32. + + @jitter: the jitter instance + @modules_info: the LoadedModules instance + + """ + + log.debug("Fix InInitializationOrderModuleList") + main_pe = modules_info.name2module.get(main_pe_name, None) + kernel32_pe = modules_info.name2module.get("kernel32.dll", None) + ntdll_pe = modules_info.name2module.get("ntdll.dll", None) + special_modules = [main_pe, kernel32_pe, ntdll_pe] + if not all(special_modules): + log.warn('No main pe, ldr data will be unconsistant') + loaded_modules = modules_info.modules + else: + loaded_modules = [module for module in modules_info.modules + if module not in special_modules] + loaded_modules[0:0] = [ntdll_pe] + loaded_modules[1:1] = [kernel32_pe] + + set_link_list_entry(jitter, loaded_modules, modules_info, 0x10) + + +def add_process_env(jitter): + """ + Build a process environment structure + @jitter: jitter instance + """ + + env_unicode = 'ALLUSEESPROFILE=C:\\Documents and Settings\\All Users\x00'.encode('utf-16le') + env_unicode += b"\x00" * 0x10 + jitter.vm.add_memory_page( + process_environment_address, + PAGE_READ | PAGE_WRITE, + env_unicode, + "Process environment" + ) + jitter.vm.set_mem(process_environment_address, env_unicode) + + +def add_process_parameters(jitter): + """ + Build a process parameters structure + @jitter: jitter instance + """ + + o = b"" + o += pck32(0x1000) # size + o += b"E" * (0x48 - len(o)) + o += pck32(process_environment_address) + jitter.vm.add_memory_page( + process_parameters_address, + PAGE_READ | PAGE_WRITE, + o, "Process parameters" + ) + + +# http://blog.fireeye.com/research/2010/08/download_exec_notes.html +seh_count = 0 + + +def init_seh(jitter): + """ + Build the modules entries and create double links + @jitter: jitter instance + """ + + global seh_count + seh_count = 0 + tib_ad = jitter.cpu.get_segm_base(jitter.cpu.FS) + build_teb(jitter, tib_ad) + build_peb(jitter, peb_address) + + modules_info = create_modules_chain(jitter, name2module) + fix_InLoadOrderModuleList(jitter, modules_info) + fix_InMemoryOrderModuleList(jitter, modules_info) + fix_InInitializationOrderModuleList(jitter, modules_info) + + build_ldr_data(jitter, modules_info) + add_process_env(jitter) + add_process_parameters(jitter) + + + +def regs2ctxt(jitter, context_address): + """ + Build x86_32 cpu context for exception handling + @jitter: jitload instance + """ + + ctxt = ContextException(jitter.vm, context_address) + ctxt.memset(b"\x00") + # ContextFlags + # XXX + + # DRX + ctxt.dr0 = 0 + ctxt.dr1 = 0 + ctxt.dr2 = 0 + ctxt.dr3 = 0 + ctxt.dr4 = 0 + ctxt.dr5 = 0 + + # Float context + # XXX + + # Segment selectors + ctxt.gs = jitter.cpu.GS + ctxt.fs = jitter.cpu.FS + ctxt.es = jitter.cpu.ES + ctxt.ds = jitter.cpu.DS + + # Gpregs + ctxt.edi = jitter.cpu.EDI + ctxt.esi = jitter.cpu.ESI + ctxt.ebx = jitter.cpu.EBX + ctxt.edx = jitter.cpu.EDX + ctxt.ecx = jitter.cpu.ECX + ctxt.eax = jitter.cpu.EAX + ctxt.ebp = jitter.cpu.EBP + ctxt.eip = jitter.cpu.EIP + + # CS + ctxt.cs = jitter.cpu.CS + + # Eflags + # XXX TODO real eflag + + # ESP + ctxt.esp = jitter.cpu.ESP + + # SS + ctxt.ss = jitter.cpu.SS + + +def ctxt2regs(jitter, ctxt_ptr): + """ + Restore x86_32 registers from an exception context + @ctxt: the serialized context + @jitter: jitload instance + """ + + ctxt = ContextException(jitter.vm, ctxt_ptr) + + # Selectors + jitter.cpu.GS = ctxt.gs + jitter.cpu.FS = ctxt.fs + jitter.cpu.ES = ctxt.es + jitter.cpu.DS = ctxt.ds + + # Gpregs + jitter.cpu.EDI = ctxt.edi + jitter.cpu.ESI = ctxt.esi + jitter.cpu.EBX = ctxt.ebx + jitter.cpu.EDX = ctxt.edx + jitter.cpu.ECX = ctxt.ecx + jitter.cpu.EAX = ctxt.eax + jitter.cpu.EBP = ctxt.ebp + jitter.cpu.EIP = ctxt.eip + + # CS + jitter.cpu.CS = ctxt.cs + + # Eflag + # XXX TODO + + # ESP + jitter.cpu.ESP = ctxt.esp + # SS + jitter.cpu.SS = ctxt.ss + + +def fake_seh_handler(jitter, except_code, previous_seh=None): + """ + Create an exception context + @jitter: jitter instance + @except_code: x86 exception code + @previous_seh: (optional) last SEH address when multiple SEH are used + """ + global seh_count + log.info('Exception at %x %r', jitter.cpu.EIP, seh_count) + seh_count += 1 + + # Get space on stack for exception handling + new_ESP = jitter.cpu.ESP - 0x3c8 + exception_base_address = new_ESP + exception_record_address = exception_base_address + 0xe8 + context_address = exception_base_address + 0xfc + fake_seh_address = exception_base_address + 0x14 + + # Save a CONTEXT + regs2ctxt(jitter, context_address) + jitter.cpu.ESP = new_ESP + + # Get current seh (fs:[0]) + tib = NT_TIB(jitter.vm, tib_address) + seh = tib.ExceptionList.deref + if previous_seh: + # Recursive SEH + while seh.get_addr() != previous_seh: + seh = seh.Next.deref + seh = seh.Next.deref + + log.debug( + 'seh_ptr %x { old_seh %r eh %r} ctx_addr %x', + seh.get_addr(), + seh.Next, + seh.Handler, + context_address + ) + + # Write exception_record + except_record = EXCEPTION_RECORD(jitter.vm, exception_record_address) + except_record.memset(b"\x00") + except_record.ExceptionCode = except_code + except_record.ExceptionAddress = jitter.cpu.EIP + + # Prepare the stack + jitter.push_uint32_t(context_address) # Context + jitter.push_uint32_t(seh.get_addr()) # SEH + jitter.push_uint32_t(except_record.get_addr()) # ExceptRecords + jitter.push_uint32_t(return_from_exception) # Ret address + + # Set fake new current seh for exception + log.debug("Fake seh ad %x", fake_seh_address) + fake_seh = EXCEPTION_REGISTRATION_RECORD(jitter.vm, fake_seh_address) + fake_seh.Next.val = tib.ExceptionList.val + fake_seh.Handler = 0xaaaaaaaa + tib.ExceptionList.val = fake_seh.get_addr() + dump_seh(jitter) + + # Remove exceptions + jitter.vm.set_exception(0) + jitter.cpu.set_exception(0) + + # XXX set ebx to nul? + jitter.cpu.EBX = 0 + + log.debug('Jumping at %r', seh.Handler) + return seh.Handler.val + + +def dump_seh(jitter): + """ + Walk and dump the SEH entries + @jitter: jitter instance + """ + log.debug('Dump_seh. Tib_address: %x', tib_address) + cur_seh_ptr = NT_TIB(jitter.vm, tib_address).ExceptionList + loop = 0 + while cur_seh_ptr and jitter.vm.is_mapped(cur_seh_ptr.val, + len(cur_seh_ptr)): + if loop > MAX_SEH: + log.debug("Too many seh, quit") + return + err = cur_seh_ptr.deref + log.debug('\t' * (loop + 1) + 'seh_ptr: %x { prev_seh: %r eh %r }', + err.get_addr(), err.Next, err.Handler) + cur_seh_ptr = err.Next + loop += 1 + + +def set_win_fs_0(jitter, fs=4): + """ + Set FS segment selector and create its corresponding segment + @jitter: jitter instance + @fs: segment selector value + """ + jitter.cpu.FS = fs + jitter.cpu.set_segm_base(fs, tib_address) + segm_to_do = set([x86_regs.FS]) + return segm_to_do + + +def return_from_seh(jitter): + """Handle the return from an exception handler + @jitter: jitter instance""" + + # Get object addresses + seh_address = jitter.vm.get_u32(jitter.cpu.ESP + 0x4) + context_address = jitter.vm.get_u32(jitter.cpu.ESP + 0x8) + + # Get registers changes + log.debug('Context address: %x', context_address) + status = jitter.cpu.EAX + ctxt2regs(jitter, context_address) + + # Rebuild SEH (remove fake SEH) + tib = NT_TIB(jitter.vm, tib_address) + seh = tib.ExceptionList.deref + log.debug('Old seh: %x New seh: %x', seh.get_addr(), seh.Next.val) + tib.ExceptionList.val = seh.Next.val + dump_seh(jitter) + + # Handle returned values + if status == 0x0: + # ExceptionContinueExecution + log.debug('SEH continue') + jitter.pc = jitter.cpu.EIP + log.debug('Context::Eip: %x', jitter.pc) + + elif status == 1: + # ExceptionContinueSearch + log.debug("Delegate to the next SEH handler") + # exception_base_address: context_address - 0xfc + # -> exception_record_address: exception_base_address + 0xe8 + exception_record = EXCEPTION_RECORD(jitter.vm, + context_address - 0xfc + 0xe8) + + pc = fake_seh_handler(jitter, exception_record.ExceptionCode, + seh_address) + jitter.pc = pc + + else: + # https://msdn.microsoft.com/en-us/library/aa260344%28v=vs.60%29.aspx + # But the type _EXCEPTION_DISPOSITION may take 2 others values: + # - ExceptionNestedException = 2 + # - ExceptionCollidedUnwind = 3 + raise ValueError("Valid values are ExceptionContinueExecution and " + "ExceptionContinueSearch") + + # Jitter's breakpoint compliant + return True |