mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
new_generate.py replace old one
This commit is contained in:
@@ -11,4 +11,5 @@ Since 0.2:
|
||||
* CurrentProcessReadSyswow doest not use ``current_process.handle`` anymore but ``OpenProcess(current_process.pid)`` (Compat windows10 where method 1 doest not work)
|
||||
* Add: system.handles (winobject\handle.py)
|
||||
* You can have multiple execute_python_unsafe at the same time in the same process (didn't know: consequence of new injection code)
|
||||
* WinProcess.execute_python does not regenerate/reinject the python_exec_shellcode for each execution
|
||||
* WinProcess.execute_python does not regenerate/reinject the python_exec_shellcode for each execution
|
||||
* generate.py cleaned with class and stuff / usable for extern project (cc lkd)
|
||||
+427
-388
@@ -3,6 +3,7 @@ import os
|
||||
import os.path
|
||||
import re
|
||||
import glob
|
||||
import textwrap
|
||||
|
||||
import dummy_wintypes
|
||||
import struct_parser
|
||||
@@ -10,6 +11,9 @@ import func_parser
|
||||
import def_parser
|
||||
import com_parser
|
||||
|
||||
pjoin = os.path.join
|
||||
pexists = os.path.exists
|
||||
dedent = textwrap.dedent
|
||||
|
||||
|
||||
TYPE_EQUIVALENCE = [
|
||||
@@ -92,441 +96,476 @@ TYPE_EQUIVALENCE = [
|
||||
('PALPC_PORT_ATTRIBUTES', 'PVOID'),
|
||||
]
|
||||
|
||||
# For functions returning void
|
||||
TYPE_EQUIVALENCE.append(('VOID', 'DWORD'))
|
||||
# TRICHE
|
||||
TYPE_EQUIVALENCE.append(('ITypeInfo', 'PVOID'))
|
||||
BASIC_TYPE = dummy_wintypes.names + list([x[0] for x in TYPE_EQUIVALENCE])
|
||||
|
||||
class CtypesGenerator(object):
|
||||
common_header = "#Generated file\n"
|
||||
|
||||
PARSER = None
|
||||
IMPORT_HEADER = "{deps}"
|
||||
|
||||
def __init__(self, infilename, outfilename, dependances=()):
|
||||
self.infilename = infilename
|
||||
self.outfilename = outfilename
|
||||
self.infile = open(self.infilename)
|
||||
self.data = None
|
||||
self.dependances = dependances
|
||||
|
||||
self.exports = set([])
|
||||
self.imports = set([])
|
||||
|
||||
self.parse()
|
||||
self.analyse(self.data)
|
||||
self.check_dependances()
|
||||
|
||||
def parse(self):
|
||||
if self.data is None:
|
||||
print("Parsing <{0}>".format(self.infilename))
|
||||
self.data = self.PARSER(self.infile.read()).parse()
|
||||
return self.data
|
||||
|
||||
def analyse(self, data):
|
||||
raise NotImplementedError("<{0}> doest not implement <analyse>".format(type(self).__name__))
|
||||
|
||||
def check_dependances(self):
|
||||
missing = self.imports
|
||||
for dep in self.dependances:
|
||||
missing -= dep.exports
|
||||
if missing:
|
||||
raise ValueError("Missing dependance <{0}> in <{1}>".format(missing, self.infilename))
|
||||
|
||||
def generate_import(self):
|
||||
deps = "\n".join(["from {0} import *".format(os.path.basename(dep.outfilename).rsplit(".")[0]) for dep in self.dependances])
|
||||
return self.IMPORT_HEADER.format(deps = deps)
|
||||
|
||||
def add_imports(self, *names):
|
||||
self.imports.update(names)
|
||||
|
||||
def add_exports(self, *names):
|
||||
self.exports.update(names)
|
||||
|
||||
def generate(self):
|
||||
raise NotImplementedError("<{0}> doest not implement <generate>".format(type(self).__name__))
|
||||
|
||||
class DefGenerator(CtypesGenerator):
|
||||
PARSER = def_parser.WinDefParser
|
||||
HEADER = dedent("""
|
||||
import sys
|
||||
import platform
|
||||
if sys.version_info.major == 3:
|
||||
long = int
|
||||
|
||||
bits = platform.architecture()[0]
|
||||
bitness = int(bits[:2])
|
||||
|
||||
NATIVE_WORD_MAX_VALUE = 0xffffffff if bitness == 32 else 0xffffffffffffffff
|
||||
|
||||
class Flag(long):
|
||||
def __new__(cls, name, value):
|
||||
return super(Flag, cls).__new__(cls, value)
|
||||
|
||||
def __init__(self, name, value):
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}({1})".format(self.name, hex(self))
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
""")
|
||||
|
||||
IMPORT_HEADER = "{deps}"
|
||||
|
||||
def analyse(self, data):
|
||||
self.add_exports("Flag")
|
||||
self.add_exports("NATIVE_WORD_MAX_VALUE")
|
||||
for defin in data:
|
||||
self.add_exports(defin.name)
|
||||
|
||||
|
||||
known_type = dummy_wintypes.names + list([x[0] for x in TYPE_EQUIVALENCE])
|
||||
known_type += ["void"]
|
||||
def generate(self):
|
||||
ctypes_lines = [self.common_header, self.HEADER]
|
||||
#deps = "\n".join(["from {0} import *".format(os.path.basename(dep.outfilename).rsplit(".")[0]) for dep in self.dependances])
|
||||
ctypes_lines += [self.generate_import()]
|
||||
ctypes_lines += [d.generate_ctypes() for d in self.parse()]
|
||||
ctypes_code = "\n".join(ctypes_lines)
|
||||
with open(self.outfilename, "w") as f:
|
||||
f.write(ctypes_code)
|
||||
print("<{0}> generated".format(self.outfilename))
|
||||
return ctypes_code
|
||||
|
||||
class StructGenerator(CtypesGenerator):
|
||||
PARSER = struct_parser.WinStructParser
|
||||
IMPORT_HEADER = dedent ("""
|
||||
from ctypes import *
|
||||
from ctypes.wintypes import *
|
||||
{deps}
|
||||
|
||||
{types_equivalences}
|
||||
""")
|
||||
TYPES_HEADER = dedent("""
|
||||
class EnumValue(Flag):
|
||||
def __new__(cls, enum_name, name, value):
|
||||
return super(EnumValue, cls).__new__(cls, name, value)
|
||||
|
||||
def __init__(self, enum_name, name, value):
|
||||
self.enum_name = enum_name
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}.{1}({2})".format(self.enum_name, self.name, hex(self))
|
||||
|
||||
|
||||
FUNC_FILE = "definitions\\winfunc.txt"
|
||||
STRUCT_FILE = "definitions\\winstruct.txt"
|
||||
DEF_FILE = "definitions\\windef.txt"
|
||||
NTSTATUS_FILE = "definitions\\ntstatus.txt"
|
||||
NAME_TO_IID_FILE = "definitions\\interface_to_iid.txt"
|
||||
COM_INTERFACE_DIR_GLOB = "definitions\\com/*.txt"
|
||||
class EnumType(DWORD):
|
||||
values = ()
|
||||
mapper = {}
|
||||
|
||||
GENERATED_STRUCT_FILE = "winstructs"
|
||||
GENERATED_FUNC_FILE = "winfuncs"
|
||||
GENERATED_DEF_FILE = "windef"
|
||||
GENERATED_NTSTATUS_FILE = "ntstatus"
|
||||
GENERATED_COM_FILE = "interfaces"
|
||||
#GENERATED_NAME_TO_IID_FILE = "com_iid"
|
||||
@property
|
||||
def value(self):
|
||||
raw_value = super(EnumType, self).value
|
||||
return self.mapper.get(raw_value, raw_value)
|
||||
|
||||
OUT_DIRS = ["..\windows\generated_def"]
|
||||
if len(sys.argv) > 1:
|
||||
OUT_DIRS.append(sys.argv[1])
|
||||
def __repr__(self):
|
||||
raw_value = super(EnumType, self).value
|
||||
if raw_value in self.values:
|
||||
value = self.value
|
||||
return "<{0} {1}({2})>".format(type(self).__name__, value.name, hex(raw_value))
|
||||
return "<{0}({1})>".format(type(self).__name__, hex(self.value))
|
||||
|
||||
def get_all_struct_name(structs, enums):
|
||||
res = []
|
||||
for s in structs + enums:
|
||||
res.append(s.name)
|
||||
res.extend(s.typedef)
|
||||
return res
|
||||
""")
|
||||
|
||||
def generate_type_equiv_code(type_equiv):
|
||||
ctypes_str = ""
|
||||
for type_equiv in type_equiv:
|
||||
ctypes_str += "{0} = {1}\n".format(*type_equiv)
|
||||
ctypes_str += "\n"
|
||||
return ctypes_str
|
||||
def analyse(self, data):
|
||||
structs, enums = data
|
||||
for btype in BASIC_TYPE:
|
||||
self.add_exports(btype)
|
||||
for enum in enums:
|
||||
self.add_exports(enum.name)
|
||||
self.add_exports(*enum.typedef)
|
||||
for struct in structs:
|
||||
self.add_exports(struct.name)
|
||||
self.add_exports(*struct.typedef)
|
||||
for field_type, field_name, nb_rep in struct.fields:
|
||||
if field_type.name not in self.exports:
|
||||
self.add_imports(field_type.name)
|
||||
try:
|
||||
int(nb_rep)
|
||||
except:
|
||||
self.add_imports(nb_rep)
|
||||
|
||||
def verif_funcs_type(funcs, structs, enums):
|
||||
all_struct_name = get_all_struct_name(structs, enums)
|
||||
missing_types = False
|
||||
for f in funcs:
|
||||
ret_type = f.return_type
|
||||
if ret_type not in known_type and ret_type not in all_struct_name:
|
||||
print ValueError("UNKNOW RET TYPE {0}".format(ret_type))
|
||||
missing_types = True
|
||||
|
||||
for param_type, _ in f.params:
|
||||
# Crappy but fuck it !
|
||||
if param_type.startswith("POINTER(") and param_type.endswith(")"):
|
||||
param_type = param_type[len("POINTER("): -1]
|
||||
if param_type not in known_type and param_type not in all_struct_name:
|
||||
print ValueError("UNKNOW PARAM TYPE {0}".format(param_type))
|
||||
missing_types = True
|
||||
if missing_types:
|
||||
raise ValueError("Missing types")
|
||||
def generate(self):
|
||||
type_equivalences = "\n".join(["{0} = {1}".format(*x) for x in TYPE_EQUIVALENCE])
|
||||
deps = "\n".join(["from {0} import *".format(os.path.basename(dep.outfilename).rsplit(".")[0]) for dep in self.dependances])
|
||||
|
||||
try:
|
||||
yolo_struct = [x[:-4] for x in os.listdir(r"C:\Users\hakril\Documents\Work\COM\dump")]
|
||||
except WindowsError:
|
||||
yolo_struct = []
|
||||
HEADER = self.HEADER.format(types_equivalences=type_equivalences, deps=deps)
|
||||
HEADER += self.TYPES_HEADER
|
||||
|
||||
def verif_com_interface_type(vtbls, struc, enum):
|
||||
all_struct_name = get_all_struct_name(structs, enums)
|
||||
all_interface_name = [vtbl.name for vtbl in vtbls]
|
||||
all_interface_name += [vtbl.typedefptr for vtbl in vtbls if vtbl.typedefptr is not None]
|
||||
missing_types = False
|
||||
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:
|
||||
print ValueError("UNKNOW RET TYPE {0}".format(ret_type))
|
||||
missing_types = True
|
||||
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":
|
||||
if param_type in yolo_struct:
|
||||
import pdb;pdb.set_trace()
|
||||
print("Ned to extract <{0}> from dump".format(param_type))
|
||||
import shutil
|
||||
#shutil.copy(r"C:\Users\hakril\Documents\Work\COM\dump\{0}.txt".format(param_type), "com")
|
||||
continue
|
||||
print ValueError("UNKNOW PARAM TYPE {0}".format(param_type))
|
||||
missing_types = True
|
||||
if missing_types:
|
||||
raise ValueError("Missing types")
|
||||
structs, enums = self.data
|
||||
ctypes_lines = [self.common_header, self.generate_import()] + [d.generate_ctypes() for l in (enums, structs) for d in l]
|
||||
ctypes_code = "\n".join(ctypes_lines)
|
||||
with open(self.outfilename, "w") as f:
|
||||
f.write(ctypes_code)
|
||||
print("<{0}> generated".format(self.outfilename))
|
||||
return ctypes_code
|
||||
|
||||
def check_in_define(name, defs):
|
||||
return any(name == d.name for d in defs)
|
||||
class FuncGenerator(CtypesGenerator):
|
||||
PARSER = func_parser.WinFuncParser
|
||||
IMPORT_HEADER = dedent ("""
|
||||
from ctypes import *
|
||||
from ctypes.wintypes import *
|
||||
{deps}
|
||||
|
||||
def validate_structs(structs, enums, defs):
|
||||
all_struct_name = get_all_struct_name(structs, enums)
|
||||
for struct in structs:
|
||||
for field_type, field_name, nb_rep in struct.fields:
|
||||
import pdb;pdb.set_trace()
|
||||
if field_type.name not in known_type + all_struct_name:
|
||||
raise ValueError("UNKNOW TYPE {0}".format(field_type))
|
||||
""")
|
||||
|
||||
def analyse(self, data):
|
||||
for func in data:
|
||||
self.add_imports(func.return_type)
|
||||
for param_type, _ in func.params:
|
||||
if param_type.startswith("POINTER(") and param_type.endswith(")"):
|
||||
param_type = param_type[len("POINTER("): -1]
|
||||
self.add_imports(param_type)
|
||||
|
||||
def generate(self):
|
||||
deps = "\n".join(["from {0} import *".format(os.path.basename(dep.outfilename).rsplit(".")[0]) for dep in self.dependances])
|
||||
HEADER = self.generate_import()
|
||||
|
||||
func_list = "functions = {0}\n\n".format(str([f.name for f in self.data]))
|
||||
|
||||
ctypes_lines = [self.common_header, HEADER, func_list] + [d.generate_ctypes() for d in self.parse()]
|
||||
ctypes_code = "\n".join(ctypes_lines)
|
||||
with open(self.outfilename, "w") as f:
|
||||
f.write(ctypes_code)
|
||||
print("<{0}> generated".format(self.outfilename))
|
||||
return ctypes_code
|
||||
|
||||
class NtStatusGenerator(CtypesGenerator):
|
||||
HEADER_IMPORT = dedent("""
|
||||
import ctypes
|
||||
{deps}
|
||||
|
||||
""")
|
||||
HEADER = dedent("""
|
||||
class NtStatusException(WindowsError):
|
||||
ALL_STATUS = {}
|
||||
def __init__(self , code):
|
||||
try:
|
||||
int(nb_rep)
|
||||
except ValueError:
|
||||
if not check_in_define(nb_rep, defs):
|
||||
raise ValueError("UNKNOW DEFINE {0}".format(nb_rep))
|
||||
x = self.ALL_STATUS[code]
|
||||
except KeyError:
|
||||
x = (code, 'UNKNOW_ERROR', 'Error non documented in ntstatus.py')
|
||||
self.code = x[0]
|
||||
self.name = x[1]
|
||||
self.descr = x[2]
|
||||
x = ctypes.c_long(x[0]).value, x[1], x[2]
|
||||
return super(NtStatusException, self).__init__(*x)
|
||||
|
||||
common_header = "#Generated file\n"
|
||||
def __str__(self):
|
||||
return "{e.name}(0x{e.code:x}): {e.descr}".format(e=self)
|
||||
|
||||
defs_header = common_header + """
|
||||
import sys
|
||||
import platform
|
||||
if sys.version_info.major == 3:
|
||||
long = int
|
||||
def __repr__(self):
|
||||
return "{0}(0x{1:08x}, {2})".format(type(self).__name__, self.code, self.name)
|
||||
|
||||
bits = platform.architecture()[0]
|
||||
bitness = int(bits[:2])
|
||||
@classmethod
|
||||
def register_ntstatus(cls, code, name, descr):
|
||||
if code in cls.ALL_STATUS:
|
||||
return # Use the first def
|
||||
cls.ALL_STATUS[code] = (code, name, descr)
|
||||
return Flag(name, code)
|
||||
""")
|
||||
|
||||
NATIVE_WORD_MAX_VALUE = 0xffffffff if bitness == 32 else 0xffffffffffffffff
|
||||
def parse_ntstatus(self, content):
|
||||
nt_status_defs = []
|
||||
for line in content.split("\n"):
|
||||
if not line:
|
||||
continue
|
||||
code, name, descr = line.split("|", 2)
|
||||
code = int(code, 0)
|
||||
descr = re.sub(" +", " ", descr[:-1]) # remove \n
|
||||
descr = descr.replace('"', "'")
|
||||
nt_status_defs.append((code, name, descr))
|
||||
self.data = nt_status_defs
|
||||
return self
|
||||
|
||||
class Flag(long):
|
||||
def __new__(cls, name, value):
|
||||
return super(Flag, cls).__new__(cls, value)
|
||||
# Hack for PARSER
|
||||
def parse(self):
|
||||
if self.data is None:
|
||||
print("Parsing <{0}>".format(self.infilename))
|
||||
self.parse_ntstatus(self.infile.read())
|
||||
return self.data
|
||||
|
||||
def __init__(self, name, value):
|
||||
self.name = name
|
||||
def analyse(self, data):
|
||||
self.add_imports("Flag")
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}({1})".format(self.name, hex(self))
|
||||
|
||||
__str__ = __repr__
|
||||
"""
|
||||
|
||||
def generate_defs_ctypes(defs):
|
||||
ctypes_lines = [defs_header] + [d.generate_ctypes() for d in defs]
|
||||
ctypes_code = "\n".join(ctypes_lines)
|
||||
return ctypes_code
|
||||
def generate(self):
|
||||
#deps = "\n".join(["from {0} import *".format(os.path.basename(dep.outfilename).rsplit(".")[0]) for dep in self.dependances])
|
||||
HEADER = self.generate_import() + self.HEADER
|
||||
ctypes_lines = [HEADER]
|
||||
for code, name, descr in self.parse():
|
||||
ctypes_lines.append('{1} = NtStatusException.register_ntstatus({0}, "{1}", "{2}")'.format(hex(code).strip("L"), name, descr))
|
||||
ctypes_code = "\n".join(ctypes_lines)
|
||||
with open(self.outfilename, "w") as f:
|
||||
f.write(ctypes_code)
|
||||
print("<{0}> generated".format(self.outfilename))
|
||||
return ctypes_code
|
||||
|
||||
|
||||
funcs_header = common_header + """
|
||||
from ctypes import *
|
||||
from ctypes.wintypes import *
|
||||
from .{0} import *
|
||||
class InitialCOMGenerator(CtypesGenerator):
|
||||
PARSER = com_parser.WinComParser
|
||||
IGNORE_INTERFACE = ["ITypeInfo"]
|
||||
IMPORT_HEADER = dedent("""
|
||||
import functools
|
||||
import ctypes
|
||||
{deps}
|
||||
|
||||
"""[1:].format(GENERATED_STRUCT_FILE)
|
||||
""")
|
||||
HEADER = dedent("""
|
||||
class IID(IID):
|
||||
def __init__(self, Data1, Data2, Data3, Data4, name=None, strid=None):
|
||||
self.name = name
|
||||
self.strid = strid
|
||||
super(IID, self).__init__(Data1, Data2, Data3, Data4)
|
||||
|
||||
def generate_funcs_ctypes(funcs):
|
||||
ctypes_code = funcs_header
|
||||
def __repr__(self):
|
||||
if self.strid is None:
|
||||
return super(IID, self).__repr__()
|
||||
if self.name is None:
|
||||
return '<IID "{0}">'.format(self.strid.upper())
|
||||
return '<IID "{0}({1})">'.format(self.strid.upper(), self.name)
|
||||
|
||||
all_funcs_name = [f.name for f in funcs]
|
||||
ctypes_code += "functions = {0}\n\n".format(str(all_funcs_name))
|
||||
for func in funcs:
|
||||
ctypes_code += func.generate_ctypes() + "\n"
|
||||
return ctypes_code
|
||||
@classmethod
|
||||
def from_string(cls, iid):
|
||||
part_iid = iid.split("-")
|
||||
datas = [int(x, 16) for x in part_iid[:3]]
|
||||
datas.append(int(part_iid[3][:2], 16))
|
||||
datas.append(int(part_iid[3][2:], 16))
|
||||
for i in range(6):
|
||||
datas.append(int(part_iid[4][i * 2:(i + 1) * 2], 16))
|
||||
return cls.from_raw(*datas, strid=iid)
|
||||
|
||||
structs_header = common_header + """
|
||||
from ctypes import *
|
||||
from ctypes.wintypes import *
|
||||
from .windef import *
|
||||
@classmethod
|
||||
def from_raw(cls, Data1, Data2, Data3, Data41, Data42, Data43, Data44, Data45, Data46, Data47, Data48, **kwargs):
|
||||
return cls(Data1, Data2, Data3, (BYTE*8)(Data41, Data42, Data43, Data44, Data45, Data46, Data47, Data48), **kwargs)
|
||||
|
||||
class EnumValue(Flag):
|
||||
def __new__(cls, enum_name, name, value):
|
||||
return super(EnumValue, cls).__new__(cls, name, value)
|
||||
generate_IID = IID.from_raw
|
||||
|
||||
def __init__(self, enum_name, name, value):
|
||||
self.enum_name = enum_name
|
||||
self.name = name
|
||||
GUID = IID
|
||||
LPGUID = POINTER(GUID)
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}.{1}({2})".format(self.enum_name, self.name, hex(self))
|
||||
class COMInterface(ctypes.c_void_p):
|
||||
_functions_ = {
|
||||
}
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self._functions_:
|
||||
return functools.partial(self._functions_[name], self)
|
||||
return super(COMInterface, self).__getattribute__(name)
|
||||
""")
|
||||
|
||||
class EnumType(DWORD):
|
||||
values = ()
|
||||
mapper = {}
|
||||
def __init__(self, indirname, iiddef, outfilename, dependances=()):
|
||||
self.indirname = indirname
|
||||
self.infilename = indirname
|
||||
self.outfilename = outfilename
|
||||
self.data = None
|
||||
self.dependances = dependances
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
raw_value = super(EnumType, self).value
|
||||
return self.mapper.get(raw_value, raw_value)
|
||||
data = open(iiddef).read()
|
||||
self.iids_def = {}
|
||||
for line in data.split("\n"):
|
||||
name, iid = line.split("|")
|
||||
self.iids_def[name] = self.parse_iid(iid), iid
|
||||
|
||||
def __repr__(self):
|
||||
raw_value = super(EnumType, self).value
|
||||
if raw_value in self.values:
|
||||
value = self.value
|
||||
return "<{0} {1}({2})>".format(type(self).__name__, value.name, hex(raw_value))
|
||||
return "<{0}({1})>".format(type(self).__name__, hex(self.value))
|
||||
self.exports = set([])
|
||||
self.imports = set([])
|
||||
|
||||
"""[1:]
|
||||
self.parse()
|
||||
self.analyse(self.data)
|
||||
self.check_dependances()
|
||||
|
||||
def generate_struct_ctypes(structs, enums):
|
||||
ctypes_str = structs_header
|
||||
ctypes_str += generate_type_equiv_code(TYPE_EQUIVALENCE)
|
||||
def parse(self):
|
||||
if self.data is not None:
|
||||
return self.data
|
||||
data = []
|
||||
for filename in glob.glob(self.indirname):
|
||||
print("Parsing <{0}>".format(filename))
|
||||
data.append(self.PARSER(open(filename).read()).parse())
|
||||
self.data = data
|
||||
return data
|
||||
|
||||
all_struct_name = [s.name for s in structs]
|
||||
ctypes_str += "structs = {0}\n\n".format(str(all_struct_name))
|
||||
|
||||
all_enum_name = [e.name for e in enums]
|
||||
ctypes_str += "enums = {0}\n\n".format(str(all_enum_name))
|
||||
|
||||
# Enums declarations
|
||||
for enum in enums:
|
||||
ctypes_str += "# Enum {0} definitions\n".format(enum.name)
|
||||
ctypes_str += enum.generate_ctypes() + "\n"
|
||||
|
||||
# Struct declarations
|
||||
for struct in structs:
|
||||
ctypes_str += "# Struct {0} definitions\n".format(struct.name)
|
||||
ctypes_str += struct.generate_ctypes() + "\n"
|
||||
|
||||
return ctypes_str
|
||||
|
||||
def parse_iid(iid_str):
|
||||
part_iid = iid_str.split("-")
|
||||
str_iid = []
|
||||
str_iid.append("0x" + part_iid[0])
|
||||
str_iid.append("0x" + part_iid[1])
|
||||
str_iid.append("0x" + part_iid[2])
|
||||
str_iid.append("0x" + part_iid[3][:2])
|
||||
str_iid.append("0x" + part_iid[3][2:])
|
||||
for i in range(6): str_iid.append("0x" + part_iid[4][i * 2:(i + 1) * 2])
|
||||
return ", ".join(str_iid)
|
||||
|
||||
|
||||
data = open(NAME_TO_IID_FILE).read()
|
||||
iids_def = {}
|
||||
for line in data.split("\n"):
|
||||
name, iid = line.split("|")
|
||||
iids_def[name] = parse_iid(iid), iid
|
||||
#full_name_to_iid = name_to_iid_header + "\n".join(iids_def)
|
||||
|
||||
|
||||
com_interface_header = """
|
||||
import functools
|
||||
import ctypes
|
||||
from winstructs import *
|
||||
|
||||
class IID(IID):
|
||||
def __init__(self, Data1, Data2, Data3, Data4, name=None, strid=None):
|
||||
self.name = name
|
||||
self.strid = strid
|
||||
super(IID, self).__init__(Data1, Data2, Data3, Data4)
|
||||
|
||||
def __repr__(self):
|
||||
if self.strid is None:
|
||||
return super(IID, self).__repr__()
|
||||
if self.name is None:
|
||||
return '<IID "{0}">'.format(self.strid.upper())
|
||||
return '<IID "{0}({1})">'.format(self.strid.upper(), self.name)
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, iid):
|
||||
part_iid = iid.split("-")
|
||||
datas = [int(x, 16) for x in part_iid[:3]]
|
||||
datas.append(int(part_iid[3][:2], 16))
|
||||
datas.append(int(part_iid[3][2:], 16))
|
||||
for i in range(6):
|
||||
datas.append(int(part_iid[4][i * 2:(i + 1) * 2], 16))
|
||||
return cls.from_raw(*datas, strid=iid)
|
||||
|
||||
@classmethod
|
||||
def from_raw(cls, Data1, Data2, Data3, Data41, Data42, Data43, Data44, Data45, Data46, Data47, Data48, **kwargs):
|
||||
return cls(Data1, Data2, Data3, (BYTE*8)(Data41, Data42, Data43, Data44, Data45, Data46, Data47, Data48), **kwargs)
|
||||
|
||||
generate_IID = IID.from_raw
|
||||
|
||||
GUID = IID
|
||||
LPGUID = POINTER(GUID)
|
||||
|
||||
class COMInterface(ctypes.c_void_p):
|
||||
_functions_ = {
|
||||
}
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self._functions_:
|
||||
return functools.partial(self._functions_[name], self)
|
||||
return super(COMInterface, self).__getattribute__(name)
|
||||
"""
|
||||
|
||||
com_interface_template = """
|
||||
class {0}(COMInterface):
|
||||
IID = generate_IID({2}, name="{0}", strid="{3}")
|
||||
|
||||
_functions_ = {{
|
||||
{1}
|
||||
}}
|
||||
"""
|
||||
|
||||
com_interface_comment_template = """ #{0} -> {1}"""
|
||||
com_interface_method_template = """ "{0}": ctypes.WINFUNCTYPE({1})({2}, "{0}"),"""
|
||||
|
||||
def generate_com_interface_ctype(vtbls):
|
||||
define = []
|
||||
all_name = [vtbl.name for vtbl in vtbls] + [vtbl.typedefptr for vtbl in vtbls if vtbl.typedefptr is not None]
|
||||
|
||||
for vtbl in vtbls:
|
||||
methods_string = []
|
||||
for method_nb, method in enumerate(vtbl.methods):
|
||||
args_to_define = method.args[1:] #ctypes doesnt not need the This
|
||||
def analyse(self, data):
|
||||
self.real_type = {}
|
||||
#self.add_exports("IID")
|
||||
#self.add_exports("GUID")
|
||||
#self.add_exports("LPGUID")
|
||||
#self.add_exports("COMInterface")
|
||||
for cominterface in data:
|
||||
#import pdb;pdb.set_trace()
|
||||
str_args = []
|
||||
methods_string.append(com_interface_comment_template.format(method.name, ", ".join([arg.name +":"+ ("*"* arg.byreflevel) +arg.type for arg in args_to_define])))
|
||||
for arg in args_to_define:
|
||||
type = arg.type
|
||||
byreflevel = arg.byreflevel
|
||||
if type in all_name:
|
||||
type = "PVOID"
|
||||
byreflevel -= 1
|
||||
if type == "void":
|
||||
type = "PVOID"
|
||||
if byreflevel == 0:
|
||||
raise ValueError("{0}.{1} take a parameter <void>".format(vtbl.name, method.name))
|
||||
byreflevel -= 1
|
||||
for i in range(byreflevel):
|
||||
type = "POINTER({0})".format(type)
|
||||
str_args.append(type)
|
||||
methods_string.append(com_interface_method_template.format(method.name, ", ".join([method.ret_type] + str_args), method_nb))
|
||||
#import pdb;pdb.set_trace()
|
||||
if vtbl.iid is not None:
|
||||
iid_str = vtbl.iid
|
||||
iid_python = parse_iid(iid_str)
|
||||
else:
|
||||
iid_python, iid_str = iids_def[vtbl.name]
|
||||
define.append((com_interface_template.format(vtbl.name, "\n".join(methods_string), iid_python, iid_str)))
|
||||
return com_interface_header + "\n".join(define)
|
||||
self.add_exports(cominterface.name)
|
||||
if cominterface.typedefptr:
|
||||
self.add_exports(cominterface.typedefptr)
|
||||
for cominterface in data:
|
||||
for method in cominterface.methods:
|
||||
self.add_imports(method.ret_type)
|
||||
for pos, arg in enumerate(method.args):
|
||||
initial_arg = arg
|
||||
if arg.type in self.exports or arg.type in self.IGNORE_INTERFACE:
|
||||
# COM Interface ? -> PVOID !
|
||||
atype = "PVOID"
|
||||
byreflevel = arg.byreflevel - 1
|
||||
method.args[pos] = arg = type(arg)(atype, byreflevel, arg.name)
|
||||
self.real_type[arg] = initial_arg
|
||||
|
||||
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()
|
||||
elif arg.type == "void" and arg.byreflevel > 0:
|
||||
# **void -> *PVOID
|
||||
atype = "PVOID"
|
||||
byreflevel = arg.byreflevel - 1
|
||||
method.args[pos] = arg = type(arg)(atype, byreflevel, arg.name)
|
||||
self.real_type[arg] = initial_arg
|
||||
|
||||
self.add_imports(arg.type)
|
||||
|
||||
com_interface_comment_template = """ #{0} -> {1}"""
|
||||
com_interface_method_template = """ "{0}": ctypes.WINFUNCTYPE({1})({2}, "{0}"),"""
|
||||
com_interface_template = dedent("""
|
||||
class {0}(COMInterface):
|
||||
IID = generate_IID({2}, name="{0}", strid="{3}")
|
||||
|
||||
_functions_ = {{
|
||||
{1}
|
||||
}}
|
||||
""")
|
||||
|
||||
def generate(self):
|
||||
define = []
|
||||
for cominterface in self.data:
|
||||
methods_string = []
|
||||
for method_nb, method in enumerate(cominterface.methods):
|
||||
args_to_define = method.args[1:] #ctypes doesnt not need the This
|
||||
args_for_comment = [self.real_type.get(arg, arg) for arg in args_to_define]
|
||||
#import pdb;pdb.set_trace()
|
||||
str_args = []
|
||||
methods_string.append(self.com_interface_comment_template.format(method.name, ", ".join([arg.name +":"+ ("*"* arg.byreflevel) +arg.type for arg in args_for_comment])))
|
||||
for arg in args_to_define:
|
||||
type = arg.type
|
||||
for i in range(arg.byreflevel):
|
||||
type = "POINTER({0})".format(type)
|
||||
str_args.append(type)
|
||||
methods_string.append(self.com_interface_method_template.format(method.name, ", ".join([method.ret_type] + str_args), method_nb))
|
||||
#import pdb;pdb.set_trace()
|
||||
if cominterface.iid is not None:
|
||||
iid_str = cominterface.iid
|
||||
iid_python = self.parse_iid(iid_str)
|
||||
else:
|
||||
print("Lookup of IID for <{0}>".format(cominterface.name))
|
||||
iid_python, iid_str = self.iids_def[cominterface.name]
|
||||
define.append((self.com_interface_template.format(cominterface.name, "\n".join(methods_string), iid_python, iid_str)))
|
||||
|
||||
deps = "\n".join(["from {0} import *".format(os.path.basename(dep.outfilename).rsplit(".")[0]) for dep in self.dependances])
|
||||
|
||||
ctypes_code = self.generate_import() + "\n" + self.HEADER + "\n".join(define)
|
||||
with open(self.outfilename, "w") as f:
|
||||
f.write(ctypes_code)
|
||||
print("<{0}> generated".format(self.outfilename))
|
||||
return ctypes_code
|
||||
|
||||
def parse_iid(self, iid_str):
|
||||
part_iid = iid_str.split("-")
|
||||
str_iid = []
|
||||
str_iid.append("0x" + part_iid[0])
|
||||
str_iid.append("0x" + part_iid[1])
|
||||
str_iid.append("0x" + part_iid[2])
|
||||
str_iid.append("0x" + part_iid[3][:2])
|
||||
str_iid.append("0x" + part_iid[3][2:])
|
||||
for i in range(6): str_iid.append("0x" + part_iid[4][i * 2:(i + 1) * 2])
|
||||
return ", ".join(str_iid)
|
||||
|
||||
|
||||
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
|
||||
class COMGenerator(InitialCOMGenerator):
|
||||
IMPORT_HEADER = "{deps}"
|
||||
HEADER = ""
|
||||
|
||||
|
||||
def_code = open(DEF_FILE, 'r').read()
|
||||
funcs_code = open(FUNC_FILE, 'r').read()
|
||||
structs_code = open(STRUCT_FILE, 'r').read()
|
||||
|
||||
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))
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
print(SCRIPT_DIR)
|
||||
from_here = lambda path: pjoin(SCRIPT_DIR, path)
|
||||
|
||||
validate_structs(structs, enums, defs)
|
||||
verif_funcs_type(funcs, structs, enums)
|
||||
verif_com_interface_type(vtbls, structs, enums)
|
||||
DEFAULT_INTERFACE_TO_IID = from_here("definitions\\interface_to_iid.txt")
|
||||
|
||||
# A partial define without the dependance to ntstatus defintion
|
||||
# BOOTSTRAP!!
|
||||
non_generated_def = DefGenerator(from_here("definitions\\windef.txt"), from_here(r"..\windows\generated_def\\windef.py"))
|
||||
ntstatus = NtStatusGenerator(from_here("definitions\\ntstatus.txt"), from_here(r"..\windows\generated_def\\ntstatus.py"), dependances=[non_generated_def])
|
||||
# Not a real circular def (import not at the begin of file
|
||||
defs_with_ntstatus = DefGenerator(from_here("definitions\\windef.txt"), from_here(r"..\windows\generated_def\\windef.py"), dependances=[ntstatus])
|
||||
structs = StructGenerator(from_here("definitions\\winstruct.txt"), from_here(r"..\windows\generated_def\\winstructs.py"), dependances=[defs_with_ntstatus])
|
||||
functions = FuncGenerator(from_here("definitions\\winfunc.txt"), from_here(r"..\windows\generated_def\\winfuncs.py"), dependances=[structs])
|
||||
com = InitialCOMGenerator(from_here("definitions\\com\\*.txt"), DEFAULT_INTERFACE_TO_IID, from_here(r"..\windows\generated_def\\interfaces.py"), dependances=[structs])
|
||||
|
||||
|
||||
# Create Flags for ntstatus
|
||||
nt_status_defs = []
|
||||
for line in open(NTSTATUS_FILE):
|
||||
code, name, descr = line.split("|", 2)
|
||||
nt_status_defs.append(def_parser.WinDef(name, code))
|
||||
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)
|
||||
|
||||
# Create name -> IID file
|
||||
|
||||
name_to_iid_header = """
|
||||
from winstructs import IID, BYTE
|
||||
|
||||
"""
|
||||
|
||||
|
||||
#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):
|
||||
os.mkdir(out_dir)
|
||||
|
||||
write_to_out_file(GENERATED_DEF_FILE, defs_ctypes)
|
||||
write_to_out_file(GENERATED_FUNC_FILE, funcs_ctypes)
|
||||
write_to_out_file(GENERATED_STRUCT_FILE, structs_ctypes)
|
||||
write_to_out_file(GENERATED_COM_FILE, com_interface_ctypes)
|
||||
#write_to_out_file(GENERATED_NAME_TO_IID_FILE, full_name_to_iid)
|
||||
|
||||
NTSTATUS_HEAD = """
|
||||
import ctypes
|
||||
|
||||
class NtStatusException(WindowsError):
|
||||
ALL_STATUS = {}
|
||||
def __init__(self , code):
|
||||
try:
|
||||
x = self.ALL_STATUS[code]
|
||||
except KeyError:
|
||||
x = (code, 'UNKNOW_ERROR', 'Error non documented in ntstatus.py')
|
||||
self.code = x[0]
|
||||
self.name = x[1]
|
||||
self.descr = x[2]
|
||||
x = ctypes.c_long(x[0]).value, x[1], x[2]
|
||||
return super(NtStatusException, self).__init__(*x)
|
||||
|
||||
def __str__(self):
|
||||
return "{e.name}(0x{e.code:x}): {e.descr}".format(e=self)
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}(0x{1:08x}, {2})".format(type(self).__name__, self.code, self.name)
|
||||
|
||||
@classmethod
|
||||
def register_ntstatus(cls, code, name, descr):
|
||||
if code in cls.ALL_STATUS:
|
||||
return # Use the first def
|
||||
cls.ALL_STATUS[code] = (code, name, descr)
|
||||
"""
|
||||
|
||||
nt_status_exceptions = [NTSTATUS_HEAD]
|
||||
for line in open(NTSTATUS_FILE):
|
||||
code, name, descr = line.split("|", 2)
|
||||
code = int(code, 0)
|
||||
b = descr
|
||||
descr = re.sub(" +", " ", descr[:-1]) # remove \n
|
||||
descr = descr.replace('"', "'")
|
||||
nt_status_exceptions.append('NtStatusException.register_ntstatus({0}, "{1}", "{2}")'.format(hex(code), name, descr))
|
||||
|
||||
|
||||
write_to_out_file(GENERATED_NTSTATUS_FILE, "\n".join(nt_status_exceptions))
|
||||
|
||||
for out_dir in OUT_DIRS:
|
||||
print("Files generated in <{0}>".format(os.path.abspath(out_dir)))
|
||||
if __name__ == "__main__":
|
||||
ntstatus.generate()
|
||||
defs_with_ntstatus.generate()
|
||||
structs.generate()
|
||||
functions.generate()
|
||||
com.generate()
|
||||
@@ -1,571 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import glob
|
||||
import textwrap
|
||||
|
||||
import dummy_wintypes
|
||||
import struct_parser
|
||||
import func_parser
|
||||
import def_parser
|
||||
import com_parser
|
||||
|
||||
pjoin = os.path.join
|
||||
pexists = os.path.exists
|
||||
dedent = textwrap.dedent
|
||||
|
||||
|
||||
TYPE_EQUIVALENCE = [
|
||||
('PWSTR', 'LPWSTR'),
|
||||
('PCWSTR', 'LPWSTR'),
|
||||
('SIZE_T', 'c_ulong'),
|
||||
('PSIZE_T', 'POINTER(SIZE_T)'),
|
||||
('PVOID', 'c_void_p'),
|
||||
('PPS_POST_PROCESS_INIT_ROUTINE', 'PVOID'),
|
||||
('NTSTATUS', 'DWORD'),
|
||||
('PULONG', 'POINTER(ULONG)'),
|
||||
('PDWORD', 'POINTER(DWORD)'),
|
||||
('LPDWORD', 'POINTER(DWORD)'),
|
||||
('LPTHREAD_START_ROUTINE', 'PVOID'),
|
||||
('WNDENUMPROC', 'PVOID'),
|
||||
('PHANDLER_ROUTINE', 'PVOID'),
|
||||
('LPBYTE', 'POINTER(BYTE)'),
|
||||
('ULONG_PTR','PVOID'),
|
||||
('KAFFINITY','ULONG_PTR'),
|
||||
('KPRIORITY','LONG'),
|
||||
('CHAR', 'c_char'),
|
||||
('INT', 'c_int'),
|
||||
('UCHAR', 'c_char'),
|
||||
('CSHORT', 'c_short'),
|
||||
('VARTYPE', 'c_ushort'),
|
||||
('PBOOL', 'POINTER(BOOL)'),
|
||||
('PSTR', 'LPSTR'),
|
||||
('PCSTR', 'LPSTR'),
|
||||
('va_list', 'c_char_p'),
|
||||
('BSTR', 'c_wchar_p'),
|
||||
('OLECHAR', 'c_wchar'),
|
||||
('POLECHAR', 'c_wchar_p'),
|
||||
('PUCHAR', 'POINTER(UCHAR)'),
|
||||
('double', 'c_double'),
|
||||
('FARPROC', 'PVOID'),
|
||||
('HGLOBAL', 'PVOID'),
|
||||
('PSID', 'PVOID'),
|
||||
('PVECTORED_EXCEPTION_HANDLER', 'PVOID'),
|
||||
#('HRESULT', 'c_long'), # VERY BAD : real HRESULT raise by itself -> way better
|
||||
('ULONGLONG', 'c_ulonglong'),
|
||||
('LONGLONG', 'c_longlong'),
|
||||
('ULONG64', 'c_ulonglong'),
|
||||
('LONG64', 'c_longlong'),
|
||||
('LARGE_INTEGER', 'LONGLONG'),
|
||||
('PLARGE_INTEGER', 'POINTER(LARGE_INTEGER)'),
|
||||
('DWORD64', 'ULONG64'),
|
||||
('SCODE', 'LONG'),
|
||||
('CIMTYPE', 'LONG'),
|
||||
('NET_IFINDEX', 'ULONG'),
|
||||
('IF_INDEX', 'NET_IFINDEX'),
|
||||
('IFTYPE', 'ULONG'),
|
||||
('PULONG64', 'POINTER(ULONG64)'),
|
||||
('PUINT', 'POINTER(UINT)'),
|
||||
('PHANDLE', 'POINTER(HANDLE)'),
|
||||
('HKEY', 'HANDLE'),
|
||||
('HCATADMIN', 'HANDLE'),
|
||||
('HCATINFO', 'HANDLE'),
|
||||
('SC_HANDLE', 'HANDLE'),
|
||||
('LPHANDLE', 'POINTER(HANDLE)'),
|
||||
('PHKEY', 'POINTER(HKEY)'),
|
||||
('ACCESS_MASK', 'DWORD'),
|
||||
('REGSAM', 'ACCESS_MASK'),
|
||||
('SECURITY_CONTEXT_TRACKING_MODE', 'BOOLEAN'),
|
||||
("DISPID", "LONG"),
|
||||
("MEMBERID", "DISPID"),
|
||||
('PSECURITY_DESCRIPTOR', 'PVOID'),
|
||||
('LPUNKNOWN', 'POINTER(PVOID)'),
|
||||
#STUFF FOR COM (will be replace at runtime
|
||||
# real def in com_interface_header
|
||||
('GUID', 'PVOID'),
|
||||
('LPGUID', 'PVOID'),
|
||||
# STUFF FOR DBGENGINE
|
||||
('PWINDBG_EXTENSION_APIS32', 'PVOID'),
|
||||
('PWINDBG_EXTENSION_APIS64', 'PVOID'),
|
||||
#('PDEBUG_SYMBOL_PARAMETERS', 'PVOID'),
|
||||
# Will be changed at import time
|
||||
('LPCONTEXT', 'PVOID'),
|
||||
('HCERTSTORE', 'PVOID'),
|
||||
('HCRYPTMSG', 'PVOID'),
|
||||
('PALPC_PORT_ATTRIBUTES', 'PVOID'),
|
||||
]
|
||||
|
||||
TYPE_EQUIVALENCE.append(('VOID', 'DWORD'))
|
||||
# TRICHE
|
||||
BASIC_TYPE = dummy_wintypes.names + list([x[0] for x in TYPE_EQUIVALENCE])
|
||||
|
||||
class CtypesGenerator(object):
|
||||
common_header = "#Generated file\n"
|
||||
|
||||
PARSER = None
|
||||
IMPORT_HEADER = "{deps}"
|
||||
|
||||
def __init__(self, infilename, outfilename, dependances=()):
|
||||
self.infilename = infilename
|
||||
self.outfilename = outfilename
|
||||
self.infile = open(self.infilename)
|
||||
self.data = None
|
||||
self.dependances = dependances
|
||||
|
||||
self.exports = set([])
|
||||
self.imports = set([])
|
||||
|
||||
self.parse()
|
||||
self.analyse(self.data)
|
||||
self.check_dependances()
|
||||
|
||||
def parse(self):
|
||||
if self.data is None:
|
||||
print("Parsing <{0}>".format(self.infilename))
|
||||
self.data = self.PARSER(self.infile.read()).parse()
|
||||
return self.data
|
||||
|
||||
def analyse(self, data):
|
||||
raise NotImplementedError("<{0}> doest not implement <analyse>".format(type(self).__name__))
|
||||
|
||||
def check_dependances(self):
|
||||
missing = self.imports
|
||||
for dep in self.dependances:
|
||||
missing -= dep.exports
|
||||
if missing:
|
||||
raise ValueError("Missing dependance <{0}> in <{1}>".format(missing, self.infilename))
|
||||
|
||||
def generate_import(self):
|
||||
deps = "\n".join(["from {0} import *".format(os.path.basename(dep.outfilename).rsplit(".")[0]) for dep in self.dependances])
|
||||
return self.IMPORT_HEADER.format(deps = deps)
|
||||
|
||||
def add_imports(self, *names):
|
||||
self.imports.update(names)
|
||||
|
||||
def add_exports(self, *names):
|
||||
self.exports.update(names)
|
||||
|
||||
def generate(self):
|
||||
raise NotImplementedError("<{0}> doest not implement <generate>".format(type(self).__name__))
|
||||
|
||||
class DefGenerator(CtypesGenerator):
|
||||
PARSER = def_parser.WinDefParser
|
||||
HEADER = dedent("""
|
||||
import sys
|
||||
import platform
|
||||
if sys.version_info.major == 3:
|
||||
long = int
|
||||
|
||||
bits = platform.architecture()[0]
|
||||
bitness = int(bits[:2])
|
||||
|
||||
NATIVE_WORD_MAX_VALUE = 0xffffffff if bitness == 32 else 0xffffffffffffffff
|
||||
|
||||
class Flag(long):
|
||||
def __new__(cls, name, value):
|
||||
return super(Flag, cls).__new__(cls, value)
|
||||
|
||||
def __init__(self, name, value):
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}({1})".format(self.name, hex(self))
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
""")
|
||||
|
||||
IMPORT_HEADER = "{deps}"
|
||||
|
||||
def analyse(self, data):
|
||||
self.add_exports("Flag")
|
||||
self.add_exports("NATIVE_WORD_MAX_VALUE")
|
||||
for defin in data:
|
||||
self.add_exports(defin.name)
|
||||
|
||||
|
||||
def generate(self):
|
||||
ctypes_lines = [self.common_header, self.HEADER]
|
||||
#deps = "\n".join(["from {0} import *".format(os.path.basename(dep.outfilename).rsplit(".")[0]) for dep in self.dependances])
|
||||
ctypes_lines += [self.generate_import()]
|
||||
ctypes_lines += [d.generate_ctypes() for d in self.parse()]
|
||||
ctypes_code = "\n".join(ctypes_lines)
|
||||
with open(self.outfilename, "w") as f:
|
||||
f.write(ctypes_code)
|
||||
print("<{0}> generated".format(self.outfilename))
|
||||
return ctypes_code
|
||||
|
||||
class StructGenerator(CtypesGenerator):
|
||||
PARSER = struct_parser.WinStructParser
|
||||
IMPORT_HEADER = dedent ("""
|
||||
from ctypes import *
|
||||
from ctypes.wintypes import *
|
||||
{deps}
|
||||
|
||||
{types_equivalences}
|
||||
""")
|
||||
TYPES_HEADER = dedent("""
|
||||
class EnumValue(Flag):
|
||||
def __new__(cls, enum_name, name, value):
|
||||
return super(EnumValue, cls).__new__(cls, name, value)
|
||||
|
||||
def __init__(self, enum_name, name, value):
|
||||
self.enum_name = enum_name
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}.{1}({2})".format(self.enum_name, self.name, hex(self))
|
||||
|
||||
|
||||
class EnumType(DWORD):
|
||||
values = ()
|
||||
mapper = {}
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
raw_value = super(EnumType, self).value
|
||||
return self.mapper.get(raw_value, raw_value)
|
||||
|
||||
def __repr__(self):
|
||||
raw_value = super(EnumType, self).value
|
||||
if raw_value in self.values:
|
||||
value = self.value
|
||||
return "<{0} {1}({2})>".format(type(self).__name__, value.name, hex(raw_value))
|
||||
return "<{0}({1})>".format(type(self).__name__, hex(self.value))
|
||||
|
||||
""")
|
||||
|
||||
def analyse(self, data):
|
||||
structs, enums = data
|
||||
for btype in BASIC_TYPE:
|
||||
self.add_exports(btype)
|
||||
for enum in enums:
|
||||
self.add_exports(enum.name)
|
||||
self.add_exports(*enum.typedef)
|
||||
for struct in structs:
|
||||
self.add_exports(struct.name)
|
||||
self.add_exports(*struct.typedef)
|
||||
for field_type, field_name, nb_rep in struct.fields:
|
||||
if field_type.name not in self.exports:
|
||||
self.add_imports(field_type.name)
|
||||
try:
|
||||
int(nb_rep)
|
||||
except:
|
||||
self.add_imports(nb_rep)
|
||||
|
||||
|
||||
def generate(self):
|
||||
type_equivalences = "\n".join(["{0} = {1}".format(*x) for x in TYPE_EQUIVALENCE])
|
||||
deps = "\n".join(["from {0} import *".format(os.path.basename(dep.outfilename).rsplit(".")[0]) for dep in self.dependances])
|
||||
|
||||
HEADER = self.HEADER.format(types_equivalences=type_equivalences, deps=deps)
|
||||
HEADER += self.TYPES_HEADER
|
||||
|
||||
structs, enums = self.data
|
||||
ctypes_lines = [self.common_header, self.generate_import()] + [d.generate_ctypes() for l in (enums, structs) for d in l]
|
||||
ctypes_code = "\n".join(ctypes_lines)
|
||||
with open(self.outfilename, "w") as f:
|
||||
f.write(ctypes_code)
|
||||
print("<{0}> generated".format(self.outfilename))
|
||||
return ctypes_code
|
||||
|
||||
class FuncGenerator(CtypesGenerator):
|
||||
PARSER = func_parser.WinFuncParser
|
||||
IMPORT_HEADER = dedent ("""
|
||||
from ctypes import *
|
||||
from ctypes.wintypes import *
|
||||
{deps}
|
||||
|
||||
""")
|
||||
|
||||
def analyse(self, data):
|
||||
for func in data:
|
||||
self.add_imports(func.return_type)
|
||||
for param_type, _ in func.params:
|
||||
if param_type.startswith("POINTER(") and param_type.endswith(")"):
|
||||
param_type = param_type[len("POINTER("): -1]
|
||||
self.add_imports(param_type)
|
||||
|
||||
def generate(self):
|
||||
deps = "\n".join(["from {0} import *".format(os.path.basename(dep.outfilename).rsplit(".")[0]) for dep in self.dependances])
|
||||
HEADER = self.generate_import()
|
||||
|
||||
func_list = "functions = {0}\n\n".format(str([f.name for f in self.data]))
|
||||
|
||||
ctypes_lines = [self.common_header, HEADER, func_list] + [d.generate_ctypes() for d in self.parse()]
|
||||
ctypes_code = "\n".join(ctypes_lines)
|
||||
with open(self.outfilename, "w") as f:
|
||||
f.write(ctypes_code)
|
||||
print("<{0}> generated".format(self.outfilename))
|
||||
return ctypes_code
|
||||
|
||||
class NtStatusGenerator(CtypesGenerator):
|
||||
HEADER_IMPORT = dedent("""
|
||||
import ctypes
|
||||
{deps}
|
||||
|
||||
""")
|
||||
HEADER = dedent("""
|
||||
class NtStatusException(WindowsError):
|
||||
ALL_STATUS = {}
|
||||
def __init__(self , code):
|
||||
try:
|
||||
x = self.ALL_STATUS[code]
|
||||
except KeyError:
|
||||
x = (code, 'UNKNOW_ERROR', 'Error non documented in ntstatus.py')
|
||||
self.code = x[0]
|
||||
self.name = x[1]
|
||||
self.descr = x[2]
|
||||
x = ctypes.c_long(x[0]).value, x[1], x[2]
|
||||
return super(NtStatusException, self).__init__(*x)
|
||||
|
||||
def __str__(self):
|
||||
return "{e.name}(0x{e.code:x}): {e.descr}".format(e=self)
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}(0x{1:08x}, {2})".format(type(self).__name__, self.code, self.name)
|
||||
|
||||
@classmethod
|
||||
def register_ntstatus(cls, code, name, descr):
|
||||
if code in cls.ALL_STATUS:
|
||||
return # Use the first def
|
||||
cls.ALL_STATUS[code] = (code, name, descr)
|
||||
return Flag(name, code)
|
||||
""")
|
||||
|
||||
def parse_ntstatus(self, content):
|
||||
nt_status_defs = []
|
||||
for line in content.split("\n"):
|
||||
if not line:
|
||||
continue
|
||||
code, name, descr = line.split("|", 2)
|
||||
code = int(code, 0)
|
||||
descr = re.sub(" +", " ", descr[:-1]) # remove \n
|
||||
descr = descr.replace('"', "'")
|
||||
nt_status_defs.append((code, name, descr))
|
||||
self.data = nt_status_defs
|
||||
return self
|
||||
|
||||
# Hack for PARSER
|
||||
def parse(self):
|
||||
if self.data is None:
|
||||
print("Parsing <{0}>".format(self.infilename))
|
||||
self.parse_ntstatus(self.infile.read())
|
||||
return self.data
|
||||
|
||||
def analyse(self, data):
|
||||
self.add_imports("Flag")
|
||||
|
||||
def generate(self):
|
||||
#deps = "\n".join(["from {0} import *".format(os.path.basename(dep.outfilename).rsplit(".")[0]) for dep in self.dependances])
|
||||
HEADER = self.generate_import() + self.HEADER
|
||||
ctypes_lines = [HEADER]
|
||||
for code, name, descr in self.parse():
|
||||
ctypes_lines.append('{1} = NtStatusException.register_ntstatus({0}, "{1}", "{2}")'.format(hex(code).strip("L"), name, descr))
|
||||
ctypes_code = "\n".join(ctypes_lines)
|
||||
with open(self.outfilename, "w") as f:
|
||||
f.write(ctypes_code)
|
||||
print("<{0}> generated".format(self.outfilename))
|
||||
return ctypes_code
|
||||
|
||||
|
||||
class InitialCOMGenerator(CtypesGenerator):
|
||||
PARSER = com_parser.WinComParser
|
||||
IGNORE_INTERFACE = ["ITypeInfo"]
|
||||
IMPORT_HEADER = dedent("""
|
||||
import functools
|
||||
import ctypes
|
||||
{deps}
|
||||
|
||||
""")
|
||||
HEADER = dedent("""
|
||||
class IID(IID):
|
||||
def __init__(self, Data1, Data2, Data3, Data4, name=None, strid=None):
|
||||
self.name = name
|
||||
self.strid = strid
|
||||
super(IID, self).__init__(Data1, Data2, Data3, Data4)
|
||||
|
||||
def __repr__(self):
|
||||
if self.strid is None:
|
||||
return super(IID, self).__repr__()
|
||||
if self.name is None:
|
||||
return '<IID "{0}">'.format(self.strid.upper())
|
||||
return '<IID "{0}({1})">'.format(self.strid.upper(), self.name)
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, iid):
|
||||
part_iid = iid.split("-")
|
||||
datas = [int(x, 16) for x in part_iid[:3]]
|
||||
datas.append(int(part_iid[3][:2], 16))
|
||||
datas.append(int(part_iid[3][2:], 16))
|
||||
for i in range(6):
|
||||
datas.append(int(part_iid[4][i * 2:(i + 1) * 2], 16))
|
||||
return cls.from_raw(*datas, strid=iid)
|
||||
|
||||
@classmethod
|
||||
def from_raw(cls, Data1, Data2, Data3, Data41, Data42, Data43, Data44, Data45, Data46, Data47, Data48, **kwargs):
|
||||
return cls(Data1, Data2, Data3, (BYTE*8)(Data41, Data42, Data43, Data44, Data45, Data46, Data47, Data48), **kwargs)
|
||||
|
||||
generate_IID = IID.from_raw
|
||||
|
||||
GUID = IID
|
||||
LPGUID = POINTER(GUID)
|
||||
|
||||
class COMInterface(ctypes.c_void_p):
|
||||
_functions_ = {
|
||||
}
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self._functions_:
|
||||
return functools.partial(self._functions_[name], self)
|
||||
return super(COMInterface, self).__getattribute__(name)
|
||||
""")
|
||||
|
||||
def __init__(self, indirname, iiddef, outfilename, dependances=()):
|
||||
self.indirname = indirname
|
||||
self.infilename = indirname
|
||||
self.outfilename = outfilename
|
||||
self.data = None
|
||||
self.dependances = dependances
|
||||
|
||||
data = open(iiddef).read()
|
||||
self.iids_def = {}
|
||||
for line in data.split("\n"):
|
||||
name, iid = line.split("|")
|
||||
self.iids_def[name] = self.parse_iid(iid), iid
|
||||
|
||||
self.exports = set([])
|
||||
self.imports = set([])
|
||||
|
||||
self.parse()
|
||||
self.analyse(self.data)
|
||||
self.check_dependances()
|
||||
|
||||
def parse(self):
|
||||
if self.data is not None:
|
||||
return self.data
|
||||
data = []
|
||||
for filename in glob.glob(self.indirname):
|
||||
print("Parsing <{0}>".format(filename))
|
||||
data.append(self.PARSER(open(filename).read()).parse())
|
||||
self.data = data
|
||||
return data
|
||||
|
||||
def analyse(self, data):
|
||||
self.real_type = {}
|
||||
#self.add_exports("IID")
|
||||
#self.add_exports("GUID")
|
||||
#self.add_exports("LPGUID")
|
||||
#self.add_exports("COMInterface")
|
||||
for cominterface in data:
|
||||
#import pdb;pdb.set_trace()
|
||||
self.add_exports(cominterface.name)
|
||||
if cominterface.typedefptr:
|
||||
self.add_exports(cominterface.typedefptr)
|
||||
for cominterface in data:
|
||||
for method in cominterface.methods:
|
||||
self.add_imports(method.ret_type)
|
||||
for pos, arg in enumerate(method.args):
|
||||
initial_arg = arg
|
||||
if arg.type in self.exports or arg.type in self.IGNORE_INTERFACE:
|
||||
# COM Interface ? -> PVOID !
|
||||
atype = "PVOID"
|
||||
byreflevel = arg.byreflevel - 1
|
||||
method.args[pos] = arg = type(arg)(atype, byreflevel, arg.name)
|
||||
self.real_type[arg] = initial_arg
|
||||
|
||||
elif arg.type == "void" and arg.byreflevel > 0:
|
||||
# **void -> *PVOID
|
||||
atype = "PVOID"
|
||||
byreflevel = arg.byreflevel - 1
|
||||
method.args[pos] = arg = type(arg)(atype, byreflevel, arg.name)
|
||||
self.real_type[arg] = initial_arg
|
||||
|
||||
self.add_imports(arg.type)
|
||||
|
||||
com_interface_comment_template = """ #{0} -> {1}"""
|
||||
com_interface_method_template = """ "{0}": ctypes.WINFUNCTYPE({1})({2}, "{0}"),"""
|
||||
com_interface_template = dedent("""
|
||||
class {0}(COMInterface):
|
||||
IID = generate_IID({2}, name="{0}", strid="{3}")
|
||||
|
||||
_functions_ = {{
|
||||
{1}
|
||||
}}
|
||||
""")
|
||||
|
||||
def generate(self):
|
||||
define = []
|
||||
for cominterface in self.data:
|
||||
methods_string = []
|
||||
for method_nb, method in enumerate(cominterface.methods):
|
||||
args_to_define = method.args[1:] #ctypes doesnt not need the This
|
||||
args_for_comment = [self.real_type.get(arg, arg) for arg in args_to_define]
|
||||
#import pdb;pdb.set_trace()
|
||||
str_args = []
|
||||
methods_string.append(self.com_interface_comment_template.format(method.name, ", ".join([arg.name +":"+ ("*"* arg.byreflevel) +arg.type for arg in args_for_comment])))
|
||||
for arg in args_to_define:
|
||||
type = arg.type
|
||||
for i in range(arg.byreflevel):
|
||||
type = "POINTER({0})".format(type)
|
||||
str_args.append(type)
|
||||
methods_string.append(self.com_interface_method_template.format(method.name, ", ".join([method.ret_type] + str_args), method_nb))
|
||||
#import pdb;pdb.set_trace()
|
||||
if cominterface.iid is not None:
|
||||
iid_str = cominterface.iid
|
||||
iid_python = self.parse_iid(iid_str)
|
||||
else:
|
||||
print("Lookup of IID for <{0}>".format(cominterface.name))
|
||||
iid_python, iid_str = self.iids_def[cominterface.name]
|
||||
define.append((self.com_interface_template.format(cominterface.name, "\n".join(methods_string), iid_python, iid_str)))
|
||||
|
||||
deps = "\n".join(["from {0} import *".format(os.path.basename(dep.outfilename).rsplit(".")[0]) for dep in self.dependances])
|
||||
|
||||
ctypes_code = self.generate_import() + "\n" + self.HEADER + "\n".join(define)
|
||||
with open(self.outfilename, "w") as f:
|
||||
f.write(ctypes_code)
|
||||
print("<{0}> generated".format(self.outfilename))
|
||||
return ctypes_code
|
||||
|
||||
def parse_iid(self, iid_str):
|
||||
part_iid = iid_str.split("-")
|
||||
str_iid = []
|
||||
str_iid.append("0x" + part_iid[0])
|
||||
str_iid.append("0x" + part_iid[1])
|
||||
str_iid.append("0x" + part_iid[2])
|
||||
str_iid.append("0x" + part_iid[3][:2])
|
||||
str_iid.append("0x" + part_iid[3][2:])
|
||||
for i in range(6): str_iid.append("0x" + part_iid[4][i * 2:(i + 1) * 2])
|
||||
return ", ".join(str_iid)
|
||||
|
||||
|
||||
class COMGenerator(InitialCOMGenerator):
|
||||
IMPORT_HEADER = "{deps}"
|
||||
HEADER = ""
|
||||
|
||||
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
print(SCRIPT_DIR)
|
||||
from_here = lambda path: pjoin(SCRIPT_DIR, path)
|
||||
|
||||
DEFAULT_INTERFACE_TO_IID = from_here("definitions\\interface_to_iid.txt")
|
||||
|
||||
# A partial define without the dependance to ntstatus defintion
|
||||
# BOOTSTRAP!!
|
||||
non_generated_def = DefGenerator(from_here("definitions\\windef.txt"), from_here(r"..\windows\generated_def\\windef.py"))
|
||||
ntstatus = NtStatusGenerator(from_here("definitions\\ntstatus.txt"), from_here(r"..\windows\generated_def\\ntstatus.py"), dependances=[non_generated_def])
|
||||
# Not a real circular def (import not at the begin of file
|
||||
defs_with_ntstatus = DefGenerator(from_here("definitions\\windef.txt"), from_here(r"..\windows\generated_def\\windef.py"), dependances=[ntstatus])
|
||||
structs = StructGenerator(from_here("definitions\\winstruct.txt"), from_here(r"..\windows\generated_def\\winstructs.py"), dependances=[defs_with_ntstatus])
|
||||
functions = FuncGenerator(from_here("definitions\\winfunc.txt"), from_here(r"..\windows\generated_def\\winfuncs.py"), dependances=[structs])
|
||||
com = InitialCOMGenerator(from_here("definitions\\com\\*.txt"), DEFAULT_INTERFACE_TO_IID, from_here(r"..\windows\generated_def\\interfaces.py"), dependances=[structs])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ntstatus.generate()
|
||||
defs_with_ntstatus.generate()
|
||||
structs.generate()
|
||||
functions.generate()
|
||||
com.generate()
|
||||
Reference in New Issue
Block a user