XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
"""Extract the pinned client's embedded HDR programs without accessing the game.

These are the actual programs loaded by 3521E90, not similarly named shaders
from Effects.arc. All permutations are indexed; only neutral variants are
disassembled to keep the evidence reviewable. Runtime permutation selection
and parameter overrides remain separate evidence requirements.
"""
from pathlib import Path
import ctypes, hashlib, json, re, struct, zlib
import pefile

image=Path('artifacts/ship-model-reference/client-copy/EliteDangerous64.exe')
image_bytes=image.read_bytes()
image_hash=hashlib.sha256(image_bytes).hexdigest()
if image_hash!='6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93':
    raise ValueError('Pinned image changed')
pe=pefile.PE(data=image_bytes,fast_load=True)
base=pe.OPTIONAL_HEADER.ImageBase
out=Path('artifacts/galaxy-query-research/power-outer-sources/background/hdr-programs')
out.mkdir(parents=True,exist_ok=True)

programs=[
    ('final-hdr',0x54C4548,0x53980C0,1229941,28284792,
     'c9cbe6ed3816ef95b426682d8958ea8340d72a56e79f9f9ff8185877baaecb18'),
    ('auto-exposure',0x54C6248,0x54C4568,7370,95472,
     '62ef0e8e73e653df972031438fdf5a44e5c152e3c7aa0c9af8fd42e14df7cea7'),
    ('downscale-luminance',0x54C6920,0x54C6268,1698,5408,
     '8db7fd84bca3f6fba12086d8ce6e361e4d7a35d61a7026eebe70c3114cd00720')]
dll=ctypes.WinDLL('d3dcompiler_47.dll')
disassemble=dll.D3DDisassemble
disassemble.argtypes=[ctypes.c_void_p,ctypes.c_size_t,ctypes.c_uint,ctypes.c_char_p,ctypes.POINTER(ctypes.c_void_p)]
disassemble.restype=ctypes.c_int32

def assembly(blob):
    result=ctypes.c_void_p()
    data=ctypes.create_string_buffer(blob)
    if disassemble(data,len(blob),0,None,ctypes.byref(result))<0:
        raise ValueError('Original HDR DXBC disassembly failed')
    table=ctypes.cast(result,ctypes.POINTER(ctypes.POINTER(ctypes.c_void_p)))[0]
    try:
        p=ctypes.WINFUNCTYPE(ctypes.c_void_p,ctypes.c_void_p)(table[3])(result)
        n=ctypes.WINFUNCTYPE(ctypes.c_size_t,ctypes.c_void_p)(table[4])(result)
        return ctypes.string_at(p,n).decode().rstrip('\0')
    finally:
        ctypes.WINFUNCTYPE(ctypes.c_uint,ctypes.c_void_p)(table[2])(result)

