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

 

 

Чтение и запись в память

После установки точки останова следующей задачей является исследование па­мяти. Если вы хотите воспользоваться одним из средств отладки, рассмотренных в этой книге, необходимо исследовать память и попытаться обнаружить введенные пользователем данные. Операции чтения из памяти и записи в память легко реали­зуются в среде Windows с помощью простого API. Также операции чтения и записи в память можно осуществлять с помощью программ, подобных memcpy.
Если вы хотите запросить область памяти для определения ее доступности или свойств (чтение, запись, неперемещаемая область памяти и т.д.), лучше воспользо­ваться функцией VirtualQueryEx.
I и и / и i i i и i i / т и i и 11 и и 11 т ш н 111 ш п i ш i т
II Проверим, что мы можем читать искомый адрес памяти.
П1111 i 1111П11111 i IП11111 i i 11111111111111111 I t! tJ M I II
bool can_read( CDThread *theThread, void *p )
i
bool ret = FALSE;
MEMORY BASIC INFORMATION mbi;
int sz ■
VirtualQueryEx( theThread->m_hProcess,
(void *)p, Smbi,
sizeof(MEMORY BASIC INFORMATION)) ;
if(        (mbi.State == MEM_COMMIT) £6
(mbi.Protect  != PAGEREADONLY) SS
(mbi.Protect !- PAGE_EXECUTE_READ) Si
(mbi.Protect  != PAGEGUARD) SS
(mbi.Protect != PAGENOACCESS) )
ret
TRUE;
)
return ret;
В этом примере функция определяет, доступна ли для чтения область памяти. При необходимости выполнить операции чтения или записи в память, рекомендует­ся использовать вызовы API ReadProcessMemory и WriteProcessMemory.
Отладка многопотоковых программ
ЕСЛИ В программе есть несколько потоков, вполне реально контролировать "поведение"каждого отдельного потока (что очень полезно при проведении атак на

современное программное обеспечение). Для этого существуют определенные API-вызовы. У каждого потока есть собственный набор регистров процессора, называе­мый контекстом потока. Контекст отражает состояние регистров процессора на мо­мент последнего исполнения потока и записывается в структуру CONTEXT. Кон­текст — это структура данных, которая контролирует важные данные процесса, на­пример, текущий указатель команд. Изменяя и запрашивая структуры контекста, можно отслеживать и управлять всеми потоками многопотоковой программы. Рас­смотрим пример установки указателя команд для данного потока.
bool SetEIP(DW0RD theEIP)
{
CONTEXT ctx; HANDLE hThread = fOpenThread(
THREAD_ALL_ACCESS,
FALSE,
m_thread_id
) ;
if(hThread — NULL)
{
_error_out(""[!] OpenThread failed ! Пп");
return FALSE;
}
ctx-OonttesxttFLlaggs = CONTEXT_FULL;
if(!::GetThreadContext(hThread,   &ctx))
{
_ersor_outt   [ ]] ] GGeSThreadContext failedd !! \Vrt");
return FALSE;
}
ctx.Eip = theEIP;
ctx..<CoilteexttFlaags = CONTEXTMFULL ;
if(!::SetThreadContext(hThread,   &ctx))
{
_er^^joUt((^^^!!] SetThrieadCortttestt failed !!\Пп") ;
return FALSE;
}
CloseHandle(hThread); return TRUE;
}
В этом щримере показано, как жюжно (считывать и устанавливать значения эле­ментов для структуры CONTEXT потока. Структура CONTEXT потока полностью до­кументирована в заголовочных файлах Microsoft. Обратите внимание, что флаг кон­текста CONlEXT_FULL устанавливается в ходе операций get или set. Это позволя­ет управлять всеми значениями элементов в (структуре CONTEXT.
Не забывайте закрывать обработчик потока при завершении операции.. В против­ном случае произойдет утечка ресурсов. В нашем примере мы воспользовались функ­цией API OpenThread. Если программу нельзя связать с сфункцией (OperiThread, придется импортировать выззовв функции вручную . В нашем примере это было сделано с гпомощью указателя (функции ffO^enTThread. Джя инициализации fOpenThreead необходимо импортировать) указатель функции непосредственно из ке гпе 13 2 . dll 1, жж ппожжзано ниже.

typedef void *
[_stdcall *FOPENTHREAD)
(
DWORD dwDesiredAccess,   // Право доступа
BOOL blnheritHandle,   // Обработать параметр наследования DWORD dwThreadld // Идентификатор потока ) ;
FOPENTHREAD fOpenThread=NULL;
fOpenThread = (FOPENTHREAD) GetProcAddress(
GetModuleHandle("kernel32.dll"), "OpenThread"  ); if ( !fOpenThread) (
_error_out("[!]  ошибка при доступе к функции openthread!\n");
)
Это особенно полезный фрагмент кода, поскольку в нем проиллюстрировано, как определять функцию и как ее импортировать из библиотеки DLL вручную.
Инвентаризация потоков или процессов
С помощью специального API, который входит в состав операционной системы Windows, можно организовать запросы ко всем запущенным процессам и потокам. Этот программный код можно использовать для запроса всех запущенных потоков в процессе, проверяемом с помощью отладчика.
// Для исследуемого процесса, создадим // структуру для каждого потока.
HANDLE hProcessSnap = NULL;
hProcessSnap = CreateToolhelp32Snapshot(
TH32CS_SNAPTHREAD,
mPID) ;
if   (hProcessSnap == INVALID_HAN DLE_VALUE) (
_error_out("toolhelp snap failed\n"); return;
)
else (
THREADENTRY32 the;
the.dwSize = sizeof(THREADENTRY32) ;
BOOL bret = Thread32First( hProcessSnap,   Sthe);
while (bret)
(
// Создать структуру потока.
if(the.th320wnerProcessID == mPID)
I
CDThread *aThread = new CDThread; aThread->m_thread_id = the.th32ThreadID; aThread->m_hProcess = m_hProcess;
mThreadList.push_back( aThread );
)
bret = Thread32Next(hProcessSnap, Sthe);
}
)

В этом примере для каждого потока был создан и инициализирован объект CDThread. Мы получили структуру потока THREADENTRY32, в которой сохранены многие интересные для отладчика значения. Мы рекомендуем прочитать специаль­ное руководство Microsoft по этому API. Обратите внимание, что в коде выполняют­ся проверки значения владельца идентификатора процесса (PID) для каждого пото­ка с целью гарантировать, что данный поток принадлежит исследуемому процессу.
Пошаговый режим
Отслеживание выполнения программы имеет огромное значение в плане опреде­ления того, способен ли хакер управлять логикой программы. Например, если 13-й байт пакета передается оператору switch, то злоумышленник вполне контролирует этот оператор, поскольку он контролирует значение 13-го байта пакета.
Пошаговый режим является свойством чипсета х8б. При установке специального флага TRAP FLAG (флаг трассировки или флаг пошагового режима) процессор вы­полняет только по одной команде, за каждой из которых следует прерывание. Ис­пользуя пошаговые прерывания, отладчик может проверить каждую выполняемую команду. Кроме того, с помощью описанных выше программ можно исследовать со­стояние памяти на каждом шаге. Например, для этой цели можно воспользоваться программой The PIT (http : / /www. hbgary. com). Эти методы достаточно просты, но их умелая комбинация обеспечит создание очень мощного отладчика.
Для перевода процессора в пошаговый режим нужно установить флаг трассиров­ки, как показано в следующем листинге.
bool SetSingleStep () {
CONTEXT ctx;
HANDLE hThread =
fOpenThread(
THREAD_ALL_ACCESS,
FALSE,
m_thread_id
) ;
if (hThread « NULL)
{
error out ((*<[!]] ошибка три открытии потока ВРХ! \пи) ;
return FALSE,-
}
// Вернуть на одну инструкцию. Больше нельзя сделать копий состояния объек­та .
ctx.ComtextFlagE - CONTTEXT_FULL;
if(!::GetThreadContext(hThread, &ctx))
{
error out("[!] ошибка BGetThreadC©nite<xtt!! \Ппп) ;
return FALSE;
}
// Установить пошаговый режим для этого потока.
ctx.EFlags И TF_iIT ; ctx.ConteextFlagss = CONTEXT_FULL;
if(!::SetThreadContext(hThread, &ctx))
{
_error_o»t('"[[! ]] ошибка в SeftCThreaaiConttextt !! \Пл") ;
return FALSE;
}

CloseHandle(hThread) ; return TRUE;
}
Обратите внимание, что мы устанавливаем флаг трассировки с помощью структуры CONTEXT для потока. Идентификатор потока хранится в переменной m_thread_id Для пошаговой отладки многопоточной программы все потоки этой программы должны быть переведены в пошаговый режим.

 

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