55 lines
3.0 KiB
Python
55 lines
3.0 KiB
Python
"""Read PE imports/exports to explain the mod_php startup failure, without loading DLLs."""
|
|
from pathlib import Path
|
|
import struct, hashlib, json
|
|
ROOT=Path(__file__).resolve().parent.parent
|
|
PRIVATE=ROOT/'storage/app/private/investmoz-v2/portal-audit'
|
|
APACHE=Path('C:/laragon/bin/apache/httpd-2.4.54-win64-VS16/bin')
|
|
PHP=Path('C:/laragon/bin/php/php-8.5.11-Win32-vs17-x64')
|
|
class PE:
|
|
def __init__(self,file):
|
|
self.b=Path(file).read_bytes();header=struct.unpack_from('<I',self.b,0x3c)[0]
|
|
assert self.b[header:header+4]==b'PE\0\0'
|
|
n=struct.unpack_from('<H',self.b,header+6)[0];size=struct.unpack_from('<H',self.b,header+20)[0];opt=header+24
|
|
self.wide=struct.unpack_from('<H',self.b,opt)[0]==0x20b
|
|
self.dd=opt+(112 if self.wide else 96)
|
|
self.sections=[]
|
|
for i in range(n):
|
|
p=opt+size+40*i;vsize,va,rawsize,raw=struct.unpack_from('<IIII',self.b,p+8)
|
|
self.sections.append((va,max(vsize,rawsize),raw))
|
|
def off(self,rva):
|
|
for va,size,raw in self.sections:
|
|
if va<=rva<va+size:return raw+rva-va
|
|
return rva
|
|
def string(self,rva):
|
|
p=self.off(rva);return self.b[p:self.b.index(b'\0',p)].decode('ascii')
|
|
def imports(self):
|
|
rva=struct.unpack_from('<I',self.b,self.dd+8)[0];p=self.off(rva);result={}
|
|
while rva:
|
|
original,_,_,name,thunk=struct.unpack_from('<IIIII',self.b,p)
|
|
if not name:break
|
|
tp=self.off(original or thunk);names=[];width=8 if self.wide else 4
|
|
while True:
|
|
value=struct.unpack_from('<Q' if self.wide else '<I',self.b,tp)[0];tp+=width
|
|
if not value:break
|
|
if value & (1<<(width*8-1)):continue
|
|
names.append(self.string(value+2))
|
|
result[self.string(name).lower()]=names;p+=20
|
|
return result
|
|
def exports(self):
|
|
rva=struct.unpack_from('<I',self.b,self.dd)[0]
|
|
if not rva:return set()
|
|
p=self.off(rva);n=struct.unpack_from('<I',self.b,p+24)[0];names=struct.unpack_from('<I',self.b,p+32)[0]
|
|
return {self.string(struct.unpack_from('<I',self.b,self.off(names)+4*i)[0]) for i in range(n)}
|
|
imports=PE(PHP/'ext/php_curl.dll').imports();result=[]
|
|
for dll,names in imports.items():
|
|
old=APACHE/dll;expected=PHP/dll
|
|
if old.is_file() and expected.is_file():
|
|
missing=set(names)-PE(old).exports();missing_expected=set(names)-PE(expected).exports()
|
|
result.append({'dependency':dll,'apache_path':str(old),'php_path':str(expected),
|
|
'apache_sha256':hashlib.sha256(old.read_bytes()).hexdigest(),
|
|
'php_sha256':hashlib.sha256(expected.read_bytes()).hexdigest(),
|
|
'missing_required_exports_in_apache':sorted(missing),'missing_in_php':sorted(missing_expected)})
|
|
proof={'result':'diagnosed','dependencies':result,'active_files_changed':False}
|
|
(PRIVATE/'portal-stability-dll-diagnosis.json').write_text(json.dumps(proof,indent=2),encoding='utf-8')
|
|
print(json.dumps(proof))
|