• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Типы данных ARM и регистры.

neopaket

Переводчик
Пользователь
Регистрация
14.05.2019
Сообщения
185
Реакции
205
Типы данных ARM и регистры.

Это вторая часть учебного пособия по основам ARM, охватывающая типы данных и регистры.

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

data-types-1.png.pagespeed.ce.fDcGOe6Jz-.png


Типы данных, которые мы можем загрузить (или сохранить), могут быть словами со знаком и без знака, полусловами или байтами. Расширения для этих типов данных: -h или -sh для полуслов, -b или -sb для байтов и без расширения для слов. Разница между подписанным и неподписанным типами данных:

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

Вот несколько примеров того, как эти типы данных могут использоваться с инструкциями Load и Store:

Код:
ldr = Load Word
ldrh = Load unsigned Half Word
ldrsh = Load signed Half Word
ldrb = Load unsigned Byte
ldrsb = Load signed Bytes

str = Store Word
strh = Store unsigned Half Word
strsh = Store signed Half Word
strb = Store unsigned Byte
strsb = Store signed Byte

Порядок байтов.

Существует два основных способа просмотра байтов в памяти: Little-Endian (LE) или Big-Endian (BE). Разница заключается в порядке байтов, в котором каждый байт объекта хранится в памяти. В первом варианте значительный байт хранится в наименьшем адресе (адрес ближайший к нулю). Во втором значительные байты хранятся на самом низком адресе. До версии 3 архитектура ARM была с прямым порядком байтов, с тех пор она является двоичным, что означает, что она имеет параметр, который допускает переключаемую последовательность. Например, в ARMv6 инструкции являются фиксированными с прямым порядком байтов, и доступ к данным может быть либо с порядком байтов Little-Endian, либо с прямым порядком байтов, как управляется битом 9, битом E регистра состояния программы (CPSR).

big-little-endian-1.png.pagespeed.ce.MrerzS_XjS.png



Регистры ARM.


Количество регистров зависит от версии ARM. Согласно Справочному руководству ARM, существует 30 32-разрядных регистров общего назначения , за исключением процессоров на базе ARMv6-M и ARMv7-M. Первые 16 регистров доступны в режиме пользовательского уровня, дополнительные регистры доступны при выполнении привилегированного программного обеспечения (за исключением ARMv6-M и ARMv7-M). В этой серии руководств мы будем работать с регистрами, доступными в привилегированном режиме: r0-15. Эти 16 регистров можно разделить на две группы: регистры общего и специального назначения.

#
Alias
Цель
R0-Общее назначение
R1-Общее назначение
R2-Общее назначение
R3-Общее назначение
R4-Общее назначение
R5-Общее назначение
R6-Общее назначение
R7-Удержание системного вызова
R8-Общее назначение
R9-Общее назначение
R10-Общее назначение
R11FPУказание кадра
Регистры специального назначения
R12IPВнутрепроцедурный вызов
R13SPУказатель стека
R14LRРегистр ссылок
R15PCСчетчик команд
CPSR-Регистр текущего состояния программы


В следующей таблице приведен краткий обзор того, как регистры ARM могут взаимодействовать с регистрами в процессорах Intel.

ARM
Описание
x86
R0Общее назначениеEAX
R1-R5,Общее назначениеEBX, ECX, EDX, ESI, EDI
R6-R10Общее назначение-
R11 (FP)Указатель кадровEBP
R12Внутрипроцедурный вызов-
R13 (SP)Указатель стекаESP
R14 (LR)Регистр ссылок-
R15 (PC)<- Счетчик программ / Указатель команд ->EIP
CPSRCодержит информацию о состоянии процессора и управляющую информациюEFLAGS

R0-R12 : может использоваться во время обычных операций для хранения временных значений, указателей (местоположений в памяти) и т. Д. R0, например, может упоминаться как аккумулятор во время арифметических операций или для сохранения результата ранее вызванной функции. R7 становится полезным при работе с системными вызовами, поскольку он хранит номер системного вызова, а R11 помогает нам отслеживать границы в стеке, служащем указателем кадра (будет рассмотрено позже). Кроме того, соглашение о вызове функции в ARM указывает, что первые четыре аргумента функции хранятся в регистрах r0-r3.

R13: SP (указатель стека). Указатель стека указывает на вершину стека. Стек - это область памяти, используемая для хранения функции, которая восстанавливается при возврате функции. Поэтому указатель стека используется для выделения места в стеке путем вычитания значения (в байтах), которое мы хотим выделить из указателя стека. Другими словами, если мы хотим выделить 32-битное значение, мы вычитаем 4 из указателя стека.

R14: LR (Link Link). При выполнении вызова функции регистр связи обновляется с помощью адреса памяти, ссылающегося на следующую инструкцию, из которой была инициирована функция. Это позволяет программе вернуться к функции «parent», которая инициировала вызов функции «child» после завершения функции «child».

R15: ПК (программный счетчик). Счетчик программ автоматически увеличивается на размер выполняемой команды. Этот размер всегда составляет 4 байта в состоянии ARM и 2 байта в режиме THUMB. Когда выполняется инструкция перехода, ПК содержит адрес назначения. Во время выполнения ПК сохраняет адрес текущей инструкции плюс 8 (две инструкции ARM) в состоянии ARM, а текущую инструкцию плюс 4 (две инструкции Thumb) в состоянии большого пальца (v1). Это отличается от x86, где ПК всегда указывает на следующую инструкцию, которая будет выполнена.

Давайте посмотрим, как ПК ведет себя в отладчике. Мы используем следующую программу, чтобы сохранить адрес pc в r0 и включить две случайные инструкции. Давай посмотрим что происходит.

Код:
.section .text
.global _start

_start:
mov r0, pc
mov r1, #2
add r2, r1, r1
bkpt

В GDB мы устанавливаем Breakpoint на _start и запускаем ее:

Код:
gef> br _start
Breakpoint 1 at 0x8054
gef> run

А вот и выведенный код:

Код:
$r0 0x00000000   $r1 0x00000000   $r2 0x00000000   $r3 0x00000000 
$r4 0x00000000   $r5 0x00000000   $r6 0x00000000   $r7 0x00000000 
$r8 0x00000000   $r9 0x00000000   $r10 0x00000000  $r11 0x00000000 
$r12 0x00000000  $sp 0xbefff7e0   $lr 0x00000000   $pc 0x00008054 
$cpsr 0x00000010 

0x8054 <_start> mov r0, pc     <- $pc
0x8058 <_start+4> mov r0, #2
0x805c <_start+8> add r1, r0, r0
0x8060 <_start+12> bkpt 0x0000
0x8064 andeq r1, r0, r1, asr #10
0x8068 cmnvs r5, r0, lsl #2
0x806c tsteq r0, r2, ror #18
0x8070 andeq r0, r0, r11
0x8074 tsteq r8, r6, lsl #6

Мы видим, что ПК содержит адрес (0x8054) следующей инструкции (mov r0, pc), которая будет выполнена. Теперь давайте выполним следующую инструкцию, после которой R0 должен содержать адрес ПК (0x8054), верно?

Код:
$r0 0x0000805c   $r1 0x00000000   $r2 0x00000000   $r3 0x00000000 
$r4 0x00000000   $r5 0x00000000   $r6 0x00000000   $r7 0x00000000 
$r8 0x00000000   $r9 0x00000000   $r10 0x00000000  $r11 0x00000000 
$r12 0x00000000  $sp 0xbefff7e0   $lr 0x00000000   $pc 0x00008058 
$cpsr 0x00000010

0x8058 <_start+4> mov r0, #2       <- $pc
0x805c <_start+8> add r1, r0, r0
0x8060 <_start+12> bkpt 0x0000
0x8064 andeq r1, r0, r1, asr #10
0x8068 cmnvs r5, r0, lsl #2
0x806c tsteq r0, r2, ror #18
0x8070 andeq r0, r0, r11
0x8074 tsteq r8, r6, lsl #6
0x8078 adfcssp f0, f0, #4.0

…правильно? Неправильно. Посмотрите на адрес R0.Пока мы ожидали, что R0 будет содержать ранее прочитанное значение ПК (0x8054), оно вместо этого содержит значение, которое на две инструкции впереди ПК, которое мы ранее прочитали (0x805c). Из этого примера вы можете видеть, что когда мы непосредственно читаем PC, это следует из определения, что PC указывает на следующую инструкцию; но при отладке ПК указывает две инструкции перед текущим значением ПК (0x8054 + 8 = 0x805C). Это связано с тем, что старые процессоры ARM всегда извлекали две инструкции перед выполняемыми в данный момент инструкциями. Причина, по которой ARM сохраняет это определение, заключается в обеспечении совместимости с более ранними процессорами.


