Статья ASM – техники из старого сундука [3]

Hello All!
Делиться со-всякими алго и наработками уже входит у меня в привычку, и в продолжении цикла этих заметок предлагаю разбор всякого хлама из старого сундука. На повестке дня сегодня следующее:


1. Фиктивный стек;
2. Список установленных программ в реестре;
3. Базовые операции с текстом;
4. Заключение.
----------------------------------------------


1. Фиктивный стек

Герою восточного фольклора Ходже Насреддину приписывают выражение: -"Если гора не идёт к Магомеду, то Магомед пойдёт к горе". В этой части статьи попробуем спроецировать данное утверждение на системный стек, но для начала рассмотрим микро-архитектуру центрального процессора, и какое место занимает в ней этот стек.

На рисунке ниже представлена структурная схема входящих в состав CPU основных блоков.
При запуске нашей программы, функции системного загрузчика с префиксом LDR загружают образ двоичного кода с диска в оперативную память DDR-SDRAM (Synchronous Dynamic Random Access Memory), после чего код становится доступным центральному процессору CPU. Как только регистр-указатель EIP упрётся в точку-входа в программу EntryPoint, диспетчер памяти тут-же в пакетном режиме считывает из ОЗУ как-минимум по одной 4 Кбайтной странице из секции-кода и секции-данных (итого 8Кб), и сбрасывает их в кэш процессора L3.

Такого алго придерживаются процессоры только на старте, а дальше – данные читаются из ОЗУ исключительно по-востребованию, блоками по 64-байт, чтобы их можно было поместить в одну линейку кэш "Cache-Line". Если софт гигантских размеров типа Photoshop или Word из пакета Office, то диспетчер может заполнить кодом\данными весь кэш L3, что влечёт за собой тормоза на старте. Здесь всё в штатном режиме, а вот дальше уже интересней..

Кэши L2 и L3 не разделяют информацию на код и данные, более того в архитектуре НТ (гипертрейдинг) они являются общими для всех ядер одного процессора. Зато кэшей L1 уже два – отдельно для кода и отдельно для данных. Структура кэш-линеек такова, что помимо самой информации, в них имеются и специальные поля под названием "Tag", где хранится старшая часть виртуального адреса ОЗУ, от куда была скопирована инфа. Проверяя эти теги процессор ищет в L2 байты, которые принадлежат секции-кода и отправляет их в L1-инструкций, и далее в исполнительный конвейер. Соответственно если в теге прописан адрес секции-данных, то линейка отправляется в L1-данных, который ведёт диалог исключительно с блоками Load\Store ядра процессора Execute, минуя его Front-End.


Push_Pop.png


Теперь рассмотрим ситуацию, когда декодер обнаруживает в L1 инструкцию PUSH – это может быть, например, передача параметров функции через стек. Поскольку стек представляет собой своеобразную секцию-данных, процессору приходится перегонять операнд инструкции PUSH по большой ветке кровообращения, из L1 инструкций, в L1 данных и обратно. Здесь становится очевидно, что в алгоритме вызова процедур и функций имеются недочёты, поскольку бесполезный транспорт данных явно снижает общую производительность. Для инженеров это было легче запрограммировать, чем искать компромиссы, ..тем-более что ситуации бывают разные и лучше выбрать золотую середину.

Однако фанатиков нетрадиционного кода такой расклад не устраивал, и ещё на третьих пеньках они придумали вполне разумное решение этой проблемы (салам Магомед). В основе оригинальной мысли лежал тот факт, что если в секции-данных заранее подготовить стековый фрейм с готовыми аргументами, можно будет не копировать их в стек, а наоборот натравить на этот фрейм регистр-указатель стека ESP (Stack-Pointer). Поскольку процессор слепо верит этому регистру, то примет подложный стек за чистую монету и без лишних слов отработает запрос. Тут главное правильно расположить все аргументы функции в секции-данных, не забыв при этом зарезервировать место под адрес-возврата, куда его неявно помещает инструкция CALL. Посмотрим на такой пример:


C-подобный:
.data
text        db  'Hello!',0
mes1        db  'Codeby.net',0
mes2        db  'Marylin',0

align 16
localStack  rd  64
funcArg     dd  0,text,2222h,3333h,4444h,5555h   ;//<--- аргументы функции (0 = резерв под RetAddr)
realStack   rd  64

buff        db  0
;//-------
.code
start:   mov     ebp,esp          ;// EBP = линк на оригинальный стек системы
         mov     esp,funcArg +4   ;// ESP = линк на наш стек с аргументами!
         call    foo              ;// funcArg +0 = адрес возврата
         mov     esp,ebp          ;// вернуть указатель стека

        cinvoke  printf,<10,' Func: call OK!',0>

@exit:  cinvoke  _getch
        cinvoke  exit,0
;//-------
proc foo a1,a2,a3,a4,a5   ;//<---- функция с 5 аргументами

local  i  dd  mes1    ;//<-------- Локальные переменные функции
local  j  dd  mes2    ;//<-------- ^^^^

         mov      eax,[a1]     ;// EAX = значение первого аргумента
         mov      ebx,[a2]     ;// EBX = второго
         push     eax ebx      ;// здесь можно пользоваться стеком
         pop      eax ebx      ;// ^^^^
         mov      esi,[i]      ;// ESI и EDI = локальные переменные
         mov      edi,[j]      ;// ^^^^
         ret                   ;// уйти по адресу возврата!
endp

Немного комментов к этому исходнику..

#1. Значит директива align 16 в секции-данных тут обязательна, т.к. стек должен быть выровнен как-минимум на 4-байтную границу (в х64 на 8-байт), иначе поведение CPU непредсказуемо, и он может уйти в штопор в самый неподходящий момент.

#2. Вызываемая функция может иметь локальные переменные, которые процессор кладёт выше указателя командой SUB ESP,размер лок.переменных. Поэтому выделяем под них 64 двойных слова в блоке памяти с меткой localStack. Чтобы функция имела возможность пользоваться стеком в привычном нам понимании, необходимо выделить такой-же блок ниже стекого фрейма с аргументами – в примере на него указывает метка realStack.

#3. Теперь в самом коде запоминаем в регистре EBP указатель на системный стек MOV EBP,ESP, после чего меняем его расположение инструкцией MOV ESP,funcArg+4. Смещением[+4] мы резервируем место под адрес-возврата из функции, для чего предусмотрен dword со-значением нуль в нашем фрейме.

В результате, на момент вызова функции стек будет выглядеть примерно так (обратите внимание на адрес, который ведёт к секции-данных):


Stack.png


Если адрес-возврата заранее известен (а в большинстве случаях это именно так), то можно сразу поместить его во-фрейм с аргументами и вызывать функцию не через CALL, а обычным переходом JMP. В этом случае указатель ESP не требует уже смещения, и его нужно сфокусировать на макушке стека, как в примере ниже:

C-подобный:
.data
text        db  'Hello!',0
align 16
localStack  rd  64
funcArg     dd  @ret,text,2,3,4,5  ;//<--- стековый фрейм (@ret = RetAddr)
realStack   rd  64
buff        db  0
;//-------
.code
start:   mov     ebp,esp      ;// EBP = линк на оригинальный стек
         mov     esp,funcArg  ;// ESP = линк на наш стек, без смещения +4
         jmp     foo          ;// прыгаем на функцию!
@ret:    mov     esp,ebp      ;// восстановить указатель стека

        cinvoke  printf,<10,' Func: jmp OK!',0>
@exit:  cinvoke  _getch
        cinvoke  exit,0
;//-------
proc foo a1,a2,a3,a4,a5   ;//<---- функция с 5 аргументами
         mov      eax,[a2]  ;// EAX = значение второго аргумента
         mov      ebx,[a5]  ;// EBX = пятого
         push     eax       ;// здесь можно пользоваться стеком
         pop      ebx       ;// ^^^^
         ret                ;// уйти по адресу-возврата на метку @ret!
endp

К сожалению такие шутки не понимают функции системных библиотек типа Kernel32.dll и прочие. Однако для своих внутренних самопальных процедур метод вполне себя оправдывает, причём чем больше мы передаём аргументов, тем в более выигрышном положении оказываемся. Попробуйте натравить профайлер на вызовы с фиктивным и обычным стеком, и легко убедитесь в этом.


2. Список программ в системном реестре

