From 34ae5084cdcf03f850dcfa3ea866b1f0d1ed5a45 Mon Sep 17 00:00:00 2001 From: Clement Rouault Date: Wed, 23 Mar 2016 19:04:15 +0100 Subject: [PATCH] Working/Trying com ctypes generator --- ctypes_generation/com_parser.py | 163 ++++++++++++++++++++++++++++ ctypes_generation/dummy_wintypes.py | 2 +- ctypes_generation/generate.py | 85 +++++++++++++++ ctypes_generation/struct_parser.py | 39 ++++--- ctypes_generation/winstruct.txt | 149 ++++++++++++++++++++++++- 5 files changed, 419 insertions(+), 19 deletions(-) create mode 100644 ctypes_generation/com_parser.py diff --git a/ctypes_generation/com_parser.py b/ctypes_generation/com_parser.py new file mode 100644 index 0000000..6a55f03 --- /dev/null +++ b/ctypes_generation/com_parser.py @@ -0,0 +1,163 @@ +import StringIO +from collections import namedtuple + +import dummy_wintypes +import struct_parser +from winstruct import WinStruct, WinUnion, WinStructType, Ptr, WinEnum +from simpleparser import * + + +def initial_processing( data): + # https://gcc.gnu.org/onlinedocs/cpp/Initial-processing.html#Initial-processing + # Step 1 -> use correct end of line + add last \n if not existing + data = data.replace("\r\n", "\n") + if not data.endswith("\n"): + data = data + "\n" + # Step 2: Trigraph : fuck it + pass + # Step 3: Line merge ! + data = data.replace("\\\n", "") + # Step 4 Remove comments: + + ins = StringIO.StringIO(data) + outs = StringIO.StringIO() + + in_str = False + res = [] + while ins.tell() != len(data): + c = ins.read(1) + if ins.tell() == len(data): + outs.write(c) + break + if not in_str and c == "/": + nc = ins.read(1) + if nc == "/": + while c != "\n": + c = ins.read(1) + outs.write(c) + continue + elif nc == "*": + while c != "*" or nc != "/": + c = nc + nc = ins.read(1) + if not nc: + raise ValueError("Unmatched */") + outs.write(" ") + continue + else: + outs.write(c) + ins.seek(ins.tell() - 1) + continue + # TODO: escape in str + elif c == '"': + in_str = not in_str + outs.write(c) + outs.seek(0) + return outs.read() + +class WinComParser(Parser): + PARAM_INFO = ["__RPC__deref_out", "__RPC__in", "__RPC__deref_out_opt", "__RPC__out"] + PARAM_INFO_WITH_VALUE = ["__RPC__in_ecount"] + + def __init__(self, data): + data = initial_processing(data) + #print(data) + super(WinComParser, self).__init__(data) + + def assert_name(self, expected_name, n=None): + if n is None: + n = self.assert_token_type(NameToken) + if n.value != expected_name: + raise ParsingError("Expected name {0} got {1} instead".format(expected_name, n.value)) + return n + + def parse_argument(self): + byreflevel = 0 + # Pass __RPC__deref_out + while self.peek() in [NameToken(x) for x in self.PARAM_INFO + self.PARAM_INFO_WITH_VALUE]: + ign = self.assert_token_type(NameToken) + if ign.value in self.PARAM_INFO_WITH_VALUE: + # pass __RPC__in_ecount(cNames) + self.assert_token_type(OpenParenthesisToken) + while type(self.peek()) != CloseParenthesisToken: + self.next_token() + self.next_token() + type_name = self.assert_token_type(NameToken) + + while type(self.peek()) == StarToken: + byreflevel += 1 + discard_star = self.next_token() + arg_name = self.assert_token_type(NameToken) + if type(self.peek()) not in [CommaToken, CloseParenthesisToken]: + raise ParsingError("COM PARSING: argument decl should finish by <,> or <)> (arg {0})".format(type_name.value)) + if type(self.peek()) == CommaToken: + self.assert_token_type(CommaToken) + return type_name.value, byreflevel, arg_name.value + + def parse_method(self): + ret_type = self.assert_token_type(NameToken) + #print(ret_type) + self.assert_token_type(OpenParenthesisToken) + self.assert_name("STDMETHODCALLTYPE") + #if type(self.peek()) == StarToken: + self.assert_token_type(StarToken) + method_name = self.assert_token_type(NameToken) + self.assert_token_type(CloseParenthesisToken) + + args = [] + self.assert_token_type(OpenParenthesisToken) + while type(self.peek()) != CloseParenthesisToken: + args.append(self.parse_argument()) + #print("Pass <{0}>".format(p)) + self.next_token() + self.assert_token_type(ColonToken) + return ret_type.value, method_name.value, args + + def parse(self): + self.assert_keyword("typedef") + self.assert_keyword("struct") + + vtable_name = self.assert_token_type(NameToken).value + self.assert_token_type(OpenBracketToken) + self.assert_name("BEGIN_INTERFACE") + + res = WinCOMVTABLE(vtable_name) + + while self.peek() != NameToken("END_INTERFACE"): + ret_type, method_name, args = self.parse_method() + #print("Method name is {0}".format(method_name)) + for arg in args: + pass + #print(" Param is {0}".format(arg)) + res.add_method(ret_type, method_name, args) + end_interface = self.assert_name("END_INTERFACE") + self.assert_token_type(CloseBracketToken) + typdef = self.assert_token_type(NameToken) + self.assert_token_type(ColonToken) + return res + + #print(self.data) + +Method = namedtuple("Method", ["ret_type", "name", "args"]) +MethodArg = namedtuple("MethodArg", ["type", "byreflevel", "name"]) +class WinCOMVTABLE(object): + def __init__(self, name): + self.name = name + self.methods = [] + + def add_method(self, ret_type, method_name, args): + new_args = [] + for type, byreflevel, name in args: + if type in ["long", "int"]: + type = type.upper() + new_args.append(MethodArg(type, byreflevel, name)) + + if ret_type in ["long", "int"]: + ret_type = ret_type.upper() + self.methods.append(Method(ret_type, method_name, new_args)) + + +if __name__ == "__main__": + import sys + x = WinComParser(open(sys.argv[1]).read()).parse() + print(x) \ No newline at end of file diff --git a/ctypes_generation/dummy_wintypes.py b/ctypes_generation/dummy_wintypes.py index 95177c3..bcc1f40 100644 --- a/ctypes_generation/dummy_wintypes.py +++ b/ctypes_generation/dummy_wintypes.py @@ -1 +1 @@ -names = ['ATOM', 'BOOL', 'BOOLEAN', 'BYTE', 'COLORREF', 'DOUBLE', 'DWORD', 'FILETIME', 'FLOAT', 'HACCEL', 'HANDLE', 'HBITMAP', ' HBRUSH', 'HCOLORSPACE', 'HDC', 'HDESK', 'HDWP', 'HENHMETAFILE', 'HFONT', 'HGDIOBJ', 'HGLOBAL', 'HHOOK', 'HICON', 'HINSTA NCE', 'HKEY', 'HKL', 'HLOCAL', 'HMENU', 'HMETAFILE', 'HMODULE', 'HMONITOR', 'HPALETTE', 'HPEN', 'HRGN', 'HRSRC', 'HSTR', 'HTASK', 'HWINSTA', 'HWND', 'INT', 'LANGID', 'LARGE_INTEGER', 'LCID', 'LCTYPE', 'LGRPID', 'LONG', 'LPARAM', 'LPCOLESTR' , 'LPCSTR', 'LPCVOID', 'LPCWSTR', 'LPOLESTR', 'LPSTR', 'LPVOID', 'LPWSTR', 'MAX_PATH', 'MSG', 'OLESTR', 'POINT', 'POINTL ', 'RECT', 'RECTL', 'RGB', 'SC_HANDLE', 'SERVICE_STATUS_HANDLE', 'SHORT', 'SIZE', 'SIZEL', 'SMALL_RECT', 'UINT', 'ULARGE _INTEGER', 'ULONG', 'USHORT', 'VARIANT_BOOL', 'WCHAR', 'WIN32_FIND_DATAA', 'WIN32_FIND_DATAW', 'WORD', 'WPARAM', '_COORD ', '_FILETIME', '_LARGE_INTEGER', '_POINTL', '_RECTL', '_SMALL_RECT', '_ULARGE_INTEGER', 'tagMSG', 'tagPOINT', 'tagRECT' , 'tagSIZE'] +names = ['HRESULT', 'ATOM', 'BOOL', 'BOOLEAN', 'BYTE', 'COLORREF', 'DOUBLE', 'DWORD', 'FILETIME', 'FLOAT', 'HACCEL', 'HANDLE', 'HBITMAP', ' HBRUSH', 'HCOLORSPACE', 'HDC', 'HDESK', 'HDWP', 'HENHMETAFILE', 'HFONT', 'HGDIOBJ', 'HGLOBAL', 'HHOOK', 'HICON', 'HINSTA NCE', 'HKEY', 'HKL', 'HLOCAL', 'HMENU', 'HMETAFILE', 'HMODULE', 'HMONITOR', 'HPALETTE', 'HPEN', 'HRGN', 'HRSRC', 'HSTR', 'HTASK', 'HWINSTA', 'HWND', 'INT', 'LANGID', 'LARGE_INTEGER', 'LCID', 'LCTYPE', 'LGRPID', 'LONG', 'LPARAM', 'LPCOLESTR' , 'LPCSTR', 'LPCVOID', 'LPCWSTR', 'LPOLESTR', 'LPSTR', 'LPVOID', 'LPWSTR', 'MAX_PATH', 'MSG', 'OLESTR', 'POINT', 'POINTL ', 'RECT', 'RECTL', 'RGB', 'SC_HANDLE', 'SERVICE_STATUS_HANDLE', 'SHORT', 'SIZE', 'SIZEL', 'SMALL_RECT', 'UINT', 'ULARGE _INTEGER', 'ULONG', 'USHORT', 'VARIANT_BOOL', 'WCHAR', 'WIN32_FIND_DATAA', 'WIN32_FIND_DATAW', 'WORD', 'WPARAM', '_COORD ', '_FILETIME', '_LARGE_INTEGER', '_POINTL', '_RECTL', '_SMALL_RECT', '_ULARGE_INTEGER', 'tagMSG', 'tagPOINT', 'tagRECT' , 'tagSIZE'] diff --git a/ctypes_generation/generate.py b/ctypes_generation/generate.py index 79aae1f..9465c47 100644 --- a/ctypes_generation/generate.py +++ b/ctypes_generation/generate.py @@ -2,11 +2,13 @@ import sys import os import os.path import re +import glob import dummy_wintypes import struct_parser import func_parser import def_parser +import com_parser @@ -29,6 +31,8 @@ TYPE_EQUIVALENCE = [ ('INT', 'c_int'), ('UCHAR', 'c_char'), ('CSHORT', 'c_short'), + ('VARTYPE', 'c_ushort'), + ('BSTR', 'c_wchar_p'), ('PUCHAR', 'POINTER(UCHAR)'), ('double', 'c_double'), ('FARPROC', 'PVOID'), @@ -42,6 +46,7 @@ TYPE_EQUIVALENCE = [ ('LARGE_INTEGER', 'LONGLONG'), ('PLARGE_INTEGER', 'POINTER(LARGE_INTEGER)'), ('DWORD64', 'ULONG64'), + ('SCODE', 'LONG'), ('PULONG64', 'POINTER(ULONG64)'), ('PUINT', 'POINTER(UINT)'), ('PHANDLE', 'POINTER(HANDLE)'), @@ -54,6 +59,8 @@ TYPE_EQUIVALENCE = [ ('ACCESS_MASK', 'DWORD'), ('REGSAM', 'ACCESS_MASK'), ('SECURITY_CONTEXT_TRACKING_MODE', 'BOOLEAN'), + ("DISPID", "LONG"), + ("MEMBERID", "DISPID"), # Will be changed at import time ('LPCONTEXT', 'PVOID'), ('HCERTSTORE', 'PVOID'), @@ -63,13 +70,19 @@ TYPE_EQUIVALENCE = [ # For functions returning void TYPE_EQUIVALENCE.append(('VOID', 'DWORD')) +# TRICHE +TYPE_EQUIVALENCE.append(('ITypeInfo', 'PVOID')) + known_type = dummy_wintypes.names + list([x[0] for x in TYPE_EQUIVALENCE]) +known_type += ["void"] + FUNC_FILE = "winfunc.txt" STRUCT_FILE = "winstruct.txt" DEF_FILE = "windef.txt" NTSTATUS_FILE = "ntstatus.txt" +COM_INTERFACE_DIR_GLOB = "com/*.txt" GENERATED_STRUCT_FILE = "winstructs" GENERATED_FUNC_FILE = "winfuncs" @@ -108,6 +121,32 @@ def verif_funcs_type(funcs, structs, enums): if param_type not in known_type and param_type not in all_struct_name: raise ValueError("UNKNOW PARAM TYPE {0}".format(param_type)) + +def verif_com_interface_type(vtbls, struc, enum): + all_struct_name = get_all_struct_name(structs, enums) + all_interface_name = [] + + #ALSO accept void + for vtbl in vtbls: + if not vtbl.name.endswith("Vtbl"): + raise ValueError("Com interface are expected to finish by got <{0}".format(vtbl.name)) + all_interface_name.append(vtbl.name.rstrip("Vtbl")) + + for vtbl in vtbls: + #print(vtbl) + for method in vtbl.methods: + #print("Checking ret type <{0}>".format(method.ret_type)) + ret_type = method.ret_type + if ret_type not in known_type and ret_type not in all_struct_name + all_interface_name: + raise ValueError("UNKNOW RET TYPE {0}".format(ret_type)) + for arg in method.args: + #print("Checking arg type <{0}>".format(arg.type)) + param_type = arg.type + if param_type not in known_type and param_type not in all_struct_name + all_interface_name: + #if param_type != "ITypeInfo": + raise ValueError("UNKNOW PARAM TYPE {0}".format(param_type)) + + def check_in_define(name, defs): return any(name == d.name for d in defs) @@ -200,12 +239,51 @@ def generate_struct_ctypes(structs, enums): return ctypes_str + +com_interface_header = """ +from windows.simple_com import * +import ctypes +""" + +com_interface_template = """ +class {0}(COMInterface): + _functions_ = {{ +{1} + }} +""" + +com_interface_method_template = """ "{0}": ctypes.WINFUNCTYPE({1}),""" + +def generate_com_interface_ctype(vtbls): + print(vtbls) + define = [] + for vtbl in vtbls: + methods_string = [] + for method in vtbl.methods: + args_to_define = method.args[1:] #ctypes doesnt not need the This + #import pdb;pdb.set_trace() + methods_string.append(com_interface_method_template.format(method.name, ", ".join([method.ret_type] + [arg.type for arg in args_to_define]))) + #import pdb;pdb.set_trace() + define.append((com_interface_template.format(vtbl.name, "\n".join(methods_string)))) + return "\n".join(define) + def write_to_out_file(name, data): for out_dir in OUT_DIRS: f = open("{0}/{1}.py".format(out_dir, name), 'w') f.write(data) f.close() + +def parse_com_interfaces(filenames): + res = [] + for filename in filenames: + print("Parsing COM from <{0}>".format(filename)) + data = open(filename).read() + vtbl = com_parser.WinComParser(data).parse() + res.append(vtbl) + return res + + def_code = open(DEF_FILE, 'r').read() funcs_code = open(FUNC_FILE, 'r').read() structs_code = open(STRUCT_FILE, 'r').read() @@ -214,9 +292,11 @@ defs = def_parser.WinDefParser(def_code).parse() funcs = func_parser.WinFuncParser(funcs_code).parse() structs, enums = struct_parser.WinStructParser(structs_code).parse() +vtbls = parse_com_interfaces(glob.glob(COM_INTERFACE_DIR_GLOB)) validate_structs(structs, enums, defs) verif_funcs_type(funcs, structs, enums) +verif_com_interface_type(vtbls, structs, enums) # Create Flags for ntstatus @@ -229,6 +309,11 @@ defs = nt_status_defs + defs defs_ctypes = generate_defs_ctypes(defs) funcs_ctypes = generate_funcs_ctypes(funcs) structs_ctypes = generate_struct_ctypes(structs, enums) +com_interface_ctypes = generate_com_interface_ctype(vtbls) + +f = open("yolo.py", "w") +f.write(com_interface_ctypes) +f.close() for out_dir in OUT_DIRS: if not os.path.exists(out_dir): diff --git a/ctypes_generation/struct_parser.py b/ctypes_generation/struct_parser.py index 3aff89f..80d96e5 100644 --- a/ctypes_generation/struct_parser.py +++ b/ctypes_generation/struct_parser.py @@ -14,7 +14,7 @@ class WinStructParser(Parser): def parse_def(self): if self.peek() == KeywordToken("struct"): discard = self.next_token() - + def_type_tok = self.assert_token_type(NameToken) def_type = WinStructType(def_type_tok.value) if type(self.peek()) == StarToken: @@ -26,7 +26,7 @@ class WinStructParser(Parser): if type(self.peek()) == ColonToken: self.next_token() return (def_type, def_name, 1) - + number_rep = self.parse_array() self.assert_token_type(ColonToken) return (def_type, def_name, number_rep) @@ -65,7 +65,7 @@ class WinStructParser(Parser): else: if assigned_value: raise ParsingError("Enum {0} mix def with and without equal".format(enum_name)) - + res_enum.add_enum_entry(i, name.value) if not type(self.peek()) == CloseBracketToken: self.assert_token_type(CommaToken) @@ -75,13 +75,17 @@ class WinStructParser(Parser): #other_name = self.assert_token_type(NameToken).value #res_enum.add_typedef(other_name) #self.assert_token_type(ColonToken) - return res_enum - - - + return res_enum + + + def parse_winstruct(self): - self.assert_keyword("typedef") - + is_typedef = False + peeked = self.peek() + if peeked == KeywordToken("typedef"): + self.assert_keyword("typedef") + is_typedef = True + def_type = self.assert_token_type(KeywordToken) if def_type.value == "enum": return self.parse_enum() @@ -93,14 +97,17 @@ class WinStructParser(Parser): raise ParsingError("Expecting union or struct got <{0}> instead".format(def_type.value)) struct_name = self.assert_token_type(NameToken) self.assert_token_type(OpenBracketToken) - + result = WinDefType(struct_name.value) while type(self.peek()) != CloseBracketToken: tok_type, tok_name, nb_rep = self.parse_def() result.add_field((tok_type, tok_name.value, nb_rep)) self.assert_token_type(CloseBracketToken) - self.parse_typedef(result) + if is_typedef: + self.parse_typedef(result) + else: + self.assert_token_type(ColonToken) return result def parse(self): @@ -115,7 +122,7 @@ class WinStructParser(Parser): else: raise ValueError("Unknow returned type {0}".format(x)) return strucs, enums - + def dbg_lexer(data): for i in Lexer(data).token_generation(): print i @@ -125,9 +132,9 @@ def dbg_parser(data): def dbg_validate(data): return validate_structs(Parser(data).parse()) - - + + if __name__ == "__main__": import sys - data = open(sys.argv[1], 'r').read() - ctypes_code = generate_ctypes(data) + #data = open(sys.argv[1], 'r').read() + #ctypes_code = generate_ctypes(data) diff --git a/ctypes_generation/winstruct.txt b/ctypes_generation/winstruct.txt index c45408c..0ee5ea4 100644 --- a/ctypes_generation/winstruct.txt +++ b/ctypes_generation/winstruct.txt @@ -363,6 +363,31 @@ typedef enum _SID_NAME_USE { SidTypeLabel = 10 } SID_NAME_USE, *PSID_NAME_USE; +typedef enum NET_FW_PROFILE_TYPE2_ { + NET_FW_PROFILE2_DOMAIN = 0x1, + NET_FW_PROFILE2_PRIVATE = 0x2, + NET_FW_PROFILE2_PUBLIC = 0x4, + NET_FW_PROFILE2_ALL = 0x7fffffff +} NET_FW_PROFILE_TYPE2; + +typedef enum NET_FW_ACTION_ { + NET_FW_ACTION_BLOCK = 0, + NET_FW_ACTION_ALLOW = 1, + NET_FW_ACTION_MAX = 2 +} NET_FW_ACTION; + +typedef enum NET_FW_MODIFY_STATE_ { + NET_FW_MODIFY_STATE_OK = 0, + NET_FW_MODIFY_STATE_GP_OVERRIDE = 1 , + NET_FW_MODIFY_STATE_INBOUND_BLOCKED = 2 +} NET_FW_MODIFY_STATE; + +typedef enum NET_FW_RULE_DIRECTION_ { + NET_FW_RULE_DIR_IN = 1, + NET_FW_RULE_DIR_OUT = 2 , + NET_FW_RULE_DIR_MAX = 3 +} NET_FW_RULE_DIRECTION; + typedef struct _LDR_DATA_TABLE_ENTRY { PVOID Reserved1[2]; LIST_ENTRY InMemoryOrderLinks; @@ -1278,7 +1303,126 @@ typedef struct _GUID { USHORT Data2; USHORT Data3; UCHAR Data4[ 8 ]; -} GUID; +} GUID, IID, *REFIID; + + + +struct _TMP_signscale { + BYTE scale; + BYTE sign; +}; + +struct _TMP_lowmid { + ULONG Lo32; + ULONG Mid32; +}; + +union TMP_signscale_union { + _TMP_signscale s; + USHORT signscale; +}; + +union TMP_lowmid_union { + _TMP_lowmid s; + ULONGLONG Lo64; +}; + +typedef struct tagDEC { + USHORT wReserved; + TMP_signscale_union u1; + ULONG Hi32; + TMP_signscale_union u2; +} DECIMAL; + + +typedef struct __tagBRECORD { + PVOID pvRecord; + PVOID pRecInfo; +} _tagBRECORD; + + +union TMP_variant_sub_union{ + LONGLONG llVal; + LONG lVal; + BYTE bVal; + SHORT iVal; + FLOAT fltVal; + DOUBLE dblVal; + VARIANT_BOOL boolVal; + SCODE scode; + BSTR bstrVal; + PVOID punkVal; + PVOID pdispVal; + SAFEARRAY *parray; + BYTE *pbVal; + SHORT *piVal; + LONG *plVal; + LONGLONG *pllVal; + FLOAT *pfltVal; + DOUBLE *pdblVal; + VARIANT_BOOL *pboolVal; + SCODE *pscode; + BSTR *pbstrVal; + PVOID byref; + CHAR cVal; + USHORT uiVal; + ULONG ulVal; + ULONGLONG ullVal; + INT intVal; + UINT uintVal; + CHAR *pcVal; + USHORT *puiVal; + ULONG *pulVal; + ULONGLONG *pullVal; + INT *pintVal; + UINT *puintVal; + _tagBRECORD __VARIANT_NAME_4; +}; + +typedef enum tagTYPEKIND { + TKIND_ENUM = 0, + TKIND_RECORD = 1 , + TKIND_MODULE = 2 , + TKIND_INTERFACE = 3 , + TKIND_DISPATCH = 4 , + TKIND_COCLASS = 5 , + TKIND_ALIAS = 6 , + TKIND_UNION = 7 , + TKIND_MAX = 8 +} TYPEKIND; + + + + + +typedef struct __tagVARIANT { + VARTYPE vt; + WORD wReserved1; + WORD wReserved2; + WORD wReserved3; + TMP_variant_sub_union __VARIANT_NAME_3; +} _tagVARIANT, VARIANT, VARIANTARG, *LPVARIANT, *LPVARIANTARG; + + +typedef struct tagDISPPARAMS { + VARIANTARG *rgvarg; + DISPID *rgdispidNamedArgs; + UINT cArgs; + UINT cNamedArgs; +} DISPPARAMS; + +typedef struct tagEXCEPINFO { + WORD wCode; + WORD wReserved; + BSTR bstrSource; + BSTR bstrDescription; + BSTR bstrHelpFile; + DWORD dwHelpContext; + ULONG_PTR pvReserved; + ULONG_PTR pfnDeferredFillIn; + SCODE scode; +} EXCEPINFO; + typedef struct _CRYPTOAPI_BLOB { DWORD cbData; @@ -1797,4 +1941,5 @@ typedef struct _PUBLIC_OBJECT_BASIC_INFORMATION { DWORD UserSidOffset; DWORD DataLength; DWORD DataOffset; -} EVENTLOGRECORD, *PEVENTLOGRECORD; \ No newline at end of file +} EVENTLOGRECORD, *PEVENTLOGRECORD; +