Програмирование на fasm под Win64 часть 3 «Окна WinDbg, метки, основные команды»

В данной статье я расскажу об окнах в WinDbg, и рассмотрю некоторые машинные инструкции.

Окна в WinDbg

Для начала в одном из окон “memory” в поле “Virtual” напишите “@rsp” (без кавычек), и там же в поле “Display format” поставьте значение “Pointer and Symbol”, это — окно стека, что это я расскажу позже. В общем у меня окно WinDbg выглядит как на рис.1.
В окне “disassembly” вы видите 3 колонки: адрес, машинный код и мнемоника. Адрес — это адрес начала инструкции. Обратите внимание: Адреса в WinDbg (как и в других отладчиках) записываются в шестнадцатеричной системе счисления. Далее идёт колонка в которой показан машинный код инструкции (тоже в шестнадцатеричной системе счисления). И последняя колонка — мнемоника, т.е. запись инструкции на языке ассемблера.

В окне “Registers” 2 колонки: левая — названия регистров, правая — их значения. Обратите внимание: значения регистров представлены либо как шестнадцатеричные целые, либо как десятичные числа с плавающей точкой (вещественные).

В окне “memory”, где я сказал указать в поле “Virtual” значение “@rsp”, есть 2 колонки: левая — адрес, правая — значение типа qword по данному адресу.

В оставшемся окне “memory” — 3 колонки: адрес, значения байтов (шестнадцатеричные) по данному адресу и символы ANSI, соответствующие данным значениям байтов (ANSI — как раз однобайтовая кодировка). Можете попереключать поле “display format” и посмотреть, разные форматы отображения данных. Поле “Virtual” показывает, с какого адреса начать отображение памяти, адрес может включать и регистры, числа в этом поле считаются шестнадцатеричными.

Метки

Надо было сказать в предыдущих статьях: точка с запятой — это знак комментария, всё что есть в строке после неё ассемблер проигнорирует, многострочных комментариев нет.

Метка — это символьное имя (идентификатор) позволяющий обращаться к данной области программы.
Чтобы поставить метку в коде нужно написать её имя и поставить двоеточие, например:

proc start

	nop
        label1:;<--Метка
        nop
	ret
endp

В прошлой статье я показывал, как можно объявлять переменные, например: Variable dd ? Так вот, имя переменной — тоже метка, оно отличается от метки поставленной с помощью двоеточия тем, что оно предполагает, что данные, на которые она указывает будут интерпретированы как данные соответствующего размера. Так же имя метки может начинаться с точки, такая метка называется локальной. При ассемблировании к имени локальной метки добавляется слева имя ближайшей сверху нелокальной метки, например:

proc start
;код 1
Lbl:
;код 2
.locLbl:
;код 3
ret
endp

В данном примере полное имя метки .locLbl — “Lbl.locLbl”, любая ссылка из кода 2 и 3 на метку по имени “.locLbl”, на самом деле ссылается на “Lbl.locLbl”. Если же попытаться сослаться на эту метку из кода 1 по имени “.locLbl”, fasm будет искать метку с полным именем “start.locLbl”, не найдёт её и сообщит об ошибке. Чтобы сослаться на метку “.locLbl” из кода 1 нужно писать полное имя метки: “Lbl.locLbl”.

Основные команды

Теперь можно приступать к программированию. В прошлой статье я говорил об инструкции int3, используйте её чтобы остановить отладчик в том месте, где вам нужно. Общий вид программы будет примерно такой:

format PE64 console ;формат - PE 64 бита файла, подсистема
entry start ;точка входа (место, с которого начинается выполнение кода программы)

include 'win64w.inc';заголовки

section '.code' readable executable code;секция кода

proc start ;начало процедуры

	int3
	;<--Здесь будет располагаться ваш код.
	ret
endp ;конец процедуры

section '.data' readable writeable data; секция данных
dd ?;данные

Первая инструкция, которую я бы хотел обсудить называется mov. У этой инструкции два явных операнда (операнд — это то, что подаётся на вход инструкции, явный операнд — это операнд, который вы указываете в коде). В качестве операндов у этой инструкции может выступать константа, память или регистр. Записывается как mov <op1>,<op2>, эта команда копирует данные из <op2> в <op1>,сразу замечу, что <op1> не может быть константой (не только для “mov”) и вообще большинство (не все) инструкций с двумя явными операндами кладут результат в первый операнд, рассмотрим пример:
mov rax,0x32167
данная инструкция записывает число 0×32167 в регистр rax, посмотрите в отладчике, как при исполнении этой инструкции значение в регистре rax меняется на 0×32167. Можно вместо rax поставить любой регистр, главное, чтобы количество двоичных разрядов константы справа было меньше либо равно количеству разрядов в регистре, например конструкция mov al,0x1FF приведёт к ошибке, т.к. в числе 0×1FF больше восьми бит. Стоит также обратить внимание на то, что запись в регистр размера одного байта или слова не влияет на остальную часть регистра, но если вы пишете в регистр размером в 32 бита, то верхняя часть соответствующего 64-битного регистра обнуляется. Поэкспериментируйте, а потом двигайтесь дальше.

Так же у инструкции mov оба операнда могут быть регистрами, в этом случае значение копируется из одного регистра в другой, количество двоичных разрядов в обоих регистрах должно совпадать. Ну, и наконец одним из операндов этой инструкции может быть память, это надо рассмотреть поподробнее.

Следующие правила записи адреса верны для всех инструкций, которые принимают память в качестве одного из операндов.
Когда ссылаются на память, адрес заключают в квадратные скобки: []. Адрес может выглядеть следующим образом:

    1. [reg1+reg2*2 n +offs32]
    2. [rip+offs32]
    3. [offs64]

 

В первом случае reg1 и reg2 — регистры общего назначения, n может принимать значения от 0 до 3, offs32 — 32-битное число со знаком. Во втором случае offs32 — тоже 32-битное со знаком, прибавляется к регистру rip, замечу, что если вы сошлётесь на глобальную переменную в своём коде по имени, то fasm закодирует адрес именно таким способом. Третий случай есть не для всех инструкций и подразумевает ссылку на память по непосредственному адресу, так можно закодировать например инструкции:
mov al,[mem]
mov ax,[mem]
mov eax,[mem]
mov rax,[mem]
mov [mem],al
mov [mem],ax
mov [mem],eax
mov [mem],rax
Правда почему-то fasm отказывается ассемблировать эти инструкции с 3-м типом адреса, в документации они есть. И последнее на счёт адреса, у каждой инструкции может быть не более одного явного операнда, ссылающегося на память.
Рассмотрим простой пример:

format PE64 console
entry start

include 'win64a.inc'

section '.code' readable executable code

proc start

	int3
	mov eax,[Variable2];Запись значения Variable2 в регистр eax
	mov [Variable1],eax;запись значения регистра eax в переменную Variable1

	ret
endp

section '.data' readable writeable data
Variable1 dd ?
Variable2 dd 210h

В этом примере значение Variable2 копируется в Variable1. Чтобы посмотреть в отладчике значения Variable1 и Variable2 скопируйте адрес из инструкции mov в дизассемблере в поле “Virtual” окна “memory” и поставьте “display format” в “long hex”.
Далее я бы хотел рассмотреть пять инструкций общего назначения, которые так же имеют 2 явных операнда, все они записывают результат в <op1>:
add <op1>,<op2> — складывает <op1> и <op2>, если результат не вмещается в <op1>, тогда старшие биты отбрасываются
and <op1>,<op2> — применяет к каждому биту операцию логическое “и”
or <op1>,<op2> — применяет к каждому биту операцию логическое “или”
sub <op1>,<op2> — вычетает из <op1> <op2>, отрицательный результат записывается в дополнительном коде
xor <op1>,<op2> — применяет к каждому биту логическое “исключающее или”
Кроме них так же полезны инструкции:
xchg <op1>,<op2> — меняет местами значения <op1> и <op2>
lea reg,[memory] — считает адрес [memory] и записывает его значение в регистр reg, обратите внимание, что данная команда не выполняет реального обращения к памяти, а просто считает адрес
not <op1> — применяет логическое “не” к <op1>
Ну, и последняя инструкция о которой я расскажу в этой статье — это nop <op1> операндом этой инструкции может быть либо регистр, либо память, она ничего не делает и в случае если операнд — память обращения к данной памяти не происходит.
Попробуйте использовать эти инструкции и посмотрите в отладчике, к каким результатам приводит их исполнение.
На этом пока всё.,

Программы для развития.