уторак, 2. јул 2013.

Reverzni inženjering

 
                               
 

Reverzni inženjering u programiranju je najjednostavnije rečeno rekonstrukcija koda ako imamo samo izvršnu (.exe) verziju. Zašto je to potrebno?

Većina koda se isporučuje upravo u izvršnim verzijama, da bi se zaštitila autorska prava s jedne, i da bi se program zaštitio od zlonamjernih izmjena koda sa druge strane.
Međutim, napadači se služe metodama reverznog inženjeringa da bi rekonstruisali izvorni kod, a zatim u njega ubacili maliciozne komponente.

Da bi držali korak sa njima, odnosno predupredili njihove akcije, inženjeri u AV industriji bave se analiziranjem reverznog inženjeringa i načinima kako u kod ubaciti ometajuće elemente, koji će onemogućiti proces deasembliranja (code obfuscation).

Proces reverznog inženjeringa počinje sa deasembliranjem, što je proces prevođenja mašinskog koda ( u kome je izvršni fajl) u asembler (program višeg nivoa nego mašinski, koji se obraća procesoru, ali daleko nižeg nivoa od programskih jezika).

Svaki procesor ima svoj asembler, s obzirom na arhitekturu. Prevođenje mašinskog koda u asembler je jedan-na-jedan mapiranje, ali je asembler mnogo "čitljiviji" , odnosno lakši za razumijevanje.
Sretala sam ljude kojima je i mašinski jezik bio čitljiv, i koji su programirali direktno u "mašincu", ali to pripada ranijim periodima razvoja računarstva, i teško da danas neko razvija te vještine. Asemblerski jezik takođe zahtijeva ne samo poznavanje jezika, već i dobro poznavanje arhitekture računara, budući da se direktno obraća procesoru, memorijskoj lokaciji, registru.

Deasembliranje se vrši instrukcija po instrukcija, grana po grana (ovo je sad jako pojednostavljeno rečeno, ali to je suština). Važno je da se tačno razumije ponašanje programa bez njegovog izvršavanja. Ovo je zapravo dosta težak posao.
Ali da zakomplikujem još malo. Deasembliranje može biti statičko i dinamičko.
Statičko obrađuje čitav izvršni fajl odjednom, kao cjelinu, ali bez njegovog izvršavanja.
Dinamičko podrazumijeva izvršavanje dijelova programa sa zadanim ulazima, i rekonstrukcija na osnovu ponašanja. Ovaj vid deasembliranja više se koristio kod pronalaženja i otklanjanja grešaka ("debugging"), ali razvoj malicioznog softvera posljednjih godina nadilazi domete statičkih analiza, pa dinamičke postaju sve važnije. O tome u posebnoj temi.

Dva su glavna algoritma korištena za statičko deasembliranje: "Linear sweep" i " Recursive traversal".
Prvi se radi linearno, instrukcija po instrukcija, podrazumijevajući da kad se jedna instrukcija završi, naredna počinje. Slabost ovog algoritma je što sve interpretira kao kod, nije u mogućnosti da uoči problematičnu sekvencu. Rekurzivna metoda je tačnija, zasniva se na kontroli toka i prepoznavanju grananja i skokova u programu, pri čemu se svaka grana zasebno analizira.

Upravo ta grananja i skokovi se koriste pri formiranju izvršnog koda koji će biti nemoguće ( ili vrlo teško) deasemblirati. Tipična tehnika za takvo "zamajavanje koda" je "Branch functions" , koristi se upravo da "zbuni" proces ispitivanja toka programa i uzrokuje greške u deasembliranju.

Da budem potpuno iskrena, ovo je samo malo zagrebana površina ozbiljne, opširne i raznolike materije. Nisam smjela da se upustim u priču o arhitekturi procesora, asemblerskim instrukcijama ni funkcijama grananja, jer je to svako za sebe posebna velika oblast.

Preporuka za dalje čitanje:

1. Obfuscation of Executable Code to Improve Resistance to Static Disassembly
Cullen Linn,  Saumya Debray,Department of Computer ScienceUniversity of Arizona - link

2. N-version Disassembly: Differential Testing of x86 Disassemblers
Roberto Paleari, Lorenzo Martignoni‡ Giampaolo Fresi Roglia, Danilo Bruschi - link




Нема коментара:

Постави коментар