Страницы

Поиск по вопросам

Показаны сообщения с ярлыком ассемблер. Показать все сообщения
Показаны сообщения с ярлыком ассемблер. Показать все сообщения

суббота, 11 апреля 2020 г.

Переполнение буфера при помощи “jmp esp” в gnu/linux

#linux #ассемблер

                    
Я пытаюсь написать программу для переполнения буфера в ОС gnu/linux.

В одной статье, посвящённой этой проблеме, нашла следующий способ:

[мусор]+[адрес инструкции jmp/call esp]+[шеллкод]          


Дизассемблирование уязвимой программы не помогает.

Каким образом можно найти адрес инструкции jmp %esp в gnu/linux?
    


Ответы

Ответ 1



по вот этой инструкции собрал бинарник на 32-битной системе (на 64-битной ошибка: Error: operand type mismatch for 'jmp', см. обновление ниже). текст программы: $ cat vuln.c #include #include // hard coded jmp *esp function ;-) void jmpesp() { __asm__("jmp *%esp"); } int main(int argc, char *argv[]) { char buffer[100]; strcpy(buffer, argv[1]); printf("buffer: [%s].\n", buffer); return 0; } сборка бинарника vuln: $ gcc vuln.c -o vuln -ggdb -fno-stack-protector -z execstack а вот она, инструкция: $ objdump -d vuln | grep 'ff e4' 804844f: ff e4 jmp *%esp обновление на 64-битной системе, видимо, вместо esp следует использовать rsp: __asm__("jmp *%rsp"); тогда сборка проходит удачно. и инструкция присутствует: $ objdump -d vuln | grep 'ff e4' 40054a: ff e4 jmpq *%rsp

четверг, 9 апреля 2020 г.

masm как сделать массив с изменяемой длиной

#массивы #ассемблер #arraylist #masm32

                    
Возможно ли изменить длинну массива (dd) и затем записать её в eax?
    


Ответы

Ответ 1



И нет, и да. Дело в том, что результат декларации dd - просто последовательность байт в бинарнике, которая статически грузится в память. Так что менять размер тут просто нечему. dd не является командой динамического выделения памяти - она просто статически помещает в бинарник нужную последовательность байт. Хотите выделить память - обратитесь к операционной системе и попросите. Через системный вызов.

четверг, 2 апреля 2020 г.

Какой алгоритм перевода сишного кода в ассемблер, который понимает процессор?

#c #ассемблер

                    
Вник в написание программ для микроконтроллеров (мк). Суть такая, что у каждой модели
мк есть определенный набор команд с разными опкодами. Например возьмем ОС виндовс.
Есть различное количество компьютеров с разными процессорами. Но программы написанные
на Си на винде работают на всех этих компах. Т.е. эти проги компилируются в какой-то
виндовый asm, а после драйвера на процессор переводят эти команды в те, которые понимает
процессор, или как это происходит? Есть ведь процессоры с какими-то специализированными
командами и если на этих процах все заработает, то на остальных нет? Хочется узнать
алгоритм перевода сишного кода в циферки, которые понимает процессор.
    


Ответы

Ответ 1



Хочется узнать алгоритм перевода сишного кода в циферки, которые понимает процессор. Для большинства современных систем это двухэтапный процесс: Текст на языке программирования (например - С) переводится коды конкретного процессора. Например - х86, ARM, PowerPC... Получается "объектный модуль". К полученному объектному модулю "пришиваются" все необходимые модули из сторонних библиотек. Получается "исполнимый модуль". Разумеется, этот модуль может быть выполнен только на данном конкретном процессоре, в рамках конкретной операционной системы. Программа, скомпилировнная для х86 не может быть выполнена на ARM или PowerPC. Программа, скомпилированная для седьмой версии виндовоза, скорее всего, не может быть выполнена на устаревших версиях этой ОС. Тем более, она не может быть выполнена под Linux или Android. Здесь не говорим о т.н. "скриптовых" языках программирования, а только именно об исполняемых модулях. Никакого "драйвера" первводящего ассемблер в команды процессора не существует. Любой ассемблер "заточен" под конкретный процессор и конкретную ОС.

Ответ 2



У процессоров х86 базовый набор команд стандартизирован. Конечно есть расширения https://ru.wikipedia.org/wiki/Расширения_архитектуры_x86. Разработчики программного обеспечения, прежде чем использовать их в собственных программах, должны выполнить специальную команду CPUID, которая сообщает о наличии тех или иных расширений. Разработчики используют эти дополнительные возможности при низкоуровневой оптимизации. В основном компиляторы не пользуются расширенными инструкциями или же могут оптимизировать самый тривиальный код. Для нормальной оптимизации и качественного использования этих расширений опять таки нужен разработчик. === А насчет того что в разных ОС программы не могут работать. Так программа еще может обращаться к различным функциям ОС API и пр. Ну а в разных ОС эти функции могут различаться.

воскресенье, 29 марта 2020 г.

Насколько умны/глупы компиляторы

#ассемблер


Как реализовать задание:


  Доказать насколько умны компиляторы путем написания простых блоков оператор, написать
ассемблерную версию компилятора  


Или где можно прочитать о способе реализации?

1) A = 2+3


2) if |A>B| op1
       else    op2



3) for (int i=0, i<10, i+1)
         op3


Доказать, насколько глупы компиляторы, подставив во втором пункте значения:

5 > 3


В третьем пункте подставить:

i = 5, i < 3

    


Ответы

Ответ 1



В комментарий, отвечающий на "Не подскажете, как мне исследовать возможности компилятора(особенности, которые заложили его авторы)?(книжку, статью) как все это делается") не помещается, поэтому пишу в ответе. Честно говоря, литературу посвященную именно исследованиям возможностей компилятора, не знаю, но вот что в принципе могу посоветовать по теме компиляторов. Для общего знакомства с языками на мой взгляд хороша книга -- "Языки программирования: разработка и реализация" Т. Пратт, М. Зелковиц. Говоря непосредственно о компиляторах, безусловно, следует отметить классический труд, называемый "Книгой дракона" (в сети легко можно найти ее текст). Вероятно, для практического вхождения в тему стоит почитать Креншоу Д. "Пишем компилятор". Для знакомства с устройством процессора (системой команд, а также немного с ассемблером) посмотрите, например, "Цифровая схемотехника и архитектура компьютера". Понятно, что для каждой конкретной реализации потребуется проштудировать Programmer Rererence Manual по его ISA (instruction set architecture). И несомненно, надо рекомендовать практику компиляции примеров в ассемблерный код (например, запуская gcc -S -O2 t.c; less t.s) с различными ключами оптимизации (см., скажем man gcc), а также непосредственного экспериментирования с измерением времени работы своих примеров.

воскресенье, 15 марта 2020 г.

команда TEST assembler

#ассемблер


По какому принципу работает команда TEST?

Понятно, что это логическое и. Устанавливаются флаги. Сколько их и по какому принципу
они устанавливаются, если в результате применения к двум числам логической функции
получается число?

    mov ax,5878h
    mov bx,6B36h
    mov cx,5CFDh
P:  mov  al,bl
Q:  test bh,cl
    jpe  E
    mul ch
    shl  ax,3
    jmp F
E:  imul cl
    xor  cl,ah
F:  sub  ah,cl


Вот еще пример, в нем при применение теста - результат True, как это получилось?
    


Ответы

Ответ 1



TEST устанавливает флаги PF, SF и ZF. Parity Flag - 1, если в результате AND четное количество установленных (1) бит. Sign Flag - 1, если результат AND получился отрицательным (старший - знаковый - бит = 1) Zero Flag - 1, если результат AND = 0 и сбрасывает CF и OF. Вот общая схема: Operation TEMP ← SRC1 AND SRC2; SF ← MSB(TEMP); IF TEMP = 0 THEN ZF ← 1; ELSE ZF ← 0; FI: PF ← BitwiseXNOR(TEMP[0:7]); CF ← 0; OF ← 0; Сам результат AND при этом выбрасывается. В вашем примере: 6Bh AND FDh = 69h = 01101001b PF будет установлен в 1. jpe прыгнет.

Как сделать функцию syscall?

#c #linux #ассемблер #arm


Я хочу написать "Hello World" без glibc, для этого мне нужно написать свою маленькую
libc, я сделал несколько простых функций которые не требуют системных вызовов, но теперь
хочу сделать функцию write чтоб функция puts заработала, но для write нужна функция
syscall, а для того чтоб сделать syscall нужно знать Асембрер, а я его совсем не знаю,
я всеже рискнул попробовать написать hello world, после долгого гугления и через несколько
десятков компиляций, я на экране увидел  "Hello World!", в первые пол секунды я не
поверил что у меня вышло Гг

__asm__(
    ".data;"
    "msg:"
        ".ascii \"Hello, world!\n\";" // Строка для вывода
        "len = . - msg;" // Записуем в переменную len длину msg
);

void _start() {


    __asm__(
        "mov  r0, #1;" // Запись в поток #1 - stdout
        "ldr  r1, =msg;" // Указатель на строку
        "ldr  r2, =len;" // Длина строки
        "mov  r7, #4;" // Номер системного вызова - 4 (write)
        "swi  0;" // Системный вызов ядра
    );

    __asm__(
        "mov  r0, #0;"  // Возращаемое значение - 0
        "mov  r7, #1;" // Номер системного вызова - 1 (exit)
        "swi  0;" // Системный вызов ядра
    );
}


Насколько я понял для системного вызова нужно записать в переменную(или это ригистры,
я не знаю чем отличается первое от второго) r7 номер вызова, а в переменные r1, r2,
r3... передаваемые аргументы, но как написать функцию syscall(номер вызова,Аргументы...)
я не знаю, тут нужно либо полностью писать её на Ассемблере, а это я точно не смогу,
либо писать на C и передавать аргументы в Ассемблер, это я тоже не умею.

Может кто либо помочь написать функцию syscall для arm?
    


Ответы

Ответ 1



Вот, накидал по-быстрому "Hello World", где системные вызовы осуществляются с помощью ряда функций syscall, для различного количества аргументов N. Можно, конечно, заморочиться, и сделать еще единую функцию с переменным числом аргументов с помощью va_list. #include #define SYS_EXIT 1 #define SYS_WRITE 4 #define STDIN 0 #define STDOUT 1 #define STDERR 2 size_t strlen(const char *s) { size_t n; for (n = 0; *s; ++n, ++s) ; return n; } void syscall1(int cnum, int arg1) { __asm__ __volatile__( "mov r0, %0;" "mov r7, %1;" "swi 0;" : : "r"(arg1), "r"(cnum) : "r0", "r7" ); } void syscall3(int cnum, int arg1, int arg2, int arg3) { __asm__ __volatile__( "mov r0, %0;" "mov r1, %1;" "mov r2, %2;" "mov r7, %3;" "swi 0;" : : "r"(arg1), "r"(arg2), "r"(arg3), "r"(cnum) : "r0", "r1", "r2", "r7" ); } void _start() { char *msg = "Hello world\n"; syscall3(SYS_WRITE, STDOUT, (int)msg, strlen(msg)); syscall1(SYS_EXIT, 0); } Компиляция и запуск: $ gcc -nostdlib test.c -o test $ ./test Hello World

Ответ 2



пример отсюда для intel-архитектуры: # hello.c static inline long syscall1(long syscall, long arg1) { long ret; asm volatile ("int $0x80" : "=a" (ret) : "a" (syscall), "b" (arg1) : "memory"); return ret; } static inline long syscall3(long syscall, long arg1, long arg2, long arg3) { long ret; asm volatile ("int $0x80" : "=a" (ret) : "a" (syscall), "b" (arg1), "c" (arg2), "d" (arg3) : "memory"); return ret; } int write(int fd, const void *buf, int count) { return syscall3(4, fd, (long)buf, count); } void exit(int status) { syscall1(1, status); } void _start() { int retval; retval = write(1, "hello world\n", 12); exit(0); } компилируем: $ gcc -m32 -nostdlib -nostdinc -static hello.c -o hello запускаем: $ ./hello hello world -- у архитектуры же arm другой ассемблер. проверить не могу, т.к. arm-а под рукой нет, поэтому привожу пример отсюда как есть: void _start() __attribute__ ((naked)); void _start() { main(); asm volatile( "mov r7, #1\n" /* exit */ "svc #0\n" ); } int main() { linuxc('X'); return 42; } void linuxc(int c) { asm volatile( "mov r0, #1\n" /* stdout */ "mov r1, %[buf]\n" /* write buffer */ "mov r2, #1\n" /* size */ "mov r7, #4\n" /* write syscall */ "svc #0\n" : /* output */ : [buf] "r" (&c) : "r0", "r1", "r2", "r7", "memory" ); } компилировать, как я понимаю, надо с теми же параметрами (ну, разве что без указания битности — -m32 — которая и в первом примере была необязательна).

пятница, 13 марта 2020 г.

Переставить первый и третий байт в EAX, а четвертый обнулить

#ассемблер #инспекция_кода


Пример: из 0xAABBCCDD получить 0x00DDCCBB

Решил задачу так:

MOV  EAX, [EAX]; //инициализация
MOV  ECX, EAX;
SHR  ECX, 16
MOV  DH, $00;
MOV  DL, AL;
SHL  EDX, 16;
MOV  DH, AH;
MOV  DL, CL;
MOV  EAX, EDX;


Можно ли улучшить этот код?

ОС: win32
    


Ответы

Ответ 1



MOV EAX, [EAX] ; EAX=0xAABBCCDD XOR EBX, EBX ; BL=0x00 XCHG AH, BL ; EAX=0xAABB00DD BL=0xCC ROR EAX, 16 ; EAX=0x00DDAABB MOV AH, BL ; EAX=0x00DDCCBB

воскресенье, 8 марта 2020 г.

Выполнение процессорм несуществующих инструкций

#ассемблер #процессор


Здравствуйте! Для начала хочу прояснить как я понимаю работу процессора, поправьте,
если что-то не так. Грубо: Процессору даётся команда считывания инструкции, вводиться
сама инструкция, её код(ну из 0 и 1), потом процессор сопоставляет введённую инструкцию
с инструкциями, записанными в нём,  получает операнды(если такие есть), и делает, то
что должна делать эта инструкция.

Вопрос:

Возможно ли как-то скомпилировать програму с не валидной инструкцией, заставить процессор
перейти к ней. Если да, то что произойдёт? Просто сверит, не найдёт соответствия и
всё?Предусмотрено ли такая ситуация?

Архитектура не важна, ARM,X86
    


Ответы

Ответ 1



В случае несуществующей инструкции возникает исключение (прерывание) с определённым номером (int 6). В стеке хранится адрес возврата. По этому адресу можно "вручную" програмно выполнить иснтрукцию, сдвинуть ip-адрес возврата, и сделать возврат iret (для x86) на следущюю инструкцию. Таким образом делалось, к примеру, эмуляция сопроцессора. При этом нужно точно знать как выполнить инструкцию. Для процессора ARM режим исключения (ловушка) по несуществующей инструкции возможно тоже есть. Для ARM7 вектор 0x4 Что будет если встретилась такая инструкция: Если сделать пустой обработчик - процессор повиснет. Как вариант можно написать "программа выполнила недопустимую инструкцию и будет закрыта" (многие версии Windows так делают). Если есть эмуляция - команду выполнит эмулятор Теперь о том зачем эмуляция команд. Случай с x86 486. Там есть замечательная команда bswap eax; которую не поддерживает 386. Зараннее зная что эту команду нужно выполнять, мы пишем программу, вешаем её на прерывание по несуществующей команде, где если data[(e)ip] = код_bswap то выполняем обмен внутри eax. Потом смещаем (e)ip на длинну команды делаем возврат. Как вариант можно эмулировать mmx расширение. Можно на intel эмулировать команды amd и наоборот. Но это будет касатся только даной платформы. Теперь об АРМ. Есть программа для ARM7, теоретически её можно выполнять на ARM6 если знать все недостающие команды в этой платформе и реализовать их выполнение в виде процедур. О кроссплатформенности. Команды для x86 и команды ARM никогда по кодам не пересекутся. Поэтому обработка несуществующих команд будет для каждого процессора своя. Да, можно с помощью #define разграничить и заточить с++ на две три разные платформы реализовав решение в разных файлах - да, и возможно как-то обьеденить всё в proc1(PTR * addr, REGS * reg) но даже регистры у процессоров разные. Поэтому смысла особого в этом нет. Для каждой платформы процессора прийдётся писать свою неповторимую эмуляцию, которая учёт особенности именно этой конкретной архитектуры. Возможно ли как-то скомпилировать програму с не валидной инструкцией? Ответ да, если у процессора существуют такие (есть процессоры где всё пространство инструкций заполнено и таковых нет, как правило RISC архитектуры). это будет только тогда, когда ПО собирается под одну версию ахитектуры, а выполняется на другой (например собрали под 486 а выполняется на 286, или собрали под Pentium-III, а выполняем на Pentium-II), или намерено генерируем код не под ту платформу под которую собрано (с помощью __emit__ или выполняем команды в массиве). Но обычно так не делают. Что б программа заработала, на уровне ядра OS делают эмуляцию. И последнее - емуляция значительно замедляет выполнение:). В контексте исключительных ситуаций процессора применяется три понятия, которые часто в литературе могут быть как "синонимами" так и разными понятиями (что иногда вызывает путаницу): interrupt (int сокращённо) - прерывание (как по исключению так и нет), trap - ловушка (имеется ввиду исключительной ситуации), exception - исключение.

суббота, 7 марта 2020 г.

Как вытаскивать из стека нужные значения?