Однажды мне потребовалось создать список установленных в системе программ, и как оказалось – задача эта совсем не тривиальная. ПродумАны из мелкософт создали спец.либу для установки, получения списка и удаления программ под названием msi.dll (Microsoft Installer, см.дир System32), но этот герой с подмоченной репутацией на костылях, и воркает исключительно с продуктами самой Microsoft, пропуская ниже радаров программы сторонних разработчиков. Такие функции как MsiEnumProducts() и MsiGetProductInfo() обнаруживают только те прожки, которые система зарегистрировала у себя назначив им глобальный идентификатор GUID. Здесь уместно вспомнить выражение: "грешат не заметно – постятся на показ".

Поэтому было решено искать альтернативные пути, и гугл предложил мне ознакомиться с веткой реестра: HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall. Как видим, её название говорит само за себя – она имеет столько (под)каталогов, сколько в системе зарегано удаляемых программ. Те, у которых вместо имени GUID, это доступные функциям MSI продукты Microsoft, а дальше уже идут программы с вполне дружелюбными именами. В свойствах каждого дира будем наблюдать два строковых значения: "DisplayName" и "UninstallString", что позволит нам не только создавать списки установленного софта, но и в избирательном порядке удалять любой из них:


Reg_01.png


Так по волею судьбы пришлось парсить системный реестр, хоть я не отношу себя к сторонникам подобных методов: во-первых лучше доверить эту работу готовым системным функциям, а во-вторых – иногда доступ к реестру требует админских прав, что ущемляет наши собственные моральные права. Но поскольку ситуация зашла в тупик, пришлось смириться.

Функции API для работы с реестром изначально были сосредоточены в библиотеке advapi32.dll, но начиная с Vista их копии перекочевали и в Kernel32.dll. Это было сделано для удобства, чтобы наши приложения не тянули за собой ворох лишних библиотек. Конкретно в данном случае, мою проблему решили аж 7 функций, а в одной из них я обнаружил и нюанс, который изрядно потрепал мне нервы. Вот общий алгоритм программы:


1. GetSystemRegistryQuota() – в двух своих параметрах возвращает текущий, и макс.возможный размер реестра в системе (т.н. квота).
2. RegOpenKeyEx() – открывает ветку реестра "..\Uninstall", и возвращает её дескриптор. Чтобы была возможность читать значение ключей и перечислять (под)каталоги, нужно указать в параметре(4) этой функции флаги "KEY_QUERY_VALUE=1" + "KEY_ENUMERATE_SUB_KEYS=8".
3. RegQueryInfoKeyA() – передаём ей дескриптор открытой на предыдущем этапе ветки, и получаем взамен кол-во (под)каталогов в ней. Позже, это число будем использовать в качестве счётчика, для организации цикла перечисления всех программ. В доках на эту функцию, счётчик назвали "индексом" начиная с нуля, однако ничто не мешает вести отсчёт и в обратную сторону, т.е. от общего числа (под)каталогов и до нуля включительно.
4. •••• RegEnumKeyEx() – с этой функции начинается цикл! Она возвращает имя очередного (под)каталога в ветке "Uninstall", и на каждой итерации требует во-втором параметре изменения индекса на 1. Здесь нужно быть внимательней – именно с ней у меня случился конфуз! Дело в том, что в четвёртом её параметре мы указываем линк на переменную с макс.размером буфера для строки с именем =256, а на выходе функция сбрасывает в эту-же переменную кол-во символов в текущей найденной строке. Таким образом, если внутри цикла не восстанавливать размер буфера 256, то в ней остаётся возвращённое от предыдущего вызова значение и функция может возвратить ошибку, типа "выделенный буф слишком мал". Не зря говорят, что пара часов дебага в отладчике, избавят нас от пяти минут чтения документации. Вот это как-раз мой случай..
5. lstrcat() – на данном этапе, к строке с основной веткой реестра "Uninstall" нужно добавить имя найденного (под)каталога, чтобы сформировать полный путь до папки с программой. Для этого применяем конкатенацию и "женим" две строки.
6. RegOpenKeyEx() – эту функцию мы вызываем уже повторно (см.пункт 2). Если предыдущая возвратила нам дескриптор ветки, то теперь нужно получить дескриптор (под)каталога из этой ветки, чтобы при помощи следующей функции считать в свойствах требуемое значение.
7. RegQueryValueExA() – ищет свойство по имени (в нашем случае "DisplayName"), и возвращает его значение в буфер. Не редки случаи, когда мы удалили программу, а её пустой каталог остался в реестре. Соответственно и свойства, которое мы ищем в нём не будет. Функция даёт об этом знать ошибкой "ERROR_FILE_NOT_FOUND=2", и мы должны пропускать такие каталоги.
8. •••• В хвосте цикла уменьшаем индекс на 1 для функции RegEnumKeyEx() (т.к.отсчёт у нас обратный) и если он не перевалил за нуль = FFFFFFFFh, то уходим на п.4 для обхода всего древа.

Если мы не наделаем ошибок и угомоним системного агента правильными параметрами функций, то данный алгоритм вернёт нам полный список удаляемых программ, и остаётся лишь выводить их на консоль или в форточку мастдая. При необходимости можно применить и более жёсткие меры, например снести к чертям неугодную прожку, опираясь на свойство "UninstallString". Вот как выглядят реалии:

C-подобный:
format   pe console
entry    start
include  'win32ax.inc'
;//-------
.data
key         db  'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
subKey      rb  256
hKey        dd  0
hsubKey     dd  0

index       dd  0
counter     dd  1
maxReg      dd  0
curReg      dd  0

align 16
lpName      rb  256
nameLen     dd  256
buffLen     dd  256
buff        db  0
;---------
.code
start:   invoke  SetConsoleTitle,<'*** Install programm list v1.0 ***',0>

;// Квота реестра!
         invoke  GetSystemRegistryQuota,maxReg,curReg
         shr     [maxReg],20   ;//<--- в Мегабайтах
         shr     [curReg],20
        cinvoke  printf,<10,' Max registry size:  %4d Mb',\
                         10,' Current size.....:  %4d Mb',10,0>,[maxReg],[curReg]

;// Открыть ветку реестра "..\Uninstall"
         invoke  RegOpenKeyEx,HKEY_LOCAL_MACHINE, key,0,\
                              KEY_QUERY_VALUE + KEY_ENUMERATE_SUB_KEYS,\
                              hKey

;// Вычислить кол-во подразделов в ней (нули - типы информации)
         invoke  RegQueryInfoKeyA,[hKey],0,0,0,index,0,0,0,0,0,0,0
        cinvoke  printf,<10,' Registry Key: Uninstall. Total subKey = %d',\
                         10,' -------------------------------------------',10,0>,[index]
         dec     [index]
;//<---8<---- ЦИКЛ -----8<---------8<--------------------
@@:      mov     ecx,256/4       ;//<---- Очистить буфера
         push    ecx ecx
         xor     eax,eax
         mov     edi,lpName
         rep     stosd
         pop     ecx
         mov     edi,buff
         rep     stosd
         pop     ecx
         mov     edi,subKey
         rep     stosd

         mov     [nameLen],256   ;//<--- Внимание! Обновить размеры буферов
         mov     [buffLen],256

;// Получить имя очередного (под)раздела в разделе "Uninstall"
         invoke  RegEnumKeyEx,[hKey],[index],lpName,nameLen,0,0,0,0

         mov     byte[subKey],'\'
         invoke  lstrcat,key,lpName  ;//<--- поженить две строки

;// Открыть его на чтение\энумерацию
         invoke  RegOpenKeyEx,HKEY_LOCAL_MACHINE,key,0,\
                              KEY_QUERY_VALUE + KEY_ENUMERATE_SUB_KEYS,\
                              hsubKey

;// Запросить в буфер нужное свойство
         invoke  RegQueryValueExA,[hsubKey],<'DisplayName',0>,0,0,buff,buffLen
         cmp     eax,2           ;//<--- ERROR_FILE_NOT_FOUND = пустой каталог
         je      @notFound

         invoke  CharToOem,buff,buff  ;//<---- на случай, если встретится имя в кириллице
        cinvoke  printf,<10,' %03d.  %s'>,[counter],buff
         inc     [counter]       ;//<---- счётчик найденных +1
@notFound:
         dec     [index]         ;//<---- следующий индекс..
         cmp     [index],-1      ;//<---- проверить его не переполнение
         jnz     @b              ;//<---- нет: повторить.

@exit:  cinvoke  _getch
        cinvoke  exit,0
