Хакер - Get your Xojo. Реверсим приложение на REALbasic

Хакер - Get your Xojo. Реверсим приложение на REALbasic

hacker_frei

https://t.me/hacker_frei

МВК

Пом­нишь, как на школь­ных уро­ках информа­тики тебя зас­тавля­ли изу­чать Basic? Так вот: это был ненас­тоящий Basic. А нас­тоящий, то есть REALbasic, теперь называ­ется Mojo Xojo, и на нем до сих пор пишут при­ложе­ния. Сегод­ня я рас­ска­жу тебе, как они устро­ены изнутри и как их мож­но взло­мать.

Час­то ревер­сить прог­рам­мный про­дукт слож­но не потому, что его код запутан или на него навеси­ли какую‑то осо­бен­ную защиту, а потому, что раз­работ­чики исполь­зовали ред­кий и малорас­простра­нен­ный фрей­мворк. Сегод­ня в нашем меню — экзо­тичес­кая сре­да раз­работ­ки под наз­вани­ем REALbasic (Xojo).

Это один из пионе­ров кросс‑плат­формен­ного прог­рамми­рова­ния. REALbasic неод­нократ­но менял наз­вание, архи­тек­туру и хозя­ев и в пос­леднее вре­мя серь­езно рас­терял как свои рыноч­ные позиции, так и акту­аль­ность. Написан­ные на нем при­ложе­ния встре­чают­ся все реже и реже, в основном в таких узкоспе­циали­зиро­ван­ных областях, как колори­мет­рия. Из‑за малой рас­простра­нен­ности для него, в отли­чие от извес­тных сред раз­работ­ки (вро­де Delphi, .NET или VBS), прак­тичес­ки отсутс­тву­ют спе­циали­зиро­ван­ные инс­тру­мен­ты для ревер­са. Поэто­му мы, как обыч­но, на при­мере кон­крет­ных при­ложе­ний раз­берем прин­ципы и лай­фха­ки для изу­чения кода таких при­ложе­ний.

Нач­нем с самого прос­того слу­чая. Как ни стран­но, лег­че все­го иссле­довать при­ложе­ние, соз­данное в сов­ремен­ных акту­аль­ных вер­сиях Xojo (да‑да, он мало того что еще под­держи­вает­ся, вдо­бавок стал 64-бит­ным, Ви­кипе­дия врет).

Итак, нам попалось гра­фичес­кое при­ложе­ние, при ана­лизе которо­го наш безот­казный Detect It Easy (DIE) утвер­жда­ет, что это Xojo (x64).

Нам понадо­бит­ся самая малость: вклю­чить фун­кции при­ложе­ния, которые отка­зыва­ются работать в незаре­гис­три­рован­ной вер­сии, выдавая вмес­то это­го окош­ки с тре­бова­нием регис­тра­ции. Заг­рузив прог­рамму в отладчик x64dbg и при­тор­мозив ее в этом мес­те, мы с облегче­нием замеча­ем, что прог­рамма не зашиф­рована, не упа­кова­на и лишена средств анти­отладки. Она даже сооб­щение выда­ет стан­дар­тным MessageBoxA, при этом прог­рамма прек­расно дизас­сем­бли­рует­ся при помощи IDA. Получен­ный код, прав­да, как и сле­дова­ло ожи­дать от кросс‑плат­формен­ного бей­сика, чудовищ­но неук­люж и сло­жен для понима­ния.

Хо­чет­ся как‑то облегчить себе жизнь, хотя бы вос­ста­новив име­на вызыва­емых методов. Наш пре­дыду­щий опыт работы со ском­пилиро­ван­ным кодом, изна­чаль­но пред­назна­чен­ным под интер­пре­татор, под­ска­зыва­ет, что эта информа­ция где‑то обя­затель­но дол­жна хра­нить­ся. Прос­мотрев в IDA все ссыл­ки на про­цеду­ру, из которой была выз­вана ошиб­ка демовер­сии, находим инте­рес­ную конс­трук­цию:

...

.text:0000000141A85CF5 mov edx, 0F40h

.text:0000000141A85CFA xor r8d, r8d

.text:0000000141A85CFD lea rcx, sub_140D7D410

.text:0000000141A85D04 lea r9, aOBevelbutton_64 ; "%%o<BevelButton>" — похоже на класс нашего метода

.text:0000000141A85D0B lea r10, aBvlmeaschartAc_0 ; "BvlMeasChart_Action" — похоже на его имя

.text:0000000141A85D12 lea r11, sub_141A91350 ; Адрес нашего метода

.text:0000000141A85D19 mov [rbp+8F0h+var_438], rax

.text:0000000141A85D20 mov [rbp+8F0h+var_60], r11

.text:0000000141A85D27 mov rax, [rbp+8F0h+var_8E0]

.text:0000000141A85D2B mov [rax+0F68h], r11

.text:0000000141A85D32 mov r11, [rbp+8F0h+var_68]

.text:0000000141A85D39 mov [r11+0F40h], r10

.text:0000000141A85D40 mov r10, [rbp+8F0h+var_68]

.text:0000000141A85D47 mov [r10+0F60h], r9

.text:0000000141A85D4E mov [rbp+8F0h+var_440], rcx

.text:0000000141A85D55 mov r9, [rbp+8F0h+var_68]

.text:0000000141A85D5C mov [r9+0F70h], rcx

.text:0000000141A85D63 mov rcx, [rbp+8F0h+var_68]

.text:0000000141A85D6A mov dword ptr [rcx+0F78h], 39h ; '9'

.text:0000000141A85D74 mov rcx, [rbp+8F0h+var_68]

.text:0000000141A85D7B mov dword ptr [rcx+0F48h], 101h

.text:0000000141A85D85 mov rcx, [rbp+8F0h+var_68]

.text:0000000141A85D8C mov [rbp+8F0h+var_8E8], rcx

.text:0000000141A85D90 call RuntimeAllocateAttributeTable

...

Бег­ло прос­мотрев код, обна­ружи­ваем великое мно­жес­тво подоб­ных конс­трук­ций по все­му дизас­сем­бли­рован­ному при­ложе­нию. Оче­вид­но, что это ини­циали­зация каких‑то внут­ренних REALbasic-овских таб­лиц методов. Веро­ятнее все­го, за это ответс­твен­на фун­кция RuntimeAllocateAttributeTable из модуля XojoGUIFramework64.dll, вызыва­ет сом­нение толь­ко спо­соб переда­чи парамет­ров. Вро­де как стро­ки и адре­са перед вызовом RuntimeAllocateAttributeTable записы­вают­ся в стек, одна­ко не в область парамет­ров, а в область локаль­ных перемен­ных, при­чем по совер­шенно раз­ным адре­сам. Поп­робу­ем про­ана­лизи­ровать еще пару подоб­ных конс­трук­ций:

.text:0000000141A85D95 mov edx, 0F80h <-- Обрати внимание, что число в edx 0F80h

.text:0000000141A85D9A xor r8d, r8d

.text:0000000141A85D9D lea rcx, sub_140D7D410

.text:0000000141A85DA4 lea r9, aOBevelbutton_65 ; "%%o<BevelButton>"

.text:0000000141A85DAB lea r10, aBvldefchartAct ; "BvlDefChart_Action"

.text:0000000141A85DB2 lea r11, sub_141A95B20

.text:0000000141A85DB9 mov [rbp+8F0h+var_448], rax

.text:0000000141A85DC0 mov [rbp+8F0h+var_60], r11

.text:0000000141A85DC7 mov rax, [rbp+8F0h+var_8E8]

.text:0000000141A85DCB mov [rax+0FA8h], r11

.text:0000000141A85DD2 mov r11, [rbp+8F0h+var_68]

.text:0000000141A85DD9 mov [r11+0F80h], r10 <-- Везде эквивалентно смещению, по которому сохраняется адрес строки имени метода

.text:0000000141A85DE0 mov r10, [rbp+8F0h+var_68]

.text:0000000141A85DE7 mov [r10+0FA0h], r9

.text:0000000141A85DEE mov [rbp+8F0h+var_450], rcx

.text:0000000141A85DF5 mov r9, [rbp+8F0h+var_68]

.text:0000000141A85DFC mov [r9+0FB0h], rcx

.text:0000000141A85E03 mov rcx, [rbp+8F0h+var_68]

.text:0000000141A85E0A mov dword ptr [rcx+0FB8h], 3Ah ; ':'

.text:0000000141A85E14 mov rcx, [rbp+8F0h+var_68]

.text:0000000141A85E1B mov dword ptr [rcx+0F88h], 101h

.text:0000000141A85E25 mov rcx, [rbp+8F0h+var_68]

.text:0000000141A85E2C mov [rbp+8F0h+var_8F0], rcx <-- Относительно адреса в RCX

.text:0000000141A85E30 call RuntimeAllocateAttributeTable

Вот еще один при­мер:

.text:0000000141A85E35 mov edx, 0FC0h

.text:0000000141A85E3A xor r8d, r8d

.text:0000000141A85E3D lea rcx, sub_140D7D410

.text:0000000141A85E44 lea r9, aOBevelbutton_66 ; "%%o<BevelButton>"

.text:0000000141A85E4B lea r10, aBvlgraph2dActi_0 ; "BvlGraph2D_Action"

.text:0000000141A85E52 lea r11, sub_141A95D70

.text:0000000141A85E59 mov [rbp+8F0h+var_458], rax

.text:0000000141A85E60 mov [rbp+8F0h+var_60], r11

.text:0000000141A85E67 mov rax, [rbp+8F0h+var_8F0]

.text:0000000141A85E6B mov [rax+0FE8h], r11 <-- а относительные смещения между адресом метода

.text:0000000141A85E72 mov r11, [rbp+8F0h+var_68]

.text:0000000141A85E79 mov [r11+0FC0h], r10 <-- именем метода

.text:0000000141A85E80 mov r10, [rbp+8F0h+var_68]

.text:0000000141A85E87 mov [r10+0FE0h], r9 <-- и именем класса в каждой конструкции одинаковы

.text:0000000141A85E8E mov [rbp+8F0h+var_460], rcx

.text:0000000141A85E95 mov r9, [rbp+8F0h+var_68]

.text:0000000141A85E9C mov [r9+0FF0h], rcx

.text:0000000141A85EA3 mov rcx, [rbp+8F0h+var_68]

.text:0000000141A85EAA mov dword ptr [rcx+0FF8h], 3Bh ; ';'

.text:0000000141A85EB4 mov rcx, [rbp+8F0h+var_68]

.text:0000000141A85EBB mov dword ptr [rcx+0FC8h], 101h

.text:0000000141A85EC5 mov rcx, [rbp+8F0h+var_68]

.text:0000000141A85ECC mov [rbp+8F0h+var_8F8], rcx

.text:0000000141A85ED0 call RuntimeAllocateAttributeTable

Нем­ного порас­кинув моз­гами, мы при­ходим к выводу, что на вхо­де в RuntimeAllocateAttributeTable в регис­тре RCX находит­ся ука­затель на некую таб­лицу, каж­дый эле­мент которой занима­ет 0x40 байт и име­ет при­мер­но сле­дующую струк­туру:

DQ MethodName ;RCX[RDX+0]

DQ ?

DQ ?

DQ ?

DQ ?

DQ ?

DQ ?

DQ ?

DQ ClassName ;RCX[RDX+20h]

DQ MethodAddress ;RCX[RDX+28h]

DQ ?

DQ ?

DQ ?

DQ ?

DQ ?

DQ ?

А в регис­тре RDX хра­нит­ся отно­ситель­ное сме­щение на текущий эле­мент дан­ной таб­лицы в бай­тах. В прин­ципе, поковы­ряв­шись в коде и отладчи­ке, при желании мож­но рас­копать сак­раль­ный смысл и осталь­ных полей этой таб­лицы, но мы не будем отвле­кать­ся: получен­ной информа­ции нам уже поч­ти дос­таточ­но для даль­нейше­го иссле­дова­ния струк­туры кода.

Итак, ста­вим условную точ­ку оста­нова на фун­кцию RuntimeAllocateAttributeTable со сле­дующим тек­стом жур­нала:

ClassName: {s:[rcx+rdx+0x20]} MethodName: {s:[rcx+rdx]} Address: {[rcx+rdx+0x28]} called at {[rsp]}

Пос­ледний параметр, адрес вызова, нам необ­ходим на вся­кий слу­чай, что­бы отсле­дить мес­то, в котором что‑то пош­ло не так при вызове. Перезаг­ружа­ем прог­рамму и — бин­го! — в жур­нале x64dbg видим не иде­аль­ный, но отно­ситель­но вме­няемый спи­сок имен методов с адре­сами:

...

ClassName: "%%sp" MethodName: "_AddMenuHandler" Address: 140296DF0 called at 141A84A96

ClassName: "%o<Object>%s" MethodName: "_ControlByName" Address: 140296E70 called at 141A84B3C

ClassName: "%o<Object>%si8" MethodName: "_ControlByNameIndex" Address: 140296F60 called at 141A84BE2

ClassName: "%%" MethodName: "_DependencyList" Address: 140297050 called at 141A84C88

ClassName: "%o<Object>%s" MethodName: "_GetControl" Address: 140296E70 called at 141A84D1D

ClassName: "%o<Object>%si8" MethodName: "_GetControlArray" Address: 140296F60 called at 141A84DB2

ClassName: "%%i8i8i8i8" MethodName: "_Move" Address: 1402970F0 called at 141A84E58

ClassName: "%%" MethodName: "__Exit" Address: 140297180 called at 141A84EED

ClassName: "%%" MethodName: "__Init" Address: 1402971D0 called at 141A84F82

ClassName: "%o<Object>%p" MethodName: "__New" Address: 140297240 called at 141A85028

ClassName: "%%" MethodName: "Layout" Address: 141A86480 called at 141A850CE

ClassName: "%%" MethodName: "CreateControls" Address: 140775830 called at 141A85174

ClassName: "%%" MethodName: "_CreateControls0" Address: 141A86530 called at 141A85216

ClassName: "%o<BevelButton>%" MethodName: "BvlPaper" Address: 141A8A830 called at 141A852E6

ClassName: "%o<BevelButton>%" MethodName: "BvlMetamerism" Address: 141A8A9F0 called at 141A8538C

ClassName: "%o<BevelButton>%" MethodName: "BvlGamut" Address: 141A8ABB0 called at 141A85432

ClassName: "%o<BevelButton>%" MethodName: "BvlMeasSpec" Address: 141A8AD70 called at 141A854D5

ClassName: "%o<BevelButton>%" MethodName: "BvlMeasChart" Address: 141A8AF30 called at 141A85575

ClassName: "%o<BevelButton>%" MethodName: "BvlDefChart" Address: 141A8B0F0 called at 141A85615

ClassName: "%o<BevelButton>%" MethodName: "BvlGraph2D" Address: 141A8B2B0 called at 141A856B5

ClassName: "%o<BevelButton>%" MethodName: "BvlLight" Address: 141A8B470 called at 141A85755

ClassName: "%o<BevelButton>%" MethodName: "BvlCalculate" Address: 141A8B630 called at 141A857F5

ClassName: "%o<BevelButton>%" MethodName: "BvlDifference" Address: 141A8B7F0 called at 141A85895

ClassName: "%o<BevelButton>%" MethodName: "BvlSearch" Address: 141A8B9B0 called at 141A85935

ClassName: "%o<BevelButton>%" MethodName: "BvlMakeLightSource" Address: 141A8BB70 called at 141A859D5

ClassName: "%o<BevelButton>%" MethodName: "BvlMakeChartImage" Address: 141A8BD30 called at 141A85A75

...

К сожале­нию, суть это­го метода сос­тоит в том, что он чис­то динами­чес­кий, то есть нель­зя получить спи­сок имен прос­то из дизас­сем­бле­ра, не запус­тив прог­рамму. Но если тебя вдруг нас­тигнет прис­туп пер­фекци­ониз­ма, мож­но прос­то рас­парсить этот спи­сок и скрип­том рас­ста­вить в IDA или x64dbg мет­ки с име­нами методов по нуж­ным адре­сам.

Нам же неког­да отвле­кать­ся на подоб­ные мелочи. Пока впол­не дос­таточ­но того, что при заходе в каж­дую про­цеду­ру, которую IDA не детек­тит как стан­дар­тную бей­сиков­скую, мы можем най­ти ее имя по адре­су в получен­ном спис­ке. Если, конеч­но, она там вооб­ще находит­ся, а такое тоже быва­ет. В любом слу­чае у нас уже есть неп­лохой инс­тру­мент, бла­года­ря которо­му за десять минут работы с отладчи­ком и IDA мы обна­ружи­ваем и пат­чим все про­вер­ки на жад­ность в прог­рам­мном модуле и готовы про­дол­жать даль­нейшее зна­комс­тво с осо­бен­ностя­ми Xojo.

Что­бы нем­ного усложнить себе жизнь, рас­смот­рим чуть более ста­рый, 32-бит­ный вари­ант REALbasic. Итак, у нас есть дру­гая прог­рамма, заг­рузка которой в DIE показы­вает сле­дующее окош­ко.

Ка­залось бы, 32-бит­ная вер­сия Xojo дол­жна быть про­ще, ан нет. Заг­рузив при­ложе­ние в IDA, мы с удив­лени­ем обна­ружи­ваем прак­тичес­ки пол­ное отсутс­твие кода и импорта при вну­шитель­ном раз­мере EXE-модуля.

