Въведение

Декомпилаторът е софтуерен инструмент, който е предназначен за обратно инженерство на компилирани компютърни програми или двоични файлове обратно в четим от човека език за програмиране като C или Java. Декомпилаторите се използват за анализиране и разбиране на функционалността на дадено приложение и често се използват от програмисти за идентифициране и коригиране на грешки или за възстановяване на изгубен изходен код.

С прости думи, когато програмист пише код на програмен език от високо ниво като Java, кодът се компилира в машинен език или двоичен файл, който се изпълнява от компютъра. Декомпилаторът се използва за обръщане на процеса на компилиране чрез разграждане на двоичния код в четим формат, който може да бъде анализиран и разбран от хората.

Декомпилацията не е точна наука и полученият код често не е идентичен с оригиналния изходен код. Резултатът от декомпилатора зависи от редица фактори като сложността на оригиналния код, качеството на декомпилатора и степента на оптимизация, извършена по време на компилирането.

При някои обстоятелства декомпилаторът може да бъде полезен за следните цели:

Възстановяване на изгубен изходен код с цел запазване или архивиране на кода:

Процедурата за декомпилиране се опитва да възстанови изходния код, ако той е изгубен или повреден. Ако програмист загуби оригиналния изходен код за приложение, може да се използва декомпилатор за възстановяване на значителна част от кода, което може да бъде много полезно при повторното изграждане на приложението.

Софтуер за отстраняване на грешки:

Те могат да се използват за анализиране на поведението на приложение и идентифициране на потенциални уязвимости в сигурността. Те могат да се използват за обратно проектиране на злонамерен код, за да се разбере по-добре как работи и да се разработят контрамерки за защита срещу него. Също така, капацитетът за лесно преместване на програма между платформи и за анализиране на приложения на трети страни, за да разберете как работят заедно с техните плъгини или разширения. Това е особено полезно при разработването на софтуер за мобилни устройства, където често има ограничен достъп до основната операционна система.

Декомпилиране на архитектура

Процесът на декомпилиране включва обръщане на процеса на компилиране чрез анализиране на компилирания код и реконструиране на оригиналния изходен код. Това се прави чрез интерпретиране на двоичния код и идентифициране на моделите и структурите, които са характерни за оригиналния код.

Точното функциониране на декомпилатора зависи от езика и платформата на изпълнимия код, но обикновено включва следните стъпки:

Първо, за да се декомпилира програма, предпоставката е нейното обратно инженерство. Състои се от внимателно разглеждане на кода на машинно ниво на програмата за интерпретиране на формата, потока и целта. Обратното инженерство може да се извърши с помощта на специализирани инструменти или ръчно от самия инженер.

Разглобяване:

Декомпилаторът чете изпълнимия код и създава разглобяване, което е представяне на ниско ниво на инструкциите в кода. Разглобяването показва двоичния код в по-четим за хората формат, което улеснява анализирането от декомпилатора.

Анализ на контролния поток:

Декомпилаторът анализира разглобяването, за да определи контролния поток на програмата, като например кои функции се извикват и в какъв ред.

Анализ на потока от данни:

Декомпилаторът анализира потока от данни на програмата, като например кои променливи се използват и как се използват.

Реконструкция:

Използвайки информацията, получена от разглобяването, анализа на контролния поток и анализа на потока от данни, декомпилаторът се опитва да реконструира оригиналния изходен код. Този процес може да бъде усложнен от фактори като оптимизации на компилатора и техники за обфускация, които са предназначени да направят кода по-труден за декомпилиране.

Нека разгледаме прост пример за функция на машинен код, която добавя две числа:

55 push ebp
8B EC mov ebp, esp
8B 45 08 mov eax, [ebp+8]
03 45 0C add eax, [ebp+12]
5D pop ebp
C3 ret

Тази функция на машинния код приема два аргумента, събира ги заедно и връща резултата. Сега нека използваме декомпилатор, за да проектираме този код обратно в код на езика C.

Използвайки декомпилатор, получаваме следния C код:

int add(int a, int b) {
return a + b;
}

Както можете да видите, декомпилаторът успешно е проектирал обратно машинния код обратно в код на език C, който изпълнява същата операция като оригиналния код. Полученият C код не е идентичен с оригиналния изходен код, но е функционално еквивалентен.

Този пример илюстрира как може да се използва декомпилатор за възстановяване на изгубен изходен код или за анализиране на поведението на двоична програма. Важно е обаче да се отбележи, че качеството на получения C код зависи от редица фактори, включително сложността на оригиналния код, качеството на декомпилатора и количеството оптимизация, извършена по време на компилирането. Следователно е важно да използвате декомпилатори с повишено внимание и да проверите получения код, преди да го използвате в производствена среда.

