From 8a6181861af4c6d5be9ea80b3ce4d15228eb3a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=81=8D=E5=85=AE=E6=83=9A=E5=85=AE?= <101191390+HIllya51@users.noreply.github.com> Date: Tue, 25 Jun 2024 19:49:49 +0800 Subject: [PATCH] pe --- LunaTranslator/importanalysis.py | 233 ++++++++++++++++++++++++++++ LunaTranslator/requirements.txt | 1 - LunaTranslator/requirements_qt6.txt | 1 - LunaTranslator/retrieval.py | 27 +--- 4 files changed, 238 insertions(+), 24 deletions(-) create mode 100644 LunaTranslator/importanalysis.py diff --git a/LunaTranslator/importanalysis.py b/LunaTranslator/importanalysis.py new file mode 100644 index 00000000..95c23a77 --- /dev/null +++ b/LunaTranslator/importanalysis.py @@ -0,0 +1,233 @@ +from ctypes import ( + c_size_t, + windll, + memmove, + Structure, + c_uint64, + cast, + POINTER, + sizeof, + c_char_p, +) +from ctypes.wintypes import LPVOID, DWORD, WORD, LONG, BYTE + +ULONGLONG = c_uint64 +kernel32 = windll.kernel32 +VirtualAlloc = kernel32.VirtualAlloc +VirtualAlloc.argtypes = LPVOID, c_size_t, DWORD, DWORD +VirtualAlloc.restype = LPVOID +VirtualFree = kernel32.VirtualFree +VirtualFree.argtypes = LPVOID, c_size_t, DWORD + + +class IMAGE_DOS_HEADER(Structure): + _fields_ = [("_nouse", WORD * 30), ("e_lfanew", LONG)] + + +class IMAGE_FILE_HEADER(Structure): + _fields_ = [ + ("Machine", WORD), + ("NumberOfSections", WORD), + ("TimeDateStamp", DWORD), + ("PointerToSymbolTable", DWORD), + ("NumberOfSymbols", DWORD), + ("SizeOfOptionalHeader", WORD), + ("Characteristics", WORD), + ] + + +class IMAGE_DATA_DIRECTORY(Structure): + _fields_ = [ + ("VirtualAddress", DWORD), + ("Size", DWORD), + ] + + +IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16 + + +class IMAGE_OPTIONAL_HEADER32(Structure): + _fields_ = [ + ("Magic", WORD), + ("MajorLinkerVersion", BYTE), + ("MinorLinkerVersion", BYTE), + ("SizeOfCode", DWORD), + ("SizeOfInitializedData", DWORD), + ("SizeOfUninitializedData", DWORD), + ("AddressOfEntryPoint", DWORD), + ("BaseOfCode", DWORD), + ("BaseOfData", DWORD), + ("ImageBase", DWORD), + ("SectionAlignment", DWORD), + ("FileAlignment", DWORD), + ("MajorOperatingSystemVersion", WORD), + ("MinorOperatingSystemVersion", WORD), + ("MajorImageVersion", WORD), + ("MinorImageVersion", WORD), + ("MajorSubsystemVersion", WORD), + ("MinorSubsystemVersion", WORD), + ("Win32VersionValue", DWORD), + ("SizeOfImage", DWORD), + ("SizeOfHeaders", DWORD), + ("CheckSum", DWORD), + ("Subsystem", WORD), + ("DllCharacteristics", WORD), + ("SizeOfStackReserve", DWORD), + ("SizeOfStackCommit", DWORD), + ("SizeOfHeapReserve", DWORD), + ("SizeOfHeapCommit", DWORD), + ("LoaderFlags", DWORD), + ("NumberOfRvaAndSizes", DWORD), + ("DataDirectory", IMAGE_DATA_DIRECTORY * IMAGE_NUMBEROF_DIRECTORY_ENTRIES), + ] + + +class IMAGE_OPTIONAL_HEADER64(Structure): + _fields_ = [ + ("Magic", WORD), + ("MajorLinkerVersion", BYTE), + ("MinorLinkerVersion", BYTE), + ("SizeOfCode", DWORD), + ("SizeOfInitializedData", DWORD), + ("SizeOfUninitializedData", DWORD), + ("AddressOfEntryPoint", DWORD), + ("BaseOfCode", DWORD), + ("ImageBase", ULONGLONG), + ("SectionAlignment", DWORD), + ("FileAlignment", DWORD), + ("MajorOperatingSystemVersion", WORD), + ("MinorOperatingSystemVersion", WORD), + ("MajorImageVersion", WORD), + ("MinorImageVersion", WORD), + ("MajorSubsystemVersion", WORD), + ("MinorSubsystemVersion", WORD), + ("Win32VersionValue", DWORD), + ("SizeOfImage", DWORD), + ("SizeOfHeaders", DWORD), + ("CheckSum", DWORD), + ("Subsystem", WORD), + ("DllCharacteristics", WORD), + ("SizeOfStackReserve", ULONGLONG), + ("SizeOfStackCommit", ULONGLONG), + ("SizeOfHeapReserve", ULONGLONG), + ("SizeOfHeapCommit", ULONGLONG), + ("LoaderFlags", DWORD), + ("NumberOfRvaAndSizes", DWORD), + ("DataDirectory", IMAGE_DATA_DIRECTORY * IMAGE_NUMBEROF_DIRECTORY_ENTRIES), + ] + + +class IMAGE_NT_HEADERS32(Structure): + _fields_ = [ + ("_nouse", DWORD), + ("FileHeader", IMAGE_FILE_HEADER), + ("OptionalHeader", IMAGE_OPTIONAL_HEADER32), + ] + + +class IMAGE_NT_HEADERS64(Structure): + _fields_ = [ + ("_nouse", DWORD), + ("FileHeader", IMAGE_FILE_HEADER), + ("OptionalHeader", IMAGE_OPTIONAL_HEADER64), + ] + + +IMAGE_SIZEOF_SHORT_NAME = 8 + + +class IMAGE_SECTION_HEADER(Structure): + _fields_ = [ + ("Name", BYTE * IMAGE_SIZEOF_SHORT_NAME), + ("VirtualSize", DWORD), + ("VirtualAddress", DWORD), + ("SizeOfRawData", DWORD), + ("PointerToRawData", DWORD), + ("PointerToRelocations", DWORD), + ("PointerToLinenumbers", DWORD), + ("NumberOfRelocations", WORD), + ("NumberOfLinenumbers", WORD), + ("Characteristics", DWORD), + ] + + +MEM_COMMIT = 0x00001000 +MEM_DECOMMIT = 0x00004000 +PAGE_READWRITE = 0x04 +IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10B +IMAGE_DIRECTORY_ENTRY_IMPORT = 1 + + +class IMAGE_IMPORT_DESCRIPTOR(Structure): + _fields_ = [ + ("OriginalFirstThunk", DWORD), + ("TimeDateStamp", DWORD), + ("ForwarderChain", DWORD), + ("Name", DWORD), + ("FirstThunk", DWORD), + ] + + +def Rva2Offset(rva, psh, pnt, IMAGE_NT_HEADERS): + pSeh = psh + psh = cast(psh, POINTER(IMAGE_SECTION_HEADER)) + pnt = cast(pnt, POINTER(IMAGE_NT_HEADERS)) + for i in range(pnt.contents.FileHeader.NumberOfSections): + pSeh1 = cast(pSeh, POINTER(IMAGE_SECTION_HEADER)).contents + if ( + rva >= pSeh1.VirtualAddress + and rva < pSeh1.VirtualAddress + pSeh1.VirtualSize + ): + break + pSeh += sizeof(IMAGE_SECTION_HEADER) + pSeh = cast(pSeh, POINTER(IMAGE_SECTION_HEADER)).contents + return rva - pSeh.VirtualAddress + pSeh.PointerToRawData + + +def importanalysis(fname): + with open(fname, "rb") as ff: + bs = ff.read() + + virtualpointer = VirtualAlloc(None, len(bs), MEM_COMMIT, PAGE_READWRITE) + memmove(virtualpointer, bs, len(bs)) + ntheaders_addr = ( + virtualpointer + + cast(virtualpointer, POINTER(IMAGE_DOS_HEADER)).contents.e_lfanew + ) + ntheaders = cast(ntheaders_addr, POINTER(IMAGE_NT_HEADERS32)).contents + IMAGE_NT_HEADERS = IMAGE_NT_HEADERS32 + magic = ntheaders.OptionalHeader.Magic + if magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC: + ntheaders = cast(ntheaders_addr, POINTER(IMAGE_NT_HEADERS64)).contents + IMAGE_NT_HEADERS = IMAGE_NT_HEADERS64 + pSech = ( + ntheaders_addr + + sizeof(DWORD) + + sizeof(IMAGE_FILE_HEADER) + + ntheaders.FileHeader.SizeOfOptionalHeader + ) + + pImportDescriptor = virtualpointer + Rva2Offset( + ntheaders.OptionalHeader.DataDirectory[ + IMAGE_DIRECTORY_ENTRY_IMPORT + ].VirtualAddress, + pSech, + ntheaders_addr, + IMAGE_NT_HEADERS, + ) + pImportDescriptor_data = cast( + pImportDescriptor, POINTER(IMAGE_IMPORT_DESCRIPTOR) + ).contents + collect = [] + while pImportDescriptor_data.Name: + offset = Rva2Offset( + pImportDescriptor_data.Name, pSech, ntheaders_addr, IMAGE_NT_HEADERS + ) + name = virtualpointer + offset + collect.append((cast(name, c_char_p).value.decode(), offset)) + pImportDescriptor += sizeof(IMAGE_IMPORT_DESCRIPTOR) + pImportDescriptor_data = cast( + pImportDescriptor, POINTER(IMAGE_IMPORT_DESCRIPTOR) + ).contents + VirtualFree(virtualpointer, len(bs), MEM_DECOMMIT) + return collect diff --git a/LunaTranslator/requirements.txt b/LunaTranslator/requirements.txt index 576346fc..40b898d3 100644 --- a/LunaTranslator/requirements.txt +++ b/LunaTranslator/requirements.txt @@ -1,5 +1,4 @@ PyQt5==5.15.10 PyQt5-Qt5==5.15.2 webviewpy==1.3.0 -pefile tinycss2 \ No newline at end of file diff --git a/LunaTranslator/requirements_qt6.txt b/LunaTranslator/requirements_qt6.txt index 9932b2f6..40c5d640 100644 --- a/LunaTranslator/requirements_qt6.txt +++ b/LunaTranslator/requirements_qt6.txt @@ -1,5 +1,4 @@ PyQt6==6.7.0 PyQt6-Qt6==6.7.0 webviewpy==1.3.0 -pefile tinycss2 \ No newline at end of file diff --git a/LunaTranslator/retrieval.py b/LunaTranslator/retrieval.py index 06aa1d62..dfe71c54 100644 --- a/LunaTranslator/retrieval.py +++ b/LunaTranslator/retrieval.py @@ -1,7 +1,7 @@ -import modulefinder, shutil, os, sys, pefile +import modulefinder, shutil, os, sys import builtins, platform import sys - +from importanalysis import importanalysis pyversion = platform.python_version() pyversion2 = "".join(pyversion.split(".")[:2]) x86 = platform.architecture()[0] == "32bit" @@ -222,31 +222,15 @@ for f in collect: if f.endswith(".pyc") or f.endswith("Thumbs.db"): os.remove(f) elif f.endswith(".exe") or f.endswith(".pyd") or f.endswith(".dll"): - - try: - pe = pefile.PE(f) - import_table = pe.DIRECTORY_ENTRY_IMPORT - imports = [] - for entry in import_table: - if entry.dll.decode("utf-8").lower().startswith("api"): - imports.append(entry.dll.decode("utf-8")) - pe.close() - except: - continue if f.endswith("Magpie.Core.exe"): continue - if f.endswith("QtWidgets.pyd"): - imports += [ - "api-ms-win-crt-runtime-l1-1-0.dll", - "api-ms-win-crt-heap-l1-1-0.dll", - ] - # pefile好像有bug,仅对于QtWidgets.pyd这个文件,只能读取到导入了Qt5Widgets.dll + imports=importanalysis(f) print(f, imports) if len(imports) == 0: continue with open(f, "rb") as ff: bs = bytearray(ff.read()) - for _dll in imports: + for _dll,offset in imports: if _dll.lower().startswith("api-ms-win-core"): # 其实对于api-ms-win-core-winrt-XXX实际上是到ComBase.dll之类的,不过此项目中不包含这些 _target = "kernel32.dll" @@ -254,9 +238,8 @@ for f in collect: _target = "ucrtbase.dll" _dll = _dll.encode() _target = _target.encode() - idx = bs.find(_dll) # print(len(bs)) - bs[idx : idx + len(_dll)] = _target + b"\0" * (len(_dll) - len(_target)) + bs[offset : offset + len(_dll)] = _target + b"\0" * (len(_dll) - len(_target)) # print(len(bs)) with open(f, "wb") as ff: ff.write(bs)