Last active
April 3, 2024 09:37
-
-
Save dungsaga/ab8d2379bb566c9925b27df3bc82ca8b to your computer and use it in GitHub Desktop.
deobfuscate odttf file (extracted from M$ XPS file)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const purpose = "Purpose: deobfuscate odttf file (extracted from M$ XPS file)" | |
const usage = "Usage: node deobfuscate-odttf-in-xps.js <guid-obfuscated-font-file.odttf> [<output-file.ttf>]" | |
const obfuscatedStartOffset = 0 //start of obfuscated bytes in font file | |
const obfuscatedEndOffset = 32 //start of obfuscated bytes in font file | |
const guidSize = 32 // length of GUID string (only hex characters included) | |
const fs = require('fs') | |
const path = require('path') | |
//process.argv[0] is path to node.js | |
//process.argv[1] is path to this script | |
const fontFilepath = process.argv[2] | |
if (fontFilepath == null) { | |
console.warn(purpose) | |
console.warn(usage) | |
return | |
} | |
const fontFilename = path.basename(fontFilepath) | |
const guid = fontFilename.replace(/-/g, "").replace(/\..+$/, "") | |
if (guid.length !== guidSize) { | |
console.warn(guid, "Error: Cannot extract GUID from font filename (ex: A5C0272A-DD4C-401C-8661-BEAD77E57818.odttf)") | |
return | |
} | |
// get hex numbers from GUID | |
const hexStrings = guid.replace(/(..)/g,"$1 ").trim().split(" ") | |
const hexNumbers = hexStrings.map((hexString) => parseInt(hexString, 16)) | |
hexNumbers.reverse() | |
// deobfuscate by XORing obfuscated bytes with hexNumbers | |
const buf = fs.readFileSync(fontFilepath) | |
const obfuscatedBytes = buf.slice(obfuscatedStartOffset, obfuscatedEndOffset) | |
const recoveredBytes = obfuscatedBytes.map((byte, i) => byte ^ hexNumbers[i % hexNumbers.length] ) | |
// write result to a ttf file | |
const out = Buffer.concat([ | |
buf.slice(0, obfuscatedStartOffset), | |
recoveredBytes, | |
buf.slice(obfuscatedEndOffset) | |
]) | |
const outputFilepath = process.argv[3] || fontFilepath + '.ttf' | |
fs.writeFile( | |
outputFilepath, | |
out, | |
{ encoding: null }, | |
(error) => { if (error) throw error; } | |
) |
Thx!
BTW I've created a Python 3 version! Feel free to use!
#!/usr/bin/env python3
import os
import sys
fn_in = sys.argv[1]
fn_out = os.path.splitext(sys.argv[1])[0] + '.ttf'
print(fn_out)
# Parse
key = os.path.splitext(os.path.basename(fn_in))[0].replace('-', '')
# Convert to Int reversed
key_int = [int(key[i-2:i], 16) for i in range(32, 0, -2)]
with open(fn_in, 'rb') as fh_in, open(fn_out, 'wb') as fh_out:
cont = fh_in.read()
fh_out.write(bytes(b ^ key_int[i % len(key_int)] for i, b in enumerate(cont[:32])))
fh_out.write(cont[32:])
usage: deobfuscate.py U-U-I-D.odttf
returns: U-U-I-D.ttf
C# version adapted from http://blogs.microsoft.co.il/tamir/2008/04/17/converting-fixeddocument-xpsdocument-too-to-flowdocument/
public static void DeobfuscateXpsFont(string XpsFontFilename, string TtfOutputFilename)
{
using (FileStream stm = new FileStream(XpsFontFilename, FileMode.Open))
using (FileStream fs = new FileStream(TtfOutputFilename, FileMode.Create))
{
byte[] dta = new byte[stm.Length];
stm.Read(dta, 0, dta.Length);
//if (font.IsObfuscated)
{
string guid = new Guid(XpsFontFilename.Split('.')[0]).ToString("N");
byte[] guidBytes = new byte[16];
for (int i = 0; i < guidBytes.Length; i++)
{
guidBytes[i] = Convert.ToByte(guid.Substring(i * 2, 2), 16);
}
for (int i = 0; i < 32; i++)
{
int gi = guidBytes.Length - (i % guidBytes.Length) - 1;
dta[i] ^= guidBytes[gi];
}
}
fs.Write(dta, 0, dta.Length);
}
}
BTW I've created a Python 3 version! Feel free to use!
Thanks @dlazesz. The Python 3 version is much cleaner.
Thanks @dlazesz. for Python version.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
based on function
xps_deobfuscate_font_resource
in https://mupdf.com/docs/browse/source/xps/xps-glyphs.c.html