Форум программистов, компьютерный форум, киберфорум
C++: WinAPI
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.83/18: Рейтинг темы: голосов - 18, средняя оценка - 4.83
289 / 34 / 6
Регистрация: 20.09.2011
Сообщений: 464
1

Перехват API функций. Таблица импорта

05.12.2014, 21:01. Показов 3283. Ответов 7
Метки нет (Все метки)

Необходимо лoггиpoвaть вызовы некоторых API-шных функций (для примера CloseHandle) в заданном процессе.
После прочтения материала из книги Рихтера и поиска информации на форуме реализовать данную задачу не удалось.

Понял, что нужно использовать следующую функцию для переопределения библиотечной функции пользовательской:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
PROC pfnOrig = GetProcAddress(GetModuleHandle("Kernel32"), "ExitProcess");
HMODULE hmodCaller = GetModuleHandle("DataBase.exe");
 
void ReplaceIATEntryInOneMod(
"Kernel32.dll", // модуль, содержащий ANSI-функцию
pfnOrig, // адрес исходной функции в вызываемой DLL
MyExitProcess, // адрес заменяющей функции
hmodCaller); // описатель модуля, из которого надо вызывать новую функцию
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) {
ULONG ulSize;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
ImageDirectoryEntryToData(hmodCaller, TRUE,
IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);
if (pImportDesc == NULL)
return; // в этом модуле нет раздела импорта
// находим дескриптор раздела импорта со ссылками
// на функции DLL (вызываемого модуля)
for (; pImportDesc->Name; pImportDesc++) {
PSTR pszModName = (PSTR)
((PBYTE) hmodCaller + pImportDesc->Name);
if (lstrcmpiA(pszModName, pszCalleeModName) == 0)
break;
}
if (pImportDesc->Name == 0)
// этот модуль не импортирует никаких функций из данной DLL
return;
// получаем таблицу адресов импорта (IAT) для функций DLL
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
((PBYTE) hmodCaller + pImportDesc->FirstThunk);
// заменяем адреса исходных функций адресами своих функций
for (; pThunk->u1.Function; pThunk++) {
// получаем адрес адреса функции
PROC* ppfn = (PROC*) &pThunk->u1.Function;
// та ли это функция, которая нас интересует?
BOOL fFound = (*ppfn == pfnCurrent);
// см. текст программы-примера, в котором
// содержится трюковый код для Windows 98
if (fFound) {
// адреса сходятся; изменяем адрес в разделе импорта
WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof(pfnNew), NULL);
return; // получилось; выходим
}
}
// если мы попали сюда, значит, в разделе импорта
// нет ссылки на нужную функцию
}


Но мне не понятно, каким образом использовать данную функцию для конкретного процесса (будем считать, что мы знаем имя процесса, путь, его пид и другие характеристики). Также не совсем понятно, как должна быть реализована переопределенная функция (допустим тот же MyCloseHandle), чтобы можно было вывести в файл | консоль | MessageBox сообщение, что процесс ее вызвал.

Буду благодарен любой помощи по данному вопросу.
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
05.12.2014, 21:01
Ответы с готовыми решениями:

Перехват API вызовов через таблицу импорта
Доброго времени суток. Есть следующая задача. Нужно перехватить вызов некоторых определённых...

Перехват API функций
Разработать программу, которая запускает приложение, переданное программе на вход. После запуска...

Статья на rsdn, перехват API-функций
Разбираю статью на рсдн, а именно Метод 1. Перехват API непосредственной записью в код системной...

Перехват API
Сразу код ) DWORD dwProtect = PAGE_READWRITE; BYTE old; BYTE * fPtr; #pragma...

7
286 / 192 / 56
Регистрация: 25.12.2012
Сообщений: 640
06.12.2014, 22:54 2
Цитата Сообщение от galaid Посмотреть сообщение
Но мне не понятно, каким образом использовать данную функцию для конкретного процесса
Этот код будет работать только в том процессе который хотите логировать . Как вариант пишите dll с этим кодом и делайте инжект в чужой процесс (WriteProcessMemory, CreateRemoteThread у Рихтера об этом написано), или цепляйте вашу dll к exe сразу.
Цитата Сообщение от galaid Посмотреть сообщение
Также не совсем понятно, как должна быть реализована переопределенная функция (допустим тот же MyCloseHandle)
C++
1
2
3
4
5
6
7
8
9
10
11
12
BOOL WINAPI MyCloseHandle( __in HANDLE hObject)
{
    printf("HANDLE %8x \n", hObject);
    return CloseHandle(hObject);
}
int _tmain(int argc, _TCHAR* argv[])
{       
    PROC pfnOrig = GetProcAddress(GetModuleHandle("Kernel32"), "ExitProcess");
    HMODULE hmodCaller = GetModuleHandle("DataBase.exe");    
    ReplaceIATEntryInOneMod("Kernel32.dll",pfnOrig,(PROC)MyCloseHandle,hmodCaller);     
    return 0;
}
Добавлено через 4 минуты
А чем не устраивает debugger ? Например OllyDbg , в нем запросто можно устроить логирования WinApi. Или например отличная программка "Api Monitor" (правда на счет лога не знаю).

Добавлено через 2 часа 43 минуты
Вот примерчик перехвата WinApi Sleep при помощи вашего кода в своём приложении.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include "stdafx.h"
#include <Windows.h>
#include <Dbghelp.h>
#pragma comment(lib,"Dbghelp.lib")
 
void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller);
const HMODULE GetCurrentModule();
typedef int (__stdcall *proc)(DWORD dwMilliseconds);
BOOL WINAPI MySleep(DWORD dwMilliseconds);
proc OriginSleep;
 
int _tmain(int argc, _TCHAR* argv[])
{           
    OriginSleep = (proc)GetProcAddress(GetModuleHandle("Kernel32"), "Sleep");
    ReplaceIATEntryInOneMod("kernel32.dll", (PROC)OriginSleep, (PROC)MySleep, GetCurrentModule() );
    Sleep(123);
    return 0;
}
BOOL WINAPI MySleep(DWORD dwMilliseconds)
{
    printf("Sleep( dwMilliseconds - %d ) \n", dwMilliseconds);
    return OriginSleep(dwMilliseconds);
}
 
const HMODULE GetCurrentModule()
{
    DWORD flags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS;
    HMODULE hm = 0;
    ::GetModuleHandleEx( flags, reinterpret_cast<LPCTSTR>( GetCurrentModule ), &hm );   
    return hm;
}
void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller)
{
 
    ULONG ulSize;
 
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);
 
    if (pImportDesc == NULL) return; 
 
    for (; pImportDesc->Name; pImportDesc++)
    {   
        PSTR pszModName = (PSTR)((PBYTE) hmodCaller + pImportDesc->Name);
 
        if (lstrcmpiA(pszModName, pszCalleeModName) == 0) break;
    }   
 
    PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((PBYTE) hmodCaller + pImportDesc->FirstThunk);
 
    for (; pThunk->u1.Function; pThunk++)
    {
        if( (PROC)pThunk->u1.Function == pfnCurrent) 
        {
            WriteProcessMemory(GetCurrentProcess(), &pThunk->u1.Function, &pfnNew, sizeof(pfnNew), NULL);
 
            return; 
        }
    }
}
1
289 / 34 / 6
Регистрация: 20.09.2011
Сообщений: 464
07.12.2014, 10:09  [ТС] 3
maxillion, большое спасибо за пример. Что-то подобное получилось и у меня. Однако в обоих вариантах функция WriteProcessMemory возвращает FALSE. При этом GetLastError() выдает 998 код ошибки. Поэтому не получается переопределить функцию.
0
Ушел с форума
Эксперт С++
16429 / 7393 / 1186
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
07.12.2014, 10:36 4
Перез WriteProcessMemory следует сделать VirtualProtect, добавив
нужные права на запись в память.

