Диплом за 24 часаPorosenok (p0r0sen0k@yandex.ru) Хакер, номер #104, стр. 070 Блестящая защита по-хакерски Долгожданное лето, конечно, не может не радовать. Но вместе с наступлением лета подошел к концу и учебный год. У одних студентов в полном разгаре сессия, а у других настал тяжелый период в жизни под названием «ДИПЛОМ». Вот и моему товарищу также предстояла защита диплома. Все бы ничего, но до защиты оставалось всего лишь несколько дней :(. Видя, что он уже практически впал в отчаяние и смирился с мыслью о том, что придется защищать диплом на следующий год, я решил ему помочь. Тема его диплома была следующая: «Разработка автоматизированной системы удаленной диагностики компьютера». Если в нескольких словах, то система должна иметь клиент-серверную технологию, задачей клиентов которой является сбор и отправка значений датчиков (температура процессора, скорость вращения кулера и т.д.) на сервер. Поиск решения Первоначально для получения данных от датчиков предполагалось использовать WMI-технологию, но после нескольких часов, проведенных за работой, нам так и не удалось достичь желаемых результатов. Кроме того, в интернете была найдена статья, в которой упоминалось о безуспешной попытке получения скорости вращения кулера с использованием WMI-технологии. Тогда возник вопрос: каким же образом программы аналогичной тематики получают интересующие нас значения? Для исследования была выбрана программа Everest. В папке с программой находится около двух десятков файлов. Сразу и не сообразить, где располагается код, отвечающий за получение данных от датчиков. Поэтому, чтобы избавиться от лишнего, я стал последовательно переименовывать файлы и запускать программу, проверяя, отображаются или нет нужные нам данные. В итоге осталось всего лишь четыре файла: everest.exe, everest.bin, kerneld.wnt и everest.key, назначение которых нам предстоит выяснить. Судя по размеру файла everest.exe, который составляет всего 65 Кб, напрашивается вывод, что он не отвечает за получение информации о системе, а выполняет какие-то другие действия. Для того чтобы выяснить, какие именно, откроем его в отладчике OllyDbg. Но для начала проверим, запакован он или нет. PEiD показал, что программа запакована UPX’ом, поэтому для распаковки выполним команду upx.exe -d everest.exe. Прежде чем приступить к отладке, посмотрим, вызовы каких функций используются в программе, для этого откроем окно Found intermodular calls («ПК -> Search for -> All intermodular calls»). Сразу же привлекает внимание вызов CreateProcess. Жмем на нем <F2> для установки брейкпоинта, а затем и <F9> для запуска программы. Через несколько мгновений срабатывает бряк и выполнение программы прекращается. В качестве имени выполняемого файла функции передается строка, содержащая everest.bin. Предположение оказалось верным - выходит, что основная работа выполняется в everest.bin. Поэтому делаем следующее: переименовываем everest.bin в everest_bin.exe и запускаем на выполнение. После запуска программа выдала сообщение об окончании срока лицензии и закрылась. Значит, что файл everest.exe отвечал за проверку регистрации программы. Но ничего страшного, это мы поправим. Открываем everest_bin.exe в отладчике, не забыв сначала его распаковать все тем же UPX’ом. Программа загружена, но прежде чем нажать <F9>, поставим брейкпоинты на вызов MessageBox. После запуска сработает бряк по адресу 0x00695249h. Посмотрев немного выше, нельзя не заметить команду JBE everest_.0069525A, которая очень походит на проверку на зарегистированность. Чтобы развеять все сомнения, меняем JBE на JMP и перезапускаем программу. Все работает отлично, идем дальше. При отображении скорости вращения кулера выводится заголовок Cooling Fans, попробуем его поискать в окне отладчика Text strings referenced («ПК -> Search for -> All referenced text strings»). Такая строка действительно есть, и по адресу 0x5AECCB осуществляется взятие ее адреса, ставим бряк. Прервавшись на бряке, начинаем выполнять программу в пошаговом режиме с помощью <F8>. По адресу 0x5AECDC в EDX из памяти по адресу 0x02E9FA04 помещается число 0x96B, десятичное 2411, то есть количество оборотов кулера (смотри скрины). Теперь попробуем выяснить, где оно формируется. Для этого перейдем на начало функции, в которой мы находимся в данный момент и поставим бряк. Для того чтобы определить момент записи числа 0x96B в память, перейдем в окне дампа памяти по адресу 0x02E9FA04. Итак, выполнение программы прервалось на бряке, поставленном в начале функции. В памяти по адресу 0x02E9FA04 находятся нули. Теперь начинаем выполнять программу в режиме анимации, без входа в процедуры (<Ctrl-F8>), и одновременно смотрим в окно дампа. После строчки «CALL everest_.005A37E8» в дампе оказалось число 0x96B. Поэтому заходим в эту функцию и ставим в ее начале бряк. При следующем проходе, прервавшись на нем, снова жмем <Ctrl-F8> (забыл сказать <F12> - пауза). Таким образом, мы дошли до адреса 0x595193, где чуть ниже из регистра AX в память записывается количество оборотов. При этом значения [LOCAL.7] и [LOCAL.8] равны 0. Теперь нам нужно найти место, где производится запись в [LOCAL.9]. /*595193*/ MOV EAX,[LOCAL.7] /*595196*/ ADD EAX,[LOCAL.8] /*595199*/ ADD EAX,[LOCAL.9] /*59519C*/ MOV DWORD PTR DS:[EBX+998],EAX Такое место находится чуть выше, по адресу 0x59502C. /*594FEB*/ MOV AL,2A /*594FED*/ CALL everest_.0058801C /*594FF2*/ MOV ESI,EAX . . . /*59500C*/ IMUL ESI,[LOCAL.11] /*595010*/ MOV [LOCAL.81],ESI /*595016*/ FILD [LOCAL.81] /*59501C*/ FLD DWORD PTR DS:[59F5AC] /*595022*/ CALL everest_.004032FC /*595027*/ CALL everest_.00402C5C /*59502C*/ MOV [LOCAL.9],EAX Здесь происходит следующее: с помощью команд сопроцессора значение, находящееся по адресу 0x59F5AC (=1350000), делится на произведение ESI (=0x46) и [LOCAL.11] (=8). Деление выполняется в вызове «CALL everest_.004032FC». В результате анализа кода было выяснено, что значения [LOCAL.11] и [0x59F5AC] постоянны. В ESI же записывается результат вызова подпрограммы, находящейся по адресу 0x0058801C. Внутри этой подпрограммы находятся два CALL’а (0x588047 и 0x588056), внутри которых происходит вызов DeviceIoControl. Выходит, что программа для получения сведений от датчиков использует обращение к драйверу. Про работу с драйверами можешь почитать в литературе. Скажу лишь, что для общения программ прикладного уровня с драйверами используются запросы, посылаемые с помощью DeviceIOControl. BOOL DeviceIoControl ( HANDLE hDevice, // дескриптор устройства DWORD dwIoControlCode, // код операции LPVOID lpInBuffer, // буфер входных данных DWORD nInBufferSize, // размер буфера входных данных LPVOID lpOutBuffer, // буфер данных результата DWORD nOutBufferSize, // размер буфера результата LPDWORD lpBytesReturned, // адрес данных для вывода LPOVERLAPPED lpOverlapped // адрес перекрывающей структуры ); Ну и что теперь делать, если с WMI ничего не вышло? Не писать же свой драйвер, когда сроки поджимают… А что если взять уже готовый. Это, конечно же, плагиат, но диплом-то дороже :)! Наша задача сводится к следующему: 1) узнать, какой драйвер используется; 2) узнать, какие данные передаются драйверу с помощью DeviceIoControl; 3) написать свое собственное приложение. Итак, так как первый параметр функции DeviceIoControl - это дескриптор устройства, который возвращает функция CreateFile, то ставим брейкпоинт на все вызовы CreateFile и перезапускаем программу. Сработал брейкпоинт, в качестве первого параметра FileName функции передается строчка «C:\Program Files\EVEREST Ultimate Edition\everest.key». Нас это мало интересует, поэтому жмем <F9> еще несколько раз, пропуская не интересующие нас вызовы, пока не увидим в стеке строку «\\.\EverestDriver». Запомним значение, возвращаемое функцией, поскольку оно нам еще пригодится (у меня оно равно 0x98). Может возникнуть вопрос: «Откуда же взялся этот драйвер?» Если посмотреть в каталог с программой, то можно увидеть, что у нас остался еще один файл, назначение которого пока еще неизвестно, - kerneld.wnt. Именно он и является драйвером. В этом можно убедиться по вызовам функций IoCreateDevice и IoCreateSymbolicLink, находящихся в нем и создающих виртуальное устройство и символическую ссылку на него. Первый пункт нашего плана выполнен, переходим ко второму. Установим брейкпоинты на адреса 0x588047 и 0x588056, по которым производится вызов DeviceIoControl. Прервавшись, внимательно переписываем значения передаваемых функциям параметров. В первом случае параметры следующие: hDevice = 0x98 dwIoControlCode = 0x8010205C lpInBuffer = 0x042BE6E0 //{0x95,0x02,0,0,0x2A,0,0,0,0,0,0,0} nInBufferSize = 0xC lpOutBuffer = 0x042BE6E0 nOutBufferSize = 0xC lpBytesReturned = 0x042BE6DC lpOverlapped = NULL hDevice равно значению, которое было получено от функции CreateFile при открытии «\\.\EverestDriver». Следовательно, мы не ошиблись. Параметры функции при втором вызове следующие: hDevice = 0x98 dwIoControlCode = 0x80102058 lpInBuffer = 0x042BE6DC //{0x96,0x02,0,0,0,0,0,0,0,0,0,0} nInBufferSize = 0xC lpOutBuffer = 0x042BE6DC nOutBufferSize = 0xC lpBytesReturned = 0x042BE6D8 lpOverlapped = NULL После второго вызова содержимое выходного буфера следующее: {0x96,0x02,0,0,0x46,0,0,0,0,0,0,0}. Пятый по счету параметр - это будущее значение ESI при вычислении количества оборотов. Теперь можно переходить и к третьему пункту плана. Реализация В качестве среды программирования я выбрал VisualC++ 6.0, и уже через несколько минут у меня было рабочее приложение. После этого я аналогично реализовал получение температуры процессора и еще кое-какие данные, а домашним заданием товарища стала реализация загрузки драйвера с помощью SCM-функций и обеспечение передачи данных от клиентов к серверу. HANDLE hFile=0; DWORD returnByte=0; long double speed; char Buf1[12]={0x95,0x02,0,0,0x2A,0,0,0,0,0,0,0}; char Buf2[12]={0x96,0x02,0,0,0,0,0,0,0,0,0,0}; SERVICE_STATUS servStat; hFile=CreateFile("\\\\.\\EverestDriver",GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); if((int)hFile==-1) { ::MessageBox(0,"Драйвер не загружен","Ошибка",MB_OK); return; } DeviceIoControl(hFile,0x8010205C,Buf1,12,Buf1,12,&returnByte,NULL); DeviceIoControl(hFile,0x80102058,Buf2,12,Buf2,12,&returnByte,NULL); speed=1350000/(Buf2[4]*8); Happy end В итоге на следующий день товарищ защитился на отлично и стал дипломированным специалистом, а я получил в качестве вознаграждения энное количество пива :). |