API Hashing можно считать одной из самых интересных и захватывающих техник обхода антивирусов, ведь жонглирование макросами требует определенных навыков и понимания работы компилятора и самого C++ в целом. В этой статье покажу реализацию техники API Hashing, с помощью которой можно спрятать импорт от антивирусного ока.
Еще по теме: Обход антивируса с помощью Haskell
Пример обхода антивирусов с помощью API Hashing
Первый раз я столкнулся с техникой API Hashing в знаменитом шифровальщике Revil, но там хэши функций были встроены в код, что облегчало создание сигнатур. Давайте покажу, как сделать так, чтобы все генерировалось на лету, тогда у антивирусов не будет шансов.
Статья в образовательных целях, для обучения этичных хакеров, специализирующихся на пентестах. Несанкционированное заражение компьютеров и систем является незаконным и рассматривается как уголовное преступление. Ни редакция spy-soft.net, ни автор не несут ответственности за ваши действия.
Собираем все в один проект, закидываем в «Студию» и запускаем.
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
#include #include #include #include #define SEED 5 typedef UINT(CALLBACK* fnMessageBoxA)( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType ); constexpr int RandomCompileTimeSeed(void) { return '0' * -40271 + __TIME__[7] * 1 + __TIME__[6] * 10 + __TIME__[4] * 60 + __TIME__[3] * 600 + __TIME__[1] * 3600 + __TIME__[0] * 36000; }; constexpr auto g_KEY = RandomCompileTimeSeed() % 0xFF; constexpr DWORD HashStringDjb2W(const wchar_t* String) { ULONG Hash = (ULONG)g_KEY; INT c = 0; while ((c = *String++)) { Hash = ((Hash << SEED) + Hash) + c; } return Hash; } constexpr DWORD HashStringDjb2A(const char* String) { ULONG Hash = (ULONG)g_KEY; INT c = 0; while ((c = *String++)) { Hash = ((Hash << SEED) + Hash) + c; } return Hash; } #define RTIME_HASHA( API ) HashStringDjb2A((const char*) API) #define RTIME_HASHW( API ) HashStringDjb2W((const wchar_t*) API) #define CTIME_HASHA( API ) constexpr auto API##_Rotr32A = HashStringDjb2A((const char*) #API); #define CTIME_HASHW( API ) constexpr auto API##_Rotr32W = HashStringDjb2W((const wchar_t*) L#API); #define CTIME_DLLHASHA(API) constexpr auto API##_FuncA = HashStringDjb2A((const char*) #API); HMODULE GetModuleHandleH(DWORD dwModuleNameHash) { if (dwModuleNameHash == NULL) return NULL; #ifdef _WIN64 PPEB pPeb = (PEB*)(__readgsqword(0x60)); #elif _WIN32 PPEB pPeb = (PEB*)(__readfsdword(0x30)); #endif PPEB_LDR_DATA pLdr = (PPEB_LDR_DATA)(pPeb->Ldr); PLDR_DATA_TABLE_ENTRY pDte = (PLDR_DATA_TABLE_ENTRY)(pLdr->InMemoryOrderModuleList.Flink); while (pDte) { if (pDte->FullDllName.Length != NULL && pDte->FullDllName.Length < MAX_PATH) { CHAR UpperCaseDllName[MAX_PATH]; DWORD i = 0; while (pDte->FullDllName.Buffer[i]) { UpperCaseDllName[i] = (CHAR)toupper(pDte->FullDllName.Buffer[i]); i++; } UpperCaseDllName[i - 4] = '\0'; if (RTIME_HASHA(UpperCaseDllName) == dwModuleNameHash) return (HMODULE)pDte->Reserved2[0]; } else { break; } pDte = *(PLDR_DATA_TABLE_ENTRY*)(pDte); } return NULL; } FARPROC GetProcAddressH(HMODULE hModule, DWORD dwApiNameHash) { PBYTE pBase = (PBYTE)hModule; PIMAGE_DOS_HEADER pImgDosHdr = (PIMAGE_DOS_HEADER)pBase; if (pImgDosHdr->e_magic != IMAGE_DOS_SIGNATURE) return NULL; PIMAGE_NT_HEADERS pImgNtHdrs = (PIMAGE_NT_HEADERS)(pBase + pImgDosHdr->e_lfanew); if (pImgNtHdrs->Signature != IMAGE_NT_SIGNATURE) return NULL; IMAGE_OPTIONAL_HEADER ImgOptHdr = pImgNtHdrs->OptionalHeader; PIMAGE_EXPORT_DIRECTORY pImgExportDir = (PIMAGE_EXPORT_DIRECTORY)(pBase + ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); PDWORD FunctionNameArray = (PDWORD)(pBase + pImgExportDir->AddressOfNames); PDWORD FunctionAddressArray = (PDWORD)(pBase + pImgExportDir->AddressOfFunctions); PWORD FunctionOrdinalArray = (PWORD)(pBase + pImgExportDir->AddressOfNameOrdinals); for (DWORD i = 0; i < pImgExportDir->NumberOfFunctions; i++) { CHAR* pFunctionName = (CHAR*)(pBase + FunctionNameArray[i]); PVOID pFunctionAddress = (PVOID)(pBase + FunctionAddressArray[FunctionOrdinalArray[i]]); if (dwApiNameHash == RTIME_HASHA(pFunctionName)) { return (FARPROC)pFunctionAddress; } } return NULL; } int main() { CTIME_DLLHASHA(USER32); // Фактически на этом месте появляется constexpr auto USER32_FuncA = HashStringDjb2A(USER32); CTIME_HASHA(MessageBoxA); // Фактически на этом месте появляется constexpr auto MessageBoxA_Rotr32A = HashStringDjb2A(MessageBoxA); if (LoadLibraryA("USER32.DLL") == NULL) { printf("[!] LoadLibraryA Failed With Error : %d \n", GetLastError()); return 0; } HMODULE hUser32Module = GetModuleHandleH(USER32_FuncA); if (hUser32Module == NULL) { printf("[!] Cound'nt Get Handle To User32.dll \n"); return -1; } fnMessageBoxA pMessageBoxA = (fnMessageBoxA)GetProcAddressH(hUser32Module, MessageBoxA_Rotr32A); if (pMessageBoxA == NULL) { printf("[!] Cound'nt Find Address Of Specified Function \n"); return -1; } pMessageBoxA(NULL, "HI", "HI", MB_OK | MB_ICONEXCLAMATION); return 0; } |
Работающий вариант обхода антивируса с использованием API Hashing:
Вызов функции отлично отрабатывает. Хеши генерируются на лету, и нам не требуется ничего хардкодить! Тем не менее руками каждый раз заводить все эти функции может быть не очень удобно, поэтому рекомендую изучить репозиторий на GitHub, где есть поддержка многих методов хеширования.
ПОЛЕЗНЫЕ ССЫЛКИ:
- Как скрыть вирус в архиве ZIP и RAR
- Как скрыть процессы от антивирусов
- Как обойти антивирус с помощью Chimera