Доброго времени суток.
Само крякми: http://forum.reverse4you.org/attachm...0&d=1358541694
Сегодня рассмотрим решение крякмиса, с которого мы сможем вынести информацию о том как класс Qt QString хранит внутри себя данные.
Собственно, посмотрим на то с чем мы будем иметь дело. Видим, что программа написана на С++ с использованием фреймворка Qt. Запустим ее и попробуем ввести что-то в поле Login и нажав кнопку Enter видим, что никаких окошек не всплывает. А жаль... Очень жаль.... Но не будем долго печалиться и начнем как-то выкручиваться с этой ситуации.
Для начала посмотрим, что используется в данном приложении. Для этого зайдем в IDA и нажмем View->Open subview->Names window. Немного посмотрев, меня очень сильно заинтересовал класс QlineEdit. Вбив в гугле, нашел интересную функцию у этого класса под названием text(), которая возвращает Qstring, где хранится введенная нами строка. Т.к. Введеная нами строка вероятно где-то должна сравниваться, то попробуем найти в окне Names window класс Qstring и посмотрем, какие операторы его используются. Видим, что используется Qstring::operator==(QString const &). Нажимаем на ней 2 раза и видим, что наша строка сравнивается дважды по адресам 0х00401341 и 0х004013F2. Поставим на этих адресах сразу бряки. Теперь проанализируем, что находится возле этих адресов. Начнем с адреса 0х00401341:
PHP Code:
.text:00401329 cmp byte ptr [esi+24h], 1
.text:0040132D mov edi, ds:??8QString@@QBE_NABV0@@Z ; QString::operator==(QString const &)
.text:00401333 jnz loc_4013E4
.text:00401339 lea eax, [esi+20h]
.text:0040133C push eax
.text:0040133D lea ecx, [esp+60h+var_40]
.text:00401341 call edi ; QString::operator==(QString const &) ; QString::operator==(QString const &)
.text:00401343 test al, al
.text:00401345 jz loc_4013E4
.text:0040134B push 0FFFFFFFFh
.text:0040134D push offset aYouVeBrokenMe ; "You' ve broken me!"
.text:00401352 call ds:?fromAscii_helper@QString@@CAPAUData@1@PBDH@Z ; QString::fromAscii_helper(char const *,int)
.text:00401358 mov [esp+64h+var_38], eax
.text:0040135C push 0FFFFFFFFh
.text:0040135E mov bl, 9
.text:00401360 push offset aCongratulation ; "Congratulation"
.text:00401365 mov byte ptr [esp+6Ch+var_4], bl
.text:00401369 call ds:?fromAscii_helper@QString@@CAPAUData@1@PBDH@Z ; QString::fromAscii_helper(char const *,int)
Ура, мы видим нужные для нас сообщения вида «You' ve broken me!» и «Congratulation». Они не могу не радовать, но давайте попробуем понять, что должно произойти, что бы мы туда попали. Исходя из листинга выше, можно сделать вывод, что для этого должны выполниться следующие условия: 1. по адресу esi+24h, мы должны получить значение 1 ( похоже на флажок) и 2. какие-то две строки, можно предположить, что это введенная нами строка и правильная, должны быть равны.
Если мы запустим приложение, введем какое-то значение и нажмем кнопку Enter, то попадем на оператор сравнения, который находится по адресу 0х004013F2. Почему же мы не попали на оператор сравнения описанный выше? Наверно, все из-за того, флажок оказался равным 0. Это мы можем легко проверить, если поставим бряк на строчке и посмотрев, что находится по адресу esi+24h:
HTML Code:
.text:00401329 cmp byte ptr [esi+24h], 1
Т.к. мы попали на 2й оператор сравнения, то давайте проанализируем код, который находится вокруг него:
PHP Code:
.text:004013EA lea eax, [esi+1Ch]
.text:004013ED push eax
.text:004013EE lea ecx, [esp+60h+var_40]
.text:004013F2 call edi ; QString::operator==(QString const &) ; QString::operator==(QString const &)
.text:004013F4 test al, al
.text:004013F6 jz short loc_401448
.text:004013F8 push 0FFFFFFFFh
.text:004013FA push offset aPassword ; "Password:"
.text:004013FF call ds:?fromAscii_helper@QString@@CAPAUData@1@PBDH@Z ; QString::fromAscii_helper(char const *,int)
.text:00401405 add esp, 8
.text:00401408 mov [esp+5Ch+var_38], eax
.text:0040140C lea ecx, [esp+5Ch+var_38]
.text:00401410 push ecx
.text:00401411 mov ecx, [esi+18h]
.text:00401414 mov byte ptr [esp+60h+var_4], 0Bh
.text:00401419 call ds:?setText@QLabel@@QAEXABVQString@@@Z ; QLabel::setText(QString const &)
.text:0040141F mov edx, [esp+5Ch+var_38]
.text:00401423 mov byte ptr [esp+5Ch+var_4], 6
.text:00401428 or eax, 0FFFFFFFFh
.text:0040142B lock xadd [edx], eax
.text:0040142F jnz short loc_40143B
.text:00401431 mov ecx, [esp+5Ch+var_38]
.text:00401435 push ecx
.text:00401436 call ebx ; QString::free(QString::Data *) ; QString::free(QString::Data *)
.text:00401438 add esp, 4
.text:0040143B
.text:0040143B loc_40143B: ; CODE XREF: sub_401140+2EF#j
.text:0040143B mov ecx, [esi+14h]
.text:0040143E call ds:?clear@QLineEdit@@QAEXXZ ; QLineEdit::clear(void)
.text:00401444 mov byte ptr [esi+24h], 1
В этом листинге можем видеть, что если 2 строки равны, то Qlabel с текущим текстом «Login» поменяется на Passoword и флажок, к-ый мы упоминали выше, установится в 1! Исходя из всего можем сделать вывод, что по адресу 0x004013F2 у нас проверяется правильность логина, а по адресу 00401341 — правильность пароля. И для того, что бы это проверить нам надо разобраться, как QString хранит строки. Посмотрим какой код находится внутри функции Qstring::operator==(QStting const &):
PHP Code:
QtCore4.dll:6705BFD0 qtcore4_??8QString@@QBE_NABV0@@Z:
QtCore4.dll:6705BFD0 mov eax, [esp+4]
QtCore4.dll:6705BFD4 mov edx, [ecx]
QtCore4.dll:6705BFD6 mov eax, [eax]
QtCore4.dll:6705BFD8 mov ecx, [edx+8]
QtCore4.dll:6705BFDB cmp ecx, [eax+8]
QtCore4.dll:6705BFDE jz short loc_6705BFE5
QtCore4.dll:6705BFE0 xor al, al
QtCore4.dll:6705BFE2 retn 4
QtCore4.dll:6705BFE5 ; ---------------------------------------------------------------------------
QtCore4.dll:6705BFE5
QtCore4.dll:6705BFE5 loc_6705BFE5: ; CODE XREF: QtCore4.dll:qtcore4_??8QString@@QBE_NABV0@@Z+E#j
QtCore4.dll:6705BFE5 push ecx
QtCore4.dll:6705BFE6 mov ecx, [eax+0Ch]
QtCore4.dll:6705BFE9 mov eax, [edx+0Ch]
QtCore4.dll:6705BFEC call near ptr unk_6705BB20
QtCore4.dll:6705BFF1 add esp, 4
QtCore4.dll:6705BFF4 retn 4
С этого листинга, можно заключить, что [esp+4] содержит указатель на объект Qstring, a [ecx] — this. Далее проверяется не равны ли указатели объектов и если не равны, то получаем указатели сравниваемых строк: [*QString+0Ch] и [*this + 0Сh]
. Соответственно, если к тому что передается в функцию сравнения прибавить 0Ch, то увидим указатель на введенную и верную строки. Проверим это.
Перезапускаем программу, вводим логин, нажимает кнопку Enter. И смотрим, что передается функции operator==. Посмотрим, какая строка передается оператору сравнения:
PHP Code:
debug035:00A16E58 db 1
debug035:00A16E59 db 0
debug035:00A16E5A db 0
debug035:00A16E5B db 0
debug035:00A16E5C db 0Ah
debug035:00A16E5D db 0
debug035:00A16E5E db 0
debug035:00A16E5F db 0
debug035:00A16E60 db 8
debug035:00A16E61 db 0
debug035:00A16E62 db 0
debug035:00A16E63 db 0
debug035:00A16E64 db 6Ah ; j
debug035:00A16E65 db 6Eh ; n
debug035:00A16E66 db 0A1h ; �
debug035:00A16E67 db 0
debug035:00A16E68 db 0
debug035:00A16E69 db 0F0h ; �
debug035:00A16E6A db 2Eh ; .
debug035:00A16E6B db 0
debug035:00A16E6C db 29h ; )
debug035:00A16E6D db 0
debug035:00A16E6E db 2Eh ; .
debug035:00A16E6F db 0
debug035:00A16E70 db 34h ; 4
debug035:00A16E71 db 0
debug035:00A16E72 db 2Eh ; .
debug035:00A16E73 db 0
debug035:00A16E74 db 23h ; #
debug035:00A16E75 db 0
debug035:00A16E76 db 22h ; "
debug035:00A16E77 db 0
debug035:00A16E78 db 35h ; 5
debug035:00A16E79 db 0
debug035:00A16E7A db 0
Это строка 2Eh 29h 2eh 34h 2eh 34h 2eh 23h 22h 35h. Переводим это все в ASCII пробуем ввести в логин нажимает Enter и все равно видим, что наша программа не приняла логин, соответственно, что тут не так и по ходу логин тем или иным образом шифруется. Что бы это проверить давайте проанализируем функцию, в которой находится наши операторы сравнения. Посмотрим на ее начало ( что бы долго не капашиться, посмотрим на код в Hex-rays):
PHP Code:
v1 = this;
v25 = this;
enteredString = QLineEdit::text(*(_DWORD *)(this + 20), &v28);
v32 = 0;
v3 = QString::toAscii(enteredString, &v27);
LOBYTE(v32) = 1;
QString::QString(&v21, v3);
LOBYTE(v32) = 3;
if ( !_InterlockedExchangeAdd((signed __int32 *)v27, 0xFFFFFFFFu) )
qFree(v27);
v4 = QString::free;
LOBYTE(v32) = 4;
if ( !_InterlockedExchangeAdd(v28, 0xFFFFFFFFu) )
QString::free(v28);
v22 = (signed __int32 *)QString::shared_null;
_InterlockedExchangeAdd((signed __int32 *)QString::shared_null, 1u);
LOBYTE(v32) = 5;
v26 = QString::fromAscii_helper("g", -1);
v5 = *(_DWORD *)(v21 + 8) == 0;
v6 = *(_DWORD *)(v21 + 8) < 0;
LOBYTE(v32) = 6;
v23 = 0;
if ( !(v6 | v5) )
{
do
{
v7 = QString::toAscii(&v26, &v29);
LOBYTE(v32) = 7;
v8 = QString::toAscii(&v21, &v24);
LOBYTE(v32) = 8;
v9 = QByteArray::operator__(v7, &v30, 0);
v10 = **(_DWORD **)v9;
v11 = *(_DWORD *)(v9 + 4);
if ( v11 >= *(_DWORD *)(v10 + 8) )
v12 = 0;
else
v12 = *(_BYTE *)(v11 + *(_DWORD *)(v10 + 12));
v13 = QByteArray::operator__(v8, &v31, v23);
v14 = **(_DWORD **)v13;
v15 = *(_DWORD *)(v13 + 4);
if ( v15 >= *(_DWORD *)(v14 + 8) )
v16 = 0;
else
v16 = *(_BYTE *)(v15 + *(_DWORD *)(v14 + 12));
LOBYTE(v32) = 7;
v17 = (v12 ^ v16) % 127;
if ( !_InterlockedExchangeAdd((signed __int32 *)v24, 0xFFFFFFFFu) )
qFree((void *)v24);
LOBYTE(v32) = 6;
if ( !_InterlockedExchangeAdd((signed __int32 *)v29, 0xFFFFFFFFu) )
qFree(v29);
if ( v17 < 32 )
v17 += 32;
QString::operator__(&v22, v17);
v18 = __OFSUB__(v23 + 1, *(_DWORD *)(v21 + 8));
v6 = v23++ + 1 - *(_DWORD *)(v21 + 8) < 0;
}
while ( v6 ^ v18 );
v1 = v25;
v4 = QString::free;
}
Видим, что вначале мы получаем исходную строку, и потом в цикле с каждым символом делаем операции: взять ASCII очередного введенного символа, сделать с ним операцию xor, резудьтат взять по модулю 127 и далее если получившиеся значение меньше 32 (20h), то добавить еще к нему 20h.
Соответственно, что бы получить строку, к-ую мы должны ввести, нам надо поставить бряку на строчку, где xor'ся значения и получить ключ с которым происходит эта операция. Для этого анализируем ассемблерный код и ставим бряку в этой строчке:
PHP Code:
004012A0 xor eax, edx
Запускаем программу, попадаем на эту точку и смотрим чему равно значение edx — это и есть наш ключ. Он у нас получается равным 67h.
Т.к. У нас есть строка, к-ую мы должны были бы получить, то делаем обратные операции и получаем строку, к-ую мы должны ввести:
Искомый символ = (Символ полученной строки) xor 0x67.
Получаем строку, к-ую должны получить, как было описано выше:
PHP Code:
debug025:003DB040 db 1
debug025:003DB041 db 0
debug025:003DB042 db 0
debug025:003DB043 db 0
debug025:003DB044 db 0Dh
debug025:003DB045 db 0
debug025:003DB046 db 0
debug025:003DB047 db 0
debug025:003DB048 db 0Dh
debug025:003DB049 db 0
debug025:003DB04A db 0
debug025:003DB04B db 0
debug025:003DB04C db 52h ; R
debug025:003DB04D db 0B0h ; -
debug025:003DB04E db 3Dh ; =
debug025:003DB04F db 0
debug025:003DB050 db 0
debug025:003DB051 db 0F0h ; �
debug025:003DB052 db 37h ; 7
debug025:003DB053 db 0
debug025:003DB054 db 26h ; &
debug025:003DB055 db 0
debug025:003DB056 db 35h ; 5
debug025:003DB057 db 0
debug025:003DB058 db 26h ; &
debug025:003DB059 db 0
debug025:003DB05A db 23h ; #
debug025:003DB05B db 0
debug025:003DB05C db 2Eh ; .
debug025:003DB05D db 0
debug025:003DB05E db 34h ; 4
debug025:003DB05F db 0
debug025:003DB060 db 22h ; "
debug025:003DB061 db 0
debug025:003DB062 db 47h ; G
debug025:003DB063 db 0
debug025:003DB064 db 2Bh ; +
debug025:003DB065 db 0
debug025:003DB066 db 28h ; (
debug025:003DB067 db 0
debug025:003DB068 db 34h ; 4
debug025:003DB069 db 0
debug025:003DB06A db 33h ; 3
debug025:003DB06B db 0
И начинаем восстанавливать исходную строку:
X1 = ( 37h — 20h ) xor 67h = 70h = P
X2 = (26h — 20h ) xor 67h = 61h = A
…
Проделывая для всех символов — paradise lost. Проверяем этот логин, и смотрим о чудо, он подходит. Мы проходим проверку, которая находится сразу за оператором сравнения, находящийся по адресу 004013F2. Видим, что надпись Login поменяется на Password и наш флажок установится в 1.
Теперь если мы введем какой либо пароль, то мы попадем на оператор сравнения, находящийся по адресу 00401341. Где скорее всего понятно, что мы не правильно ввели строку ;). Сразу смотрим чему должна быть равна правильная строка:
PHP Code:
debug024:003DB088 db 1
debug024:003DB089 db 0
debug024:003DB08A db 0
debug024:003DB08B db 0
debug024:003DB08C db 0Dh
debug024:003DB08D db 0
debug024:003DB08E db 0
debug024:003DB08F db 0
debug024:003DB090 db 0Dh
debug024:003DB091 db 0
debug024:003DB092 db 0
debug024:003DB093 db 0
debug024:003DB094 db 9Ah ; �
debug024:003DB095 db 0B0h ; -
debug024:003DB096 db 3Dh ; =
debug024:003DB097 db 0
debug024:003DB098 db 0
debug024:003DB099 db 0F0h ; �
debug024:003DB09A db 42h ; B
debug024:003DB09B db 0
debug024:003DB09C db 52h ; R
debug024:003DB09D db 0
debug024:003DB09E db 4Fh ; O
debug024:003DB09F db 0
debug024:003DB0A0 db 4Bh ; K
debug024:003DB0A1 db 0
debug024:003DB0A2 db 45h ; E
debug024:003DB0A3 db 0
debug024:003DB0A4 db 4Eh ; N
debug024:003DB0A5 db 0
debug024:003DB0A6 db 20h
debug024:003DB0A7 db 0
debug024:003DB0A8 db 44h ; D
debug024:003DB0A9 db 0
debug024:003DB0AA db 52h ; R
debug024:003DB0AB db 0
debug024:003DB0AC db 45h ; E
debug024:003DB0AD db 0
debug024:003DB0AE db 41h ; A
debug024:003DB0AF db 0
debug024:003DB0B0 db 4Dh ; M
debug024:003DB0B1 db 0
debug024:003DB0B2 db 53h ; S
debug024:003DB0B3 db 0
debug024:003DB0B4 db 0
Теперь по тому же принципу, что и с логином получаем правильный пароль. Он оказывается равным - %5(,")G#5"&*4
Вот, собственно и все.
Все, что хочется вынести с этого крякми, как это то, что если внимательно посмотреть на тот как хранятся строки в Qstring, то можно сделать вывод (испольую то, что писал root, очень хорошо написал ;) ):

Code:
[0xaaaabbbb] = 0x12345670
0x12345670 struct {
0x0 reserved <= *QString
0x8 pSize <= *QString + 8
0xC pString <= *QString + C
}
QString = this(QString) = 0xaaaabbbb
*QString = [QString]
pSize = *QString + 8
Size = [pSize]
pString = *QString + C
String = [pString]
И также, хочется отметить, что каждый символы в QString хранится формате unicode.
Вроде как все. Всем спасибо за внимание и всем кто помогал мне разбираться.
Жду гневных отзывов, комментариев и критики по написанному.
П.С.: может заняться исследованием типичных конструкций для ООП на языке С++ и как это все выглядит в дизасме? Кого-то такое заинтересует?