Раз­гадка прос­та: прак­тичес­ки весь EXE-модуль сос­тоит из огромно­го овер­лея, который под­гру­жает­ся на про­изволь­ные адре­са во вре­мя заг­рузки прог­раммы. А зна­чит, даже дам­пить для изу­чения уже заг­ружен­ный модуль осо­бого смыс­ла нет, хотя сама прог­рамма не меша­ет ни отладке, ни дам­пу. Погуг­лив, обна­ружи­ваем, что более ста­рую вер­сию нашего фрей­мвор­ка на­род уже изу­чал и даже запилил на ее осно­ве пи­тонов­ский скрипт под IDA, который пар­сит овер­лей.

Же­лающие могут озна­комить­ся с этим скрип­том самос­тоятель­но, я же не ста­ну оста­нав­ливать­ся на нем под­робно, пос­коль­ку скрипт на нашем модуле не работа­ет, а струк­тура овер­лея нем­ного отли­чает­ся от опи­сан­ной в упо­мяну­той статье. Будь у нас в запасе поболь­ше вре­мени, мы бы мог­ли разоб­рать новую струк­туру по вин­тикам, понять, что изме­нилось, и соз­дать на осно­ве ста­рого новый работос­пособ­ный скрипт, под­держи­вающий наш фор­мат. Но мы пой­дем дру­гим путем.

Пом­нишь, как мы вык­рутились в слу­чае 64-бит­ной вер­сии? Повеси­ли бряк на фун­кцию RuntimeAllocateAttributeTable из модуля XojoGUIFramework64.dll и ловили им ини­циали­зацию каж­дого метода. Разуме­ется, при заг­рузке в отладчик прог­раммы в ней нет никако­го импорта, кро­ме нес­коль­ких базовых фун­кций kernel32, необ­ходимых для заг­рузки овер­лея. Одна­ко, прер­вавшись в отладчи­ке внут­ри уже заг­ружен­ной прог­раммы, мы видим необ­ходимый модуль в спис­ке — здесь он называ­ется XojoGUIFramework32.dll, и необ­ходимая фун­кция RuntimeAllocateAttributeTable там есть.

Ста­вим на нее бряк и перезаг­ружа­ем прог­рамму в отладчи­ке. Бряк сто­пит­ся уже при вызове из заг­ружен­ного овер­лея, код вызова RuntimeAllocateAttributeTable до боли похож на ана­логич­ный из 64-бит­ного модуля. Пос­коль­ку мы уже в кур­се, как все было устро­ено там, нам не сос­тавит боль­шого тру­да при­думать ана­логию для каж­дого нуж­ного нам поля:

0197FCA6 mov esp,dword ptr ss:[ebp-E4]

0197FCAC mov dword ptr ss:[ebp-D0],eax

0197FCB2 mov ecx,198074F

0197FCB7 mov dword ptr ss:[ebp-58],ecx

0197FCBA mov edx,dword ptr ss:[ebp-50]

0197FCBD mov dword ptr ds:[edx+248],ecx

0197FCC3 mov ebx,1AA2296 <-- Адрес класса

0197FCC8 mov dword ptr ss:[ebp-58],ebx

0197FCCB mov dword ptr ds:[edx+250],ebx

0197FCD1 mov ecx,377907D <-- Имя метода

0197FCD6 mov dword ptr ds:[edx+258],ecx

0197FCDC mov esi,3774A0C <-- Имя класса

0197FCE1 mov dword ptr ds:[edx+268],esi

0197FCE7 xor edi,edi

0197FCE9 mov dword ptr ds:[edx+26C],edi

0197FCEF mov esi,11

0197FCF4 mov dword ptr ds:[edx+25C],esi

0197FCFA push eax

0197FCFB push 0

0197FD00 push 258 <-- Смещение до элемента таблицы

0197FD05 push edx <-- Адрес таблицы

0197FD06 call <xojoguiframework32.RuntimeAllocateAttributeTable>

Су­дя по все­му, эта струк­тура в 32-бит­ном исполне­нии занима­ет 0x28 байт и сос­тоит из десяти полей. Нес­ложно переде­лать текст жур­нала в условной точ­ке оста­нова под новую информа­цию. Что я и пред­лагаю читате­лю сде­лать самос­тоятель­но.

Читайте ещё больше платных статей бесплатно: https://t.me/hacker_frei



Report Page