Язык ассемблера


Assembly.

В этом модуле находятся задачи на работу с asm.

Чтобы сдать свой код, вы можете указать путь до ELF-файла с вашим кодом (пр. /challenge/run /path/to/your/elf), или передать секцию .text напрямую в stdin (пр. /challenge/run < /path/to/your/code).



Challenges

В этом упражнении вы будете работать с регистрами. Вам будет необходимо прочитать значение из регистра или записать в него.

Установите следующее:

rdi = 0x1337

Подключиться по SSH

Привяжите свой SSH-ключ, затем подключитесь: ssh hacker@tasks.ctfd.infosec.moscow

В этой задаче вы будете работать с несколькими регистрами. Установите следующее:

rax = 0x1337

r12 = 0xCAFED00D1337BEEF

rsp = 0x31337

Подключиться по SSH

Привяжите свой SSH-ключ, затем подключитесь: ssh hacker@tasks.ctfd.infosec.moscow

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

Для краткости, когда мы говорим A += B, мы имеем в виду A = A + B.

Вот несколько полезных инструкций:

    add reg1, reg2       <=>     reg1 += reg2

    sub reg1, reg2       <=>     reg1 -= reg2

    imul reg1, reg2      <=>     reg1 *= reg2

div чуть более сложен, мы обсудим его позднее. Примечание: все 'regX' здесь могут быть заменены константой или адресом в памяти

Используя эти знания, добавьте 0x331337 к rdi.

Подключиться по SSH

Привяжите свой SSH-ключ, затем подключитесь: ssh hacker@tasks.ctfd.infosec.moscow

В ассемблере x86 деление работает иначе, чем в обычной математике. Это называется целочисленным делением.

Пример: 10 / 3 = 3 в целочисленном делении.

Почему?

Потому что 3.33 округляется вниз до целого числа.

Наиболее полезными инструкциями для этой задачи являются: mov rax, reg1; div reg2

Примечание: div - это особенная инструкция, которая умеет делить 128-битное делимое на 64-битный делитель, сохраняя разность и остаток, и используя при этом только один регистр в качестве операнда.

Как же эта сложная конструкция работает и оперирует над 128-битным делимым (размер которого в два раза превышает размер регистра)?

Например, если при использовании инструкции div reg, произойдёт следующее:

rax = rdx:rax / reg

rdx = остаток

rdx:rax значит, что rdx составит первые (старшие) 64 бита от 128-битного делимого, а rax составит последние (младшие) 64 бита.

К содержимому rdx и rax необходимо относиться внимательно, прежде чем исполнять инструкцию div.

Вычислите следующее: скорость = путь / время, где:

путь = rdi

время = rsi

скорость = rax

Подключиться по SSH

Привяжите свой SSH-ключ, затем подключитесь: ssh hacker@tasks.ctfd.infosec.moscow

До этого вы работали с регистрами как с единственным способом хранить вещи, фактически используя их как переменные (пр. 'x' в математике).

Оказывается, мы также можем хранить байты в памяти!

Вспомните, что всё в памяти имеет адрес, и по каждому адресу расположено какое-то значение.

В ассемблере x86 мы можем получить данные по адресу в памяти (это называется "дереференс"), вот так:

    mov rax, [some_address]        <=>     Копирует значение по адресу 'some_address' в rax

Это работает и с регистрами:

    mov rax, [rdi]         <=>     Копирует значение по адресу, находящемуся в rdi, в регистр rax

Это, в том числе, работает и для записи в память:

    mov [rax], rdi         <=>     Копирует значение rdi по адресу, находящемуся в rax

Например, если бы rax был равен 0xdeadbeef, тогда rdi был бы записан по адресу 0xdeadbeef:

    [0xdeadbeef] = rdi

Примечание: адреса в памяти линейно увеличиваются, и в x86_64 они могут быть от 0 до 0xffffffffffffffff (много-много-много).

Сделайте следующее: Установите rax равным значению, находящемуся по адресу 0x404000

Подключиться по SSH

Привяжите свой SSH-ключ, затем подключитесь: ssh hacker@tasks.ctfd.infosec.moscow

Сделайте следующее:

  • Скопируйте значение по адресу 0x404000 в rax
  • Увеличьте значение по адресу 0x404000 на 0x1337

Убедитесь, что после исполнени вашего кода в rax находится оригинальное значение, а по адресу [0x404000] - увеличенное.

Подключиться по SSH

Привяжите свой SSH-ключ, затем подключитесь: ssh hacker@tasks.ctfd.infosec.moscow

В этом упражнении мы поработаем со стеком.

Стек - это часть памяти, которая может хранить данные для использования в будущем.

Чтобы хранить данные на стеке, мы используем инструкцию push, а чтобы достать значения со стека - pop.

Стек - структура данных "последнивй вошёл - первый вышел" (LIFO). Это значит, что последнее сохранённое на стеке значение будет первым вернувшимся из него.

В ассемблере x86, инструкция pop возьмёт значение с вершины стека и положит его в регистр.

Аналогично, инструкция push возьмёт значение из регистра и положит его на вершину стека.

Используя эти инструкции, возьмите значение с вершины стека, вычтите из него rdi и положите значение обратно на стек.

Подключиться по SSH

Привяжите свой SSH-ключ, затем подключитесь: ssh hacker@tasks.ctfd.infosec.moscow

В предыдуших заданиях вы использовали инструкции push и pop, чтобы хранить данные на стеке.

Оказывается, что вы можете работать со стеком и без этих инструкция!

Вспомните, что стек - это просто часть памяти. А всё в памяти имеет адрес. В прошлых заданиях вы уже получали доступ к памяти по адресу. Самое время повторить это.

В архитектуре x86, указатель на вершину стека находится в регистре rsp (Register for Stack Pointer) В rsp всегда находится адрес вершины стека, т.е. адрес последнего положенного на стек значения.

Без использования инструкции pop, вычислите среднее значение 4 подряд лежащих qwords на стеке.

Положите результат на стек.

Подсказка:

    RSP+0x?? Quad Word A
    RSP+0x?? Quad Word B
    RSP+0x?? Quad Word C
    RSP      Quad Word D

Помните, что адресное пространство стека (rsp) растёт в отрицательную сторону.

Подключиться по SSH

Привяжите свой SSH-ключ, затем подключитесь: ssh hacker@tasks.ctfd.infosec.moscow

30-дневный скорборд:

Место Пользователь Баджи Очки