Регистр текущего состояния программы

Когда вы откладываете двоичный файл ARM с помощью gdb, вы видите нечто, называемое Flags:

cpsr.png.pagespeed.ce.9sO9rmvAHq.png


Регистр $ cpsr показывает значение регистра текущего состояния программы (CPSR), под которым вы можете видеть флаги, прерывания, переполнения, переносы, нули и минусы. Эти флаги представляют определенные биты в регистре CPSR и устанавливаются в соответствии со значением CPSR и становятся полужирными при активации. Биты N, Z, C и V идентичны битам SF, ZF, CF и OF в регистре EFLAG на x86. Эти биты используются для поддержки условного выполнения в условных выражениях и циклах на уровне сборки. Мы рассмотрим коды условий, использованные в части 6: условное выполнение и ветвление .

XRsEMvX.png


На рисунке выше показана схема 32-разрядного регистра (CPSR), где левая (<-) сторона содержит младшие значащие биты, а правая (->) - младшие значащие биты. Каждая ячейка (кроме секции GE и M вместе с пустыми) имеет размер один бит. Эти однобитовые разделы определяют различные свойства текущего состояния программы.


ФлагОписание
N
(отрицательный)
Включено, если в результате выполнения инструкции получено отрицательное число.
Z
(ноль)
Включено, если результат инструкции дает нулевое значение.
C
(Carry)
Включается, если результат инструкции дает значение, которое требует 33-го бита для полного представления.
V
(переполнение)
Включается, если в результате выполнения инструкции получено значение, которое не может быть представлено в дополнении 32-битного второго.
E
(Endian-бит)
ARM может работать с прямым или прямым порядком байтов. Этот бит установлен в 0 для младшего порядка или в 1 для старшего порядка.
T
("большой палец")
Этот бит устанавливается, если вы находитесь в состоянии "большого пальца", и отключается, когда вы находитесь в состоянии ARM.
М
(биты режима)
Эти биты определяют текущий режим привилегий (USR, SVC и т. Д.).
J
(Джазель)
Третье состояние выполнения, которое позволяет некоторым процессорам ARM выполнять аппаратный байт-код Java.

Давайте предположим, что мы будем использовать инструкцию CMP для сравнения чисел 1 и 2. Результат будет «отрицательным», потому что 1 - 2 = -1. Когда мы сравниваем два равных числа, например, 2 против 2, устанавливается флаг Z (ноль), потому что 2 - 2 = 0. Имейте в виду, что регистры, используемые с инструкцией CMP, не будут изменены, только CPSR будет изменен на основе результата сравнения этих регистров друг с другом.

Вот как это выглядит в GDB (с установленным GEF): В этом примере мы сравниваем регистры r1 и r0, где r1 = 4 и r0 = 2. Вот как выглядят флаги после выполнения операции cmp r1, r0:


cpsr2.png.pagespeed.ce._tsMzNJfns.png


Флаг переноса установлен, потому что мы используем cmp r1, r0 для сравнения 4 с 2 (4 - 2). Напротив, Отрицательный флаг (N) устанавливается, если мы используем cmp r0, r1 для сравнения меньшего числа (2) с большим числом (4).
Вот выдержка из инфоцентра ARM :

APSR содержит следующие флаги состояния ALU:
N - установить, когда результат операции был отрицательным.
Z - установить, когда результатом операции был ноль.
C - Установите, когда операция привела к переносу.
V - Установите, когда операция вызвала oVerflow.
Перенос происходит:

  • если результат сложения больше или равен 2^32 (^ - показатель степени)
  • если результат вычитания положительный или ноль
  • в результате встроенной операции смены ствола в движении или логической инструкции.
Переполнение происходит, если результат сложения, вычитания или сравнения больше или равен 2^31 или меньше 2^31 .

Спасибо за прочтение данной статьи и хорошего дня ;)
neopaket
Оригинальная статья: https://azeria-labs.com/arm-data-types-and-registers-part-2/
 
Последнее редактирование модератором:
Пожалуйста, обратите внимание, что пользователь заблокирован
о знакомые буковки, как давно я это не видел) ебать, а ведь я не одну бессоную ночь провел с этим ^_^
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх