Een binair bestand van Microsoft waarvan je de specificatie niet hebt. En nu dan?

Ingediend door Dirk Hornstra op 06-feb-2023 19:30

Ik ben met veel zaken bezig waarbij het lukt om iets werkend op te leveren. En die verhalen deel ik natuurlijk graag. Maar soms, heel soms, heb je een probleem waar je tijd in steekt en op een bepaald moment moet beslissen wat je gaat doen. Je hebt er al flink wat tijd in gestopt, als je nu gaat stoppen, dan is dat eigenlijk "zonde van je tijd". Maar als je nu al het idee hebt dat je er nu nog wel 3 dagen mee aan de slag kunt gaan, maar het dan waarschijnlijk nog niet lukt, dan kun je beter nu halverwege stoppen. En er een artikel op je eigen blog over schrijven. Want misschien is er iemand die dit leest die er wel een oplossing voor heeft. Of iemand die het idee had om er mee aan de slag te gaan, mijn bloed-zweet-en-tranen verhaal hier leest en denkt, laat ik die tijd maar besparen, ik begin er niet aan :)

In 2009 heeft mijn oud studiegenoot (en oud-medewerker van TRES) Dirk Lemstra een .NET applicatie gemaakt die dagelijks de handtekening in Outlook van de medewerkers bijwerkt met data die in Active Directory staat. Dus als daar een mobiel nummer wordt ingevuld: de volgende dag staat het onderaan je e-mail. Wijzigt je titel omdat je ander werk gaat doen, het wordt mooi centraal bijgewerkt. En op Windows-systemen werkt dat makkelijk, je download een zip-bestandje, pakt die dingen uit naar [user]/AppData/Roaming/Microsoft/Signatures en gaan met die banaan! Je zet daar een tekstbestandje neer voor de tekstversie, een .htm-bestand voor de HTML-versie en nog wat plaatjes die je kunt koppelen.

Nu zijn er natuurlijk ook collega's die op een Mac werken. En ook daar heb je een Outlook-applicatie voor. Dus eerst online zoeken op welke locatie de bestanden opgeslagen worden. Ik kon het eerst niet vinden, maar misschien komt dat doordat ik nog geen handtekening had aangemaakt en dat de map er niet was. In ieder geval, een lege handtekening aangemaakt en toen kon ik deze wel vinden. Op je Mac ga je in de terminal naar ~/Library/Group Containers/UBF...Office/Outlook/Outlook 15 Profiles/Main Profile/Data/Signatures. En elke handtekening die je maakt komt in een mapje in deze map te staan, de naam van die map is een getal. Niet sequentieel, ik had een map met de naam 7, de volgende had naam 131 en die daarna 155.

Dat UBF...Office pad is niet een "fixed" pad, maar om dat te bepalen, daar had ik wel uit kunnen komen. Gelukkig heb ik dat "probleem" niet als eerste aangepakt, maar ben ik in zo'n map gaan kijken, dus in map 7. Want ik had gehoopt om daar een tekstbestand, een HTML-bestand te zien. Maar nee: hier staat 1 bestand, de bestandsnaam is een GUID (dus d9029b6f-5452-4604-8c4f-4417c7e71aac) en de extensie was .olk15Signature

Die extensie, dat is iets wat Microsoft bedacht heeft. Het is een binair bestand, maar als ik deze open in een teksteditor kan ik wel een deel van de HTML in de content terug zien. Maar er zitten "binaire karakters voor" en "binaire karakters na". De vraag is nu, hoe is dat opgebouwd? Openbare bestandstypes zoals GZIP hebben een RFC specificatie waar je mee aan de slag kunt gaan: link. Maar dat heb je hier dus niet, natuurlijk even via Google gezocht (nooit meteen zelf starten als iemand anders al een uitwerking online gezet heeft), maar helaas: hier zie ik het niet terug.

Wat doe je dan? Dan probeer je om de bytes ervoor en de bytes erna in stand te houden en zelf de inhoud te vervangen. Hier de code die ik hiervoor gemaakt heb:


const string startMatch = "<html";
const string endMatch = "</html>";
var startBytes = new List<byte>();
var endBytes = new List<byte>();
var atStart = true;
var atEnd = false;
var originalFile = File.ReadBinary("bestand.olk15Signature");
var replacementHtml = File.ReadBinary("inhoudmail.htm");
var outputFile = "nieuw_bestand.olk15Signature";