#ассемблер #stack


Я сначала сохраняю PUSH EAX, потом PUSH EAX, то есть значения двух регистров. Возможно,
есть специальная инструкция, которая позволяет взять самое нижнее значение стэка? Вершиной
стэка - значение EAX, дно стэка значение ECX. Как взять значение из "дна", изменить
его и положить обратно на дно?
Благодарю за ответы!
    


Ответы

Ответ 1



Машинный стек - это просто участок памяти, с ним можно работать как с любым другим участком памяти, например с помощью команды mov можно обратиться к любому адресу выше текущей вершины (адреса, лежащего в esp). Действия из вопроса можно выполнить так: push eax push ecx mov edx, [esp+4] ; берем значение, которое было добавлено в стек предпоследним ... ; как-то меняем mov [esp+4], edx ; кладем обратно Дном стека для процедуры условно можно считать значение указателя стека, которое он имеет в момент входа в процедуру. Есть стандартный прием, когда это значение сохраняется в регистр ebp, потом в стеке выделяется память под локальные переменные (указатель стека просто уменьшается на размер локальных переменных), а потом в процессе работы процедуры от ebp в большую сторону ([ebp+N]) рассчитываются адреса аргументов процедуры, а в меньшую сторону ([ebp-N]) - адреса локальных переменных. При этом значение esp в процессе работы процедуры может меняться, а значение ebp остается одним и тем же. Код процедуры будет выглядеть примерно так: ; Стандартный пролог для создания стекового фрейма push ebp mov ebp, esp sub esp, M ; M - размер локальных переменных ... ; основной код процедуры ; Стандартный эпилог mov esp, ebp pop ebp ret

пятница, 6 марта 2020 г.

Исключение по адресу в программе на ассемблере

#ассемблер #исключения


Мне нужно написать программу на ассемблере х64 в VS 2017, Windows 10.
Нужно ввести в окошко строку, а программа должна заменить * на пробелы и вывести
в строку результат.

Пример нашей преподавательницы запускается, но при нажатии на кнопку RUN происходит
исключение по адресу.

Не знаете, в чем проблема? Сама она тоже не знает. Код ниже. 
Заранее спасибо.

ExitProcess PROTO 
DlgProc PROTO :QWORD,:QWORD, :QWORD, :QWORD
PostQuitMessage PROTO
GetModuleHandleA PROTO
DialogBoxParamA PROTO
GetDlgItemTextA PROTO
SendDlgItemMessageA PROTO

include windows.inc

.data
DName db "dial",0
hinst dq ?
result_buffer db 20 dup ("0")

hwnd dq 0
uMsg dq 0
wParam dq 0
lParam dq 0

.code
main proc
 sub rsp, 56

 mov rcx,0
 call GetModuleHandleA 
 mov hinst,rax

 mov rcx,hinst
 lea rdx, DName
 mov r8,0
 lea r9,DlgProc
 mov     dword ptr [rsp+32], 0
 call DialogBoxParamA
 mov rcx,0
 call ExitProcess 
 main endp

DlgProc proc q:QWORD,w:QWORD, e:QWORD, r:QWORD
  cmp edx, WM_CLOSE ;10h   
  je Exit_Button

  cmp edx, WM_COMMAND   ;111h   
  je go_button
  jne End_it 

 go_button:
 cmp r8,4
 je Exit_Button
 cmp r8,3
 je next
 jne End_it

 next:
    mov hwnd,rcx
    mov uMsg,rdx
    mov wParam,r8
    mov lParam,r9

    mov rdx,1
    lea r8,result_buffer
    mov r9,20
    call GetDlgItemTextA

    mov rcx,  hwnd
    mov rdx,  uMsg
    mov r8,   wParam
    mov r9,   lParam

        mov hwnd,rcx

    mov rcx,rax
    lea rsi,result_buffer
    m1:
     cmp byte ptr [rsi],'*'
     je m2
     jne m3
     m2:
      mov byte ptr [rsi],' '
     m3:
      inc rsi
     loop m1    

        mov rcx,hwnd

    mov rdx,2
    mov r8,LB_DELETESTRING  ;  182h - LB_DELETESTRING
    mov r9,0
    lea rsi,result_buffer
    mov qword ptr [rsp+32],rsi 
    call SendDlgItemMessageA

        mov rcx,  hwnd
        mov rdx,  uMsg
        mov r8,   wParam
        mov r9,   lParam

    mov rdx,2
    mov r8,LB_ADDSTRING  ;  182h - LB_DELETESTRING
    mov r9,0
    lea rsi,result_buffer
    mov qword ptr [rsp+32],rsi 
    call SendDlgItemMessageA
    jmp End_it

Exit_Button:
    mov rcx,0
    call PostQuitMessage

End_it:    
    mov rax,0
    ret
DlgProc endp 
end


Файл RSRC.RC

dial DIALOG 0, 0, 309, 95
CAPTION "mY dialog"
BEGIN
    EDITTEXT 1, 48,7,222,14
    ListBox 2,48,24,222,14
    PUSHBUTTON "Run", 3, 167,41,50,14  
    PUSHBUTTON "Exit",4, 220,41,50,12
    LTEXT "Input string:", -1, 7,10,40,8    
END 


Ошибка в отладчике:


    


Ответы

Ответ 1



