Miracle
#03
16 июля 1999 |
|
Кто там кодит? - Packer'ы и Depacker'ы: вся правда о паковщиках, или разглагольствования сэра Kot'а.
(c) Kot/North Star group 07.1998 --------------------------------- Компрессоры, декомпрессоры и не только они... Всем прювет от группы North Star. В этой статье я коснусь ряда проблем ка- чества продукции "заводов софтоварения" нaшего необъятного СНГ. Вероятно, маломальски уважающий себя кодер (хакер) практически всегда пользу- ется компрессорами данных, будь то скрин или код. В настоящее время количество программ данного типа неуклонно растет и есть что выбрать. Однако сервисные пока- затели, а иногда и програмные характе- ристики продукта зачастую оставляют же- лать лучшего. Начнем с программных. Известно, что профессионально сделанные экранные де- компрессоры релоцируемы, то бишь старт осуществляется с адреса загрузки. Клас- сическая процедурка "само-пере-адреса- ции" выглядит примерно так: DI ;!!! CALL ret_adr;#0052,#1792,#33C5 etc ;e1> DEC SP ;e2> DEC SP POP reg_p ;HL,DE,BC и т.д. ....... Какой-то умник (не будем показывать пальцем: смотрите ZF#7Перспрективы ПОS.O.T.A.п.п. Screen Compressor) по- советовал (и, насколько я помню, называл ламеризмом) убрать команду DI вначале всей процедуры. Видите ли,доставляет не- удовольствие слышать торможение музона во время вызова декомпрессора... Так да- вайте ж убирать этот вонючий DI или мор- фировать его в NOP и программа будет смотреться "элитно". То есть, он хочет сказать, что многоуважаемый Андрейка Сендецкий для общего понту в своем ASC поставил вначале DI потому что он - ла- мер? Ан нет, батенька. Что же мы видим при разрешенных прерываниях? Вы только подумайте, какую херню снимет со стэка POP reg_p, если процессор начнет обра- ботку прерывания в точках e1 и e2! Если кто-то не понял, почему так происходит, то пусть вместо e1 или e2 подставит ка- кой-нибудь PUSH и такой же POP (эмуляция прохождения прерывания). Вот и я тут "запачил" картинку в новом LAZY_PACK. Потом загрузил в STS: в начале декомп- рессора вышеприведенная "классика"... только без DI... А жаль. Вы прикиньте - а ведь некоторые убирают DI и в декомп- рессорах по типу MS_PACK'а, где данные снимаются стэком... Я еще представляю вариант, когда при операциях со стэком SP всегда в результате SP+2, но ведь в том же декомпрессоре MS_PACK'а имеется: ......... DEC SP POP AF ......... Потрассируйте STS'ом запакованный MS_PACK'oм блок с его оригинальным де- компрессором и посчитайте, сколько раз программа обращается к вышеприведенному куску кода. А потом по собственным тео- риям рассчитайте: придет прерывание или не придет в этот момент и успеет ли dec- runcher за 1/50 секунды... Успехов... Ярким примером является TRANCE MD (SBG), где, например, вместе с иерогли- фами вылезает что-то "более ужасьное"... Перейдем к так называемому техническо- му сервису. Вот все пишут: мы, мол, прогрессивные пиплы и т.д., однако за- частую вся числовая индикация в прогах - десятичная (это касается sys-прогов лю- бого назначения). Меня (я уверен, не только меня) этот факт "выводит из се- бя", грубо говоря, бесит. Интересно, много ли таких "пиплов", которые за пару секунд скажут, что #B700 это 46848? Я думаю, таких немного и я не из их числа. Неужели нельзя сделать ввод (и индика- цию) числовых значений и в HEX'е и в DEC'е. О самих decruncher'ах особый разговор. Давно бы уже пора, извините за баналь- ность, "принять за стандарт" запрос типа "А хотите ли вы подшить decruncher?" (Decompressor ON/OFF). Ведь, например, как удобно запачить все файлы (части MD, уровни игр или оверлеи систем) в комп- рессоре и отгрузить их без дcrnchr'а, а в процессе распаковки пользоваться одним единственным декомпрессором. Это позво- ляют делать PCD, MegaLZ и DSQ, хотя это касается компрессоров любых видов дан- ных, в том числе и экранных. Желательно в комплекте давать два (или более) де- компрессора - на выбор, отвечающих, нап- ример, следующим условиям: с использова- нием стэка при запрещенных прерываниях :-) или без использования стэка и IY при любом статусе прерываний и т.д. Вариан- тов может быть сколько угодно, но если таковой всего один, то он должен рабо- тать при разрешенных прерываниях (это лично мое мнение). Некоторые скажут: вот у DSQ декомпресор тоже работает при лю- бом статусе прерываний, поэтому он, на- верное, такой тормозной. Отвечаю: что касается DSQ, так там данные хрянятся в непрерывном виде и эти самые данные име- ют разнобитную величину (см. рис), да и сам декомпрессор имеет довольно компакт- ные размеры. bit 76543210|76543210|76543210| ----------------------------- l) 000|1101|0101010|01|01110110| ───────────────────────────── f) 00011010|10101001|01110110| l) - логические данные, полученные при компресси (в таком виде принимаются де- компрессором); НЕПРЕРЫВНЫ, т.е. началь- ный бит лог. единицы данных не совапада- ет с начальным битом физ. ед. данных, что дает эффект экономии битов сообразно размеру файла (прямопропорционально); f) - физический вид, т.е. таким образом данные расположены в памяти компьютера; Логически мысля, можно прийти к выводу, что для трансформации данных из вида l) в вид f) нужно применять сдвиги (RR,RL и т.д.). Вот на это и уходит львинная доля времени у декомпрессора DSQ, хотя есть смысл его оптимизировать. Ну, уж если речь пошла о DSQ, то оста- новимся на нем поподробнее. Как вы знае- те, в DSQ предусмотрена возможность отк- лючения декомпрессора. Но это полдела. В проге DSQ_INFO, как это часто бывает, вместо описания - приветы, коминсуны и всякая такая дребедень. Так вот, если вы решили использовать кастрированный (без дкмпрср'a) датасквизнутый блочок, то из- вольте знать следующие вещи: оказывает- ся, у этого блока есть заголовок (len: 6 byte, но по идее должно быть 7 - см. да- лее), в котором указаны: N0 (byte) - количество "вырезаемых" оконных битов (очень, кстати, подходящее определение); N1 (word) - адрес, введенный на запрос DECRUNCH CODE TO:NNNN ; N2 (word) - адрес, N1-1+реальная длина файла, другими словами - адрес последне- го байта файла (именно последнего, а не следующего за ним) ; N3 (byte, а должен быть word) - сильно не смейтесь, но это младший байт адреса, введенного на запрос AUTOSTART: NNNN, просто маленький глюк DSQ; Итого: byte+word+word+byte = 6 byte. На самом деле, кроме байта N0 больше ни хрена не надо, тем более младший байт запуска :-). Можно, в принципе, оставить на месте N1 полную длину исходного фай- ла. Но это - рекомендации для тех, кто может быть переделает DSQ на новый лад (с крутым сервисом и т.д.). Резонный вопрос: а куда девать N0? ......... LD B,NN ;nuf LD H,#02 ADD HL,DE CALL NNNN ADD HL,BC POP BC LDDR LD HL,XXXX ;nif SBC HL,DE JR C,NNNN POP HL EXX RET .......... Аргумент рег. B в строке nuf и есть тот самый N0, а для полного понимания формулировки про "оконные биты" потрас- сируйте декомпрессор debugger'ом. Пара- метр N0 зависит от того, каков был выб- ран размер окна при паковке файла. А как вообще распаковать блок "не род- ным" декомпрессором? Во-первых, DSQ во- обще является неординарным компрессором. Объясню, почему. Если вы знаете порядок трансляции данных "родными" декомпрессо- рами паковщиков LPC, PCD, CC, MS_PACK, то могли наблюдать следующую картину: 1) block LDIR (LDDR) on body file (для выполнения end_block = end_file) 2) source (HL) = start_block 3) destination (DE) = start_file LP 4) translate (HL)bits to (DE)bytes 5) inc HL, inc DE 6) go to LP if DE<(end_file+1) У DSQ все "up side down": 1) block LDIR (LDDR) on body file (для выполнения start_block=start_file) 2) source (HL) = еnd_block 3) destination (DE) = end_file LP 4) translate (HL)bits to (DE)bytes 5) dec HL, dec DE 6) go to LP if DE<(start_file-1) Ненормативная лексика: file - исходный файл; block - результат компрессии; source - откуда берем; destin - куда кладем; start_file - адрес начала дкмпрсируе- мых данных; end_file - адрес последнего байта дкмпрсируемых данных; start_block - начало данных block'а; end_block - адрес последнего байта block'а; inc, dec - увеличение, уменьшение на NNNN байтов (вопреки легенде - не только на единицу); body file - некоторый участок памяти, резервируемый под file; Вообще же, эти схемы приведены в иде- але. На самом же деле в отношении DSQ есть некоторые внушительные поправки. Если вы вообще пользовались DSQ, то ве- роятно помните нечто (-2). Это возникает потому, что destin'ация идет быстрее, чем sour'сирование, в результате, когда остается распаковать 1-3 байта destina- tion равен source, а на следующем цикле вообще опережает его. Здесь и возникает ситуация когда, например, вместо маркера конца берется "нечто", вероятность кото- рого стать маркером конца 1/256... Поэ- тому LDIR'ить или LDDR'ить надо так, чтобы start_blok=start_file-2 (-2, -3 и более, но не менее -2), а если block DSQ с заголовком, то start_block=start_fi- le-2-6 (-8, -9 и более, но не менее -8). Все это, конечно, славы DSQ не прибавля- ет, а ведь можно было как в PCD или MS_PACK'е с использованием буфера - там все работает по вышеприведенной схеме как с "родными", так и "не родными" дкмпрсрами. Примечание: nif в куске ди- зассемблера дехрюнчера поставлен не слу- чайно - здесь обязательно должно быть start_file-1 !! Покончим с DSQ и вернемся к #1B00, т.е. к скриновым пакерам. Выдвину еще одно предложение: а почему бы даже в компрессорах экрана не предусмотреть возможность смены адреса распаковки? О чем это ты, батенька? Вы, вероятно, сталкивались с ситуацией, когда надо распаковать картинку на SCR1 (в 7-ой странице), т.е. под адрес #C0 или вообще распаковать и не в #40 и не в #C0, а ку- да-нибудь "вообще ни туда" (например, при реализации всяких гасилок/зажигалок и других "проявлений"). Мое предложение заключается в том, чтобы можно было выб- рать хотя бы старшую часть destin'ации, a младшая будет нулевой "по умолчанию", например, #70,#67,#BF,#9A,#DE и т.д. А затем после распаковки каким-либо обра- зом ее (картинку) "проявить" (можно даже LDIR'ом :-)). Теперь вернемся к "релоцируемому" за- пуску программ. Я приведу некий набор маленьких процедур, которые дают "само- определение местонахождения": 1) DI ;обязательно! (см. выше) CALL ROM_RET ;#52, #7C, #33C5 и т.д. DEC SP DEC SP POP reg_p ;HL, DE, BC, IX и т.д. ........ ;length: 7-8 байтов 2) LD HL,#E9E1 ;POP HL (#E1) LD (#4000),HL ;JP (HL)(#E9) CALL #4000 ; .......... ;результат в HL ;length: 9 байтов 3) LD HL,#4000 ;<- start_1 PUSH HL ;<- start_2 LD (HL),#E1 INC HL LD (HL),#E9 DEC HL CALL ROM_JHL ;JP (HL): #162C, #6F ........ ;результат в HL POP reg_p ;length: 14 байтов Первый вариант работает только при запрещенных прерываниях, однако адрес может быть помещен в любую из регистро- вых пар. Используется ПЗУ-48. Второй вариант больше на 1-2 байтa, но работает при любом статусе прервываний и не использует ПЗУ. Третий вариант явяется универсальным, но опять используется ПЗУ. Здесь имеется две точки входа start_1 и start_2. Через start_1 осуществляется привязка распа- ковки данных к фиксированному адресу па- мяти, при использовании же start_2 вы сами вольны в выборе адреса, но важно, чтобы младшая тоже была равна нулю (смотря каким образом происходит перес- чет "экранных" адресов дкмпрсром - усло- вие: (не)допустимо использование ненуле- вой младшей части). Вобщем, вариантов может быть сколько угодно, главное, чтобы это было удобно, круто и без глюков :-(. На днях смотрел инлайтские демы и в одной из них (Art Vision by etc group) узнал, что, дескать, дема использует decruncher ((c) LAZY) MS_PACK'а, не ис- пользующий стэк для "выкачивания" данных и т.д. Так как в MSP не предусмотрено отключение декомпрессора, то "новый" дкмпрср работает со стандартными блоками MSP, у которых уже есть свой декомпрес- сор. ;MS_PACK blocks decruncher ;Written by LAZY/etc group ; Entry: IX - adr of block ;(wiz standard decruncher) INC HX LD L,(IX-#17) LD H,(IX-#16) LD DE,MSPBUF+4 LD BC,#0005 LDDR LD L,(IX-#15) LD H,(IX-#14) LD E,(IX-#13) LD D,(IX-#12) LD C,(IX-#11) LD B,(IX-#10) LDDR INC DE INC DE INC DE EX DE,HL LD E,(HL) INC HL LD D,(HL) INC HL EX DE,HL LD BC,#1010 EXX LD E,(IX-#0F) LD D,(IX-#0E) MSP_LP CALL MSPCOD JR C,LL631D CALL MSPBYT LD (DE),A INC DE JR MSP_LP LL631D LD HL,#0302 LL6320 LD B,#02 CALL BCOD_A CP H JR C,LL632F ADD A,L LD L,A CP #11 JR NZ,LL6320 XOR A LL632F ADD A,L CP #05 JR NC,LL6364 CP #02 JR NZ,LL6367 LD C,A LL6339 XOR A LL633A LD H,A CALL MSPBYT LD L,A LD A,E SCF SBC A,L LD L,A LD A,D SBC A,H LD H,A LDIR JR MSP_LP LL634A CALL MSPBYT INC A JR Z,MSPEND INC A JR NZ,LL635D CALL MSPBYT LD C,A CALL MSPBYT LD B,A JR LL6368 LL635D ADD A,#0F JR NC,LL6367 INC B JR LL6367 LL6364 JR Z,LL634A DEC A LL6367 LD C,A LL6368 CALL MSPCOD JR C,LL6339 LD L,B LD B,H CALL BCOD_A LD B,L CP #02 JR NC,LL637A INC A JR LL633A LL637A CALL LL63B1 CP #08 JR NC,LL6384 DEC A JR LL633A LL6384 CALL LL63B1 CP #17 JR NC,LL638F SUB #09 JR LL633A LL638F CALL LL63B1 AND #1F CP #1F JR C,LL633A CALL MSPBYT JR LL633A MSPEND LD HL,MSPBUF LD BC,#0005 LDIR LD HL,#2758 EXX RET BCOD_A XOR A CALL LL63B1 DJNZ $-3 RET LL63B1 CALL MSPCOD RLA RET MSPCOD EXX ADD HL,HL DJNZ NONEXT LD B,C EX DE,HL LD E,(HL) INC HL LD D,(HL) INC HL EX DE,HL NONEXT EXX RET MSPBYT EXX LD A,(DE) INC DE EXX RET MSPBUF DEFB 0,0,0,0,0 Ну вот, наверное, и все, что я хотел вам сообщить. Если у кого-то есть ка- кие-либо комментарии, пусть пишет в ре- дакцию MIRACLE или мне (Kot'у). Bye'те! Kot/NSG 07.1998 Кировск-Апатиты-Оленегорск ----------------------------------------
Другие статьи номера:
Похожие статьи:
В этот день... 21 ноября