В гостях у Инны. Ломаем инсталлятор InnoSetup

Ес­ли уста­нов­ка прог­раммы про­тека­ет сов­сем не так, как хочет­ся поль­зовате­лю, при­ходит­ся вме­шивать­ся в работу инстал­лятора. Сегод­ня мы погово­рим о том, как устро­ен инстал­ляци­онный пакет InnoSetup, и научим­ся менять его логику изнутри.

warning

Статья написа­на в иссле­дова­тель­ских целях, име­ет озна­коми­тель­ный харак­тер и пред­назна­чена для спе­циалис­тов по безопас­ности. Автор и редак­ция не несут ответс­твен­ности за любой вред, при­чинен­ный с при­мене­нием изло­жен­ной информа­ции. Исполь­зование или рас­простра­нение ПО без лицен­зии про­изво­дите­ля может прес­ледовать­ся по закону.

В статье «Ло­маем инстал­лятор. Как обма­нуть инстал­лятор MSI методом для ленивых» я писал, что час­тень­ко при­ходит­ся допили­вать не толь­ко саму прог­рамму, но и ее инстал­лятор. В той замет­ке мы рас­смат­ривали прин­ципы реверс‑инжи­нирин­га и пат­чинга инстал­ляци­онных пакетов, соз­данных при помощи инстал­лятора InstallShield (MSI). Сегод­ня нашей целью будет дру­гой популяр­ный пакет — InnoSetup. Думаю, этот тип инстал­ляторов нас­толь­ко широко рас­простра­нен, что не нуж­дает­ся в под­робном опи­сании, поэто­му сра­зу перей­дем к кон­крет­ной задаче.

Итак, у нас име­ется уста­нов­щик для неко­его гра­фичес­кого пла­гина, офор­млен­ный в виде самодос­таточ­ного исполня­емо­го EXE-модуля. При запус­ке он про­сит ввес­ти серий­ный номер, судя по все­му, про­веря­ет его на уда­лен­ном сер­вере и при неп­равиль­ном вво­де выда­ет сооб­щение об ошиб­ке.

От­крыв наш инстал­лятор в Detect It Easy, выяс­няем сра­зу два фак­та: во‑пер­вых, это наш паци­ент, а во‑вто­рых, InnoSetup нас­квозь писан на Delphi.

Как под­ска­зыва­ет опыт, откры­вать инстал­лятор в IDA нет ни малей­шего смыс­ла. Преж­де все­го, это дель­фи, тут ско­рее помог бы IDR. С дру­гой сто­роны, файл чуть менее чем целиком сос­тоит из упа­кован­ного или зашиф­рован­ного овер­лея (стро­ка Serial Number is invalid пред­ска­зуемо не находит­ся в нем в откры­том виде), то есть его заг­рузчик не несет ничего полез­ного для решения нашей проб­лемы.

По­это­му сра­зу поп­робу­ем пощупать про­цеду­ру про­вер­ки серий­ного номера «изнутри», в про­цес­се работы прог­раммы. Заг­рузив инстал­лятор в отладчик x64dbg, мы обна­ружи­ваем, что наши пред­положе­ния вер­ны. Заг­рузчик порож­дает нес­коль­ко про­цес­сов, которые далее живут собс­твен­ной жизнью незави­симо от него. В час­тнос­ти, окно сооб­щения и все осталь­ные диало­говые окна вызыва­ются из про­цес­са, порож­даемо­го модулем, который находит­ся во вло­жен­ной пап­ке is-JJ5LI.tmp катало­га вре­мен­ных фай­лов сис­темы.

При заг­рузке инстал­лятор пер­во‑напер­во соз­дает этот каталог, рас­паковы­вает в него дан­ный модуль, который потом запус­кает, а в кон­це инстал­ляции уби­рает за собой, уда­ляя и файл, и каталог. Рас­смот­рим этот модуль более деталь­но.

Стро­ка Serial Number is invalid отсутс­тву­ет в откры­том виде и здесь тоже. Detect It Easy не говорит про модуль ничего внят­ного, кро­ме того, что он тоже написан на Delphi и содер­жит в ресур­се еще один модуль, написан­ный на Microsoft Visual C.

Поп­робу­ем коп­нуть чуть глуб­же: атта­чим­ся с помощью x64dbg к про­цес­су в момент появ­ления диало­гово­го окна "Serial Number is invalid". Код вызова MessageBox выг­лядит при­мер­но так:

...
005B8CBB | mov dword ptr fs:[ecx],esp
005B8CBE | push esi
; [ebp-8]:L"Setup"005B8CBF | mov eax,dword ptr ss:[ebp-8]
005B8CC2 | push eax
; edi:L"Serial Number is invalid. Please enter valid license you received or contact support"005B8CC3 | push edi
005B8CC4 | push ebx
005B8CC5 | call <JMP.&MessageBoxW>
005B8CCA | mov dword ptr ss:[ebp-C],eax
005B8CCD | xor eax,eax
005B8CCF | pop edx
005B8CD0 | pop ecx
...

От­крыв модуль в IDR и най­дя этот фраг­мент кода, мы видим, что он явля­ется частью метода _Unit72.TApplication.MessageBox, — что ж, впол­не логич­но. Поп­робу­ем теперь отсле­дить, отку­да было выз­вано это сооб­щение об ошиб­ке.

От­кры­ваем вклад­ку «Стек вызовов» и бук­валь­но семью вло­жени­ями выше (или ниже, кому как боль­ше нра­вит­ся) обна­ружи­ваем инте­рес­ный метод _Unit76.TPSExec.RunScript. Этот метод и по наз­ванию, и по логике работы силь­но напоми­нает так час­то встре­чаемый нами интер­пре­татор шитого байт‑кода. Лег­ко и прос­то находит­ся мес­то выбор­ки и рас­шифров­ки текущей коман­ды.

На скрин­шоте вид­но, что байт‑код извле­кает­ся в регистр esi из потока по адре­су [edx+eax], где edx — базовый адрес текущей про­цеду­ры, а eax — текущее сме­щение отно­ситель­но него. Что же это за скрип­ты такие и какой байт‑код им соот­ветс­тву­ет?

По­гуг­лив по наз­ванию клас­са TPSExec, мы сра­зу натыка­емся на тер­мин Pascal Script. В двух сло­вах — это пас­калепо­доб­ный скрип­товый язык, исполь­зуемый, в час­тнос­ти, в сце­нари­ях InnoSetup.

Как толь­ко мы разоб­рались, с чем име­ем дело, даль­нейший путь прев­раща­ется в ско­рос­тное шос­се. Для начала поп­робу­ем вытащить ском­пилиро­ван­ный байт‑код скрип­та из инстал­лятора. Ока­зыва­ется, для это­го вов­се не обя­затель­но тан­цевать с буб­ном, дам­пя ском­пилиро­ван­ный байт‑код из памяти отладчи­ка. Спе­циаль­но обу­чен­ные энту­зиас­ты соз­дали нес­коль­ко про­ектов рас­паков­щиков дис­три­бути­вов InnoSetup, при­чем с откры­тым кодом. Нап­ример, innoextract и innounp. Запус­тив innounp.exe из пос­ледне­го пакета с клю­чом -m, мы получа­ем информа­цию о встро­енном в него Pascal-скрип­те (не путать с инстал­ляци­онным скрип­том .iss, пред­став­ляющим собой спи­сок фай­лов уста­нав­лива­емо­го дис­три­бути­ва):

; Version detected: 6100 (Unicode)

Compression used: lzma

Files: 457 ; Bytes: 218186809

Compiled Pascal script: 10715 byte(s)

Ес­ли мы рас­паку­ем дис­три­бутив этой ути­литой с клю­чом -m, то ком­пилиро­ван­ный код Pascal Script будет сох­ранен в файл с капитан­ским наз­вани­ем CompiledCode.bin. Что же за код находит­ся внут­ри дан­ного фай­ла?

По счастью, и здесь от нас не тре­бует­ся изоб­ретать велоси­пед — все уже при­дума­но до нас. Слег­ка погуг­лив, находим про­ект IFPSTools, вклю­чающий в себя дизас­сем­блер Pascal Script ifpsdasm. Сущес­тву­ет даже весь­ма тол­ковый деком­пилятор CompiledCode в исходный пас­калев­ский код Inno Setup Decompiler. К сожале­нию, про­ект, похоже, мертв, одна­ко сам деком­пилятор все еще мож­но ска­чать по ссыл­ке. С него мы и нач­нем иссле­довать наш код. Доволь­но быс­тро мы находим в нем вызов окна сооб­щения:

...v_58 := 'Status';v_59 := 0;v_60 := v_1;v_54 := IDISPATCHINVOKE(v_60, v_59, v_58, v_55);v_53 := v_54 < 500;v_45 := v_45 and v_53;label_8570:flag := not v_45;if flag then goto label_8846; Этот переход надо заменить безусловнымlabel_8583:v_62 := 0;v_63 := 2;v_64 := 'Serial Number is invalid. Please enter valid license you received or contact support';v_61 := MSGBOX(v_64, v_63, v_62);result := 0;goto label_8858;label_8846:result := 1;label_8858:goto label_9271;...

Поп­робу­ем теперь най­ти это мес­то в ском­пилиро­ван­ном коде, что­бы поп­равить его. Дизас­сем­бли­ровав CompiledCode.bin при помощи ifpsdasm, находим ассем­блер­ный экви­валент при­веден­ного выше скрип­тового кода:

...
lt Var3, Var4, S32(500) pop ; StackCount = 3
and Var2, Var3
pop ; StackCount = 2
loc_4ec:
sfz Var2
pop ; StackCount = 1
jf loc_600 ; Этот переход надо заменить безусловным
pushtype S32 ; StackCount = 2
pushtype S32 ; StackCount = 3
assign Var3, S32(0) pushtype TMSGBOXTYPE ; StackCount = 4
assign Var4, TMSGBOXTYPE(2) pushtype UnicodeString_2 ; StackCount = 5
assign Var5, UnicodeString_3("Serial Number is invalid. Please enter valid license you received or contact support") pushvar Var2 ; StackCount = 6
call MSGBOX
pop ; StackCount = 5
pop ; StackCount = 4
pop ; StackCount = 3
pop ; StackCount = 2
pop ; StackCount = 1
assign RetVal, BOOLEAN(0) jump loc_60c
loc_600:
assign RetVal, BOOLEAN(1)loc_60c:
...

Источник: xakep.ru

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *