Skip to content
Open
17 changes: 15 additions & 2 deletions smda/common/labelprovider/OrdinalHelper.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class OrdinalHelper:
# TODO POC implementation, extend list. ole32.dll and mfc42.dll are candidates here
# Central mapping for DLL ordinals to function names.
# Only include mappings that are stable across Windows versions (XP, Win7, Win10+).
ORDINALS = {
"ws2_32.dll": {
1: "accept",
Expand Down Expand Up @@ -35,7 +36,19 @@ class OrdinalHelper:
21: "setsockopt",
22: "shutdown",
23: "socket",
}
},
"oleaut32.dll": {
2: "SysAllocString",
4: "SysAllocStringLen",
6: "SysFreeString",
7: "SysStringLen",
8: "VariantInit",
9: "VariantClear",
10: "VariantCopy",
144: "DllCanUnloadNow",
149: "SysStringByteLen",
150: "SysAllocStringByteLen",
},
}

@staticmethod
Expand Down
14 changes: 12 additions & 2 deletions smda/intel/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,33 +138,43 @@
],
2: [
b"\x66\x90", # NOP2_OVERRIDE_NOP - AMD / nop - INTEL
b"\x8b\xc0",
b"\x8b\xc0", # mov eax, eax
b"\x89\xc0", # mov eax, eax
b"\x8b\xff", # mov edi, edi
b"\x89\xff", # mov edi, edi
b"\x8d\x00", # lea eax, dword ptr [eax]
b"\x86\xc0", # xchg al, al
b"\x66\x2e", # NOP2_OVERRIDE_NOP - AMD / nop - INTEL
b"\xeb\x00", # jmp $+2 (jumps to next instruction; used as padding by some MSVC versions)
b"\x8b\xc9", # mov ecx, ecx
b"\x8b\xd2", # mov edx, edx
b"\x8b\xdb", # mov ebx, ebx
b"\x8b\xf6", # mov esi, esi
],
3: [
b"\x0f\x1f\x00", # NOP3_OVERRIDE_NOP - AMD / nop - INTEL
b"\x8d\x40\x00", # lea eax, dword ptr [eax]
b"\x8d\x00\x00", # lea eax, dword ptr [eax]
b"\x8d\x49\x00", # lea ecx, dword ptr [ecx]
b"\x8d\x64\x24", # lea esp, dword ptr [esp]
b"\x8d\x24\x24", # lea esp, dword ptr [esp]
b"\x8d\x76\x00",
b"\x66\x66\x90",
],
4: [
b"\x0f\x1f\x40\x00", # NOP4_OVERRIDE_NOP - AMD / nop - INTEL
b"\x8d\x74\x26\x00",
b"\x66\x66\x66\x90",
b"\x8d\x64\x24\x00", # lea esp, [esp+0]
],
5: [
b"\x0f\x1f\x44\x00\x00", # NOP5_OVERRIDE_NOP - AMD / nop - INTEL
b"\x90\x8d\x74\x26\x00",
b"\x66\x0f\x1f\x40\x00",
],
6: [
b"\x66\x0f\x1f\x44\x00\x00", # NOP6_OVERRIDE_NOP - AMD / nop - INTEL
b"\x8d\xb6\x00\x00\x00\x00",
b"\x8d\xbf\x00\x00\x00\x00", # lea edi, [edi]
],
7: [
b"\x0f\x1f\x80\x00\x00\x00\x00", # NOP7_OVERRIDE_NOP - AMD / nop - INTEL,
Expand Down
56 changes: 56 additions & 0 deletions tests/testDefinitionsExpansion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import unittest

from smda.common.labelprovider.OrdinalHelper import OrdinalHelper
from smda.intel.definitions import GAP_SEQUENCES


class TestDefinitionsExpansion(unittest.TestCase):
def test_gap_sequences(self):
expected_sequences = [
(2, b"\xeb\x00"), # jmp $+2 (NOP-equivalent padding)
(2, b"\x89\xc0"), # mov eax, eax
(2, b"\x8b\xc9"), # mov ecx, ecx
(2, b"\x8b\xd2"), # mov edx, edx
(2, b"\x8b\xdb"), # mov ebx, ebx
(2, b"\x8b\xf6"), # mov esi, esi
(3, b"\x8d\x24\x24"), # lea esp, [esp] (fixed from incorrect \x8d\x64\x24)
(4, b"\x8d\x64\x24\x00"), # lea esp, [esp+0]
(5, b"\x66\x0f\x1f\x40\x00"), # multi-byte NOP (operand-size prefix)
(6, b"\x8d\xbf\x00\x00\x00\x00"), # lea edi, [edi]
]

for length, seq in expected_sequences:
self.assertIn(
seq, GAP_SEQUENCES[length], f"Sequence {seq.hex()} of length {length} not found in GAP_SEQUENCES"
)

def test_no_rex_sequences_in_shared_dict(self):
# REX-prefixed (0x48) sequences must not be in the shared dict:
# in 32-bit mode 0x48 is "dec eax", making these non-NOP sequences.
rex_sequences = [
b"\x48\x8b\xc0",
b"\x48\x89\xc0",
b"\x48\x8d\x00",
b"\x48\x8d\x40\x00",
b"\x48\x0f\x1f\x40\x00",
b"\x48\x8d\x64\x24\x00",
]
for seq in rex_sequences:
length = len(seq)
self.assertNotIn(
seq, GAP_SEQUENCES[length], f"REX sequence {seq.hex()} should not be in shared GAP_SEQUENCES"
)

def test_ordinal_expansion(self):
# Test new oleaut32.dll ordinals
self.assertEqual(OrdinalHelper.resolveOrdinal("oleaut32.dll", 6), "SysFreeString")
self.assertEqual(OrdinalHelper.resolveOrdinal("oleaut32.dll", 2), "SysAllocString")
self.assertEqual(OrdinalHelper.resolveOrdinal("oleaut32.dll", 149), "SysStringByteLen")
# Case insensitivity
self.assertEqual(OrdinalHelper.resolveOrdinal("OLEAUT32.DLL", 144), "DllCanUnloadNow")
# Existing ws2_32.dll entries still resolve
self.assertEqual(OrdinalHelper.resolveOrdinal("ws2_32.dll", 23), "socket")


if __name__ == "__main__":
unittest.main()
Loading