Обман Дизассемблеров (Как защитить приложения от реверсивного инжениринга)
Я недавно работал c дизассемблером и подумал о способах защиты, которые могли бы обманывать их. Всё достаточно просто. Заметим, что дизассемблеры не имеют искусственного интеллекта:-) Подходы, которые дизассемблеры не способны обходить:
1 - инструкции Jump, передающие управление сами на себя. Предположим, что у вас есть участок кода, который никогда не будет выполняться. Вы можете организовать бесконечный цикл так:
0000: EB FF ;jmp 0000
Это то же самое что: 0000: F4 ;hlt
Дизассемблер может войти в этот цикл навечно.Хотя, заметьте, что с hlt этого бы не случилось.
2 – ложная инструкция jmp, если следующей по шагу инструкцией, которую Вы планируете использовать является inc, dec, call, jmp, или push, и если всё начинается с кода операции FF. Взгляните сюда::
0000: EB FF ;jmp
0001 0002: ?? ??
Это то же самое что: 0000: 90 ;nop
0001: FF ?? ;inc, dec, call, jmp, push
Это должно запутать дизассемблеры.
3 – это инструкция call, tесли вы переходите на выполнение подпрограммы, но не возвращаетесь из неё. Рассмотрим вызов подпрограммы с выталкиванием байтов из стека:
0000: call sub1 ...
......
sub1
1001: pop dx ;избавляемся от байтов для возвращения
Это то же самое что: 0000: jmp sub1
sub1
1000: pop dx
Дизассемблер обычно будет считать, что за инструкцией вызова должен следовать код и будет его продолжать искать.
4 – Ret, использованный как jmp. Если вы планируете вызывать подпрограмму, взгляните на это:
1000: push 92F(адрес подпрограммы)
…..
1003: ret
Это то же самое что: 1000: jmp 92F
Дизассемблеры не найдут вашу подпрограмму по адресу 92F. Вы можете найти это в упакованных файлах. Подходы, которые дизассемблеры не смогут обойти
5 – ложный условный переход с единственным условием выхода.
1000: cmp ax, ax
1002: je loc1
...
1040: loc1:
Это то же самое что: 1000: jmp loc1
.....
1040: loc1
Дизассемблеры будут вынуждены предположить, что имеется код с другим условием перехода, но они этот путь найти не смогут, потому что им не хватает для этого мозгов! Вывод: заметьте, что в случаях 1, 2, и 5, если Вы используете переход с rel 16 или rel 32 - вам понадобится подправить расстояние прыжка. Заметьте, что в случаях 3 и 4, если Вы используете полный адрес сегмента (страницы), вам понадобиться ещё сделать ему push и pop (запомнить и восстановить). Эти подходы могут только применяться, если Вы программируете на ассемблер или используете этот язык для вставок. Вам, вероятно, придется хорошо выучить ассемблер, чтобы понять эти 5 способов.
Удачи вам и вашим “защитам”.
© TeamSuxx
© Dark Yukon
® Snatch (учись писать грамотно, Snatch) )