// eerst de vervangende HTML opmaken, elk karakter moet een extra \0 krijgen en er zitten wat extra bytes voor het echte begin
var contentReplaceStart = false;
var replacementBytes = new List<byte>();
foreach(var b in originalFile)
{
  char c = (char)b;
  if (c == '<')
  {
      contentReplaceStart = true;
  }
  if ((contentReplaceStart == false) || (c == '\0'))
  {
      continue;
  }
  replacementBytes.Add(b);
  replacementBytes.Add(0);
}

// nu door alle bytes heen en start en einde bewaren
var tempString = "";
foreach(var b in originalFile)
{
    char c = (char)b;
    if (b == '\0')
    {
        if (atStart)
        {
            startBytes.Add(b);
        }
        else if (atEnd)
        {
            endBytes.Add(b);
        }
        continue;
    }
    if (atStart)
    {
        startBytes.Add(b);
        if (string.IsNullOrEmpty(tempString) && c == startMatch[0])
        {
              tempString += c;
              continue;
        }
        if (string.IsNullOrEmpty(tempString))
        {
              continue;
        }
        if (startMatch == tempString)
        {
              atStart = false;
        }
        if (!startMatch.StartsWith(tempString))
        {
             tempString = "";
              continue;
        }
    }
    else if (!atStart && !atEnd)
    {
        if (string.IsNullOrEmpty(tempString) && c == endMatch[0])
        {
            tempString += c;
            continue;
        }
        if (string.IsNullOrEmpty(tempString))
        {
            continue;
        }
        if (endMatch == tempString)
        {
            atEnd = true;
        }
        if (!endMatch.StartsWith(tempString))
        {
            tempString = "";
            continue;
        }
    }
    if (atEnd)
    {
        endBytes.Add(b);
    }
   tempString += c;
}
var combinedOutput = new List<byte>();
combinedOutput.AddRange(startBytes.GetRange(0,startBytes.Count() - (startMatch.Length * 2) - 1 ));
combinedOutput.AddRange(replacementBytes.ToArray());
combinedOutput.AddRange(endBytes);
File.WriteAllBytes(outputFile, combinedOutput.ToArray());
 

Je ziet dat ik wat met een \0 karakter doe. Elk teken wordt gevolgd door een 0-byte. Om te matchen om <html> en </html> moet ik die er even tussenuit halen, omdat ik anders zou moeten matchen op <\0h\0t\0\m\0l\0

Het is even pielen om de output er goed uit te krijgen, maar uiteindelijk lijk ik een resultaat te krijgen waarvan ik denk: dit ziet er goed uit. Ik vervang dus het bestand op de Mac, start Outlook, nieuw bericht en ga daar naar de Handtekeningen-kiezer: geen enkele handtekening.

Mijn aangepaste handtekening wordt niet getoond en nog erger: de handtekening die er was is nu dus weg.

Nog een paar testen gedaan, maar het lukt niet, dus ik werp die handdoek in de ring. Tenminste, het vervangen van dit bestand gaat niet werken. Ik weet dat je in Visual Studio ook zaken voor Office kunt bouwen. Bij de Visual Studio Installer kun je ook Office/Sharepoint development aanvinken. Maar goed, ook daar heb ik nog geen ervaring mee, dus dat is misschien iets voor de toekomst.

Ik wil nog wel even terugkomen op "waarom het niet lukt". Het komt omdat je niet weet hoe het intern in de applicatie werkt. Ik zag dat de beginbytes en eindbytes niet altijd gelijk zijn. Het is dus mogelijk dat Microsoft een soort ingebouwde CRC-check heeft, er wordt een checksum berekend op basis van de inhoud en dat wordt in het bestand toegevoegd. Als je dan de inhoud verandert, klopt die checksum niet meer en zegt Microsoft: deze accepteren we niet! Want is het dan alleen een getal met aantal bytes, of zit er standaard aan het type bestand .olk15Signature een bepaalde hash die gebruikt wordt om dat te versleutelen? Of zit het helemaal niet in het bestand zelf, maar heeft Outlook een interne SQL-lite database waarin de gegevens van het bestand ook worden bijgehouden en als het aantal bytes niet meer klopt: deze niet meer accepteren!

Ik snap nu wel dat een Mac qua security beter scoort dan Windows, want een virus op jouw pc kan niet even op een makkelijke manier "even" wat content in je handtekening plaatsen wat je vervolgens ongemerkt naar je relaties doormailt. Aan de andere kant is het wel frustrerend dat je zoiets als een "simpele handtekening" niet op een gescripte manier bij kunt werken. Mocht je een manier hebben om het wel voor elkaar te krijgen, dan hoor ik het graag. En mocht ik later met Visual Studio een soort plugin bouwen en het daar wel mee werken, dan laat ik het hier natuurlijk ook nog even weten!