@@ -296,6 +296,7 @@ def __init__(self, minidump_file, *, trace=False, quiet=False, thread_id=None, d
296
296
self .debug (f"total commit: { hex (self ._pages .total_commit )} , pages: { self ._pages .total_commit // PAGE_SIZE } " )
297
297
self ._setup_modules ()
298
298
self .syscalls = []
299
+ self .win32k_syscalls = []
299
300
self ._setup_syscalls ()
300
301
self ._setup_emulator (thread )
301
302
self .handles = HandleManager ()
@@ -779,10 +780,10 @@ def _setup_modules(self):
779
780
def _setup_syscalls (self ):
780
781
# Load the ntdll module from memory
781
782
ntdll = self .modules ["ntdll.dll" ]
782
- syscalls = []
783
+ nt_syscalls = []
783
784
for export in ntdll .exports :
784
785
if export .name and export .name .startswith ("Zw" ):
785
- syscalls .append ((export .address , export .name ))
786
+ nt_syscalls .append ((export .address , export .name ))
786
787
elif export .name == "Wow64Transition" :
787
788
patch_addr = self .read_ptr (export .address )
788
789
self .info (f"Patching Wow64Transition: { hex (export .address )} -> { hex (patch_addr )} " )
@@ -795,20 +796,46 @@ def _setup_syscalls(self):
795
796
elif export .name == "LdrLoadDll" :
796
797
self .LdrLoadDll = export .address
797
798
798
- syscalls .sort ()
799
- for index , (rva , name ) in enumerate (syscalls ):
800
- cb = syscall_functions .get (name , None )
801
- argcount = 0
802
- if cb :
803
- argspec = inspect .getfullargspec (cb )
804
- argcount = len (argspec .args ) - 1
805
- self .syscalls .append ((name , cb , argcount ))
799
+ def add_syscalls (syscalls , table ):
800
+ # The index when sorting by RVA is the syscall index
801
+ syscalls .sort ()
802
+ for index , (rva , name ) in enumerate (syscalls ):
803
+ cb = syscall_functions .get (name , None )
804
+ argcount = 0
805
+ if cb :
806
+ argspec = inspect .getfullargspec (cb )
807
+ argcount = len (argspec .args ) - 1
808
+ table .append ((name , cb , argcount ))
809
+
810
+ add_syscalls (nt_syscalls , self .syscalls )
811
+
812
+ # Get the syscalls for win32u
813
+ win32u = self .modules .find ("win32u.dll" )
814
+ if win32u is not None :
815
+ win32k_syscalls = []
816
+ for export in win32u .exports :
817
+ if export .name and export .name .startswith ("Nt" ):
818
+ win32k_syscalls .append ((export .address , export .name ))
819
+
820
+ add_syscalls (win32k_syscalls , self .win32k_syscalls )
821
+
806
822
807
823
def push (self , value ):
808
824
csp = self .regs .csp - self .ptr_size ()
809
825
self .write_ptr (csp , value )
810
826
self .regs .csp = csp
811
827
828
+ def pop (self ):
829
+ csp = self .regs .csp
830
+ value = self .read_ptr (csp )
831
+ self .regs .csp = csp + self .ptr_size ()
832
+ return value
833
+
834
+ def ret (self , imm = 0 ):
835
+ return_address = self .pop ()
836
+ self .regs .csp -= imm
837
+ return return_address
838
+
812
839
def read (self , addr , size ):
813
840
if not isinstance (addr , int ):
814
841
addr = int (addr )
@@ -1464,19 +1491,36 @@ def _hook_syscall(uc: Uc, dp: Dumpulator):
1464
1491
# Flush the trace for easier debugging
1465
1492
if dp .trace is not None :
1466
1493
dp .trace .flush ()
1467
- index = dp .regs .cax & 0xffff
1468
- if index < len (dp .syscalls ):
1469
- name , syscall_impl , argcount = dp .syscalls [index ]
1494
+
1495
+ # Extract the table and function number from eax
1496
+ service_number = dp .regs .cax & 0xffff
1497
+ table_number = (service_number >> 12 ) & 0xf # 0: ntoskrnl, 1: win32k
1498
+ function_index = service_number & 0xfff
1499
+ if table_number == 0 :
1500
+ table = dp .syscalls
1501
+ table_prefix = ""
1502
+ elif table_number == 1 :
1503
+ table = dp .win32k_syscalls
1504
+ table_prefix = "win32k "
1505
+ else :
1506
+ table = []
1507
+ table_prefix = f"unknown:{ table_number } "
1508
+
1509
+ if function_index < len (table ):
1510
+ name , syscall_impl , argcount = table [function_index ]
1470
1511
if syscall_impl :
1471
1512
argspec = inspect .getfullargspec (syscall_impl )
1472
1513
args = []
1473
1514
1474
1515
def syscall_arg (index ):
1516
+ # There is an extra call that adds a return address to the stack
1517
+ if dp .wow64 :
1518
+ index += 1
1475
1519
if index == 0 and dp .ptr_size () == 8 :
1476
1520
return dp .regs .r10
1477
1521
return dp .args [index ]
1478
1522
1479
- dp .info (f"[{ dp .sequence_id } ] syscall ( index: { hex (index ) } ): { name } ( " )
1523
+ dp .info (f"[{ dp .sequence_id } ] { table_prefix } syscall: { name } ( /* index: { hex (service_number ) } */ " )
1480
1524
for i in range (0 , argcount ):
1481
1525
argname = argspec .args [1 + i ]
1482
1526
argtype = argspec .annotations [argname ]
@@ -1499,9 +1543,9 @@ def syscall_arg(index):
1499
1543
argvalue = argtype (dp , argvalue )
1500
1544
elif issubclass (argtype , Enum ):
1501
1545
try :
1502
- argvalue = argtype (dp . args [ i ] & 0xFFFFFFFF )
1546
+ argvalue = argtype (argvalue & 0xFFFFFFFF )
1503
1547
except KeyError as x :
1504
- raise Exception (f"Unknown enum value { dp . args [ i ] } for { type (argtype )} " ) from None
1548
+ raise Exception (f"Unknown enum value { argvalue } for { type (argtype )} " ) from None
1505
1549
else :
1506
1550
argvalue = argtype (argvalue )
1507
1551
args .append (argvalue )
@@ -1535,9 +1579,9 @@ def syscall_arg(index):
1535
1579
finally :
1536
1580
dp .sequence_id += 1
1537
1581
else :
1538
- raise dp .raise_kill (NotImplementedError (f"syscall index: { index :x } -> { name } not implemented!" )) from None
1582
+ raise dp .raise_kill (NotImplementedError (f"{ table_prefix } syscall { hex ( service_number ) } -> { name } not implemented!" )) from None
1539
1583
else :
1540
- raise dp .raise_kill (IndexError (f"syscall index { index :x } out of range" )) from None
1584
+ raise dp .raise_kill (IndexError (f"{ table_prefix } syscall { hex ( service_number ) } ( index: { hex ( function_index ) } ) out of range" )) from None
1541
1585
1542
1586
def _emulate_unsupported_instruction (dp : Dumpulator , instr : CsInsn ):
1543
1587
if instr .id == X86_INS_RDRAND :
0 commit comments