---
# System prepended metadata

title: 'Занятие 3. Ассемблер. Структура разработки, инструкции, регистры.'
tags: [module11, revers, assembler, programming language]

---

    
###### 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