;//-------
section '.idata' import data readable
library   msvcrt,'msvcrt.dll',kernel32,'kernel32.dll',user32,'user32.dll'
include  'api\msvcrt.inc'
include  'api\kernel32.inc'
include  'api\user32.inc'

Reg_02.png


Обратите внимание, что функция RegQueryInfoKeyA() нашла всего 103 (под)каталога в ветке "Uninstall", и судя по логу 13 из них пустые – итого имеем 90 установленных в системе программ, где файлы приплюснутого си отнимают львиную долю. Такие дела..


3. Базовые операции с текстом

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

• Если нас интересует регистр символов, то ставка здесь делается на их расположении в таблице ASCII. Заглянув в неё можно обнаружить, что разница между кодами прописных и заглавных составляет ровно 20h. Например код латиницы(А) в верхнем регистре (заглавной) равен 41h, а её-же прописной равен 61h. Аналогичный сдвиг наблюдаем и со-всеми остальными буквами, хоть в латинице, хоть в кириллице. Такой расклад наводит на мысль, что таблицу составляли отнюдь не глупые люди.


ASCII.png


Теперь, если принять во-внимание, что 20h равно в двоичном 0010.0000, то оперируя всего одним битом(5) можно с лёгкостью менять регистр с верхнего на нижний и обратно. Для бинарных операций в ассемблере имеются свои инструкции – OR взводит указанный в маске бит (прописные +20h), а AND его сбрасывает (заглавные -20h).

C-подобный:
format   pe console
entry    start
include 'win32ax.inc'
;//----------
.data
inpBuff    rb  128
bigBuff    rb  128
smallBuff  rb  128

inpHndl    dd  0
strLen     dd  0
buff       db  0
;//----------
.code
start:  invoke  SetConsoleTitle,<'*** Big\Small text example ***',0>

;// Запросить дескриптор ввода для ReadConsoleA()
        invoke  GetStdHandle,STD_INPUT_HANDLE
        mov     [inpHndl],eax

;// Запрос на ввод строки в буфер
       cinvoke  printf,<10,' String:  ',0>
        invoke  ReadConsoleA,[inpHndl],inpBuff,128,strLen,0

;// Перевод в верхний регистр
        mov     ecx,[strLen]  ;// длина строки\цикла
        mov     esi,inpBuff   ;// источник
        push    ecx esi       ;// (про запас..)
        mov     edi,bigBuff   ;// приёмник
@@:     lodsb                 ;// AL = очередной символ из ESI
        cmp     al,'A'        ;// фильтр букв, отсеивая цифры и знаки
        jb      @fuck1        ;//  ^^^^ (меньше Below)
        cmp     al,'z'        ;//   ^^^^
        ja      @fuck1        ;//    ^^^^ (больше Above)
        and     al,11011111b  ;// сбросить бит(5) маской
@fuck1: stosb                 ;// записать в приёмник EDI
        loop    @b            ;// промотать цикл ECX-раз..

;// Перевод в нижний регистр
        pop     esi ecx
        mov     edi,smallBuff
@@:     lodsb
        cmp     al,'A'
        jb      @fuck2
        cmp     al,'z'
        ja      @fuck2
        or      al,00100000b  ;//<--- взвести бит(5)
@fuck2: stosb
        loop    @b

;// Результат операций
       cinvoke  printf,<10,' Big...:  %s',\
                           ' Small.:  %s',0>,bigBuff,smallBuff

@exit: cinvoke  _getch
       cinvoke  exit,0
;//---------------
section '.idata' import data readable
library  msvcrt,'msvcrt.dll',kernel32,'kernel32.dll'
include 'api\msvcrt.inc'
include 'api\kernel32.inc'

Bit5.png


• Избавиться от лишних пробелов в строке – ещё одна часто встречающаяся задача.
В виду того-что готовой функции API для этих целей в природе не существует, всё приходится делать в ручную. Суть в том, чтобы запоминать предыдущий символ, и сравнивать его с текущим. Если оба пробелы, то пропускаем перезапись текущего в буфер, иначе всё в штатном режиме, без изменений. Вот простая как 2-копейки реализация, зато пользу от неё можно наблюдать в консоли:


C-подобный:
format   pe console
entry    start
include 'win32ax.inc'
;//----------
.data
inpBuff    rb  128

inpHndl    dd  0
strLen     dd  0
buff       db  0
;//----------
.code
start:  invoke  SetConsoleTitle,<'*** Space killer ***',0>

