Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ on:
pull_request:
branches: [master]

permissions: read-all

jobs:
lint:
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion speakeasy/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "2.0.0a1"
__version__ = "2.0.0b1"
12 changes: 10 additions & 2 deletions speakeasy/windows/winemu.py
Original file line number Diff line number Diff line change
Expand Up @@ -1626,8 +1626,16 @@ def _hook_mem_unmapped(self, emu, access, address, size, value, ctx):
elif address == winemu.API_CALLBACK_HANDLER_ADDR:
run = self.get_current_run()
if run.api_callbacks:
pc, orig_func, args = run.api_callbacks.pop(0)
self.do_call_return(len(args), pc)
ret, orig_func, args = run.api_callbacks.pop(0)
if run.api_callbacks:
next_ret, next_func, next_args = run.api_callbacks[0]
if next_ret is None:
run.api_callbacks[0] = (ret, next_func, next_args)
sp = self.get_stack_ptr()
self.set_func_args(sp, winemu.API_CALLBACK_HANDLER_ADDR, *next_args)
self.set_pc(next_func)
else:
self.do_call_return(len(args), ret)
self._unset_emu_hooks()
return True
Comment on lines 1626 to 1640
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this nesting doesn't seem correct. why is if run.api_callbacks tested twice?

return self._handle_invalid_fetch(emu, address, size, value, ctx)
Expand Down
29 changes: 19 additions & 10 deletions speakeasy/winenv/api/usermode/msvcrt.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,29 +179,38 @@ def _wcsnicmp(self, emu, argv, ctx={}):

return rv

def _read_func_table(self, pfbegin, pfend):
ptrsize = self.get_ptr_size()
funcs = []
ptr = pfbegin
while ptr < pfend:
addr = int.from_bytes(self.mem_read(ptr, ptrsize), "little")
if addr:
funcs.append(addr)
ptr += ptrsize
return funcs

# Reference: https://wiki.osdev.org/Visual_C%2B%2B_Runtime
@apihook("_initterm_e", argc=2, conv=e_arch.CALL_CONV_CDECL)
def _initterm_e(self, emu, argv, ctx={}):
"""
static int _initterm_e(_PIFV * pfbegin,
_PIFV * pfend)
"""

pfbegin, pfend = argv

rv = 0

return rv
funcs = self._read_func_table(pfbegin, pfend)
if funcs:
argv.append(", ".join(f"0x{f:x}" for f in funcs))
return 0

@apihook("_initterm", argc=2, conv=e_arch.CALL_CONV_CDECL)
def _initterm(self, emu, argv, ctx={}):
"""static void _initterm (_PVFV * pfbegin, _PVFV * pfend)"""

pfbegin, pfend = argv

rv = 0

return rv
funcs = self._read_func_table(pfbegin, pfend)
if funcs:
argv.append(", ".join(f"0x{f:x}" for f in funcs))
return 0

@apihook("__getmainargs", argc=5)
def __getmainargs(self, emu, argv, ctx={}):
Expand Down
32 changes: 32 additions & 0 deletions tests/test_initterm.py
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think these tests add enough value, lets remove the file.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
def test_initterm_reports_function_table(config, load_test_bin, run_test):
"""_initterm and _initterm_e should parse the function pointer table
and include the entries in the API event args."""
data = load_test_bin("argv_test_x86.exe.xz")
report = run_test(config, data)
ep = report.entry_points

initterm_events = []
for evt in ep[0].events or []:
if evt.event == "api" and "_initterm" in evt.api_name:
initterm_events.append(evt)

assert len(initterm_events) > 0, "expected at least one _initterm call"

for evt in initterm_events:
assert len(evt.args) == 3, f"expected 3 args (pfbegin, pfend, func_table) but got {len(evt.args)}: {evt.args}"
func_table_str = evt.args[2]
assert "0x" in func_table_str, f"expected hex addresses in func table: {func_table_str}"


def test_initterm_does_not_crash_emulation(config, load_test_bin, run_test):
"""_initterm should not crash the emulation - main() should still execute."""
data = load_test_bin("argv_test_x86.exe.xz")
report = run_test(config, data, argv=["arg1"])
ep = report.entry_points

printfs = []
for evt in ep[0].events or []:
if evt.event == "api" and "__stdio_common_vfprintf" in evt.api_name:
printfs.append(evt)

assert len(printfs) > 0, "main() should have executed and called printf"
Loading