В коде две проблемы, приводящих к вылетам: Не проверяется что введена пустая строка (значение, возвращенное из GetDlgItemTextA, равно нулю), из-за чего при первом попадании на инструкцию loop 0 в регистре rcx превращается в -1, получаем "бесконечный" цикл и попытку записи за пределы секции данных. В начале процедуры DlgProc нужно выделить на стеке место под параметры процедуры SendDlgItemMessageA, при выходе из DlgProc стек восстановить. Из-за того что это не выполнено, скорее всего при выполнении SendDlgItemMessageA (которое происходит только при нажатии кнопки "Run") происходит затирание каких-то данных вызывающего кода, при выходе из процедуры происходит вылет. Вот рабочий код, адаптированный под fasm, с моими замечаниями: include 'win64a.inc' format MS64 COFF public main extrn PostQuitMessage extrn SendDlgItemMessageA extrn GetModuleHandleA extrn GetDlgItemTextA extrn DialogBoxParamA extrn ExitProcess section '.data' readable writeable DName db "dial",0 result_buffer db 20 dup ("0") section '.code' code readable executable main: sub rsp, 5*8 ; место под 5 параметров для вызова DialogBoxParamA mov rcx,0 call GetModuleHandleA mov rcx, rax ; [hinst] mov rdx, DName mov r8, 0 mov r9, DlgProc mov dword [rsp+32], 0 call DialogBoxParamA mov rcx,0 call ExitProcess proc DlgProc hwnd, uMsg, wParam, lParam ; место под параметры на стеке выделено как раз для того, ; чтобы можно было сохранить там значения тех же параметров из регистров mov [hwnd], rcx mov [uMsg], rdx mov [wParam], r8 mov [lParam], r9 sub rsp, 5*8 ; резервируем на стеке место под 5 параметров для вызова SendDlgItemMessageA cmp edx, WM_CLOSE ; 10h je Exit_Button cmp edx, WM_COMMAND ; 111h je go_button jne End_it go_button: cmp r8,4 je Exit_Button cmp r8,3 ; je next ; лишняя проверка jne End_it ; next: mov rdx,1 mov r8, result_buffer mov r9,20 call GetDlgItemTextA ; Не нужно постоянно гонять параметры между памятью и регистрами ; mov rcx, hwnd ; mov rdx, uMsg ; mov r8, wParam ; mov r9, lParam ; mov hwnd,rcx test rax, rax ; не забываем проверить, что введена не пустая строка jz End_it mov rcx, rax mov rsi, result_buffer m1: cmp byte [rsi], '*' ; je m2 ; лишняя проверка jne m3 ; m2: mov byte [rsi], '+' ; заменяю на +, чтобы была заметнее разница m3: inc rsi loop m1 mov rcx, [hwnd] mov rdx, 2 ; ListBox mov r8, LB_DELETESTRING ; 182h - LB_DELETESTRING mov r9, 0 mov qword [rsp+32], result_buffer ; mov qword ptr [rsp+32], offset result_buffer для masm call SendDlgItemMessageA ; mov rcx, hwnd ; mov rdx, uMsg ; mov r8, wParam ; mov r9, lParam mov rcx, [hwnd] mov rdx,2 mov r8, LB_ADDSTRING mov r9,0 mov qword [rsp+32], result_buffer call SendDlgItemMessageA jmp End_it Exit_Button: mov rcx,0 call PostQuitMessage End_it: mov rax,0 add rsp, 5*8 ; восстанавливаем стек ret endp Для компиляции нужен fasm, а также GoRC и GoLink (см. The Go tools for Windows + Assembler, к языку Go не имеют никакого отношения). Для отладки использовал x64dbg. Собственно, компиляция: fasm dialog.asm gorc /machine X64 rsrc.rc golink dialog.obj rsrc.res user32.dll kernel32.dll /entry main Ну и наконец скриншот (состояние после ввода текста и нажатия на кнопку Run): P.S. "криво расставленные котлы" авторские, rsrc.rc я в неизмененном виде взял из вопроса.

четверг, 5 марта 2020 г.

Буфер — это обязательно Heap?

#winapi #ассемблер #указатели


Здравствуйте, скажите пожалуйста, вот во многих (по крайне мере которые я встречал)
строковых-winapi'шных функциях следует передавать указатель на некий буфер, буфер обязательно
должен быть заранее выделенной кучей (Heap)? Можно отправить указатель например на
заранее зарезервированный массив байт?
    


Ответы

Ответ 1



Лишь бы эта память была корректна во время работы с ней (ну и, конечно же, доступна для записи-чтения :)). Например, при выделении памяти в стеке следует не забывать, что она будет корректна только до окончания вызова функции. А так - что выделите, то и годится :)

среда, 4 марта 2020 г.

Сравнение скорости для команд оканчивающие цикл: jcxz vs cmp cx, 0 je

#ассемблер #условия #tasm


Добрый день, использую tasm (Turbo assembler) и мне стало жутко интересно какая же
конструкция работает быстрее : 

jcxz метка


или

cmp cx, 0
je метка


причем Vim не подсвечивает вариант с jcxz
    


Ответы

Ответ 1



Вторая конструкция будет дольше выполняться по тактам, следовательно медленнее. Не зря же команду придумали разработчики архитектуры x86... Подробности тут: Источник

Как не сломать компьютер учась низкоуровневому программированию?

#windows #ассемблер


При вызове какого нибудь прерывания боюсь сломать комп) Есть ли какой-нибудь аналог
песочницы для таких развлечений?
    


Ответы

Ответ 1



В университете, на курсе микропрограммирования, мы использовали Virtual Box. А так - любая виртуальная машина

Ответ 2



Важно устанавливать 32 бит операционную систему если вы будете запускать .com файл Посоветую несколько виртуальных машин VirtualBox Подходит для начала, дальше если вам нужна автоматизация то QEMU QEMU Идеален для таких целей, особенно если вы будете писать загрузчик. Автоматизация происходит путем .sh (linux, mac) или .bat (window) файлов. Сначала покажется непонятным но потом не откажитесь :) Vmware Если честно меня не очень порадовал: цена и практически никаких отличий от VB разве что поддержка Windows 7 и Vista aero, но это все свистелки. Parallels Desktop Я им не пользовался, не могу сказать, но если у тебя мак стоит попробовать. bochs свободная программа для эмуляции аппаратного обеспечения IBM PC. Для новичка посоветую VirtualBox

Ответ 3



Может быть подойдёт Oracle VM VirtualBox?

среда, 26 февраля 2020 г.

Самоучитель assembler [закрыт]

#ассемблер


        
             
                
                    
                        
                            Закрыт. Этот вопрос не по теме. Ответы на него в данный
момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он соответствовал тематике «Stack Overflow на русском».
                        
                        Закрыт 4 года назад.
                                                                                
           
                
        
Здравствуйте.
Не могли ли Вы подсказать неплохой самоучитель по ассемблеру? Желательно, чтобы в
конце каждой главы были упражнения, а также были куски кода с комментариями (например,
hello world).
P.S. На хеше похожего вопроса не нашел, а точнее ответа на него.    


Ответы

Ответ 1



Учил ассемблер по книге "Магда - Ассемблер для процессоров Intel Pentium" . Магда как автор очень понятно и доходчиво объясняет даже тяжелый материал. Также неплохие учебники: Голубь - Искусство программирования на Ассемблере. Лекции и упражнения Зубков - Assembler для DOS, Windows и UNIX Кулаков - Программирование на аппаратном уровне Ирвин - Язык ассемблера для процессоров Intel Марек - Ассемблер на примерах. Базовый курс Пирогов - Assembler. Учебный курс Assembler&Win32. Курс молодого бойца

Ответ 2



В свое время начинал изучение АСМа с советской брошюры (названия и автора, к сожалению, не помню) и подаренного учебника Юрова. Что-то типа этого: Виктор Юров "Assembler. Учебник для вузов". Но советская брошюра помогла понять идею ассемблера.

Ответ 3



Конечно, это мое мнение, но что-бы учить хлл нужно освоить низкоуровневое программирование. Мне очень понравился учебник Зубков С. - Assembler для DOS, Windows, UNIX. Примеры на TASM, конечно, но для понимания очень годится. А если нравится другой компилятор, можно перепилить голову и конец программы, на начальной стадии обучения код везде одинаковый.

Ответ 4



Про Калашникова забыли, а зря. Хорошая книга «Ассемблер — это просто. Учимся программировать».

Ответ 5



Вот очень хороший учебник Пирогов В. Ассемблер для Windows.

вторник, 25 февраля 2020 г.

Вывод на экран [закрыт]

#ассемблер


        
             
                
                    
                        
                            Закрыт. Данный вопрос необходимо конкретизировать. Ответы
на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он был сосредоточен только на одной проблеме, отредактировав его.
                        
                        Закрыт 4 месяца назад.
                                                                                
           
                
        
Подскажите как в Assebmler вывести на экран слово Hello    


Ответы

Ответ 1



Fasm, через MessageBox: include 'win32ax.inc' .code start: invoke MessageBox,HWND_DESKTOP,'Hello!','Title',MB_OK invoke ExitProcess, 0 .end start Fasm, консоль, через WriteConsole: format pe console include 'win32ax.inc' .data hOut dd ? .code sHello db 'Hello!' .length = $ - sHello start: invoke GetStdHandle, STD_OUTPUT_HANDLE mov [hOut], eax ; на случай если будет использоваться не один раз invoke WriteConsole, [hOut], sHello, sHello.length, 0, 0 invoke ExitProcess, 0 .end start Fasm, консоль, через puts из msvcrt: format pe console entry start include 'win32ax.inc' section '.code' code readable executable start: cinvoke puts, 'Hello!' ret section '.idata' import data readable library msvcrt,'msvcrt.dll' import msvcrt,\ puts,'puts' Fasm, dos, через int 21h :) org 100h mov ah, 9 mov dx, hello int 21h ret hello db 'Hello!$'

Ответ 2



MASM32+WinAPI .686 .model flat, stdcall option casemap:none includelib C:\masm32\lib\kernel32.lib include C:\masm32\include\kernel32.inc include C:\masm32\include\windows.inc includelib C:\masm32\lib\user32.lib include C:\masm32\include\user32.inc .data cWritten DWORD 0 stdout DWORD 0 _title db " " mess db "Hello, world!" .code _st: invoke MessageBox, 0, ADDR mess, ADDR _title, 0 invoke ExitProcess, 0 end _st

понедельник, 24 февраля 2020 г.

JMP FF FF FF как будет

#ассемблер


Здравствуйте.

JMP FF (JMP SHORT т.е. на один байт) байткод EB FF

JMP FF FF FF FF (JMP NEAR т.е. на четыре байт) байткод E9 FF FF FF FF

Такой вопрос, JMP на 3 байтный прыжок как будет?
    


Ответы

Ответ 1



Надо использовать косвенный jmp near с 4х байтной адресацией, заполнив недостающие байты 0 JMP 002C314C

Почему у меня SetPixel не отрисовывает пиксель?

#winapi #ассемблер #masm32 #gdi


Почему у меня SetPixel не отрисовывает пиксель на форме?

WndProc proc uses ebx edi esi, hwnd:DWORD, wmsg:DWORD, wparam:DWORD, lparam:DWORD
  switch wmsg
    case WM_INITDIALOG
      invoke SendMessage, hwnd, WM_SETICON, 1, FUNC(LoadIcon, NULL, IDI_ASTERISK)
    case WM_PAINT
      invoke SetPixel, hwnd, 15, 20, 65285
    case WM_CLOSE
      exit_program:
      invoke EndDialog, hwnd, 0
    endsw
  xor eax,eax
ret
WndProc endp

    


Ответы

Ответ 1