Добавлено через 2 минуты
А вообще, патчинг таблицы импорта - та еще возня, достаточно вспомнить, к
примеру, delayed import. Возьмите любое .NET-овское приложение: с большой
вероятностью у него в импорте будет только mscoree.dll и весь перехват накроется.
0
289 / 34 / 6
Регистрация: 20.09.2011
Сообщений: 464
07.12.2014, 12:50  [ТС] 5
Убежденный, если я правильно понял, то в примере, который выложил maxillion перед
C++
1
WriteProcessMemory(GetCurrentProcess(), &pThunk->u1.Function, &pfnNew, sizeof(pfnNew), NULL);
нужно дописать
C++
1
VirtualProtectEx(GetCurrentProcess(), (void*)&pThunk->u1.Function, sizeof(pfnNew), PAGE_EXECUTE_READWRITE, 0);
В таком случае VirtualProtectEx() также возвращает FALSE.
0
Ушел с форума
Эксперт С++
16429 / 7393 / 1186
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
07.12.2014, 12:52 6
Цитата Сообщение от galaid Посмотреть сообщение
VirtualProtectEx() также возвращает FALSE.
Сверьте внимательно передаваемые аргументы с описанием функции.
0
286 / 192 / 56
Регистрация: 25.12.2012
Сообщений: 640
07.12.2014, 19:08 7
Лучший ответ Сообщение было отмечено galaid как решение

Решение

Вообще таблица импорта находится в секции ExecutReadOnly поэтому что бы переписать её применили WriteProcessMemory, хотя этот вариант мне изначально показался странным (ведь обычно она применяется для записи в чужой процесс, а нам нужно в свой), но он выглядеть проще чем два раза дергать VirtualProtect поэтому переделывать не стал. Сначала думал это дыра в OS (ведь XP то ещё решето), но посмотрев что GetCurrentProcess возвращает дескриптор с правами PROCESS_ALL_ACCESS подумал что так и задумано. Сегодня проверил на Windows 8, код не сработал (а это ещё одно доказательство что XP - решето). Вот переделанный и проверенный вариант (на x32 точно робит).
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller)
{
 
    ULONG ulSize;
 
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hmodCaller, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);
 
    if (pImportDesc == NULL) return;
 
    for (; pImportDesc->Name; pImportDesc++)
    {
        PSTR pszModName = (PSTR)((PBYTE)hmodCaller + pImportDesc->Name);
 
        if (lstrcmpiA(pszModName, pszCalleeModName) == 0) break;
    }
 
    PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((PBYTE)hmodCaller + pImportDesc->FirstThunk);
 
    for (; pThunk->u1.Function; pThunk++)
    {
        if ((PROC)pThunk->u1.Function == pfnCurrent)
        {
            DWORD old = 0;          
            VirtualProtect(&pThunk->u1.Function, 4, PAGE_READWRITE, &old);
            pThunk->u1.Function = (DWORD)pfnNew;
            VirtualProtect(&pThunk->u1.Function, 4, PAGE_READONLY, &old);
 
            return;
        }
    }
}
Добавлено через 6 минут
Последний вызов VirtualProtect замените на
C++
1
 VirtualProtect(&pThunk->u1.Function, 4, old, &old);
0
289 / 34 / 6
Регистрация: 20.09.2011
Сообщений: 464
07.12.2014, 20:44  [ТС] 8
maxillion, спасибо огромное за проделанную работу. Последний вариант действительно работает (Windows 7 64x).
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
07.12.2014, 20:44

Перехват API функции
Доброго времени суток всем! Уважаемые форумчене помогите решить задачу, я взял функцию MessageBox и...

Перехват API, Рихтер
Всем доброго времени суток! Собственно такой вопросик... я вот читаю сейчас Рихтера и мне интересен...

Перехват API вызовов
Здравствуйте! Пытаюсь перехватить вызов функции CreateFileW и заменить на свою MyCreateFileW. Для...

Перехват API с возвратом значения
Я тут решил разобраться в перехвате API с помощью инжектированной dll. Спустя 2 дня написал...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
8
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.