Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Низкоуровневое программирование > Assembler - Ассемблер (FASM, MASM, WASM, NASM, GoASM, Gas, RosAsm, HLA) и не рекомендуем TASM
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 31.07.2016, 14:16   #31
R71MT
Участник клуба
 
Аватар для R71MT
 
Регистрация: 16.06.2011
Сообщений: 1,428
По умолчанию

Значит шифрование готовой программы..
Как говорилось выше, при двойном шифровании порядок следования ключей роли не играет, если ключи одинаковые. Но это не наш случай! У нас разные алгоритмы шифрований. Это означает, что нужно придерживаться правил работы со-стеком: первым пришёл, последним уйдёшь.

В нашей программе первым дешифрует криптор(1) ключом(A7h), поэтому шифровать этим ключом мы должны в последнюю очередь. Сначала нужно зашифровать все процедуры, причём каждую из них отдельно, с начальным значением ключа(0). Уже потом зашифруем всё тело программы. Для процедур получится двойное шифрование, да ещё и с разными алгоритмами.

При шифровании нужно быть предельно осторожными, чтобы не зашифровать лишний байтик. Это может обломать нас по-полной. Нам нужно вычислить точный размер шифруемых процедур, и адрес их начала. Адрес конца не обязателен, т.к. есть длина. На всякий/пожарный нужно иметь ввиду, что все процедуры заканчиваются командой(RET/RETN), опкод которой имеет значение(С3h).

Тут произошёл небольшой конфуз с процедурами! Статья сырая и пишу её на-ходу, опираясь только на теорию не проверив на практике. Одним словом, нужно на входе в процедуры сохранять/восстанавливать содержимое регистра(DX), т.к. криптор(2) использует его в качестве счётчика. В идеале, нужно сохранять/восстанавливать вообще все регистры командами(Pusha/Popa), но здесь нам хватит и одного(DX). Причём внутри процедуры 'Message2' есть 'Hex2Asc', которая является отдельной процедурой. Её нужно обработать отдельно. Законченный вариант будет такой:
Код:
Message2:
     push  dx                ; запомнить
     mov   ah,9
     mov   dx,sizeMess
     int   21h
     pop   dx                ; восстановить

     mov   si,start
     lea   ax,[end_Start - start]
Hex2Asc:
     push  dx                ; запомнить
;[......]
     pop   dx                ; восстановить
     ret
end_Message2:
     dw    0f0fh,9090h,1234h,8520h
Исправив мои косяки в трёх процедурах, двигаемся (по-тропе шифрования) дальше..

На подготовительном этапе удобно вставить в хвост исходника одну полезную/отладочную функцию, которая вычислит начальные адреса процедур и их длинну. Вставлять эту функцию нужно обязательно в хвост, иначе адреса изменятся и мы получим неверный результат:
Код:
;.......
copyCryptor2:      ;<--------; Сразу копируем криптор(2) в стек!
     sub   sp,length         ; Выделяем фрейм
     mov   bp,sp             ; Запомним точку входа в него
;.......
     rep   stosb             ; Затираем строку опкодов нулями
     jmp   Begin    ;<-------; На точку входа в основное тело..

; ------------------------------------------------------------------
; C этого места начинается вспомогательная функция вычисления адресов
; Нужно закомментировать 'PUSH CRYPTOR1' в голове (после ORG 100h),
; изменив его на 'PUSH testFunc'.
; После отладки - изменения нужно будет восстановить.

testFunc:
     call  @f
     db    13,10,'Proc count: 3'
     db    13,10,'--------------------'
     db    13,10,'           Addr Size'
     db    13,10,' Message1: $'
@@:  pop   dx
     call  outString                       ; Вывод мессаги
     mov   ax,Message1                     ; Адрес функции
     call  Print                           ; Выводим АХ с тире
     lea   ax,[end_Message1 - Message1]    ; Длина функции
     call  Hex2Asc                         ; Выводим АХ

     call  @f
     db    13,10,' Message2: $'
@@:  pop   dx
     call  outString
     mov   ax,Message2
     call  Print
     lea   ax,[end_Message2 - Message2]
     call  Hex2Asc

     call  @f
     db    13,10,'  inpBuff: $'
@@:  pop   dx
     call  outString
     mov   ax,inpBuff
     call  Print
     lea   ax,[end_inpBuff - inpBuff]
     call  Hex2Asc

     call  @f
     db    13,10,'--------------------'
     db    13,10,'Full size: $'
@@:  pop   dx
     call  outString
     mov   ax,start
     call  Print
     lea   ax,[end_Start - start]
     call  Hex2Asc

     xor   ax,ax
     int   16h
     int   20h

outString:
     mov   ah,9
     int   21h
     ret
Print:
     call  Hex2Asc
     mov   al,'-'
     int   29h
     ret
Небольшие телодвижения избавили нас от ковыряния кода в поисках адресов. Обычно процедур в коде намного больше трёх и вычислять их адреса вручную явное самоубийство. Замечу, что текстовые строки хранятся здесь в потоке кода. Адрес их ПУШится, а потом ПОПится в регистр(DX) для fn.9h INT-21h. Таким образом сохраняются оригинальные смещения внутри программы и её размер. Вот результат работы этой/отладочной функции:
Код:
   Proc count: 3
   --------------------
              Addr Size
    Message1: 0220-000A
    Message2: 0232-0029
     inpBuff: 0263-000A
   --------------------
   Full size: 0104-0169
Получив нужную инфу избавляемся от этой функции в исходнике, и раскомментируем голову. Компилируем исходник по-новой, и получаем готовый файл, который будем шифровать.
Проделав всю/эту работу мы встречаем процесс шифрования во-всеоружии!
Нашедшего выход - затаптывают первым..
R71MT вне форума Ответить с цитированием
Старый 31.07.2016, 14:18   #32
R71MT
Участник клуба
 
Аватар для R71MT
 
Регистрация: 16.06.2011
Сообщений: 1,428
По умолчанию

Загружаем готовый COM-файл в HIEW. Шифрование начинаем с любой из понравившихся процедур, ..пусть будет 'Message1'. Как видно из отчёта выше, эта процедура начинается с адреса(220h) и заканчивается адресом(22Аh). Сам адрес(22Аh) не входит в процедуру, т.к. отсчёт с нуля, поэтому адресом конца будет(229h) с опкодом RET(C3h).

Юзая HIEW нужно учитывать, что он считает адреса не с ORG-100h (как у нас в программе), а с ORG-0h, поэтому адрес(220h) он будет отображать как 120h. Значит загрузив программу в HIEW жмём в его окне такую последовательность клавиш: [F4->F3->F5->120->Enter] и попадаем сюда:
Код:
  00000120: 52                 push      dx
  00000121: B409               mov       ah,009 
  00000123: BA0401             mov       dx,00104
  00000126: CD21               int       021
  00000128: 5A                 pop       dx
  00000129: C3                 retn
  0000012A: 3412               xor       al,012
  0000012C: 20850F0F           and       [di][00F0F],al
  00000130: 90                 nop
  00000131: 90                 nop
У-гу.. Это как-раз процедура 'Message1' которая выводит шапку. Смотрим, что по адресу(129h) действительно находится(RET), значит мы на правильном пути. Ставим курсор на адрес(120h) и жмём последовательность: [F3->F7] (Edit->Crypt). В ответ, HIEW просит нас указать алгоритм шифрования в окне 'Assembler'. Отвечаем ему так:
Код:
    xor   cx,cx
    xor   al,cl
    add   cl,3
    loop  2
..после этих команд, выходим по(Esc) из режима ввода алгоритма, и подтверждаем серьёзность наших намерений нажав(F7). HIEW перешёл в режим шифрования, для которого так-же юзается клавиша[F7]. Значит шифруем по(F7) все значения с адреса(120h), до адреса(129h) включительно. Зашифрованные значения меняют свой цвет на жёлтый, что означает ОК! Зашифровав последнее значение(С3h) в процедуре, сохраняем изменения клавишей(F9h). Всё.. Процедуру 'Message1' зашифровали! Зашифрованный код должен выглядеть так:
Код:
  00000120: 52                 push      dx
  00000121: B70F               mov       bh,00F
  00000123: B308               mov       bl,008
  00000125: 0E                 push      cs
  00000126: DF34               fbstp     t,[si]
  00000128: 42                 inc       dx
  00000129: D834               fdiv      d,[si]
  0000012B: 1220               adc       ah,[bx][si]
  0000012D: 850F               test      [bx],cx
  0000012F: 0F909052B4         seto      [bx][si][0B452]
Чтобы приступить к шифрованию следующей процедуры 'Message2', нужно полностью выйти из HIEW'а, и по-новой загрузить программу, чтобы задать шифровальщику опять начальное значение(0). Перед выходом не помешает проверить, может мы заправили рубашку в трусы, прихватив при шифровании лишний байтик. Нет!.. Всё красиво! Можно выходить..

Шифрование остальных процедур происходит по такой-же схеме.
Адреса процедур в окне HIEW'а будут следующими:

- начало 'Message1': 220h - 100h = 0120h
- конец 'Message1': 220h + 00Ah = 022Ah - 0101h = 0129h

- начало 'Message2': 232h - 100h = 0132h
- конец 'Message2': 232h + 029h = 025Bh - 0101h = 015Ah

- начало ' inpBuff': 263h - 100h = 0163h
- конец ' inpBuff': 263h + 00Ah = 026Dh - 0101h = 016Ch

- начало 'FullSize': 104h - 100h = 0004h
- конец 'FullSize': 104h + 169h = 026Dh - 0101h = 016Ch

Зашифровав все/три процедуры одинаковым алгоритмом, приступаем к шифровке всего тела, чтобы программа на диске лежала в зашифрованном виде. Тут ничего нового, только вместо 'Edit->Crypt', нужно будет заюзать 'Edit->Xor' с ключом(А7h), т.к. криптор(1) внутри программы расшифровывает тело именно этим ключом. Значит перезапустив HIEW открываем нашу программу с зашифрованными процедурами и жмём последовательность клавиш:

[F4->F2->F5->4->Enter->F3->F8->A7->Enter]

Эта комбинация перевела HIEW в режим шифрования 'XOR' с ключом(А7). Процесс шифрования привязан к клавише(F8). Шифруем все значения от макушки с адресом(0004) и до хвоста с адресом(016Ch) включительно. Шифруемые значения меняют свой цвет с голубого на жёлтый. Достигнув адреса(016Dh) сохраняем изменения по(F9), и с чувством выполненного долга выходим из HIEW'a по(F10).

Запустив на исполнение новоиспечённый/шифрованный файл обнаруживаем, что мы всё сделали правильно и рубашка у нас ни-в-каких, ни-в-трусах, о чём свидетельствует окно консоли. Радость переполняет нас, мы бежим за-пивом, но ...через час обнаруживаем, что программа уже кем-то взломана! Не успели мы выложить нашу софтину на варез, как все/наши мечты разлетелись в-пух-и-прах.

Мы потратили пару дней чтобы организовать всё/это дело, а взломщик выловил алгоритм за пару минут. Этот мир дермово устроен! Поиск ошибок при шифровании может послужить темой для следующего разговора..
Нашедшего выход - затаптывают первым..
R71MT вне форума Ответить с цитированием
Старый 31.07.2016, 16:10   #33
Mikl___
Участник клуба
 
Регистрация: 11.01.2010
Сообщений: 1,162
По умолчанию