В качестве первого параметра SetPixel принимает не идентификатор окна, а идентификатор контекста устройства (в данном случае, идентификатор контекста окна). Контекст устройства упрощенно можно сравнить с холстом, на котором происходит рисование. В вашем случае нужно получить этот идентификатор с помощью функции BeginPaint, нарисовать пиксель, потом освободить контекст при помощи EndPaint. Код для обработки сообщения WM_PAINT будет примерно такой: invoke BeginPaint, hwnd, addr ps invoke SetPixel, eax, 15, 20, 65285 invoke EndPaint, hwnd, addr ps Также необходимо объявить переменную ps, которая является структурой PAINTSTRUCT. Скорее всего определение этой структуры уже есть в каком-то из подключаемых файлов пакета MASM. addr - ключевое слово для подстановки адреса переменной. Для нескольких вызовов SetPixel можно сделать например так: invoke BeginPaint, hwnd, addr ps push ebx mov ebx, eax invoke SetPixel, ebx, 15, 20, 65285 invoke SetPixel, ebx, 30, 20, 65285 invoke SetPixel, ebx, 45, 20, 65285 pop ebx invoke EndPaint, hwnd, addr ps Это вариант без использования дополнительной переменной. По соглашениям stdcall и cdecl, вызываемые процедуры не должны модифицировать регистры ebx, edi, esi (но могут модифицировать eax, ecx, edx), поэтому ebx, например, можно использовать для временного хранения значений, которые не должны затираться вызовами процедур. Но так как процедура WndProc тоже должна соответствовать соглашению stdcall, то перед модификацией ebx его значение нужно сохранить, а потом восстановить. Это можно сделать либо локально, вокруг места, где происходит модификция ebx (как показано в этом примере), либо сохранить в начале процедуры, а в конце (перед ret) - восстановить. Еще одно замечание - код цвета лучше писать в шестнацатеричном виде, в данном случае это будет 00FF05h, тогда будет сразу понятно, что этот цвет ближе к зеленому (порядок цветов - 0x00bbggrr, согласно описанию типа COLORREF).

Ответ 2



Потому что SetPixel ожидает первым параметром hdc, а не hwnd. Вам нужно вызвать BeginPaint для получения hdc, а после отрисовки вызвать EndPaint.

Зачем нужен ebp, если можно без него [закрыт]

#ассемблер


        
             
                
                    
                        
                            Закрыт. Этот вопрос необходимо уточнить или дополнить
подробностями. Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Добавьте больше подробностей
и уточните проблему, отредактировав это сообщение.
                        
                        Закрыт 3 года назад.
                                                                                
           
                
        
Зачем нужно использовать регистр ebp при организации стека программы, если можно
обойтись и без него(если нет хитрых асм вставок)? Почему нельзя по умолчанию использовать
такую оптимизацию? С чем это связанно?
    


Ответы

Ответ 1



Связано это в первую очередь с удобством. Никто не мешает вам адресоваться через регистр esp. Но при этом нужно постоянно держать в голове что сам стековый указатель в процессе выполнения кода может прыгать как угодно. И постоянно менять смещение для одних и тех же сущностей (а вам было бы удобно, если сейчас переменная называется foo, а через пару строк к ней нужно обращаться как к bar?). В совсем простеньких случаях это может иметь смысл, сам так делаю :) Например: print_uint: pushad push dword [esp+36] push format_u ; "%u", 0 call printf pop eax pop eax popad ret 4 Да и компиляторы могут генерировать код без enter/leave при оптимизации. Но в общем случае это "микрооптимизация" (и то под вопросом), и не стоит она того геморроя, который за собой тащит.

Ответ 2



Можно и без него (а очень жаль иногда). Например, gcc для тестовой программки #include int f() { return puts("xaxa"); } int main(void) { long a = 10; long long b = 11; int l = 100; float e = 8.8; f(a, b, l, e); return 0; } с флагами оптимизации avp@avp-ubu1:hashcode$ gcc -O -S t.c avp@avp-ubu1:hashcode$ gcc --vers gcc.real (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. avp@avp-ubu1:hashcode$ делает код без использования ebp(rbp) для организации фреймов стека .file "t.c" .section .rodata.str1.1,"aMS",@progbits,1 .LC0: .string "xaxa" .text .globl f .type f, @function f: .LFB23: .cfi_startproc subq $8, %rsp .cfi_def_cfa_offset 16 movl $.LC0, %edi call puts addq $8, %rsp .cfi_def_cfa_offset 8 ret .cfi_endproc .LFE23: .size f, .-f .globl main .type main, @function main: .LFB24: .cfi_startproc subq $8, %rsp .cfi_def_cfa_offset 16 movsd .LC1(%rip), %xmm0 movl $100, %edx movl $11, %esi movl $10, %edi movl $1, %eax call f movl $0, %eax addq $8, %rsp .cfi_def_cfa_offset 8 ret .cfi_endproc .LFE24: .size main, .-main .section .rodata.cst8,"aM",@progbits,8 .align 8 .LC1: .long 2684354560 .long 1075943833 .ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609" .section .note.GNU-stack,"",@progbits

пятница, 21 февраля 2020 г.

Вызвать BSOD в 64-битном приложении

#delphi #ассемблер


unit BSOD;

interface

uses
  WinAPI.Windows;

procedure Crash;

function RtlSetProcessIsCritical(unu: Cardinal; proc: Pointer; doi: Cardinal): Integer;
stdcall; external 'ntdll.dll';
function RtlAdjustPrivilege(Privilege: Cardinal; Enable: Boolean; CurrentThread:
Boolean; out OldPrivilege: Boolean): Cardinal; stdcall; external 'ntdll';

implementation

procedure Crash;
asm
  lea eax, dword[esp+12]
  //invoke RtlAdjustPrivilege, 20, 1, 0, eax
  push eax
  push 0
  push 1
  push 20
  call RtlAdjustPrivilege
  //invoke RtlSetProcessIsCritical, 1, 0, 0
  push 0
  push 0
  push 1
  call RtlSetProcessIsCritical
  call ExitProcess
end;

end.


В 32-битном приложении, если вызвать Crash, то винда крашится.

Как заставить этот код работать в 64-битном приложении?
    


Ответы

Ответ 1