results=[]
for role,descriptor,rva,compressed,uncompressed,expected in programs:
    platform,data_ptr=struct.unpack('<2Q',pe.get_data(descriptor,16))
    if (platform,data_ptr)!=(base+0x4E295A0,base+rva):
        raise ValueError('Embedded HDR program descriptor changed')
    if pe.get_data(0x4E295A0,11)!=b'Win64_SM50\0':
        raise ValueError('Embedded program platform changed')
    header=pe.get_data(rva,16)
    if header[:8]!=b'\0\1talfed' or struct.unpack_from('<2I',header,8)!=(compressed,uncompressed):
        raise ValueError('Embedded HDR deflate header changed')
    data=zlib.decompress(pe.get_data(rva+16,compressed),-15)
    if len(data)!=uncompressed or hashlib.sha256(data).hexdigest()!=expected:
        raise ValueError('Embedded HDR program hash/size changed')
    target=out/role
    target.mkdir(exist_ok=True)
    rows=[]
    names=set()
    for match in re.finditer(b'DXBC',data):
        offset=match.start()
        if offset+32>len(data):continue
        size,chunks=struct.unpack_from('<2I',data,offset+24)
        if not 32<=size<=1024*1024 or not 1<=chunks<=16 or offset+size>len(data):continue
        label=re.search(rb'(?:vs|ps|cs)_[A-Za-z0-9_]+_Win64_SM50\0',data[offset+size:offset+size+256])
        if label is None:continue
        name=label.group()[:-1].decode('ascii')
        if name in names:raise ValueError('Ambiguous HDR shader name')
        names.add(name)
        blob=data[offset:offset+size]
        neutral=all(token=='0' for token in re.findall(r'(?<=_)\d+(?=_)',name))
        # Luminance programs and neutral tone-map permutations are sufficient
        # for checking the math; non-neutral variants remain in the index.
        selected=(neutral or role!='final-hdr' or 'ApplyToneMapping' not in name or
            name=='ps_ApplyToneMapping_FilmicU2_ManExposure_0_0_0_0_0_0_1_0_Win64_SM50')
        row=dict(name=name,offset=offset,bytes=size,sha256=hashlib.sha256(blob).hexdigest(),exported=selected)
        if selected:
            (target/(name+'.dxbc')).write_bytes(blob)
            (target/(name+'.txt')).write_text(assembly(blob),encoding='utf-8')
        rows.append(row)
    if not rows:raise ValueError('No original HDR shaders found')
    reflection=None
    if role=='final-hdr':
        effect=data.index(b'\x08\x02xfxd3f')
        objects=[]
        cursor=effect+8
        for _ in range(2):
            marker=data.index(b'\0\1voMrtP',cursor)
            length,relocations=struct.unpack_from('<2I',data,marker+8)
            objects.append((marker+16,data[marker+16:marker+16+length],relocations))
            cursor=marker+16+length+relocations*4
        first=objects[0][1]
        passes_pointer,pass_count=struct.unpack_from('<2Q',first,8)
        if (passes_pointer,pass_count)!=(0x388,9):raise ValueError('Original HDR pass table changed')
        passes=[]
        expected_passes=['LuminanceFilter','LuminanceDownsampleInitial','LuminanceDownsample',
            'ApplyToneMapping_LinearColor','ApplyToneMapping_LinearColorRGBM',
            'ApplyToneMapping_FilmicU2_ManExposure','ApplyToneMapping_FilmicU2_Log2LumExposure',
            'ApplyToneMapping_FilmicACES_ManExposure','ApplyToneMapping_FilmicACES_Log2LumExposure']
        for index,expected_name in enumerate(expected_passes):
            entry=passes_pointer+index*0x70
            pointer=struct.unpack_from('<Q',first,entry)[0]
            name=first[pointer:first.index(b'\0',pointer)].decode('ascii')
            if name!=expected_name:raise ValueError('Original HDR pass identity changed')
            option_pointer,option_count=struct.unpack_from('<2Q',first,entry+0x20)
            options=[]
            for option_index in range(option_count):
                option=option_pointer+option_index*16
                name_pointer=struct.unpack_from('<Q',first,option)[0]
                option_name=first[name_pointer:first.index(b'\0',name_pointer)].decode('ascii')
                options.append(dict(index=option_index,name=option_name,raw=first[option:option+16].hex()))
            passes.append(dict(index=index,name=name,recordOffset=entry,recordRaw=first[entry:entry+0x70].hex(),options=options))
        second=objects[1][1]
        variables_pointer,variables_count=struct.unpack_from('<2Q',second,0x48)
        if (variables_pointer,variables_count)!=(608,77):raise ValueError('Original HDR uniform table changed')
        variables=[]
        for index in range(variables_count):
            entry=variables_pointer+index*32
            pointer=struct.unpack_from('<Q',second,entry)[0]
            name=second[pointer:second.index(b'\0',pointer)].decode('ascii')
            packed=struct.unpack_from('<H',second,entry+10)[0]
            variables.append(dict(name=name,recordOffset=entry,raw=second[entry:entry+32].hex(),
                storageBank=packed&3,dataOffset=(packed>>1)&0x7ffc))
        lookup={x['name']:x['dataOffset'] for x in variables}
        # Independent agreement with the original neutral PS: HDR read at
        # cb2[2].x, shoulder at cb2[3].x, reciprocal gamma at cb2[9].z.
        if (lookup['c_hdrBufferScalingRead']-32,lookup['c_shoulderStrength']-48,
            lookup['c_colourAdjustInt_pivot_rcpGamma']+4-152)!=(80,80,80):
            raise ValueError('Original final-HDR GPU constant-offset cross-check failed')
        reflection=dict(effectOffset=effect,objects=[dict(offset=x[0],bytes=len(x[1]),relocations=x[2]) for x in objects],
            passes=passes,variables=variables,gpuCb2DataStart=80,
            neutralShaderAnchors={'c_hdrBufferScalingRead':'cb2[2].x','c_shoulderStrength':'cb2[3].x',
                'c_colourAdjustInt_pivot_rcpGamma.y':'cb2[9].z'})
    results.append(dict(role=role,descriptorRva=hex(descriptor),dataRva=hex(rva),
        compressedBytes=compressed,uncompressedBytes=uncompressed,sha256=expected,shaders=rows))
    if reflection is not None:results[-1]['reflection']=reflection

