Кто там кодит? - Приближенный поиск заданной последовательности байт!

Miracle #03
    (c) Wocen/Triumph
    ------------------

   Приближенный поиск заданной последовательности байт !!!

   Недавно ломая какую-то защиту, или что-то еще, столкнулся
с  небольшой  проблемой.  Нужно  было  найти заданный стринг
(последовательность  байт).  Ну  и  что  за проблема, скажет
уважаемый  читатель,  сравнивай  побайтово  и все, вплоть до
получения результата. А вся проблема в том, что я не знал на
100% есть ли заданная последовательность в исходном файле. И
вот  тогда,  то  и  пришла  в  голову  идейка написать поиск
последовательности максимально приближенной к заданной. Суть
метода очень проста.


   1) Берем исходный текст:

Исходный текст   : I'am wocen !
Шестнадцатиричное: #49,#27,#61,#6D,#20,#77,#6F,#63,#65,#6E


   2) Задаем последовательность которую нужно найти:

Заданная последовательность: Wocen
В шестнадцатиричной системе: #57,#6F,#63,#65,#6E

   Видно что простейшее побайтовое сравнение ничего не даст.

   3)  Берем заданный стринг и проксориваем его по тексту (с
начала текста). Имеем:

   #57 #6F #63 #65 #6E
XOR
   #49 #27 #61 #6D #20
 = -------------------
   #1E #48 #02 #08 #4E
 
   4) Суммируем количество оставшихся бит равных единице !

  #1E = 4 бита
+ #48 = 2 бита
+ #02 = 1 бит
+ #08 = 1 бит
+ #4E = 4 бита
= --------------
       12 бит (единичек), ошибок.

   5)  Запоминаем адрес в тексте с которого было сравнение и
сохраняем число ошибок = 12.

   Далее  мы  повторяем  те  же  операции но только уже не с
начала текста, а со смещением 1. Т.е. будет:

   #57 #6F #63 #65 #6E
XOR
   #27 #61 #6D #20 #77
 = -------------------
   #48 #02 #08 #4E #19

11 бит (единичек)


   6) В этот раз мы смотрим: ага количество единичек меньше,
чем  в первый раз, значит вероятность того, что нашли нужную
последовательность  выше! Сохраняем адрес из текста, и коли-
чество ошибок.

   Так  повторяем  для  всего текста. Подробнее остановимся,
когда  адрес  в  тексте показывает на стринг "wocen" который
отличается от заданного "Wocen" только первой буквой.

   #57 #6F #63 #65 #6E = "Wocen"
XOR
   #77 #6F #63 #65 #6E = "wocen"
 = -------------------
   #20 #00 #00 #00 #00

   Количество ошибок = 1, т.к. только 1 бит равен 1. Так как
количество  ошибок  меньше предыдущего то, сохраняем адрес в
тексте, и ошибки.

   По  окончании текста берем адрес который нашли в процессе
работы и выдаем его пользователю!!!

   Где можно еще использовать эту програмку ?

   Да  где  угодно. Например в том же ассемблере, разумеется
не    вместо   побайтового  сравнения,  а  дополнительно  (в
нагрузку  >;-))  Так  как  по своему опыту знаю, при наличии
уже   4   файлов  исходного  текста,  метки  имееют свойство
забываться  а вспомнить ее точное/полное название уже и не в
силах.  Вот  здесь-то  конкретно  и  пригодится приближенный
поиск.

   Кроме   того  приближенный  поиск  можно  использовать  в
программах  для  перегонки  'Экран' в 'Текст'. Кстати на мой
субъективный   взгляд  наилучшей  из  существующих  подобных
программ  является программа "Screen to text transformer" by
Death  Moroz/Сланцы.  У меня еще есть пара аналогичных прог,
но  они  настолько убоги, что о них лучше вообще умолчать. В
"Screen  to text transformer" использован обычный побайтовый
сравнитель,   и   когда  пришлось  им  воспользоватся,  было
довольно   неприятно   учить   программу  что  буква  "а"  с
утолщением, одно и то-же, что "а" без утолщения, и так почти
со  всеми  символами.  А  если-бы  был использован побитовый
метод  то к клавиатуре вообще можно было не прикасаться, уча
программу.  Хотя возможно какие-то буквочки были-бы заменены
другими  :-)  Так  что на будущее, если кто захочет написать
класный  перекодировщик  из картинки в текст, то пусть учтет
несколько моментов:

   1 - Должна  быть возможность перекодировать цветные и ч/б
экраны,с кодом цвета 16 (#10) и после него сам цвет.
   2 - Порядок загрузки должен идти согласно пометке файлов.
   3 - Шрифтов  для  сравнения  должно-быть 2-3, для каждого
       размера  шрифта, т.е. 3 шрифта размером 4x8, 6x8, 8x8
       ну и можно сделать 3 шрифта размером 5x8.
   4 - Буфер  под  получаемый текст 16 кб, с контролем пере-
       полнения.
   5 - Муза просто необходима.
   6 - Ну  и  наконец кроме задания одного какого-то размера
       шрифта  на картинку, должна быть возможность комбини-
       рованной работы. Как определить каким шрифтом напеча-
       тано?  Элементарно!!!  Смотрим  вертикальный столбик,
       если  он  нулевой  то с вероятностью 90% можно утвер-
       ждать что это граница символа. Далее пододвигаем оче-
       редной  символ,  смотрим его длину и ищем среди теку-
       щего размера.

   А  теперь сам исходник приближенного поиска с комментари-
ями.  Исходник писан в Alasm'e с использованием его специфи-
ческих команд.

;-----------------------------------------------------------
;WRITTEN BY WOCEN/ORION/TRIUMPH - МАЙ 1998
;СКОРОСТЬ ПОИСКА ЗАДАННОЙ ПОСЛЕДОВАТЕЛЬНОСТИ ПРИМЕРНО:
;3270 БАЙТ В 1 СЕКУНДУ (БЕЗ ТУРБЫ), НА 5 БАЙТ
;1630 БАЙТ В 1 СЕКУНДУ (БЕЗ ТУРБЫ), НА 10 БАЙТ
; 850 БАЙТ В 1 СЕКУНДУ (БЕЗ ТУРБЫ), НА 20 БАЙТ
;4680 БАЙТ В 1 СЕКУНДУ (В ТУРБЕ, СКОРПИОНОВСКОЙ), НА 5 БАЙТ
;2520 БАЙТ В 1 СЕКУНДУ (В ТУРБЕ), НА 10 БАЙТ
;1260 БАЙТ В 1 СЕКУНДУ (В ТУРБЕ), НА 20 БАЙТ

ORG     #8000

ADR_TXT EQU     #0000           ;АДРЕС НАЧАЛА ПОИСКА 
LEN_TXT EQU     32768           ;ДЛИНА ПОИСКА      
LEN     EQU     5               ;ДЛИНА ЗАДАННОГО СТРИНГА

DI
XOR     A
DEC     A               ;СТАВИМ ПРИ СТАРТЕ МАКС.
LD      (OLD+1),A       ;ОШИБКУ = 255 (#FF)
LD      BC,LEN_TXT      ;ДЛИНА ПОИСКА
EXX

LD      DE,ADR_TXT      ;НАЧАЛЬНЫЙ АДРЕС ПОИСКА
L1      LD      B,LEN           ;ДЛИНА ПОСЛЕДОВАТЕЛЬНОСТИ
LD      HL,TEXT         ;НАЧАЛО ПОСЛЕДОВАТЕЛЬНОСТИ
PUSH    DE              ;СОХРАНИЛИ ТЕКУЩИЙ АДРЕС
                                ;ТЕКСТА
L2      LD      A,(DE)          ;БАЙТ ДАННЫХ
XOR     (HL)            ;СВЕРЯЕМ

ERRORS  LD      C,0             ;КОЛИЧЕСТВО ОШИБОК

;Команда DUP повторяет кусок кода после себя до директивы
;EDUP заданное в DUP раз.
;В этом случае раскрытие цикла позволило немного ускорить
;одну из _ДОХРЕНА_ВРЕМЯ_СЪЕДАЮЩИХ_ подпрограмм для работы.

DUP     8               ;ПРОВЕРЯЕМ ВСЕ 8 БИТ
RRA                     ;БИТ = 0 ?, (Т.Е. СОВПАЛ)
JR      NC,$+2+1;ПЕРЕХОД ЕСЛИ СОВПАЛ
INC     C               ;УВЕЛИЧИВАЕМ КОЛИЧЕСТВО НЕ
EDUP                    ;СОВПАДЕНИЙ (ОШИБОК)

LD      A,C
LD      (ERRORS+1),A    ;СОХРАНЯЕМ НЕ СОВПАДЕНИЯ
INC     HL              ;УВЕЛИЧИВАЕМ АДРЕС ЗАДАННОГО
                                ;СТРИНГА
INC     DE              ;УВЕЛИЧИВАЕМ АДРЕС ТЕКСТА
DJNZ    L2              ;ПОВТОР
POP     DE              ;ВОССТАНОВИЛИ АДРЕС ТЕКСТА

OLD     LD      A,#FF           ;СТАРЫЙ РЕЗУЛЬТАТ
CP      C               ;МЕНЬШЕ ЧЕМ НОВЫЙ ?
JR      C,L3            ;ДА !
JR      Z,L3            ;РАВЕН ! => ОСТАВЛЯЕМ СТАРЫЙ
                                ;РЕЗУЛЬТАТ
LD      А,C             ;ВЗЯЛИ НОВЫЙ РЕЗУЛЬТАТ
LD      (OLD+1),A       ;СОХРАНЯЕМ ЕГО
LD      (ADRES+1),DE    ;СОХРАНЯЕМ ЕГО АДРЕС В
                                ;ТЕКСТЕ
AND     A               ;НОВЫЙ РЕЗУЛЬТАТ СОВПАЛ БЕЗ
                                ;ОШИБОК ?
JR      Z,ADRES         ;ПЕРЕХОД ЕСЛИ ДА !

;Сюда можно будет вставить кусок программы приведенный ниже

L3      XOR     A
LD      (ERRORS+1),A    ;ОБНУЛЯЕМ ОШИБКИ
INC     DE              ;УВЕЛИЧИВАЕМ АДРЕС В ТЕКСТЕ

EXX
DEC     BC              ;УМЕНЬШАЕМ ОБРАБАТЫВАЕМУЮ
LD      A,B             ;ДЛИНУ
OR      C
EXX
JR      NZ,L1           ;ПЕРЕХОД ЕСЛИ НЕ ЗАВЕРШИЛИ

ADRES   LD      HL,0            ;НА ВЫХОДЕ ЗДЕСЬ АДРЕС В
                                ;ТЕКСТЕ,
LD      A,(OLD+1)       ;А ЗДЕСЬ КОЛИЧЕСТВО ОШИБОК !
RET

TEXT    DEFB    "Wocen"         ;ЗАДАННЫЙ СТРИНГ


   Ну   как  вы  видно  из  исходника  выход  из  процедурки
осуществиться  только если будет 100% совпадение или пока не
перелопатим  всю  заданную  память.  Поэтому  можно вставить
следущий кусок программы перед меткой 'L3':


ERROR   EQU     4               ;ПРЕДЕЛ ДЛЯ ОШИБКИ
                                ;Т.Е. ЕСЛИ МЕНЬШЕ ЭТОГО
                                ;ВСТРЕТИТЬСЯ ОШИБКА, ТО
                                ;БУДЕМ СЧИТАТЬ ЧТО ЭТО
                                ;НОРМАЛЬНО. ТЕМ САМЫМ
                                ;РЕГУЛИРУЕМ ЧУВСТВИТЕЛЬНОСТЬ
...
CP      ERROR           ;ПРОВЕРКА НА ПРЕДЕЛ ОШИБКИ
JR      C,ADRES         ;ЕСЛИ МЕНЬШЕ ЗАДАННОГО
                                ;ПРЕДЕЛА, ТО ПЕРЕХОД
L3      ...                     ;ПРОДОЛЖЕНИЕ ПРОГИ


   Разумеется этот код не претендует на крутость и наверняка
можно ускорить его применив чтение байт из памяти стеком, ну
и наверное заменив все 'JR' на 'JP'.

   Ссылка  на  автора  при  перепечатывании обязательно, при
использовании желательна.


              ---===Питательной вам пищи===---