Sann skräckupplevelse för mjukvaruutveckling
För många, många år sedan arbetade jag med Windows-skrivbordsappar för C♯ och C++. Appen var tillgänglig via publika gränssnitt, som vi internt kallade Terminaler. Terminaler var väldigt lika bankomater. Varje terminal var inget annat än en dator förpackad i en glänsande rustning och pekskärm.
En dag bestämde sig företaget för att använda smartkort för att spåra användare. Jag var tvungen att skriva en ny smartkortsfunktion för appen. Vi var inga nybörjare; vi hade en del erfarenhet. Dagligen arbetade vi med USB, RS232, RFID, NFC, ccTalk, SAS, AT, ICT, specialtillverkade kretskort och alla andra typer av hårdvaruenheter. Att arbeta med en smartkortsfunktion såg ut som en uppgift som man tilldelar sig själv när man vill ta en paus från de riktiga uppgifterna .
För att utföra uppgiften fick vi en billig, namnlös plug-n-play-kortläsare, några tomma PVC-smartkort och ett SDK. Det fanns ingen manual, instruktionsguide eller uppsättning instruktioner som berättade var vi skulle börja, vad vi kunde förvänta oss, som varnade oss för eventuella faror (!), som berättade vilken typ av smartkort som krävs (det finns massor av olika korttyper på marknaden), som gav någon användbar information – ingenting.
Kommunikationen med leverantören var dålig, praktiskt taget värdelös. Av okänd anledning insisterade företaget på att vi skulle fortsätta med just den leverantören.
Snart insåg vi att SDK inte var det riktiga SDK:et. SDK:et innehöll bara två filer. En fil var körbar. (.exe)-filen, och det var en demoapp med ett enda syfte – att visa oss att enheten och kortet fungerar som förväntat.
Den andra filen var en dynamisk länkbiblioteksfil (.dll). Vi antog att magin utspelade sig inuti .dll-filen, och att vi skulle importera och använda .dll-filen i vår app.
Även om det fanns så många frågor verkade det som att Demo-appen och kortläsaren kunde kommunicera. Med verktyg som dumpbin och Dependency Walker , vi listade ut definitionerna av exporterade .dll-funktioner. Namnen på funktionerna var nästan identiska med etiketterna på knapparna i demoappen. Det var ett gott tecken – vi kunde associera .dll-funktionen med en specifik del av demoappen.
När vi försökte importera .dll till vår källkod fungerade ingenting som förväntat. Vi kunde faktiskt inte upprätta någon kommunikation mellan vår kod och .dll. Oavsett vilken metod vi provade, och vi provade många av dem, blev resultatet alltid dåligt. Vi försökte till och med dekompilera både Demo App och .dll i hopp om att kunna återanvända den dekompilerade koden, men båda filerna blev obfuserade , så den dekompilerade versionen var inte särskilt användbar.
Av en slump upptäckte vi att korten var Siemens SLE4442 EEPROM-kort. SLE4442 har en säkerhetsmekanism (PSC-verifiering), och felaktig användning av kortet kan blockera kortet för vidare användning. Senare upptäckte vi att vi hade blockerat vissa kort.
Vi upptäckte också att demoappen inte fungerar om Smart Cards for Windows Service (SCWS) är aktiverat. Detta faktum var en stor överraskning. SCWS är kärndelen av operativsystemet. Det hanterar all interaktion mellan smartkortsenheter och operativsystemet. Bara med sunt förnuft skulle man förvänta sig att Smart Card Service måste vara aktiverat när man interagerar med en smartkortläsare. Hur som helst drog vi slutsatsen att detta ologiska faktum innebär att vår kortläsare förmodligen inte körs med PC/SC Workgroup-specifikationerna.
Dagarna gick. Frustrationen var som störst. Vi var nästan besegrade, utan idéer om hur vi skulle gå vidare. Det enda som fick oss att kämpa var att Demo-appen kunde interagera med kortläsaren.
Till slut försökte vi avlyssna kommunikationen mellan Demo-appen och .dll-filen. Tanken var att spela in data som skickades från Demo-appen och sedan skicka samma data från vår källkod.
Tekniken för avlyssning kallas API-hooking . Konceptet är jämförbart med man-in-the-middle-attackkonceptet . API-hooking används ofta av antivirusprogram, spelfuskare, skadlig programvara etc. Det är nära släkt med dll-kapning. teknik.
Avlyssningen skapade ännu fler frågetecken ovanför oss. Det fanns ingen kommunikation mellan demoappen och .dll-filen. Magin pågick inuti demoappen. DLL-filen var meningslös. Hela tiden försökte vi göra det omöjliga.
Till slut upptäckte vi att Demo-appen kommunicerade med några av operativsystemets kärnfiler (det var länge sedan, jag minns inte exakt). Även om det var en lång och mödosam process, spelade vi in och rekonstruerade alla datapaket som skickades från Demo-appen. Vi lade in dessa rekonstruerade meddelanden i vår källkod och allt fungerade bra.
Det tog några veckor bara att etablera kommunikation mellan vår källkod och kortläsaren, men så småningom hamnade funktionen i produktionsmiljön. Många, många år senare körs den fortfarande på terminaler över hela världen.
Två–tre år senare skapade företaget en ny terminal, även kallad Terminal 2. Terminal 2 använder SLE5542-kort istället för SLE4442. En liten mjukvaruuppgradering krävdes för att köra SLE5542-kort. Vi fick samma paket som tidigare: en kortläsare, tomma PVC-kort och SDK (Demo App + dll). Versionen av .dll-filen ökades från 1.0 till 1.1 – det var inte samma fil som tidigare.
Av ren nyfikenhet försökte jag importera en ny .dll till källkoden och testa det. Det tog mig mindre än 3 minuter att förbereda allt. Den nya .dll-filen fungerade perfekt.
Detta är ett exempel på HUR MAN INTE SKA ANTA SIG UNDER UPPGIFTEN.
Även om den här resan var unik, och även om vissa hårdföra tillvägagångssätt användes, visar den hur dålig kommunikation, låg budget och personliga intressen kan och kommer att leda till alltför komplicerade lösningar, enormt slöseri med tid och resurser, oerhörda frustrationer och i de flesta fall dåliga resultat.
Istället för att konstruera kod använde vi oss av reverse engineering . Det finns användningsfall där reverse engineering är tillämpligt. Men om du behöver lägga till en enkel ny funktion i den befintliga kodbasen är det i de flesta fall inte rätt plats för reverse engineering.