[СТАТЬЯ] Использование отладчика для изучения RFOnline на примере разбора авотризации
Данная статься предназначена для тех, кто хочет научиться исследовать программы, но не знает, с чего начать. В этой статье покажу простой пример использования отладчика на примере изучения протокола связи с логин сервером РФО.
После того, как вы ввели имя и пароль, логин программа РФО посылает введенную вами информацию в зашифрованном виде. В этой статье я покажу, как узнать применяемый при этом алгоритм шифрования.
Перед тем, как исследовать программы желательо научиться:
1. программироать хотя бы на 1-м языке программирования
2. понимать язык ассемблера (хорошо бы научиться программировать и на нем)
3. прочитать про регистры процессора, хотя бы здесь
Для работы понадобятся 3 программы:
1. OllyDbg 1.09b - отладчик
2. WPE PRO Alpha 0.9a - сниффер
3. родная логин программа РФО (в дальнейшем ЛП)
Версии программ могут отличаться от приведенных выше.
часть 1
Первым делом запускаем логин-прогу РФ'а.
Пускаем WPE и пускаем прослушивание трафика ЛП.
в ЛП в качестве логина и пароля указываем "1111111111" и тыкаем войти, естественно получим сообшение вроде: логин или пароль неверны.
Смотрим на перехваченный с помошью WPE трафик, получаем что-то вроде (стрелкой > обозначен входящий пакет, < - исходящий):
>05 00 15 0C FF
<07 00 15 0D 03 F8 02
>1F 00 15 03 CE CE CE CE CE CE CE CE CE CE CE FF FF CE CE CE CE CE CE CE CE CE CE CE CE FF 00
<0E 00 15 04 06 CC CC CC CC 00 00 00 00 CC
жирным указаны заголовки пакетов, справа от них - тело пакета.
Как можно видеть, 1-й пакет в обоих случаях одинаковый, 2-й отличается только последними 3-мя байтами. Логично предположить следующий протокол:
1. Клиент запрашивает у сервера ключ (1-й пакет)
2. Сервер ключ пересылает клиенту (2-й пакет)
3. Клиент полученным ключом шифрует имя и пасс и отсылает эту инфу серверу (3-й пакет)
4. сервер высылает клиенту результат проверки (4-й пакет)
В принципе, все это можно вычитать в описании форматов пакетов:
Итак, ключ есть. Теперь основная задача выяснить, каким образом ЛП шифрует пакет. Есть несколько способов это сделать:
1. повторить часть 1 раз 100 и выявить закономерность.
2. Посмотреть, как это делает ЛП.
Будем действовать 2-м путем (естественно )) ).
Можно заметить, что алгоритм шифрования представляет собой обычный шифр гамирования с длиной гаммы = 1 и переменным ключом. Простенький алгоритм в общем-то.
часть 2
Первым делом выключаем ЛП (если она была запущена)
1. Запускаем OllyDb
2. File->Open выбираем rf.exe. Olly напишет нам дизассемблированный код ЛП.
3. Запускаем (тычем F9).
Теперь нужно найти кусод кода, в котором происходит кодирование пакета. Для этого проще всего найти код, в котором происходит прием пакета с ключем, а затем посмотреть, где данные этого пакета используются.
Связь с сервером осуществляется посредством TCP протокола. В Windows этот протокол реализован в API функциях. Для приема пакета используется функция recv. Т.о. нужно найти место в программе, где вызывается эта функция.
Заметим, что ключ передается ЛП во 2-м пакете. Т.е. первый присланный пакет и будет содержать ключ.
4. ищем.
Правой кнопкой мыши тычем на код ЛП, в отмывшемся меню выбираем: Search for -> All intermodular calls. нам выпадает большой список всех вызываемых программой внешних функций. Среди них ищем (в поле Destanation)
WS2_32.recv.
и дважды кликаем по ней.
Код:
MOV EAX,DWORD PTR SS:[ESP+C]
PUSH ESI
PUSH EDI
PUSH 0 ; /Flags = 0
PUSH EAX ; |BufSize
MOV EAX,DWORD PTR SS:[ESP+14] ; |
MOV ESI,ECX ; |
MOV ECX,DWORD PTR SS:[ESP+18] ; |
LEA EDX,DWORD PTR DS:[EAX+EAX*8] ; |
MOV EAX,DWORD PTR DS:[ESI+10] ; |
PUSH ECX ; |Buffer
SHL EDX,4 ; |
MOV ECX,DWORD PTR DS:[EDX+EAX] ; |
PUSH ECX ; |Socket
CALL DWORD PTR DS:[<&WS2_32.#16>] ; \recv
MOV EDI,DWORD PTR SS:[ESP+18]
CMP EAX,-1
MOV DWORD PTR DS:[EDI],EAX
JNZ SHORT rf.00488CCB
CALL DWORD PTR DS:[<&WS2_32.#111>] ; [WSAGetLastError
CMP EAX,2733
MOV DWORD PTR DS:[EDI],EAX
JE SHORT rf.00488CC4
INC DWORD PTR DS:[ESI+48]
POP EDI
XOR AL,AL
POP ESI
RETN 10
POP EDI
MOV AL,1
POP ESI
RETN 10
Вот эта функция и принимает пакет. Кое-что о ней: начиная с 4-й строчки (PUSH 0) в стек заталкиваются параметры API функции recv. Сама функция вызывается на 15-й строчке (CALL DWORD PTR DS:[<&WS2_32.#16>] ). Важно: в качестве параметра функции передается адрес на область памяти, куда нужно положить принятый пакет. Это происходит на 11-й строчке (PUSH ECX помещаем в стек значение регистра ECX). Дебаггер эту команду пометил справа словом Buffer. После исполнения функции recv в памяти по адресу, помещенному в стек на 11-й строчке, будет находится принятый пакет. Этот адрес нужно узнать.
5. Ставим брекпоинт на 11-ю строчку функции (на команду PUSH ECX). Для этого выделяем нужную строчку и тычем F2. Брекпоинт обозначается слева красным прямоугольником.
6. Вспоминаем, что у нас есть запущенная из-под OllyDb логин прогарамма. Переходим на нее указываем логин и пароль (лучше всего указать "11111111111"). и нажимаем "войти".
Если все сделали правильно, то выполнение ЛП прекратится.
7. Переходим на дебаггер. Смотрим адрес, написанный в регистре ECX (запоминаем его). Значение регистров описаны справа. Выделяем значение справа от ECX -> ПКМ -> Follow in Dump. В нижнем левом разделе находится дамп памяти ЛП. После выполнения команды recv по адресу, указанному в ECX будет находится содержмое пакета.
8. Ставим брекпоинт на последнюю строчку функции.
9. продолжаем выполнение ЛП (тычем F9)
10. Срабатывает последний поставленный нами брекпоинт.
Смотрим на дамп памяти. Пакет появился там где и должен.
Мой соответствующий дамп: 04164E40 07 00 15 0D 04 2C 03
Вспоминаем: 1-е 4 байта - описание пакета. Нам же нужен сам ключ, т.е. последние 3 байта.
11. убираем проставленные брекпоинты
12. в дампе памяти для 5-го байта принятого пакета устанавливаем брекпоинт на чтение памяти. Для этого выделяем 5-й пакет, ПКМ -> breakpoint -> memory, on access
Брекпоинт сработает в тот момент, когда ЛП прочитает содержимое пакета.
13. Продолжаем выполнение программы.
14. Если все сделано верно, то выполнение программы опять прервется на следующей функции:
Функция RecvCryptKeyRequest
Код:
PUSH ESI
MOV ESI,ECX
PUSH rf.004D31B4 ; ASCII "<4> RecvCryptKeyRequest"
MOV EAX,DWORD PTR DS:[ESI+435F4]
PUSH EAX
CALL rf.00451A70
PUSH EAX
CALL rf.00451D00
MOV EAX,DWORD PTR SS:[ESP+18]
ADD ESP,0C
0MOV CX,WORD PTR DS:[EAX+1]
MOV DL,BYTE PTR DS:[EAX]
ADD CX,3
INC DL
PUSH ECX
PUSH EDX
MOV ECX,ESI
CALL rf.???????? - сохраняет значения ключа в память (на месте ? - число)
MOV ECX,ESI
CALL rf.???????? - <- в этой функции шифруется пакет
MOV AL,1
POP ESI
RETN 8
в этой функции производятся некороые преобразования ключа. на 11-й строке функции (0MOV CX,WORD PTR DS:[EAX+1]) в регистр CX заносится 7-й и 6-й байты принятого пакета (именно в таком порядке), на 12-й строчке (MOV DL,BYTE PTR DS:[EAX]) в регистр DL заносится 5-й байт.
13-18 строки:
Код:
ADD CX,3 прибавили к CX 3
INC DL прибавили к DL 1
PUSH ECX поместили в стек значение регистра ECX
PUSH EDX поместили в стек значение регистра EDX
MOV ECX,ESI ECX=ESI хз зачем, не разбирался)
CALL rf.???????? вызывается функция rf.???????? (на месте ? - числа)
в функции rf.???????? полученные значения для ECX и EDX (которые были сохранены в стеке), запмсываются в память, а из стека удаляются.
Код:
MOV BYTE PTR DS:[ECX+435E0],AL AL содержит 5-й байт пакета+3
MOV WORD PTR DS:[ECX+435E2],DX DX содержит 7-й 6-й байт пакета +1
15. Теперь ставим брекпоинт на 19-й строчке функции RecvCryptKeyRequest (см выше, эта строка выделена жирным).
16. тычем F9
17.Выполнение программы прервалось на 19-й строчке. Дальше будем выполнять программу пошагово. Для выполнения следующего шага нажимаем F7.
18. После 1-го нажатия F7 происходит вызов функции, в которой кодируется пакет. Привожу начало этой функции с небольшими комментами. Жирным выделен цикл, в котором кодируется пакет.
Код:
SUB ESP,20
XOR EAX,EAX
PUSH EBX
PUSH ESI
MOV EBX,ECX
PUSH EDI
MOV ECX,6
LEA EDI,DWORD PTR SS:[ESP+10]
REP STOS DWORD PTR ES:[EDI]
STOS WORD PTR ES:[EDI]
STOS BYTE PTR ES:[EDI]
MOV EAX,DWORD PTR DS:[EBX+435F4]
PUSH rf.004D31FC ; ASCII "<5> SendLoginAccept Normal"
PUSH EAX
CALL rf.00451A70
PUSH EAX
CALL rf.00451D00
MOV EDI,DWORD PTR DS:[EBX+435EC]
OR ECX,FFFFFFFF
XOR EAX,EAX
ADD ESP,0C
REPNE SCAS BYTE PTR ES:[EDI]
NOT ECX
SUB EDI,ECX
LEA EDX,DWORD PTR SS:[ESP+10]
MOV EAX,ECX
MOV ESI,EDI
MOV EDI,EDX
LEA EDX,DWORD PTR SS:[ESP+1D]
SHR ECX,2
REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
MOV ECX,EAX
XOR EAX,EAX
AND ECX,3
REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
MOV EDI,DWORD PTR DS:[EBX+435F0]
OR ECX,FFFFFFFF
REPNE SCAS BYTE PTR ES:[EDI]
NOT ECX
SUB EDI,ECX
MOV EAX,ECX
MOV ESI,EDI
MOV EDI,EDX
SHR ECX,2
REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
MOV ECX,EAX
MOV AX,WORD PTR DS:[EBX+435E2] ; Здесь в AX заносятся 7-й и 6-й байты пакета, к ним прибавили 3
AND ECX,3
REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
MOV CL,BYTE PTR DS:[EBX+435E0] ; В CL заносятся 5-й байт пакета, к нему прибавили 1 (см выше)
LEA ESI,DWORD PTR SS:[ESP+10]
MOV EDI,1B
/MOV DL,BYTE PTR DS:[ESI] - поместили очередной байт пакета в DL
|ADD DL,CL - прибавили к DL значение регистра Cl
|XOR DL,AL - Исключающее ИЛИ
|MOV BYTE PTR DS:[ESI],DL - разультат запомнили
|INC ESI
|DEC EDI
\JNZ SHORT rf.0044ABF6
Вот пожалуй и все. Итак, алгорим шифрования:
X - байт, который нужно зашифровать
Y - зашифрованный байт
Y = (X + 6-й байт пакета + 3) XOR (5-й байт пакета +1)
Как видите, ничего сложного. Заметим, что перед циклом а в регистр AX заносятся 7-й и 6-й байты пакета (+3), Однако в цикле участвует не AX, а AL. Т.е. старший байт регистра AX, он называтся AH и он содержит 7-й байт принятого пакета (возможно +1 в некоторых случаях), в шифровании не участвует.
7-й байт не участвует в шифровании.
На сегодня все. Если тема покатит, напишу еще что-нибудь подобное.
Если чего-нибудь непонятно - юзайте гугл.
Последний раз редактировалось Тигрь; 24.05.2012 в 10:12.
Кто-нить знает, в 2.2.3 поменяли ключ шифрования? И кстати, здесь вроде ошибка должно быть Y = (X + 5-й байт пакета + 1) XOR (6-й байт пакета +3). Но всеравно шифр ни тот , ни автора не подходит, разве что я не так понял формулу :(
Спс большое. Еще вопрос, посылаю я пакет с логином и пассом, а сервак отвечает пакетом у которого первые четыре байта цифрами а остальное все нули, ето может быть запрос на пересылку логина и паса изза неправильной шифровки?
Да и алгоритм странный какой-то, оба подходят
Последний раз редактировалось AnderSon; 01.02.2011 в 10:37.
нет, тигрь. Привет. Я просто давно не программировал, вот и немного подзабылось. Я уже допёр до этого, но проблема в другом. Пишу мини консольную прогу на си шарпе. Не могу никак зашифровать логин и пароль. Сможешь помочь? А то эрроры выдаёт...
Кстати по поводу 31 и 49. Тут в статье вогела про написание ланчера использована делфёвая функция ord, она возвращает по идее не hex значение, а аски код буквы.
А вообще у меня проблема с тем что не вдупляю почему на си шарпе (новый для меня язык) крашится приложение. Точнее я понимаю почему крашится, но не понимаю почему не правильно. в общем опишу ситуёвину.
Отправил 1 пакет, в ответ получил пакет с ключом. А вот дальше начинается жопа. Пытаюсь зашифровать логин и пароль, а в результате получаю ошибку, которая говорит что значение слишком большое чтоб сконвертить его в байт (в моём случае там значение 360 получается).