import * as e from './common.mjs'; const { fs, path, fail, sha256, matches, escapeRE, atomic_json } =e; export const align=(v, b)=>Math.ceil(v/b)*b; export function build(client, dump, classes, output){ const text=e.readText(dump), methods=new Map(); for(const name of classes){ const m=text.match(new RegExp('// Full Name: [^\\n]*\\.'+escapeRE(name)+'\\n(.*?)(?=\\n// TypeDefIndex:|$)', 's')); if(!m)fail('Missing native metadata class '+name); for(const line of m[1].split('\n')){ const mm=line.match(/\b([\w.<>]+)\([^\n]*RVA: (0x[\da-fA-F]+)/); if(!mm)continue; const rva=Number(mm[2]); if(rva&&!methods.has(rva))methods.set(rva, (name+'_'+mm[1]).replace(/[^A-Za-z0-9_]/g, '_')); } } if(!methods.size)fail('No methods selected'); const starts=[...methods.keys()].sort((a, b)=>a-b), all=[...new Set(matches(text, /RVA: (0x[\da-fA-F]+)/g).map(m=>Number(m[1])).filter(Boolean))].sort((a, b)=>a-b), ends=new Map(starts.map(r=>[r, all.find(v=>v>r)??r+0x1000])), windows=[]; for(const rva of starts){ if(windows.length&&rva{ const a=ss+i*40; return { vs:blob.readUInt32LE(a+8), rva:blob.readUInt32LE(a+12), size:blob.readUInt32LE(a+16), raw:blob.readUInt32LE(a+20) }; }), segments=windows.map(([start, end])=>{ const s=sections.find(s=>s.rva<=start&&end<=s.rva+Math.min(s.vs, s.size)); if(!s)fail('Code window exceeds original file-backed section'); const offset=s.raw+start-s.rva; return { rva:start, source_offset:offset, bytes:blob.subarray(offset, offset+end-start) }; }), base=blob.readBigUInt64LE(pe+48), nt=Buffer.from(blob.subarray(pe, pe+24+opt)), exportRva=align(Math.max(...segments.map(s=>s.rva+s.bytes.length)), 0x1000), count=starts.length, order=Array.from({ length:count }, (_, i)=>i).sort((a, b)=>{ const x=methods.get(starts[a])+'_'+starts[a].toString(16), y=methods.get(starts[b])+'_'+starts[b].toString(16); return xy?1:0; }); let exports=Buffer.alloc(40+count*10); const names=[]; for(const i of order){ names.push(exports.length); exports=Buffer.concat([exports, Buffer.from(methods.get(starts[i])+'_'+starts[i].toString(16)+'\0')]); } const dllName=exports.length; exports=Buffer.concat([exports, Buffer.from('cw-native-analysis-only.dll\0')]); for(const [at, v]of [[0, 0], [4, 0], [12, exportRva+dllName], [16, 1], [20, count], [24, count], [28, exportRva+40], [32, exportRva+40+count*4], [36, exportRva+40+count*8]])exports.writeUInt32LE(v, at); for(let i=0; i({ name:'cw'+i, rva:s.rva, data:s.bytes, flags:0x60000020 })); dest.push({ name:'.edata', rva:exportRva, data:exports, flags:0x40000040 }); const headerSize=align(0x80+nt.length+dest.length*40, 0x200); nt.writeUInt16LE(dest.length, 6); nt.writeUInt16LE(nt.readUInt16LE(22)&~2, 22); for(const [at, v]of [[40, 0], [44, segments[0].rva], [56, 0x1000], [60, 0x200], [80, align(exportRva+exports.length, 0x1000)], [84, headerSize], [88, 0]])nt.writeUInt32LE(v, at); nt.fill(0, 136, 264); nt.writeUInt32LE(exportRva, 136); nt.writeUInt32LE(exports.length, 140); let carrier=Buffer.alloc(headerSize); carrier.write('MZ'); carrier.writeUInt32LE(0x80, 0x3c); nt.copy(carrier, 0x80); for(let i=0; i'0x'+v.toString(16), manifest={ source_sha256:sha256(blob), dump_sha256:e.sha256_file(dump), carrier_sha256:sha256(carrier), image_base:hex(base), classes, methods:e.dict(starts.map(r=>[hex(base+BigInt(r)), methods.get(r)])), method_ends:e.dict(starts.map(r=>[hex(base+BigInt(r)), hex(base+BigInt(ends.get(r)))])), ranges:segments.map(s=>({ rva:hex(s.rva), source_offset:s.source_offset, size:s.bytes.length, sha256:sha256(s.bytes) })), limitations:['Derived analysis carrier; never execute it.', 'External functions, original data and dynamic runtime state are absent.', 'Exports are analysis annotations derived from the matching metadata dump.', 'Method ends are metadata boundaries; compiler cold tails may live elsewhere.', 'Runtime hotfix overrides are not established by static analysis.'] }; const ext=path.extname(output); atomic_json(ext?output.slice(0, -ext.length)+'.manifest.json':output+'.manifest.json', manifest, 2, true); console.log(`${methods.size} methods, ${segments.length} exact code windows, ${carrier.length} bytes`); return manifest; } export function main(){ const a=e.cli(['client', 'dump', 'class', 'output'], { }, [], ['class']); build(a.client, a.dump, a.class, a.output); } e.runMain(import.meta.url, main);