Ето стъпка по стъпка разбивка на това как машинният код функционира обратно към кода на езика C:

1. Започнете с изследване на машинния код и идентифициране на използваните инструкции. В този случай имаме следните инструкции:

55 push ebp
8B EC mov ebp, esp
8B 45 08 mov eax, [ebp+8]
03 45 0C add eax, [ebp+12]
5D pop ebp
C3 ret

2. Преведете инструкциите на техния еквивалентен мнемоничен език на асемблер. В този случай асемблиращият код е:

push ebp
mov ebp, esp
mov eax, [ebp+8]
add eax, [ebp+12]
pop ebp
ret

3. Преобразувайте асемблерния код в езиков код C. Полученият C код трябва да изпълнява същата операция като функцията на оригиналния машинен код. В този случай полученият C код е:

int add(int a, int b) {
int result;
result = a + b;
return result;
}

4. Опростете получения C код, като премахнете ненужните променливи или изрази. В този случай полученият код вече е доста прост, така че няма допълнителни опростявания, които могат да бъдат направени.

int add(int a, int b) {
return a + b;
}

5. Проверете получения C код, като го тествате спрямо оригиналната функция на машинния код. В този случай полученият C код е правилен и изпълнява същата операция като функцията на оригиналния машинен код.

В примера, който използвахме, функцията на машинния код използваше три регистъра: `ebp`, `esp` и `eax`.

- `ebp` означава „разширен базов указател“ и се използва като основен адрес за достъп до параметри и локални променливи във функция. В примера с кода `mov ebp, esp` задава стойността на регистъра `ebp` на текущата стойност на указателя на стека `esp`, което създава нов базов указател, който сочи към текущата рамка на стека.

- `esp` означава „указател на разширен стек“ и се използва за проследяване на текущото местоположение в стека. В примера с код `push ebp` и `pop ebp` променят стойността на `esp`, като натискат и поставят стойността на основния указател `ebp` в стека.

- `eax` е регистър с общо предназначение, който често се използва за съхраняване на върнатата стойност на функция. В примера с код, `mov eax, [ebp+8]` премества първия аргумент на функцията (който се съхранява в `[ebp+8]`) в регистъра `eax`, а `add eax, [ebp+12] ` добавя втория аргумент на функцията (който се съхранява в `[ebp+12]`) към стойността в регистъра `eax`.

Като цяло тези регистри се използват за управление на паметта и извършване на аритметични операции във функцията.

Dex2Jar

Някои от инструментите за декомпилиране са dex2jar. Декс-четецът е проектиран да чете формата на Dalvik Executable (.dex/.odex). Той преобразува .dex в класове под формата на jar файлове. Ето как да използвате dex2jar:

Стъпка 1: Инсталирайте Java Development Kit (JDK) Уверете се, че имате Java Development Kit (JDK) инсталиран на вашия компютър. Можете да изтеглите и инсталирате JDK от официалния уебсайт на Oracle.

Стъпка 2: Изтеглете Dex2jar Изтеглете най-новата версия на dex2jar от хранилището на GitHub или други надеждни източници.

Стъпка 3: Разархивирайте Dex2jar Разархивирайте изтегления архив dex2jar в папка по ваш избор.

Стъпка 4: Преобразувайте APK в JAR Отворете команден ред или терминален прозорец и отидете до папката, в която сте извлекли dex2jar.

Изпълнете следната команда, за да конвертирате APK в JAR файл в Windows:

d2j-dex2jar.bat <path-to-apk-file>

Заменете <path-to-apk-file> с действителния път до APK файла, който искате да конвертирате.

Стъпка 5: Получаване на JAR файл След изпълнение на горната команда, dex2jar ще генерира JAR файл в същата папка, където се намира APK файлът.

Стъпка 6: Извличане на JAR файл Вече можете да използвате всеки Java декомпилатор, като JD-GUI или Fernflower, за да декомпилирате генерирания JAR файл и да получите изходния код.

Свършен!

В заключение, декомпилаторът е мощен инструмент, който се използва за анализиране и разбиране на компилирани компютърни програми. Използва се от програмисти за идентифициране и коригиране на грешки, възстановяване на изгубен изходен код и анализ на приложения на трети страни. Докато декомпилацията не е точна наука, тя е важен инструмент в процеса на разработка на софтуер и е съществена част от инструментариума за всеки сериозен програмист.

Автори: Yash Shejwal, Virat Tiwari, Rewa Wader, Aditya Warghane