Efter “Programmerare vår världs magiker?” har ett par personer frågat mig hur man går till väga när man obfuscerar kod (heter det obfuscera på svenska eller har vi ett bättre ord?). Jag kommer inte att gå igenom exemplet i posten, dels för att om det förklaras försvinner magin, del för att det är ganska komplicerat.
Anledningen till att man obfuscerar är att koden skall bli svår att läsa och förstå. Tanken är att man skall kunna dölja vad det är programmet gör och därigenom förhindra att någon skriver ett program som gör samma sak. Det brukas kallas security by obscurity och är ungefär lika säker som att kryptera en text genom att ändra typsnittet till dingbat eller wingdings, det finns alltid någon alldeles för smart person som kommer att lura ut vad det är som händer. Det finns program som kan obfuscera koden åt dig, men det är att fuska
Så därför har jag gjort ett lite exempel, det är ett ganska trivialt exempel1
, med det duger för att man i alla fall få en liten inblick i hur man kan göra och vilka tekniker som kan användas. Allt som programmet gör är att skriva ut alla jämna tal mellan 1 och 10.
int main (void)
{
int i;
for (i = 1;i < 11; i++){
if (i%2==0)
printf ("%i\n",i);
}
return 0;
}
Inga direkt konstigheter, en for loop som itererar över i och skriver ut i om det är jämt delbart med 2 dvs om resten från moduloräkningen (% operatorn) är 0. Där har vi hela vår program nu skall vi börja obfuscera. (Fast det får vänta lite för nu ringer frugan och säger att jag skall laga mat)
Så, vi börjar med iterationen. Som någon2 har sagt:
“To iterate is human … but to recurse is divine.”
Alltså alla iterationer kan ersättas med rekursion (tror även att det omvända gäller, men jag är inte helt säker). Rekursioner är oftast lite klurigare att räkna ut vad de gör än iterationer. Efter att vi gjort om iterationen kan det se ut något så här.
void recursiveFunction(int talet)
{
if (talet < 11){
if (i%2==0)
printf ("%i\n",talet);
talet++;
recursiveFunction(talet);
}
}
int main (void)
{
int i=1;
recursiveFunction(i);
return 0;
}
recursiveFunction tar talet kollar om det är mindre än 11 (alltså inte större än 10), ifall det är det kollar den om det är ett jämt tal på samma sätt som i första versionen och skriver ut det om det är jämt. Sen ökas värdet på talet och recursiveFunction anropas med det nya värdet på talet.
Nästa grej vi gör är att byta ut if-satsen genom att använda ?-operatorn. ?-operatorn fungerar så att den utvärderar villkor och returnerar resultatet av en av två satser beroende på om villkoret är sant eller falskt
(vilkoret) ? "returneras om det är sant" : "returneras om det är falskt";
result = (vilkor<0)? -1 : 1; //om variabeln vilkor är mindre än noll kommer ?-operatorn att retunera -1 vilket tilldelas variabeln result, om det är större returneras 1 vilket då tilldelas result
Efter som ?-operatorn till skillnad från en if-sats är avsedd att returnera ett värde måste det minst finnas ett värde att returnera om villkoret evalueras till falskt. I vår if-sats finns ingen else efter som om villkoret är sant så gör något annars gör inget alls (man kunde haft en tom else del men det är onödigt). Därför måste vi vända på våra villkor i recursiveFunction så att om testet inte är sant skrivs något ut på skärmen och talet ökas med 1 om det inte är större än 10.
void recursiveFunction(int talet)
{
(talet%2!=0) ? : printf ("%i\n",talet);
(talet > 10) ? : recursiveFunction(++talet) ;
}
int main (void)
{
int i=1;
recursiveFunction(i);
return 0;
}
I nästa steg flyttar vi in den rekursiva funktionen till main och anropar man rekursivt istället.
main(x)
{
(x%2!=0) ? : printf ("%i\n",x);
(x > 10) ? : main(++x) ;
}
Samtidigt tar vi bort deklarationen att variabeln i skall vara ett heltal (int) och byter namn på variabeln till x (återkommer till varför just x senare), vi tar även bort deklarationen till main. Vad vi gör är en implicit deklaration av variabeln och funktionen dvs vi låter kompilatorn försöka lura ut vilken datatyp det är vi vill använda. Retur värdet på main tas också bort, det är inte nödvändigt men allt för att förvirra så mycket som möjligt. Detta är naturligtvis inget man bör göra och kan ställa till med en hel del problem.
Något man också kan göra är att ändra på kodning, tex om man skall skriva ut text kan man istället använda heltasvärderna på tecknen eller man kan skapa en egen teckentabell i en array och sen hålla på att hoppa runt i den med hjälp av pekare. Pekare är förövrigt något jag inte gått igenom här men jag misstänker att man kan producera mycket svåröverskådlig kod med hjälp av pekare (funktionspekare gör ju till exempel alltid att det blir klurigt). En annan sak är att ändra på variabel och funktionsnamn så att det blir mer svårläst.
main(x)
{
(x%(0x9c/0x4e)!=0x0)?:printf("%i\n",x);
(x >0xa)?:main(++x);
}
Nu har vi inte så många variabel och funktionsnamn men om vi tex hade haft en funktion som räknade ut volymen på en låda så skulle den kunna ta emot längd, bredd och höjd.
volume(length, width, width)
{
return length*width*width;
}
Om man då bytte ut namnen mot understreck ( _ ) så volym blir ett understreck, length blir två understreck, width tre och height fyra. Då skulle den se ut såhär istället
_(__,___,____)
{
return __*___*____;
}
Inte helt lätt att tolka vad som händer. Men i vårat lilla exempel har vi bara ett variabelnamn och det kallar vi för x detta är för att vi har skrivit om våra heltal till hexadecimala tal vi gör också lite onödig matematik istllet för att skriva %2 vilket i hex skulle skrivits %0×2 så skriver vi 156/78 vilket är 2 och i hex skrivs 0x9c/0x4e och efter som hexadecimala tal tals skrivs med ett x i så byter vi namn på variabeln för att få lite mer x. Man kunde kanske kunnat bytt till något ännu klurigare.
Det sista vi skall göra är att ta bort all mellanslag, tabbar och radbrytningar sk whitespace för i C och många andra språk spelar de ingen roll förutom att öka läsbarheten (det finns undantag tex python).
main(x){(x%(0x9c/0x4e)!=0x0)?:printf("%i\n",x);(x>0xa)?:main(++x);}
Så några små tips på hur man kan använda sig av för att obfuscera sin kod. Nu är jag ingen expert på att obfuscera och om någon av er skulle vara det så tveka inte med att rätta eller komma med förslag på hur man kan förbättra.
1 Kan man inte programera över huvud taget är det naturligtvis inte trivialt. Men har man någonsin programmerat borde det vara försåtligt.
2 Den som vet vem som sa det får en guldstjärna, för jag har ingen aning.