###### tags: `module11` `revers` `programming language` `assembler` [ToC] # Занятие 3. Ассемблер. Структура разработки, инструкции, регистры. ## Видеозапись урока {%youtube zng8LSTzwDE %} ### timecodes 00:00:00 Ждем 00:02:25 что было и что будет 00:09:10 Создание исполняемого файла 00:15:40 Граф потока управления 00:19:55 Регистры процессора x86 00:36:10 Флаги процессора 00:41:25 Регистры процессора x64 00:44:00 Перерыв 00:50:15 Операции с данными. 01:00:55 Обращение к памяти 01:07:17 Логические инструкции 01:10:00 Сдвиги бит 01:13:20 Инструкции контроля управления 01:25:18 Инструкции работы со строками 01:30:26 Общие инструкции 01:36:00 Перерыв 01:47:50 Различие синтаксисов Intel / AT&T 01:50:00 Стек 01:51:53 Соглашения о вызовах 02:09:00 NASM 02:12:30 Получение информации о процессе 02:15:40 Практика. Смотрим код crackme. 02:24:40 Пробуем разобраться с проблемами 02:32:30 Дебажим crackme 02:36:45 Немного о следующем занятии 02:39:30 Смотрим код import_by_hash.cpp 02:02:30 Дебажим import_by_hash 02:15:40 Заключение ## Концепт обратной разработки ## Создание исполняемого файла ![](https://i.imgur.com/wx85cwR.png) импорт - экспорт на этапе линкования лоадер выделяет все необходимые ресурсы и загружают библиотеки и передает управление на точку входа приложения задача из исходных кодов понять всю лгику работы приложения ### Граф потока управления + Порядок инструкций, которые будут исполнены + Поток исполняет инструкции последовательно, пока не встретит инструкцию ветвления + Блоки кода – логически сгруппированные инструкции ![](https://i.imgur.com/3scVI0h.png "пример графа потока управления") ## Примитивы ассемблера большинство малвари создано под архитектуру x86, так как гарантированно будет работать и на 64-bit ### Регистры процессора x86 имена регистров это аббревиатура и у них есть расшифровка. + Общего назначения + EAX - сложение и умножение **(может вычитание)**;в винде часто может содержать результута возврата функции. ?особое внимание? + EBX - позволяет раотать с масивами, структурах, перечеслениях. выступает как индекс + ECX - счетчик для ?"циклов"? + EDX - деление и **умножение опять** остаток от деления + + работает со стеком. + EBP - разделяет локальные переменные и аргументы функции (локальные переменный <-EBP+> аргументы функции) + ESP - указывает вершина стека и постоянно меняется + работа с памятью и строками, например для сравнения данных EDI с ESI; заполнения нулями данных положенных в EDI + ESI Source + EDI Distination + Специальные + EIP - указывает на выполяемую инструкцию, может быть изменен для принудительного переключения на нужную иструкцию + EFLAGS + Сегментные: + CS - адрес на сегмент кода + DS - адресс на сегмент данных + ES + FS - используется в винде, содержит много стуктур процесса + GS + SS - стэк EAX, EBX, ECX, EDX могут быть представлены как dword, word и byte На примере EAX • EAX – dword • AX – младший word в EAX • AH – старший byte в AX • AL – младший byte в AX EBP, ESP, ESI, EDI могут быть только dword и word На примере EBP • EBP – dword • BP – младший word в EBP ![](https://i.imgur.com/mqBkNDr.png) #### EFLAGS • CF(care flag) – флаг переноса • PF(parity адфп) – флаг чётности если крайний бит равен 1, то число нечетное • ZF(zero flag) – флаг нуля • SF(signed flag) – флаг знака если старший бит равен 1 то число отрицательное • DF(direction) – флаг направления, указывает направление обработки, например ESI и EDI • OF(overflow) – флаг переполнения в чем разница между CF & OF? ### Регистры процессора x64 • Общего назначения - теже самые как и в x86 но уже в 64Bit + RAX, RBX, RCX, RDX, RBP, RSP, RSI, RDI - бьются на EAX,AX т.е до 16bit + R8-R15 (R8D, R8W, R8B) - деляться до 8bit • Специальные • RIP • RFLAGS паремтры функци передаются церез регистры процессора, а не через стек, а то что не влезло уже через стек. эти достигется ускорение, так как не надо обращаться к памяти. ### Арифметические операции • ADD / SUB модем работать как с регистрами так и с памятью • (I)MUL / (I)DIV умножение и деление • INC / DEC инкремент/декримент. например, для циклов • NEG получение двоичного числа, кторое в суммме с текущим даст нам 0 • CMP сравнение двух значений. результат которой меняет регистры, кторые потом могут быть провереныю ### Перемещения данных • MOV - помещает значение в память • CMOVcc - заполняет масив какимито данными. сс - тип байт, ворд • XCHG - поменяет ячейки местами • MOVSc/MOVZx - используя esi/edi происходит заполнение данными где x/c -B W DW • PUSH / POP поместить в стек/ забрать из стека • PUSHA / PUSHAD поместить в стек все значения текущих регистров/ • POPA / POPAD забрать из стека все значения текущих регистров/ • PUSHF / PUSHFD поместить в стек все значения текущих флагов • POPF / POPFD забрать из стека все значения текущих флагов ### Обращение к памяти Задание числом: `mov eax, 0x3418` Задание регистром: `mov eax, ebx` Задание переменной: `mov eax, dword_412420` Задание с помощью сегменты: `mov eax, [DS:41242]` если мы поместим адрес [] , то мы прочитаем знаечение по этому адресу. Например: [EAX] – доступ к выделенной памяти [EBP + 0x20] – доступ к переменной на стеке [EAX + EBX * 8] – массив с 8-байтными структурами [EAX + EBX + 0xC] – доступ к полю в структуры в массиве структур ### Логические инструкции • AND • OR • XOR часто используемая для шифрования инструкция. легко обратимо обратно • NOT ![](https://inf1.info/images/and_or_xor_not.png) https://inf1.info/bitwise-operations ### Сдвиги бит • SHL / SHR арифметический сдвиг влево/вправо (* и / на 2) новые биты заполняются нулями • SAL / SAR арифметический сдвиг влево/вправо (* и / на 2) новые биты заполняются нулями • ROL / ROR циклический сдвиг влево/вправо биты не теряются а переходят по кругу в переменной. тоже часот может быть применнено для запутывания, так как легко обратима обратна. ### Инструкции контроля управления • JMP безусловный переход по указанному адресу • TEST похоже на cmd, только выполянет логическое сравнеие AND. так же как cmd предворяет иструкции переходя • Jcc переход по указанному адресу в зависимости от значений флага(cc) сс=z/s/.... • LOOP - встречать будем редко, простой цикл. счетчик - ECX • CALL - вызов процедуры(передача управления по указанному адресу) • RET - завершение функции, прочитать адрес возврата со стека и вернуться в место откуда были вызваны. • INT - вызывает обработчик прерываний, INT3 или 0xCC в 16ном виде - будем встречать часто. Вызывает передачу отладчику. Анлог brakepoint'a. злоумыщленники могут следатть за этим же прерыванием, что бы определить что их зловред отлаживают или наоборот испольщзовать, что бы запутать аналитика. В прилождениях отладки этот брэйкпоинт могут заменять на кастомный. ### Инструкции работы со строками c=b/w/dw • MOVSc копирует данные из ESI в EDI • CMPSc сравнивает данные согласно указанного счетчика и направления в памяти • SCASc сравнивает **?регистры?** • STOSc сохраняет в память содержимое в EAX. для быстрой инициализации строк и др данных. опять же учитывается направление • LODSc считывает из памяти в регистр. обраное STOSc • REP повтор иструкции заданное в регистре раз ### Общие инстукции • LEA - почти как mov, но с некоторыми отличиями. mov больше для работы с регистрами, lea больше для работы с памятью • NOP - пустая операция • UD2 - неопределенная операция. тоже позволяет выполянть точку остановки • CPUID - позволяет идетентифицировать текущий процессор. ## Синтаксис ### Intel / AT&T ```a Intel AT&T mov eax,[ecx] movl (%ecx),%eax mov ax,[bx+3] movw 3(%bx),%ax mov eax,[ebx+20h] movl 0x20(%ebx),%eax add eax,[ebx+ecx*2h] addl (%ebx,%ecx,0x2),%eax lea eax,[ebx+ecx] leal (%ebx,%ecx),%eax sub eax,[ebx+ecx*4h-20h] subl -0x20(%ebx,%ecx,0x4),%eax jmp large fword ptr ds:[666h] ljmp *0x666 pop ax popw %ax ``` ### Стек ![](https://i.imgur.com/LNGPxm9.png) ## Соглашения о вызовах ### Пролог функции ![](https://i.imgur.com/saZDdG0.png) cdecl ![](https://i.imgur.com/pYX5ZlX.png) после вызова фунции необходимо вернуть стек в исходное состояние stdcall / winapi ![](https://i.imgur.com/tEmQVq5.png) функция сама выполняет очистку стека fastcall ![](https://i.imgur.com/W8ygTK9.png) для ускорения вызова, некотрые параметры помещаютмся в регисрт, а не в стек thiscall при вызове \этой функции в ECX кладется адрес вызываемого класса. ## NASM ![](https://i.imgur.com/Bt0vfdi.png) ![](https://i.imgur.com/bbxdfS6.png) ## Получение информации о процессе TIB/TEB/PEB TIB (FS) – блок информации о текущем рабочем потоке, существует для каждого процесса • FS:[0x00] - Structured Exception Handling - используют злоумышленики • FS:[0x18] - Адрес TEB • FS:[0x30] - Адрес PEB, можно нати все что замапленно процессом в память. тоже очень любят злоумышленники ## Практика 2:37:20 злоумышленник всегда будет использовать строки, но скорее всего будет их шифровать ловим функции loadlibrary getprocaddress . ищмем все строки котрые ссылються в эти функции и дальше возможно определяем наши функции и востанавливаем их. ### import_by_hash.cpp использует PEB только под x86 ```c= #define STDCALL __stdcall #define VK_ESCAPE 27 typedef unsigned long DWORD; #include "stdio.h" #include "string.h" // Define some function pointers for 1,2, ...5 args typedef DWORD(STDCALL* funcPtr0)(void); typedef DWORD(STDCALL* funcPtr1)(DWORD); typedef DWORD(STDCALL* funcPtr2)(DWORD, DWORD); typedef DWORD(STDCALL* funcPtr3)(DWORD, DWORD, DWORD); typedef DWORD(STDCALL* funcPtr4)(DWORD, DWORD, DWORD, DWORD); typedef DWORD(STDCALL* funcPtr5)(DWORD, DWORD, DWORD, DWORD, DWORD); // Here the function name hashes go unsigned int hashTable[] = { 0xE9826FC6, // LoadLibraryA 0x38A66AE8, // ExitProcess 0xDE59F860, // GetAsyncKeyState // add your hashes here 0 // zero terminate (cheaper than storing size) }; // add your functions here in the order the hashtable has here #define LoadLibraryA ((funcPtr1)functionTable[0]) #define ExitProcess (functionTable[1]) #define GetAsyncKeyState ((funcPtr1)functionTable[2]) void ResolveHashes(unsigned long module, funcPtr0* functionTable) { int i = (unsigned char)((i ^ i) - 1); long tmp = *(long*)(*(long*)(module + 0x3C) + module + 0x78) + module; int numImp = *(long*)(tmp + 0x18); // number of imports while (--numImp >= 0) { // resolve import #numImp unsigned char* importName = (unsigned char*)(*(long*)(*(long*)(tmp + 0x20) + module + numImp * 4) + module); // now hash the import name and check it against a stored hash value // code taken from [http://www.20to4.net/dat/importbyhash_v3.cpp](http://www.20to4.net/dat/importbyhash_v3.cpp "http://www.20to4.net/dat/importbyhash v3.cpp") // so dont blame me for this asm passage, use your own hashing if you want to :) // I however, was too lazy to use another hashfunc and recalc all my hashes... unsigned int hash; __asm { pushad mov esi, importName xor ebx, ebx lodsb calcloop : xor bl, al rol ebx, 6 lodsb test al, al jnz calcloop mov hash, ebx popad } //if (!strcmp((char const*)importName, "CreateFileA")) //{ // printf("CreateFileA 'hash'"); //} // now lookup hash int j = (unsigned char)((j ^ j)); while (hashTable[j]) { if (hash == hashTable[j]) { // found the hash now resolve function entry #define IMPORT_INDEX *(unsigned short*) (*(long*)((tmp + 0x24)) + module + numImp * 2) functionTable[j] = (funcPtr0)(*(long*)(*(long*)(tmp + 0x1C) + module + 4 * IMPORT_INDEX) + module); } j++; } } } // this is important! dont let the compiler optimize stackframes // for the main because we _WANT_ ebp addressing (smaller!!!) #pragma optimize("y",off) // need stdcall here to generate appropriate stackframe int main() { // use locally (ebp indexed) function table funcPtr0 functionTable[30]; // here your actual functions are stored // note that there is a maximum of about 30 // if you use more only around 30 get translated // to small calls, while you pay alot to the rest! unsigned int module; // temporarily needed for calculating kernel32 // please forgive me this, but its really absolutly necessary // if we want to detect kernel32 via PEB // other methods like topstack could might hacked via plain c code // however those methods arent as small and not guaranted to run! // topstack especially does not run on XP64! __asm { mov eax, DWORD ptr fs : [30h] mov module, eax } // to check out why this works, search for "using PEB for retrieving kernel32" module = *(long*)(*(long*)(*(long*)(*(long*)(module + 0x0C) + 0x1C)) + 0x8); // resolve everything from kernel32 ResolveHashes(module, functionTable); // and now since we have LoadLibraryA everything from other dlls... ResolveHashes(LoadLibraryA((DWORD)"user32"), functionTable); // ... load opengl32 etc ... // your initcode goes here do { // your rendercode goes here } while (!GetAsyncKeyState(VK_ESCAPE)); return 0; } ``` --- http://i-assembler.ru/25/Text/Command.htm https://faydoc.tripod.com/cpu/stosb.htm