-
-
Save dfirfpi/113ff71274a97b489dfd to your computer and use it in GitHub Desktop.
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
# | |
# Copyright 2015, Francesco "dfirfpi" Picasso <francesco.picasso@gmail.com> | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
# | |
"""Python Windows-only utility to decompress MAM compressed files.""" | |
import binascii | |
import ctypes | |
import struct | |
import sys | |
def tohex(val, nbits): | |
"""Utility to convert (signed) integer to hex.""" | |
return hex((val + (1 << nbits)) % (1 << nbits)) | |
def main(): | |
"""Utility core.""" | |
if len(sys.argv) != 3: | |
sys.exit('Missing params [win10compressed.pf] [win10decompressed.pf]') | |
NULL = ctypes.POINTER(ctypes.c_uint)() | |
SIZE_T = ctypes.c_uint | |
DWORD = ctypes.c_uint32 | |
USHORT = ctypes.c_uint16 | |
UCHAR = ctypes.c_ubyte | |
ULONG = ctypes.c_uint32 | |
# You must have at least Windows 8, or it should fail. | |
try: | |
RtlDecompressBufferEx = ctypes.windll.ntdll.RtlDecompressBufferEx | |
except AttributeError: | |
sys.exit('You must have Windows with version >=8.') | |
RtlGetCompressionWorkSpaceSize = \ | |
ctypes.windll.ntdll.RtlGetCompressionWorkSpaceSize | |
with open(sys.argv[1], 'rb') as fin: | |
header = fin.read(8) | |
compressed = fin.read() | |
signature, decompressed_size = struct.unpack('<LL', header) | |
calgo = (signature & 0x0F000000) >> 24 | |
crcck = (signature & 0xF0000000) >> 28 | |
magic = signature & 0x00FFFFFF | |
if magic != 0x004d414d : | |
sys.exit('Wrong signature... wrong file?') | |
if crcck: | |
# I could have used RtlComputeCrc32. | |
file_crc = struct.unpack('<L', compressed[:4])[0] | |
crc = binascii.crc32(header) | |
crc = binascii.crc32(struct.pack('<L',0), crc) | |
compressed = compressed[4:] | |
crc = binascii.crc32(compressed, crc) | |
if crc != file_crc: | |
sys.exit('Wrong file CRC {0:x} - {1:x}!'.format(crc, file_crc)) | |
compressed_size = len(compressed) | |
ntCompressBufferWorkSpaceSize = ULONG() | |
ntCompressFragmentWorkSpaceSize = ULONG() | |
ntstatus = RtlGetCompressionWorkSpaceSize(USHORT(calgo), | |
ctypes.byref(ntCompressBufferWorkSpaceSize), | |
ctypes.byref(ntCompressFragmentWorkSpaceSize)) | |
if ntstatus: | |
sys.exit('Cannot get workspace size, err: {}'.format( | |
tohex(ntstatus, 32))) | |
ntCompressed = (UCHAR * compressed_size).from_buffer_copy(compressed) | |
ntDecompressed = (UCHAR * decompressed_size)() | |
ntFinalUncompressedSize = ULONG() | |
ntWorkspace = (UCHAR * ntCompressFragmentWorkSpaceSize.value)() | |
ntstatus = RtlDecompressBufferEx( | |
USHORT(calgo), | |
ctypes.byref(ntDecompressed), | |
ULONG(decompressed_size), | |
ctypes.byref(ntCompressed), | |
ULONG(compressed_size), | |
ctypes.byref(ntFinalUncompressedSize), | |
ctypes.byref(ntWorkspace)) | |
if ntstatus: | |
sys.exit('Decompression failed, err: {}'.format( | |
tohex(ntstatus, 32))) | |
if ntFinalUncompressedSize.value != decompressed_size: | |
print 'Decompressed with a different size than original!' | |
with open(sys.argv[2], 'wb') as fout: | |
fout.write(bytearray(ntDecompressed)) | |
print 'Lucky man, you have your prefetch file ready to be parsed!' | |
if __name__ == "__main__": | |
main() |
@DJP89 don't know about the PNG, most likely you will need to adjust the script.
Hi @dfirfpi, I'm doing some prefetch research as part of some postgrad study and found your super helpful tool. Thank you!
I had a minor issue getting it running on Python 3.10.4. To get it working I just modified the code to enclose the print commands at row 104 and 109 with parenthesis as per below.
if ntFinalUncompressedSize.value != decompressed_size:
print ('Decompressed with a different size than original!')
with open(sys.argv[2], 'wb') as fout:
fout.write(bytearray(ntDecompressed))
print ('Lucky man, you have your prefetch file ready to be parsed!')
Running Python 3.9.16 on Cygwin 3.4.6 on Win10.0.19045.2965 x64.
To accommodate the possibility of running non-Windows-native Python and load the ntdll.dll library. I modified the try ... RtlDecompressBufferEx
block as follows:
try:
RtlDecompressBufferEx = ctypes.windll.ntdll.RtlDecompressBufferEx
RtlGetCompressionWorkSpaceSize = \
ctypes.windll.ntdll.RtlGetCompressionWorkSpaceSize
except AttributeError:
#sys.exit('You must have Windows with version >=8.')
# Handle running non-Windows-native Python.
try:
ntdll = ctypes.CDLL("ntdll.dll")
except Exception as e:
print(type(e).__name__ + ": " + str(e), file=sys.stderr)
exit(1)
else:
RtlDecompressBufferEx = ntdll.RtlDecompressBufferEx
RtlGetCompressionWorkSpaceSize = ntdll.RtlGetCompressionWorkSpaceSize
But when I get to the actual RtlDecompressBufferEx
call:
ntstatus = RtlDecompressBufferEx(
USHORT(calgo),
ctypes.byref(ntDecompressed),
ULONG(decompressed_size),
ctypes.byref(ntCompressed),
ULONG(compressed_size),
ctypes.byref(ntFinalUncompressedSize),
ctypes.byref(ntWorkspace))
if ntstatus:
raise ValueError('Decompression failed, err: {}'.\
format(tohex(ntstatus, 32)))
I get ValueError: Decompression failed, err: 0xc0000242
which is STATUS_BAD_COMPRESSION_BUFFER
.
I tried different ctype calls & type modifications with no luck.
I also tried running it via native-Windpws Python 3.11 from the MS Store.
Exactly same error code.
I recon either the input or ouput buffer is incorrectly cast / passed to RtlDecompressBufferEx
but I lack skill / knowledge to debug WinNT API + Python ctypes...
Any ideas?
So Im trying to decompress a .png that for some reason has MAM encoding, or appears to. I keep getting errors with the script, but did a sanity check with a file from c:\windows\prefetch and that worked. The specific error code is: 0xc0000242L