Цитата:
Сообщение от R71MT Посмотреть сообщение
Мы потратили пару дней чтобы организовать всё/это дело, а взломщик выловил алгоритм за пару минут. Этот мир дермово устроен!
Тимур, а мысль не приходила в голову, что тема, ну как бы это понятно выразить, не для сайта http://programmersforum.ru, да и выкладывать свои наработки на паблике, а потом возмущаться, что взломщик выловил алгоритм за пару минут, чего ты ожидал?
Mikl___ вне форума Ответить с цитированием
Старый 31.07.2016, 16:41   #34
R71MT
Участник клуба
 
Аватар для R71MT
 
Регистрация: 16.06.2011
Сообщений: 1,428
По умолчанию

Mikl___, так тут мысль такая, что "как сделать" и "как будут ломать".
Само-собой, что тема для новичков, т.к. за всё время сколько я на этом сайте, в этом разделе не видел ни одной темы от профи. ..только студенты и тусуются.
Нашедшего выход - затаптывают первым..
R71MT вне форума Ответить с цитированием
Старый 01.08.2016, 15:14   #35
R71MT
Участник клуба
 
Аватар для R71MT
 
Регистрация: 16.06.2011
Сообщений: 1,428
По умолчанию

Наигравшись вдоволь в VXеров, перейдём на сторону AVеров, и посмотрим, как-бы кодокопатели пытались расшифровать наш файл, какие ошибки мы допустили, чего не нужно было делать. Заметим, что в природе соотношение тех кто пишет программы, и кто их ломает - идёт примерно 50:1. Интересным фактом является и то, что взломщики в этой войне выигрывают!

В общем случае, бесполезно защищать программу, т.к. её всё-равно сломают. Для этого даже ломать ничего не нужно, а достаточно просто найти координаты защитного механизма в коде и забить его NOP'ами (no operation). Но такой подход не везде срабатывает. Например зашифрованный/основной код не забьёшь пустой операцией(NOP), т.к. программа потеряет свою функциональность, и взломщику приходиться подбирать ключ, чтобы расшифровать это тело.

Однобайтный ключ (как в нашем случае) может иметь максимум FFh = 256 значений. Чтобы найти из 256-ти именно наш ключ, взломщику потребуется всего-лишь перебрать все ключи в диапазоне 0..256. Учитывая скорость выполнения операций современными ЦП это не займёт много времени. Следуя ходу его мыслей, приведём возможный алгоритм вычисления им, нашего ключа шифрования..

Для DOS-программ, в качестве самой/юзерской скан-строки может выступить обычный вызов прерывания INT-21h, как наиболее/часто встречаемый. Опкодом этой команды является значение(CD21h), которое и будет служить сигнатурой для поиска. Читаем зашифрованный файл в свой буфер и начинаем последовательно ксорить его слова ключами 0..256. После каждого ксора проверяем результ на 'CD21h'. Вот и весь алгоритм:
Код:
     mov   si, addr         ; Адрес буфера с зашифрованным файлом
     mov   cx, 0FFFFh       ; Макс.значение ключа (CH,CL = FFh)
findKey:
     lodsw                  ; Берём в АХ слово из буфера
     xor   ax, сx           ; Ксорим его
     cmp   ax, 21CDh        ; Это int-21h ?!
     je    okey             ; Да - наш клиент!
     dec   ch               ; Иначе: сл.ключ
     dec   cl               ; ^^^^
     jnz   findKey          ; Все ключи перепробовали? Нет - продолжить..
     ret                    ; Да  - Облом.

okey:            ;<---------; Нашли ключ! (CL = ключ)
     [.....]
Как видим - однобайтный ключ взломать, как два пальца об асфальт. Чем такое шифрование, лучше-уж никакого: пустая трата времени. Но посмотрим на вариант с 2-х и-более байтными ключами:

- 1 байт = 000000FFh = 256 вариантов ключей;
- 2 байта = 0000FFFFh = 65.535 вариантов ключей;
- 4 байта = FFFFFFFFh = 4.294.967.295 вариантов ключей;
- 8 байт = FFFFFFFFFFFFFFFFh = 18.446.744.073.709.551.615 ключей!

Разрядность регистров современных ЦП составляет 64-бит, а это целых 8 байт, которые можно обрабатывать за-раз! Да сюда можно поместить целую строку символов! В таблице выше видно, сколько вариантов ключей придётся перебирать взломщику, если мы заюзаем 8-байтный ключ для шифрования всего тела нашей программы криптором(1). Ясно, что наш/однобайтный ключ(A7h) здесь отдыхает..

От сюда вывод: что ключ шифрования должен быть как-минимум 4-байтным (32-бит). Всё-что нам нужно было сделать, так это брать в регистр(EAX) сразу по 4 байта из шифруемой программы, и ксорить их 32-битным ключом например так (благо HIEW позволяет это делать по F6):
Код:
     lea   ecx,[end_Start - start]  ; Длина тушки в байтах
     shr   ecx,2                    ; Разделим её на 4 (длина в DW'ордах)
     mov   esi,start                ;
     mov   edi,esi                  ;
@@:  lodsd                          ; Берём 4 байта из ESI
     xor   eax,77E3A853h            ; Ксорим их 32-битным ключом!
     stosd                          ; Сохраняем изменения
     loop  @b                       ; Мотаем цикл СХ-раз..
Здесь, (как любят говорить рук.государств) нашим партёрам придётся малость по-потеть, чтобы во-первых: выделить скан-строку, а во-вторых: поймать ключ шифрования. Мотаем это на ус...

Помимо разрядности ключа, атмосферу подряжают ещё множество заряжённых частиц, которые могут прервать наш полёт. Одной из них является хранение самого ключа(1) в открытом виде. Это огромная брешь в системе безопасности, которая не даёт нам спокойно спать. Открытый ключ просто прикрывает защиту фиговым листиком, обнажая при этом весь алгоритм шифрования. Нужно срочно куда-то спрятать этот/чёртов ключ, ..но куда? В любом случае нам нужно будет воспользоваться им при дешифровке, а наши партнёры смогут отловить этот момент в отладчике.
Нашедшего выход - затаптывают первым..
R71MT вне форума Ответить с цитированием
Старый 01.08.2016, 15:15   #36
R71MT
Участник клуба
 
Аватар для R71MT
 
Регистрация: 16.06.2011
Сообщений: 1,428
По умолчанию

Но не будем отчаиваться, а рассмотрим такой вариант...
Если в большой программе мы не можем найти тайник куда спрятать ключ, то может пусть этим тайником будет сам юзверь! Пусть он вводит пароль, а мы заюзаем его ввод вместо ключа, но с некоторыми поправками.

В клинических случаях, парольная защита программы строится по такому алгоритму. Юзер вводит пароль, который проверяется программой на валидность. Если пароль совпадает, то типа: 'Thank-you!', иначе: 'Fack-you!'. Этот пример в аккурат показывает, как нельзя делать! Толку от пароля, который опять нужно сравнивать с оригиналом? Можно пойти по-более достойному пути, который заключается в следующем..

1. Снимаем контрольную сумму всей тушки;
2. Сохраняем её в открытом виде;
3. Шифруем всю программу 32-битным ключом: DEAD6666h;
4. Читаем юзерский пассворд, и делаем из него ключ;
5. Дешифруем юзверским ключом тушку;
6. Считаем по-новой контрольную сумму получившегося тела;
7. Сравниваем две контрольные суммы;
8. Здесь (как-правило) Fack-you!

Таким образом мы избавляемся от хранения ключа в открытом виде.
До шифрования снимается CRC-программы, которая не несёт в себе ни йоты полезной инфы. Её можно хранить обнажённой. После шифрования секретным ключом контрольная сумма меняется, и её можно восстановить только дешифровав тело таким-же ключом, которым мы шифровали. Юзерский пасс от фонаря преобразуется в ключ, которым дефишруется тело. Если после этого ни один тушканчик не постадал, то контрольная сумма совпадёт, что будет означать ОК!

Простой алгоритм может вставить такие палки в колёса наших партнёров, что мало не покажется. Ключ шифрования мы использовали только один раз закрывшись в тёмной комнате от всех глаз. Его никто не знает кроме нас и он нигде не хранится. Не знает его и сама программа, которая тупо дешифрует тем, что мы ей подсунем, ..а подсунуть ей мы можем сколько? - точно: 4 млрд. вариантов ключей.

Реализуется это просто..
Допустим мы уже посчитали CRC не зашифрованного тела, который равен 8AE5h. На следующем шаге шифруем его секретным ключом, а внутри программы помещаем такую функцию:
Код:
;[....]
len   equ   (end_Start - start)/4       ; Длина в dword
len2  equ   len*4                       ; Длина в байтах
hash  equ   8AE5h                       ; Хэш-сумма всей программы

Doberman:                ;<------; Читаем юзерский пасс
      xor   eax,eax 
      sub   ebx,ebx              ; Здесь будет ключ из пасса
      mov   ecx,8                ; Длина ввода
pass: mov   ah,1                 ; Ввод с эхом средствами DOS
      int   21h
      and   ax,0Fh               ; Оставляем только мл.тетраду
      shrd  ebx,eax,4            ; Двигаем ключ в EBX
      loop  pass                 ; Читаем остальные символы юзверя

Decrypt:                 ;<------; Ключ шифрования в ЕВХ. Дешифруем тушку!
      mov   esi,start 
      mov   edi,esi
      mov   ecx,len
@@:   lodsd
      xor   eax,ebx              ; Ксор ключом..
      stosd
      loop  @b

GetCRC:                  ;<------; Перерсчитаем хэш-сумму в ВХ (2 байта)
      push  0   0
      pop   eax ebx              ; Сбрасываем регистры в нуль
      mov   ecx,len2
      mov   esi,start
@@:   lodsb
      add   bx,ax                ; Считаем сумму в ВХ
      loop  @b

Welcome:                 ;<------; Таможня..
      cmp   bx,hash              ; Сверяем два хэша!
      jne   fack                 ; Если не совпали
      call  @f
      db    'Thank-you!$'        ; Иначе
@@:   pop   dx
      call  @prn
      jmp   Begin        ;<------; На точку входа в программу!!!

fack: call  @f                   ; Не верный пасс юзверя...
      db    'Fack-you!$'
@@:   pop   dx
      call  @prn
      ret
;-----------------------------------------------------------------
@prn: mov   ah,9          ; Функция вывода сообщений
      int   21h     
      ret
Здесь я попытался определить только основные моменты, большая часть которых осталась за-бортом. Не возможно предскать всех действий двух/враждующих сторон, т.к. нет определённого руководства ни для нас, ни для тех, от кого мы собираемся защищаться. Нужно по-возможности изобретать свой велосипед. И пусть он будет с квадратными колёсами и с седлухой выше руля, зато такого ни у кого не будет, и в этом наш выигрыш!

Можно спорить до хрипоты, какие алгоритмы лучше: готовые или самопальные, но последнее слово всегда остаётся за нами. Баян у нас на руках - как хотим, так и играем! (лишь-бы не забывать тискать пимпы иногда)
Нашедшего выход - затаптывают первым..
R71MT вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Полиморфизм Anubys Помощь студентам 1 26.12.2011 20:42
Полиморфизм Zorgan Visual C++ 22 29.08.2011 12:23
Полиморфизм MasterSporta Общие вопросы C/C++ 3 10.04.2011 23:46
полиморфизм slayerblya Общие вопросы C/C++ 1 27.02.2011 01:43
Полиморфизм mister2010 Общие вопросы C/C++ 30 24.05.2010 01:07