mirror of
https://github.com/wavestone-cdt/EDRSandblast.git
synced 2026-06-11 01:41:20 +00:00
starting removing the PE parsing in ExtractOffsets.py to get rid of r2
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
|
// Written from information found here: https://llvm.org/docs/PDB/index.html
|
||||||
|
|
||||||
typedef DWORD ulittle32_t;
|
typedef DWORD ulittle32_t;
|
||||||
|
|
||||||
typedef struct SuperBlock_t {
|
typedef struct SuperBlock_t {
|
||||||
|
|||||||
@@ -127,6 +127,9 @@
|
|||||||
</SubSystem>
|
</SubSystem>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Lib>
|
||||||
|
<IgnoreSpecificDefaultLibraries>libcmt.lib</IgnoreSpecificDefaultLibraries>
|
||||||
|
</Lib>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
@@ -147,6 +150,9 @@
|
|||||||
<OptimizeReferences>true</OptimizeReferences>
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Lib>
|
||||||
|
<IgnoreSpecificDefaultLibraries>libcmt.lib</IgnoreSpecificDefaultLibraries>
|
||||||
|
</Lib>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="EDRSandblast_API.c" />
|
<ClCompile Include="EDRSandblast_API.c" />
|
||||||
|
|||||||
+58
-24
@@ -129,33 +129,57 @@ def get_field_offset(symbols_info, field_name):
|
|||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
from pefile import PE, DIRECTORY_ENTRY
|
||||||
def get_file_version(path):
|
def get_file_version(path):
|
||||||
# dump version number using r2
|
pe = PE(path,fast_load=True)
|
||||||
r = run(["r2", "-c", "iV", "-qq", path], capture_output=True)
|
pe.parse_data_directories(directories=[DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE']])
|
||||||
for line in r.stdout.decode().splitlines():
|
if not 'VS_FIXEDFILEINFO' in pe.__dict__ or not pe.VS_FIXEDFILEINFO:
|
||||||
line = line.strip()
|
raise RuntimeError("Version info not found in {pename}")
|
||||||
if line.startswith("FileVersion:"):
|
verinfo = pe.VS_FIXEDFILEINFO[0]
|
||||||
return [int(frag) for frag in line.split(" ")[-1].split(".")]
|
filever = (verinfo.FileVersionMS >> 16, verinfo.FileVersionMS & 0xFFFF, verinfo.FileVersionLS >> 16, verinfo.FileVersionLS & 0xFFFF)
|
||||||
|
return filever
|
||||||
|
|
||||||
print(f'[!] ERROR : failed to extract version from {path}.')
|
# Takes a path to a PE file as argument, download the associated PDB
|
||||||
raise RuntimeError("get_file_version error")
|
# Return True if it succeeded of if the PDB was already present
|
||||||
|
def get_pdb(pe_path, verbose=False):
|
||||||
|
pdb_file_path = pe_path.rsplit(".", maxsplit=1)[0] + ".pdb"
|
||||||
|
if not os.path.isfile(pdb_file_path):
|
||||||
|
if verbose: print(f"[*] Downloading missing {pdb_file_path}")
|
||||||
|
pe = PE(pe_path, fast_load=True)
|
||||||
|
pe.parse_data_directories(directories=[DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG']])
|
||||||
|
guid_string = f"{pe.DIRECTORY_ENTRY_DEBUG[0].entry.Signature_Data1:08X}" + \
|
||||||
|
f"{pe.DIRECTORY_ENTRY_DEBUG[0].entry.Signature_Data2:04X}" + \
|
||||||
|
f"{pe.DIRECTORY_ENTRY_DEBUG[0].entry.Signature_Data3:04X}" + \
|
||||||
|
f"{pe.DIRECTORY_ENTRY_DEBUG[0].entry.Signature_Data4:02X}" + \
|
||||||
|
f"{pe.DIRECTORY_ENTRY_DEBUG[0].entry.Signature_Data5:02X}" + \
|
||||||
|
pe.DIRECTORY_ENTRY_DEBUG[0].entry.Signature_Data6.hex().upper()
|
||||||
|
age_string = f"{pe.DIRECTORY_ENTRY_DEBUG[0].entry.Age:X}"
|
||||||
|
pdb_filename = pe.DIRECTORY_ENTRY_DEBUG[0].entry.PdbFileName.decode().replace("\x00","")
|
||||||
|
pdb_url = f'https://msdl.microsoft.com/download/symbols/{pdb_filename}/{guid_string}{age_string}/{pdb_filename}'
|
||||||
|
try:
|
||||||
|
pdbContent = get(pdb_url)
|
||||||
|
assert len(pdbContent.content) > 0
|
||||||
|
with open(pdb_file_path, 'wb') as f:
|
||||||
|
f.write(pdbContent.content)
|
||||||
|
if verbose: print(f'[+] Finished download PDB of {pe_path} version (file: {pdb_file_path})!')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[!] ERROR : Could not download PDB of {pe_path} (URL: {pdb_url}): {str(e)}.')
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def extractOffsets(input_file, output_file, mode):
|
def extractOffsets(input_file, output_file, mode):
|
||||||
if os.path.isfile(input_file):
|
if os.path.isfile(input_file):
|
||||||
try:
|
try:
|
||||||
# check image type (ntoskrnl, wdigest, etc.)
|
# check image type (ntoskrnl, wdigest, etc.)
|
||||||
r = run(["r2", "-c", "iE", "-qq", input_file], capture_output=True)
|
pe = PE(input_file,fast_load=True)
|
||||||
for line in r.stdout.decode().splitlines():
|
pe.parse_data_directories(directories=[DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT']])
|
||||||
line = line.lower()
|
name = pe.DIRECTORY_ENTRY_EXPORT.name.decode().lower()
|
||||||
if "ntoskrnl.exe" in line:
|
if "ntoskrnl.exe" in name:
|
||||||
imageType = "ntoskrnl"
|
imageType = "ntoskrnl"
|
||||||
break
|
elif "wdigest.dll" in name:
|
||||||
elif "wdigest.dll" in line:
|
imageType = "wdigest"
|
||||||
imageType = "wdigest"
|
elif "ci.dll" in name:
|
||||||
break
|
imageType = "ci"
|
||||||
elif "ci.dll" in line:
|
|
||||||
imageType = "ci"
|
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
print(f"[*] File {input_file} unrecognized")
|
print(f"[*] File {input_file} unrecognized")
|
||||||
return
|
return
|
||||||
@@ -179,7 +203,7 @@ def extractOffsets(input_file, output_file, mode):
|
|||||||
|
|
||||||
# print(f'[*] Processing {imageType} version {imageVersion} (file: {input_file})')
|
# print(f'[*] Processing {imageType} version {imageVersion} (file: {input_file})')
|
||||||
# download the PDB if needed
|
# download the PDB if needed
|
||||||
r = run(["r2", "-c", "idpd", "-qq", input_file], capture_output=True)
|
get_pdb(input_file)
|
||||||
# dump all symbols
|
# dump all symbols
|
||||||
r = run(["r2", "-c", "idpi", "-qq", '-B', '0', input_file], capture_output=True)
|
r = run(["r2", "-c", "idpi", "-qq", '-B', '0', input_file], capture_output=True)
|
||||||
all_symbols_info = [line.strip() for line in r.stdout.decode().splitlines()]
|
all_symbols_info = [line.strip() for line in r.stdout.decode().splitlines()]
|
||||||
@@ -225,7 +249,7 @@ def extractOffsets(input_file, output_file, mode):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'[!] ERROR : Could not process file {input_file}.')
|
print(f'[!] ERROR : Could not process file {input_file}.')
|
||||||
print(f'[!] Error message: {e}')
|
print(f'[!] Error message: {e}')
|
||||||
print(f'[!] If error is of the like of "\'NoneType\' object has no attribute \'group\'", kernel callbacks may not be supported by this version.')
|
#print(f'[!] If error is of the like of "\'NoneType\' object has no attribute \'group\'", kernel callbacks may not be supported by this version.')
|
||||||
|
|
||||||
elif os.path.isdir(input_file):
|
elif os.path.isdir(input_file):
|
||||||
print(f'[*] Processing folder: {input_file}')
|
print(f'[*] Processing folder: {input_file}')
|
||||||
@@ -249,6 +273,17 @@ def loadOffsetsFromCSV(loadedVersions, CSVPath):
|
|||||||
for peLine in csvReader:
|
for peLine in csvReader:
|
||||||
loadedVersions.append(peLine[0])
|
loadedVersions.append(peLine[0])
|
||||||
|
|
||||||
|
def sortOutputFile(csvFile):
|
||||||
|
def lineKey(line):
|
||||||
|
major = int(line.split("_")[1].split("-")[0])
|
||||||
|
minor = int(line.split("-")[1].split(".")[0])
|
||||||
|
return (major, minor)
|
||||||
|
with open(csvFile) as f:
|
||||||
|
header_line = f.readline()
|
||||||
|
content = f.readlines()
|
||||||
|
with open(csvFile, "w") as f:
|
||||||
|
f.write(header_line)
|
||||||
|
f.writelines(sorted(set(content), key=lineKey))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
@@ -291,8 +326,6 @@ if __name__ == '__main__':
|
|||||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||||
print('[!] ERROR : On Linux systems, radare2 needs cabextract to be installed to work with PDB.')
|
print('[!] ERROR : On Linux systems, radare2 needs cabextract to be installed to work with PDB.')
|
||||||
exit(1)
|
exit(1)
|
||||||
if "R2_CURL" not in os.environ:
|
|
||||||
print("WARNING : On Linux systems, radare2 may have trouble to download PDB files. If offsets are reported as 0, export R2_CURL=1 prior to running the script.")
|
|
||||||
|
|
||||||
|
|
||||||
# If the output file exists, load the already analyzed image versions.
|
# If the output file exists, load the already analyzed image versions.
|
||||||
@@ -324,3 +357,4 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
# Extract the offsets from the specified file or the folders containing image files.
|
# Extract the offsets from the specified file or the folders containing image files.
|
||||||
extractOffsets(args.input, args.output, mode)
|
extractOffsets(args.input, args.output, mode)
|
||||||
|
sortOutputFile(args.output)
|
||||||
|
|||||||
Reference in New Issue
Block a user