defaults=[]
for rva,field,bits in [(0x351FBC1,0x128,0x3EFB645A),(0x351FBD2,0x12C,0x3F34FDF4),
    (0x351FBDC,0x130,0x3F343958),(0x351FBF0,0x134,0x3F97AE14),(0x351FBFA,0x138,0x3FB04189),
    (0x351FC04,0x13C,0x3FFCED91),(0x351FBE6,0x140,0x40A7BE77),
    (0x351FA03,0x184,0x3F800000),(0x351FA0D,0x188,0x400CCCCD),
    (0x351FA17,0x18C,0x3F800000),(0x351FA21,0x190,0x3F800000)]:
    expected=b'\xc7\x86'+struct.pack('<2I',field,bits)
    raw=pe.get_data(rva,len(expected))
    if raw!=expected:raise ValueError('Original HDR constructor default changed')
    defaults.append(dict(rva=hex(rva),nodeField=hex(field),bits=hex(bits),value=struct.unpack('<f',struct.pack('<I',bits))[0],bytes=raw.hex()))
for rva,expected in [(0x351FA75,'48c786fc0000000000003f'),(0x351FA95,'48c7861c0100000000803f'),
    (0x351F956,'bd02000000'),(0x351FAA0,'89ae24010000')]:
    if pe.get_data(rva,len(bytes.fromhex(expected))).hex()!=expected:raise ValueError('Original HDR method/exposure defaults changed')
bindings=[]
lookup_source=out.parent/'solo-3521440.c'
if lookup_source.exists():
    lookup_text=lookup_source.read_text()
    for index,rva_text in enumerate(re.findall(r'UNK_145([0-9a-f]+)',lookup_text)):
        rva=int('5'+rva_text,16)
        name=pe.get_data(rva,128).split(b'\0')[0].decode('ascii')
        bindings.append(dict(shadowHandleField=hex(0x778+index*8),nameRva=hex(rva),name=name))
result=dict(image=str(image),imageSha256=image_hash,initializerRva='0x3521E90',
    shadowConstructorRva='0x351F450',ownerConstructorRva='0x351F8E0',programs=results,
    gameProcessAccess=False,gameCalls=0,shaderExecution=False,
    constructorDefaults=dict(method=2,exposureMode=0,selectedPass=5,exposureEv=0,fields=defaults),
    shadowUniformBindings=bindings,runtimePermutationKnown=False,finalOutputRestored=False)
(out/'original-hdr-programs.json').write_text(json.dumps(result,indent=2)+'\n',encoding='utf-8')
print(json.dumps({p['role']:dict(shaders=len(p['shaders']),exported=sum(x['exported'] for x in p['shaders'])) for p in results}))