Если не знаете, как написать код на ассемблере, то пишите вначале код на Delphi, смотрите, какой код генерирует компилятор (ставите брекпоинт в функции и когда дойдёте до него, открывайте окно дизассемблера Ctrl+Alt+D), берёте его за основу и улучшаете. Вот для такого Delphi кода: procedure Crash; var VOldPrivilege: Boolean; begin RtlAdjustPrivilege(20, True, False, VOldPrivilege); RtlSetProcessIsCritical(1, nil, 0); ExitProcess(1); end; при компиляции в 64-битном режиме, генерируется вот такой ассемблерный код: push rbp sub rsp,$30 mov rbp,rsp mov ecx,$00000014 mov dl,$01 xor r8,r8 lea r9,[rbp+$2f] call RtlAdjustPrivilege mov ecx,$00000001 xor edx,edx xor r8,r8 call RtlSetProcessIsCritical mov ecx,$00000001 call ExitProcess lea rsp,[rbp+$30] pop rbp ret А вот такой код для 32-битного режима, можете сравнить со своим: push ebp mov ebp,esp push ecx lea eax,[ebp-$01] push eax push $00 push $01 push $14 call RtlAdjustPrivilege push $00 push $00 push $01 call RtlSetProcessIsCritical push $01 call ExitProcess pop ecx pop ebp ret Отличие кода для разных платформ в том, что для x32 используется соглашение вызовов stdcall (параметры передаются через стек), а в x64 - fastcall (первые 4 параметра передаются через регистры, остальные через стек). При разработке соглашений по вызовам (calling conventions) для архитектуры x86-64 решили положить конец существованию различных вариантов вызова функций. В Win32 существовал целый ряд соглашений о вызове: stdcall, cdecl, fastcall, thiscall и так далее. В Win64 только одно «родное» соглашение по вызовам. Модификаторы подобные __cdecl компилятором игнорируются. Думаю, что все согласятся в благородстве такого резкого сокращение числа соглашений. Соглашение по вызовам на платформе x86-64 похоже на соглашение fastcall, существующее в x86. В x64-соглашении первые четыре целочисленных аргумента (слева направо) передаются в 64-битных регистрах, выбранных специально для этой цели: RCX: 1-й целочисленный аргумент RDX: 2-й целочисленный аргумент R8: 3-й целочисленный аргумент R9: 4-й целочисленный аргумент Остальные целочисленные аргументы передаются через стек. Указатель «this» считается целочисленным аргументом, поэтому он всегда помещается в регистр RCX. Если передаются значения с плавающей точкой, то первые четыре из них передаются в регистрах XMM0-XMM3, а последующие — через стек. Цитата отсюда: https://habrahabr.ru/company/intel/blog/94340/

Ответ 2



Приведенный выше код - это код, главная задача которого сделать BSOD. Если кратко - если процесс помечен как критический функцией RtlSetProcessIsCritical и он завершился (все равно как), то винда также делать себе харакири (например, через bsod). По хорошему нужно вызвать RtlSetProcessIsCritical но с тремя нулями, что бы снять с себя этот флажок. UPD Вышеуказанная функция хочет прав админа. Иначе сделать процесс "критическим" нельзя. И это правильно. Если хочется именно бсода (я правда не понимаю, зачем оно нужно нормальному человеку), то можно написать свой драйвер, главной задачей которого будет вызвать его по желанию. Но если хочется бсод, что бы напугать пользователя, прав админа не хватает - можно использовать скринсейвер от MS - https://technet.microsoft.com/en-us/sysinternals/bluescreen.aspx

воскресенье, 16 февраля 2020 г.

Зачем нужен ebp, если можно без него [закрыт]

#ассемблер


        
             
                
                    
                        
                            Закрыт. Этот вопрос необходимо уточнить или дополнить
подробностями. Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Добавьте больше подробностей
и уточните проблему, отредактировав это сообщение.
                        
                        Закрыт 3 года назад.
                                                                                
           
                
        
Зачем нужно использовать регистр ebp при организации стека программы, если можно
обойтись и без него(если нет хитрых асм вставок)? Почему нельзя по умолчанию использовать
такую оптимизацию? С чем это связанно?
    


Ответы

Ответ 1



Связано это в первую очередь с удобством. Никто не мешает вам адресоваться через регистр esp. Но при этом нужно постоянно держать в голове что сам стековый указатель в процессе выполнения кода может прыгать как угодно. И постоянно менять смещение для одних и тех же сущностей (а вам было бы удобно, если сейчас переменная называется foo, а через пару строк к ней нужно обращаться как к bar?). В совсем простеньких случаях это может иметь смысл, сам так делаю :) Например: print_uint: pushad push dword [esp+36] push format_u ; "%u", 0 call printf pop eax pop eax popad ret 4 Да и компиляторы могут генерировать код без enter/leave при оптимизации. Но в общем случае это "микрооптимизация" (и то под вопросом), и не стоит она того геморроя, который за собой тащит.

Ответ 2



Можно и без него (а очень жаль иногда). Например, gcc для тестовой программки #include int f() { return puts("xaxa"); } int main(void) { long a = 10; long long b = 11; int l = 100; float e = 8.8; f(a, b, l, e); return 0; } с флагами оптимизации avp@avp-ubu1:hashcode$ gcc -O -S t.c avp@avp-ubu1:hashcode$ gcc --vers gcc.real (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. avp@avp-ubu1:hashcode$ делает код без использования ebp(rbp) для организации фреймов стека .file "t.c" .section .rodata.str1.1,"aMS",@progbits,1 .LC0: .string "xaxa" .text .globl f .type f, @function f: .LFB23: .cfi_startproc subq $8, %rsp .cfi_def_cfa_offset 16 movl $.LC0, %edi call puts addq $8, %rsp .cfi_def_cfa_offset 8 ret .cfi_endproc .LFE23: .size f, .-f .globl main .type main, @function main: .LFB24: .cfi_startproc subq $8, %rsp .cfi_def_cfa_offset 16 movsd .LC1(%rip), %xmm0 movl $100, %edx movl $11, %esi movl $10, %edi movl $1, %eax call f movl $0, %eax addq $8, %rsp .cfi_def_cfa_offset 8 ret .cfi_endproc .LFE24: .size main, .-main .section .rodata.cst8,"aM",@progbits,8 .align 8 .LC1: .long 2684354560 .long 1075943833 .ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609" .section .note.GNU-stack,"",@progbits