;// Запросить дескриптор ввода для ReadConsoleA()
        invoke  GetStdHandle,STD_INPUT_HANDLE
        mov     [inpHndl],eax

;// Запрос на ввод строки в буфер
       cinvoke  printf,<10,' String:  ',0>
        invoke  ReadConsoleA,[inpHndl],inpBuff,128,strLen,0

;// Парсим строку на лишние пробелы --------------------------
        mov     ecx,[strLen]  ;// длина строки\цикла
        mov     esi,inpBuff   ;// источник
        mov     edi,esi       ;// приёмник

@@:     lodsb                 ;// AL = очередной символ
        cmp     al,' '        ;// это пробел?
        jne     @miss         ;// нет: пропускаем
        cmp     ax,'  '       ;// да: тест с предыдущим
        je      @next         ;// 2 пробела - пропускаем
@miss:  stosb                 ;// перезапись символа
@next:  xchg    ah,al         ;// запомним текущий символ
        loop    @b            ;// мотаем цикл по длине ЕСХ..

        mov     byte[edi],0   ;// вставить маркер конца стоки
;//-----------------------------------------------------------
;// Результат
       cinvoke  printf,<10,' Result:  %s',0>,inpBuff

@exit: cinvoke  _getch
       cinvoke  exit,0
;//---------------
section '.idata' import data readable
library  msvcrt,'msvcrt.dll',kernel32,'kernel32.dll'
include 'api\msvcrt.inc'
include 'api\kernel32.inc'

Space.png



4. Заключение.

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

Вложения

  • OldChest_3.zip
    2,5 КБ · Просмотры: 99

Muxtar

Green Team
02.06.2021
131
53
Приятно видеть такого опытного человека, темболее ассемблерца. Лайк однозначно за труду :)
 
  • Нравится
Реакции: kryakozabr и Marylin

Hardreversengineer

Green Team
20.08.2019
48
26
Тимур, спасибо за статью! Когда читаю Ваши статьи, особенно с такими отсылками, мне кажется что я учусь у какого-то древнего восточного мудреца.
 

DragonFly

Green Team
01.06.2020
222
59
• Если нас интересует регистр символов, то ставка здесь делается на их расположении в таблице ASCII. Заглянув в неё можно обнаружить, что разница между кодами прописных и заглавных составляет ровно 20h. Например код латиницы(А) в верхнем регистре (заглавной) равен 41h, а её-же прописной равен 61h. Аналогичный сдвиг наблюдаем и со-всеми остальными буквами, хоть в латинице, хоть в кириллице. Такой расклад наводит на мысль, что таблицу составляли отнюдь не глупые люди.
да, как показывает практика, все гениальное - просто
главное разобраться
вспоминаю деление в ассемблере из предыдущих техник из сундука логическими операциями
спасибо за статью!
пс. @Mikl___ книгу, которую рекомендовал, "Алгоритмические трюки для программистов" заказал ;)
 
Последнее редактирование:
  • Нравится
Реакции: kryakozabr и Muxtar

Muxtar

Green Team
02.06.2021
131
53
да, как показывает практика, все гениальное - просто
главное разобраться
вспоминаю делание в ассемблере из предыдущих техник логическими операциями
спасибо за статью!
пс. @Mikl___ книгу, которую рекомендовал, "Алгоритмические трюки для программистов" заказал ;)
А учить Ассемблер не сложно?
 

DragonFly

Green Team
01.06.2020
222
59
А учить Ассемблер не сложно?
Все сложно, простого нет ничего))) Питон вроде не сложный, но там куча библиотек, каждая как отдельный язык. Вот Си взять, количество ключевых слов и операторов не большое, но считается, что он не легкий..
Я вот буду си учить, а там плюс минус с ассемблером надо будет сталкиваться, чтоб лучше все понимать . И вообще, надо учить компьютер сайенс, как можно сейчас называть, все сразу на места становится, имею в виду понимание процессов. Даже элементарное понятие, почему индексация с 0, а не 1
@Marylin , я прав?
 
  • Нравится
Реакции: Muxtar

Hardreversengineer

Green Team
20.08.2019
48
26
А учить Ассемблер не сложно?
Если прочтёте всю серию статей на этом форуме, даже без поисках на других форумах и даже без книг - будете знать на достаточном уровне, чтобы самостоятельно писать готовые, работающие программы. То есть, вот просто берёте и ищите самую раннюю статью (они ещё подряд в одном топике идут, так что не забывайте проматывать вниз) и читаете. Там от самых основ очень понятным языком, без мыла и воды, с примерами которые сразу компилируются в FASM. Особенно важно чувствовать это. Поэтому, возьмите скачайте компилятор, и для начала просто вставляйте готовые программы, компилируйте, балуйтесь. От части даже будет вам понятно интуитивно. А потом что нибудь меняйте. Если проникнитесь, если понравится - будете не только жонглировать битами и находить скрытые от глаз других программистов способы решения задач, но может и дойдёте до того уровня, когда сможете пользоваться дизассемблерами и дебаггерами для анализа уже существующих программ. Это многого стоит. Не ждите сразу результат, побалуйтесь с примерами в папке EXAMPLES, вдруг вам понравится как в своё время понравилось мне.
 

rusrst

Green Team
04.10.2020
21
26
Классная статья. Like за возврат из call по jmp. Я похожее для avr реализовывал, но там возвращался не из функции, а из аппаратного прерывания, но принцип тот же. Надо было проверять событие и в зависимости от него прыгать в свою часть программы.

Все сложно, простого нет ничего))) Питон вроде не сложный, но там куча библиотек, каждая как отдельный язык. Вот Си взять, количество ключевых слов и операторов не большое, но считается, что он не легкий..
Я вот буду си учить, а там плюс минус с ассемблером надо будет сталкиваться, чтоб лучше все понимать . И вообще, надо учить компьютер сайенс, как можно сейчас называть, все сразу на места становится, имею в виду понимание процессов. Даже элементарное понятие, почему индексация с 0, а не 1
@Marylin , я прав?
Если вы пишете обычное ПО на си, то ассемблер вы будете видеть чуть реже чем никогда. А если для встраиваемых систем, то иногда (но не особо так то и часто) нужно будет смотреть сгенерированный код. Но там нюансы что архитектуры есть разные (arm, pic, avr, mcs-51) и у всех есть нюансы.
 
  • Нравится
Реакции: Marylin и DragonFly

Marylin

Mod.Assembler
Red Team
05.06.2019
291
1 267
@rusrst если вы поделитесь с нами вашими наработками AVR было-бы классно.
 

rusrst

Green Team
04.10.2020
21
26
@rusrst если вы поделитесь с нами вашими наработками AVR было-бы классно.
На самом деле про avr написано очень много. Есть специализированные сайты (вроде easyelectronica, hubstab и прочих) и достаточно подробные книги. Поэтому особо писать того, что не было бы кем-то описано и нечего.
 

Hardreversengineer

Green Team
20.08.2019
48
26
На самом деле про avr написано очень много. Есть специализированные сайты (вроде easyelectronica, hubstab и прочих) и достаточно подробные книги. Поэтому особо писать того, что не было бы кем-то описано и нечего.
Что в качестве транслятора использовали?
 

rusrst

Green Team
04.10.2020
21
26
Что в качестве транслятора использовали?
Если Вы за ide, то я atmelstudia 6.1/6.2 пользовался и сейчас пользуюсь. А компилятора(транслятора) там по факту нет, это ж ассемблер. Под си есть avr-gcc.
 

Marylin

Mod.Assembler
Red Team
05.06.2019
291
1 267
Практика и опыты интересуют, вы видимо в теме

@Hardreversengineer с такой классной программой поделился,
которая делает инклуды на автомате, если позволит я выложу сюда,.. рульная вещь
 
  • Нравится
Реакции: rusrst

Hardreversengineer

Green Team
20.08.2019
48
26
@Hardreversengineer с такой классной программой поделился,
которая делает инклуды на автомате, если позволит я выложу сюда,.. рульная вещь
Я её не создавал, случайная находка. Я не знаю её автора, её текст к Вам отправил без изменений. По идее, разрешение нужно узнавать у автора. Но так как текст программы был взят из открытых источников, я думаю без коммерческого интереса и без изменений выложить можно. Я же лично, ни в коем случае ничему не препятствую.
 
  • Нравится
Реакции: Marylin и rusrst
Мы в соцсетях: