Files
2025-10-15 14:49:25 +02:00

188 lines
9.7 KiB
Python

import pytest
from windows.rpc import ndr
import windows.generated_def as gdef
from .pfwtest import *
from tests.test_rpc import UACParameters
# Memo: Padding byte is 'P' in ndr.py
# - UPTR value will be 0x01010101 * field_pos
# 20 Bytes structures alignes on 4 butees
DoubleDwordStructure = ndr.make_structure([ndr.NdrLong] * 5)
# Struct with some specificities
# Start Must be aligned on 4 (even if it begin with a short)
# full size == 13 -> usefull to test afterward padding as well
# Pack format ah follow:
# SSBPLLLLBPSSB
InternalAlignementStructure = ndr.make_structure([ndr.NdrShort, ndr.NdrByte, ndr.NdrLong, ndr.NdrByte, ndr.NdrShort, ndr.NdrByte])
# IDL code:
# typedef struct InternalAlignementStructure
# {
#short sfield0;
#byte bfield1;
#long lfield2;
#byte bfield3;
#short sfield4;
#byte bfield5;
# }InternalAlignementStructure;
class ComplexAlignementStructure(ndr.NdrStructure):
MEMBERS = [
ndr.NdrByte,
ndr.NdrUniquePTR(ndr.NdrByte),
ndr.NdrUniquePTR(ndr.NdrLong),
ndr.NdrByte,
ndr.NdrUniquePTR(ndr.NdrHyper),
ndr.NdrUniquePTR(ndr.NdrByte)
]
# Struct with Pointer & Pointed alignement
# Pack format ah follow (U for unique PTR bytes):
target = "APPP\x01\x02\x03\x04\x05\x06\x07\x08\x44PPP\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10BPPPCCCCPPPPEEEEEEEEF" + "X" * 100
# BPPPUUUUUUUUBPPPUUUUUUUUBPPPLLLLPPPPHHHHHHHHB
# IDL Code:
# struct tstndr {
# byte x;
# [unique] byte* a;
# [unique] long* b;
# byte y;
# [unique] hyper* c;
# [unique] byte* d;
# };
# NdrObject, Values, result
NDR_PACK_TEST_CASE = [
# Simple case
(ndr.make_structure([ndr.NdrLong, ndr.NdrLong]), (2, 2), b"\x02\x00\x00\x00\x02\x00\x00\x00"),
# String case, test packing works + \x00 is added if not present in string
(ndr.NdrCString, "Hello", b"\x06\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00Hello\x00PP"),
(ndr.NdrCString, "Hello\x00", b"\x06\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00Hello\x00PP"),
(ndr.NdrWString, "Hello", b"\x06\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00H\x00e\x00l\x00l\x00o\x00\x00\x00"),
(ndr.NdrWString, "Hello\x00", b"\x06\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00H\x00e\x00l\x00l\x00o\x00\x00\x00"),
# Test GUID packing
(ndr.NdrGuid, gdef.GUID.from_string("42424242-42424242-4242-4242-424242424242"), b"BBBBBBBBBBBBBBBB"),
# Test CtxHandle packing
(ndr.NdrContextHandle, gdef.GUID.from_string("42424242-42424242-4242-4242-424242424242"), b"\x00\x00\x00\x00BBBBBBBBBBBBBBBB"),
# Check alignement on small native types (dword aligned)
(ndr.make_structure([ndr.NdrShort, ndr.NdrByte]), (0x0101, 2), b"\x01\x01\x02"),
# Check alignement on small native types (dword aligned)
(ndr.make_structure([ndr.NdrShort, ndr.NdrShort]), (0x0101, 0x0202), b"\x01\x01\x02\x02"),
# Same check on parameters
(ndr.make_parameters([ndr.NdrShort, ndr.NdrByte]), (0x0101, 2), b"\x01\x01\x02"),
# Test some Hyper
(ndr.make_parameters([ndr.NdrByte, ndr.NdrHyper, ndr.NdrByte, ndr.NdrLong, ndr.NdrHyper]),
(0x01, 0x4141414141414141, 0x42, 0x43434343, 0x4444444444444444 ),
b"\x01PPPPPPPAAAAAAAABPPPCCCCDDDDDDDD"),
# Test NdrContextHandle / NdrGuid (see https://github.com/hakril/PythonForWindows/issues/30)
(ndr.NdrContextHandle, gdef.GUID.from_string("11223344-1122-3344-5566-424344454647"), b'\x00\x00\x00\x00D3"\x11"\x11D3UfBCDEFG'),
(ndr.NdrGuid, gdef.GUID.from_string("11223344-1122-3344-5566-424344454647"), b'D3"\x11"\x11D3UfBCDEFG'),
# Complexe structure (with 4B alignement of 20B structure)
(ndr.make_parameters([ndr.NdrShort, DoubleDwordStructure]),
(0x0101, [0x41414141, 0x42424242, 0x43434343, 0x44444444, 0x45454545]),
b"\x01\x01PPAAAABBBBCCCCDDDDEEEE"),
# Same check on parameters
(ndr.make_parameters([ndr.NdrShort, DoubleDwordStructure]),
(0x0101, [0x41414141, 0x42424242, 0x43434343, 0x44444444, 0x45454545]),
b"\x01\x01PPAAAABBBBCCCCDDDDEEEE"),
# Check on InternalAlignementStructure before any nested test
(InternalAlignementStructure,
[0x4141, 0x42, 0x43434343, 0x44, 0x4545, 0x46],
b"AABPCCCCDPEEF"),
# Variying array in a struct
(ndr.make_parameters([
ndr.NdrLong,
ndr.NdrCharConformantVaryingArrays,
ndr.NdrLong,
ndr.NdrLong]),
[0x42424242, [0x41, 0x42, 0x43, 0x44], 0x43434343, 0x44444444],
b"BBBB\x04\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00ABCDCCCCDDDD"),
# Nested alignement
# Alignement with a sub-structure that also have internal alignement
(ndr.make_parameters([ndr.NdrShort, InternalAlignementStructure, ndr.NdrByte]),
(0x0101, [0x4141, 0x42, 0x43434343, 0x44, 0x4545, 0x46], 0x47),
# Verified with an actual RPC server
b"\x01\x01PPAABPCCCCDPEEFG"),
# Nested alignement
# Alignement with a sub-structure that also have internal alignement
# Afterward short should be aligned on 2
(ndr.make_parameters([ndr.NdrShort, InternalAlignementStructure, ndr.NdrShort]),
(0x0101, [0x4141, 0x42, 0x43434343, 0x44, 0x4545, 0x46], 0x4747),
# Verified with an actual RPC server
b"\x01\x01PPAABPCCCCDPEEFPGG"),
# Complex struct alignement
# A struct with both primitive field & pointer to primitiv field of various size
(ComplexAlignementStructure,
[0x41, 0x42, 0x43434343, 0x44, 0x4545454545454545, 0x46],
b"APPP\x02\x02\x02\x02\x03\x03\x03\x03\x44PPP\x05\x05\x05\x05\x06\x06\x06\x06BPPPCCCCEEEEEEEEF"),
# Complex struct alignement with nesting (due to uniqueptr at the start)
# The last hyper is not aligned/pad like previous test due to the leading UniquPTR
# This is the proof that NDR packing cannot be in "context-free" sub function and must share a state
# This test fails for now (0.6) and I don't know if I will implem the full NDR logic someday
# (ndr.NdrUniquePTR(ComplexAlignementStructure),
# [0x41, 0x42, 0x43434343, 0x44, 0x4545454545454545, 0x46],
# b"\x01\x01\x01\x01APPP\x02\x02\x02\x02\x03\x03\x03\x03\x44PPP\x05\x05\x05\x05\x06\x06\x06\x06BPPPCCCCPPPPEEEEEEEEF")
]
@pytest.mark.parametrize("ndrobj, values, result", NDR_PACK_TEST_CASE)
def test_ndr_packing(ndrobj, values, result):
assert ndrobj.pack(values) == result
# Check the result of serializing a fixed 'EptMapAuthParameters' call known to works
# It allow to test for packing of real-complexe Parameter without relying on the whole ALPC/RPC stack
def test_ndr_packing_complex_epmapper_call():
# Param from a real UAC endpoint resolution
targetiid = gdef.GUID.from_string("201EF99A-7FA0-444C-9399-19BA84F12A1A")
towerarray = bytearray(b'\x04\x00\x13\x00\r\x9a\xf9\x1e \xa0\x7fLD\x93\x99\x19\xba\x84\xf1*\x1a\x01\x00\x02\x00\x00\x00\x13\x00\r\x04]\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00+\x10H`\x02\x00\x02\x00\x00\x00\x01\x00\x0c\x02\x00\x00\x00\x01\x00\x10\x00\x00')
local_system_psid = gdef.PSID.from_string("S-1-5-18")
context = (0, 0, 0, 0, 0)
nb_response = 1
packed = windows.rpc.epmapper.EptMapAuthParameters.pack([bytearray(targetiid),
(len(towerarray), towerarray),
local_system_psid,
context,
nb_response])
expected_result = b'\x9a\xf9\x1e \xa0\x7fLD\x93\x99\x19\xba\x84\xf1*\x1a@\x00\x00\x00@\x00\x00\x00\x04\x00\x13\x00\r\x9a\xf9\x1e \xa0\x7fLD\x93\x99\x19\xba\x84\xf1*\x1a\x01\x00\x02\x00\x00\x00\x13\x00\r\x04]\x88\x8a\xeb\x1c\xc9\x11\x9f\xe8\x08\x00+\x10H`\x02\x00\x02\x00\x00\x00\x01\x00\x0c\x02\x00\x00\x00\x01\x00\x10\x00\x00\x02\x02\x02\x02\x01\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x05\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00'
assert packed == packed
# Check the result of serializing a fixed 'UAC' call known to works
# It allow to test for packing of real-complexe Parameter without relying on the whole ALPC/RPC stack
def test_ndr_packing_complex_uac_call():
parameters = UACParameters.pack([
r"c:\windows\system32\notepad.exe", # Application Path
"NOT_ALIGNED_STRINGXXX", # Commandline
17, # UAC-Request Flag
gdef.CREATE_UNICODE_ENVIRONMENT, # dwCreationFlags
"", # StartDirectory
"WinSta0\\Default\x00", # Station
# Startup Info
(None, # Title
0, # dwX
0, # dwY
0, # dwXSize
0, # dwYSize
0, # dwXCountChars
0, # dwYCountChars
0, # dwFillAttribute
0, # dwFlags
5, # wShowWindow
# Point structure: Use MonitorFromPoint to setup StartupInfo.hStdOutput
(0, 0)),
0, # Window-Handle to know if UAC can steal focus
0xffffffff]) # UAC Timeout
expected_result = b'\x02\x02\x02\x02 \x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00c\x00:\x00\\\x00w\x00i\x00n\x00d\x00o\x00w\x00s\x00\\\x00s\x00y\x00s\x00t\x00e\x00m\x003\x002\x00\\\x00n\x00o\x00t\x00e\x00p\x00a\x00d\x00.\x00e\x00x\x00e\x00\x00\x00\x02\x02\x02\x02\x16\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00\x00N\x00O\x00T\x00_\x00A\x00L\x00I\x00G\x00N\x00E\x00D\x00_\x00S\x00T\x00R\x00I\x00N\x00G\x00X\x00X\x00X\x00\x00\x00\x11\x00\x00\x00\x00\x04\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00PP\x10\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00W\x00i\x00n\x00S\x00t\x00a\x000\x00\\\x00D\x00e\x00f\x00a\x00u\x00l\x00t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff'
assert parameters == expected_result