+++ title = “Maldev Academy notes” description = “Notes on my progress with Maldev Academy” weight = 1
updated = 2024-10-03
created = 2024-10-03 +++
Here I share notes on what I’m learning form Maldev Academy.
Module 3 - Required Tools
x64dbg
Some notes on the x64dbg interface.
The main tab (‘CPU’) has 4 screens:
- Disassembly (top-left): displays assembly instructions being executed
- Dump (bottom-left): displays memory contents
- Registers (top-right): displays values of CPU registers
- Stack (bottom-right): displays stack contents
Module 4 - Coding Basics
Structures/structs are declared using typedef
to give aliases. F.e.:
typedef struct _STRUCTURE_NAME {
// structure elements
} STRUCTURE_NAME, *PSTRUCTURE_NAME;
STRUCTURE_NAME
alias: refers to structure namePSTRUCTURE_NAME
alias: represents pointer to structure (P is a Microsoft convention)
Passing By Reference
Passing by reference is a method of passing arguments to a function where the argument is a pointer to the object, rather than a copy of the object’s value.
Module 5 - Windows Architecture
- User Processes - A program/application executed by the user such as Notepad, Google Chrome or Microsoft Word.
- Subsystem DLLs - DLLs that contain API functions that are called by user processes. An example of this would be
kernel32.dll
exporting the CreateFile Windows API (WinAPI) function, other common subsystem DLLs arentldll.dll
,advapi32.dll
anduser32.dll
. - Ntdll.dll - A system-wide DLL which is the lowest layer available in user mode. This is a special DLL that creates the transition from user mode to kernel mode. This is often referred to as the Native API or NTAPI.
- Executive Kernel - This is what is known as the Windows Kernel and it calls other drivers and modules available within kernel mode to complete tasks. The Windows kernel is partially stored in a file called
ntoskrnl.exe
under “C:\Windows\System32”.
Function Call Flow
The image below shows an example of an application that creates a file. It begins with the user application calling the CreateFile
WinAPI function which is available in kernel32.dll
. Kernel32.dll
is a critical DLL that exposes applications to the WinAPI and is therefore can be seen loaded by most applications. Next, CreateFile
calls its equivalent NTAPI function, NtCreateFile
, which is provided through ntdll.dll
. Ntdll.dll
then executes an assembly sysenter
(x86) or syscall
(x64) instruction, which transfers execution to kernel mode. The kernel NtCreateFile
function is then used which calls kernel drivers and modules to perform the requested task.
Module 7 - Introduction to the Windows API
Windows Data Types
Data type | function |
---|---|
DWORD | represents values up to 2^32-1 (32-bit uint) |
size_t | represents size of an object (32- or 64-bit uint) |
VOID | indicates absence of specific data type |
PVOID | pointer of any data type (32-bit or 64-bit pointer) |
HANDLE | specifies a particular object that the operating system is managing (file, process, thread) |
HMODULE | handle to a module (DLL, EXE). base address of module in memory |
LPCSTR/PCSTR | pointer to a constant null-terminated string of 8-bit Windows characters (ANSI). equivalent to const char* |
PWSTR/LPWSTR | same as LPCSTR/PCSTR but does not point to a constant variable but to a readable and writable string (equivalent to wchar*) |
wchar_t | same as wchar which is used to represent wide characters |
ULONG_PTR | represents uint that is same size as pointer (32-bit or 64-bit). Used with arithmetic expressions containing pointers. |
Data type | example |
---|---|
DWORD | DWORD dwVariable = 42; |
size_t | SIZE_T = sVariable = sizeof(int); |
VOID | void* pVariable = NULL; // This is the same as PVOID |
PVOID | PVOID pVariable = &SomeData; |
HANDLE | HANDLE hFile = CreateFile(…); |
HMODULE | HMODULE hModule = GetModuleHandle(…); |
LPCSTR/PCSTR | LCPSTR lpcString = ‘Hello, world!’ PCSTR pcwString = L’Hello, world!’ |
PWSTR/LPWSTR | LPWSTR lpwString = L’Hello, world!’ PWSTR pwString = L’Hello, world!‘ |
wchar_t | wchar_t wChar = L’A’; |
ULONG_PTR | PVOID Pointer = malloc(100); //Pointer = pointer +10; //not allowed Pointer = (ULONG_PTR)Pointer + 10; // allowed |
Ansi & Unicode Functions
- CreateFileA ← ‘A’ indicates ‘ANSI’
- first parameter for
CreateFileA
is aLPCSTR
- first parameter for
- CreateFileW ← ‘W’ indicates ‘Wide’ and represents Unicode
- first parameter for
CreateFileW
isLPCWSTR
- first parameter for
Module 8
PE Structure
DOS header data structure
typedef struct _IMAGE_DOS_HEADER {
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crcl; // Relocations
WORD e_cparhdr; //Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // Offset to the NT header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
The most important members of the struct are e_magic
and e_lfanew
.
e_magic
is 2 bytes with a fixed value of 0x5A4D
or MZ
.
e_lfanew
is a 4-byte value that holds an offset to the start of the NT Header. Note that e_lfanew
is always located at an offset of 0x3C
.
NT Header (IMAGE_NT_HEADERS)
NT Header contains FileHeader
and OptionalHeader
. NT Header can be reached via the e_lfanew
member inside the DOS Header.
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32/64 OptionalHeader;
} IMAGE_NT_HEADERS32/64, *PIMAGE_NT_HEADERS32/64;
File Header (IMAGE_FILE_HEADER)
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberofSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_OF_FILE_HEADER;
NumberOfSections
= number of sections in the PE file.
Characteristics
= Flags that specify certain attributes about the executable file, such as whether it’s a dynamic-link library (DLL) or a console application.
SizeOfOptionalHeader
= The size of the following optional header.
Optional header (IMAGE_OPTIONAL_HEADER)
32-bit-version:
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
64-bit version
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
Important struct members of _IMAGE_OPTIONAL_HEADER
Magic
- Describes the state of the image file (32- or 64-bit image)
MajorOperatingSystemVersion
- The major version number of the required operating system
MinorOperatingSystemVersion
- The minor version number of the required operating system
SizeOfCode
- The size of the .text
section
AddressOfEntryPoint
- Offset to the entry point of the file (Typically the main
function)
BaseOfCode
- Offset to the start of the .text
section
SizeOfImage
- The size of the image file in bytes
ImageBase
- It specifies the preferred address at which the application is to be loaded into memory when it is executed.
DataDirectory
- Array of IMAGE_DATA_DIRECTORY
which contains the directories in a PE files
Data Directory
Array of data type IMAGE_DATA_DIRECTORY with the following data structure (below). Data Directory array is of size IMAGE_NUMBEROF_DIRECTORY_ENTRIES
, constant value of 16
.
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
Each element of the array represents a data directory containing data about a PE section or Data Table. SPecific data directories can be indexed:
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
Export Directory
Contains addresses of exported functions and variables which can be used by other executable files to access the functions and data. Generally found in DLLs that export functions (like kernel32.dll
exporting CreateFileA
).
Import Address Table
Contains information about the addresses of functions imported from other executable files. Addresses are used to access the functions and data in other executables (like Application.exe
importing CreateFileA
from kernel32.dll
.)
PE Sections
contains code and data used to create an executable program. PE secions have a unique name and contains (1) executable code, (2) data, or (3) resource information. Sections can be added later manually, the IMAGE_FILE_HEADER.NumberOfSections
determine that number.
Important PE sections:
.text
- executable code which is the written code
.data
- initialized data which are variables initialized in the code
.rdata
- read-only data which are constant variables prefixed with const
.idata
- contains import tables which relate to functions called using the code
.reloc
- information how to fix up memory addresses so the program can be loaded
.rsrc
- store resources like icons and bitmaps
Each PE section has an IMAGE_SECTION_HEADER
data structure, saved under the NT headers and are stacked above each other, representing a section. IMAGE_SECTION_HEADER
contains:
typedef STRUCT _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // name of the section (.text, .data, .rdata)
union {
DWORD PhysicalAddress; // size of the section in memory
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress; // offset of start of section in memory
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
Module 9
Unlike EXE files, DLL files cannot execute code on their own: DLL libraries need to be invoked by other programs to execute the code.
-
There are 4 possibilities for a DLL entry point to be called:
DLL_PROCESS_ATTACH
- A process is loading the DLL.DLL_THREAD_ATTACH
- A process is creating a new thread.DLL_THREAD_DETACH
- A thread exits normally.DLL_PROCESS_DETACH
- A process unloads the DLL.
-
Try to load a DLL dynamically (link to tutorial) maldevacademy socc studie
Module 11
Virtual memory allows the operating system to use more memory than what is physically available by creating a virtual address space that can be accessed by the applications. These virtual address spaces are divided into ‘pages’ which are then allocated to processes.
Type of memory | Definition |
---|---|
Private memory | dedicated to a single process, stores specific data |
Mapped memory | can be shared, stores shared libraries, memory segments, shared files |
Image memory | contains code and data of executable, often related to DLL files loaded into process address space |
Process Environment Block (PEB)
PEB is a Windows data structure containing process information like:
- parameters
- startup information
- allocated head information
- loaded DLLs
- process ID (PID) and path to executable
PEB struct in C reserved members (can be ignored)):
typedef struct _PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID Reserved4[3];
PVOID AtlThunkSListPtr;
PVOID Reserved5;
ULONG Reserved6;
PVOID Reserved7;
ULONG Reserved8;
ULONG AtlThunkSListPtr32;
PVOID Reserved9[45];
BYTE Reserved10[96];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoute;
BYTE Reserved11[128];
PVOID Reserved12[1];
ULONG SessionId;
} PEB, *PPEB;
PEB struct reserved members:
-
BeingDebugged
- indicates whether the process is being debugged, used by the Windows loader
-
Ldr
- pointer to
PEB_LDR_DATA
struct containing information about process’s loaded DLLs - can be leveraged to find base address of particular DLL and functions
- used for a custom version of
GetModuleHandleA/W
for added stealth
- pointer to
typedef struct _PEB_LDR_DATA {
BYTE Reserved1[8];
PVOID Reserved2[3];
LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
ProcessParameters
- data structure containing command line parameters passed to process
- used for command line spoofing
typedef struct _RTL_USER_PROCESS_PARAMETERS {
BYTE Reserved1[16];
PVOID Reserved2[10];
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
-
AtlTHunkSListPtr
andAtlThunkSListPtr32
- used by the ATL (Active Template Library) to store a pointer to linked list of thunking functions
- thunking functions used to call functions implemented in different address space
- often represent functions exported from a DLL
- used by the ATL (Active Template Library) to store a pointer to linked list of thunking functions
-
PostProcessInitRoutine
- stores pointer to function called by OS after TLS (Thread Local Storage) initialization
-
SessionId
- unique identifier assigned to single session
Thread Environment Block (TEB)
typedef struct _TEB {
PVOID Reserved1[12];
PPEB ProcessEnvironmentBlock;
PVOID Reserved2[399];
BYTE Reserved3[1952];
PVOID TlsSlots[64];
BYTE Reserved4[8];
PVOID Reserved5[26];
PVOID ReservedForOle;
PVOID Reserved6[4];
PVOID TlsExpansionSlots;
} TEB, *PTEB;
ProcessEnvironmentBlock (PEB) is a pointer to the PEB structure, located inside the Thread Environment Block (TEB) and used to store information about the currently running process.
TlsSlots (Thread Local Storage) Slots are locations in the TEB used to store thread-specific data (thread-specific variables, handles, states).
TlsExpansionSlots are a set of pointers to store thead-local storage data for a thread, reserved for use by system DLLs.
Module 12 - Undocumented Structures
reserved members within stuctures are presented as arrays of BYTES
or PVOID
data types and prevent users from understanding the structure to avoid modifications to these reserved members.
One way to determine PEB’s reserved members is through the !peb command in WinDbg.
Choosing a structure definition
- Some structure definitions only work for a specific architecture (x86 or x64 f.e.)
- It can be necessary to define multiple structures due to nested structures (PEB contains a member that acts as a pointer to another structure)
- Select only one definition of a structure to avoid redefinition errors thrown by a compiler (f.e. Visual Studio’s)
Module 13 - Payload placement - .data & .rdata sections
The .data
and .rdata
sections can be merged / merged into the .text
section.
.data section
benefit of string payload in #.data section: readable and writable, so you can have an encrypted payload that is decrypted at runtime
code snippet example of payload stored in .data section:
#include <Windows.h>
#include <stdio.h>
// msfvenomn calc shellcode generated with:
// msfvenomn -p windows/x64/exec CMD=calc.exe -f c
// .data saved payload
unsigned char Data_RawData[] = {
// hex characters
};
int main() {
printf("[i] Data_RawData var : 0x%p \n", Data_RawData);
printf("[#] Press <Enter> To Quit ...");
getchar();
return 0;
}
.rdata section
The ‘r’ in .rdata
indicates read-only, which can be done using the const
(constant) qualifier for variables.
An example of this using the previous code:
#include <Windows.h>
#include <stdio.h>
// msfvenom calc shellcode
// msfvenom -p windows/x64/exec CMD=calc.exe -f c
// .rdata saved payload
const unsigned char Rdata_RawData[] = {
// hex characters
};
int main() {
printf("[i] Rdata_RawData var: 0x%p\n", Rdata_RawData);
printf("[#] Press <Enter> To Quit...");
getchar();
return 0;
}
.text section
The #.text section stores variables within executable memory permissions so they can be executed directly without editing the memory regions permissions. Mostly useful for small payloads, less than 10 bytes. Saving a variable in the .text section requires you to instruct the compiler to do so. An example:
#include <Windows.h.>
#include <stdio.h>
// mfsvenom calc shellcode
// msfvenom -p windows/x64/exec CMD=calc.exe -f c
// .text saved payload
#pragma section(".text")
__declspec(allocate(".text")) const unsigned char Text_RawData[] = {
// hex characters
};
int main() {
printf("[i] Text_RawData var: 0x%p\n", Text_RawData);
printf("[#] Press <Enter> To Quit...");
getchar();
return 0;
}
.rsrc section
Larger payloads cannot be stored in the .data
or .rdata
sections due to size limits. The #.rsrc section is cleaner (no errors during compilation).
One way to store a payload in the .rsrc
section with Visual Studio:
- right-click ‘Resource files’ ⇒ ‘Add’ ⇒ ‘New Item…’
- ‘Resource File’
- Right-click on the ‘Resource.rc’ default file ⇒ select ‘Add Resource’
- ‘Import’
- Select
calc.ico
which is raw payload - In prompt, enter ‘RCDATA’ under ‘Resource Type:’
resource.h
should be visible and named according to the previous.rc
file
Payloads stored in .rsrc
section cannot be accessed directly but through these WINAPIs:
FindResourceW
- get location of specified data stored in resource section of a special ID passed in (this is defined in the header file)LoadResource
- retrieves aHGLOBAL
handle of resource data, which can be used to obtain the base address of the specified resource in memoryLockResource
- obtains pointer to specified data in resource section from its handleSizeofResource
- gets size specified data in resource section
#include <Windows.h>
#include <stdio.h>
#include "resource.h"
int main() {
HRSRC hRsrc = NULL;
HGLOBAL hGlobal = NULL;
PVOID pPayloadAddress = NULL;
SIZE_T sPayloadSize = NULL;
// Get location to data stored in .rsrc by its id *IDR_RCDATA1*
hRsrc = FindResourceW(NULL, MAKEINTRESOURCEw(IDR_RCDATA1), RT_RCDATA);
if (hRsrc == NULL) {
// in case of function failure
printf("[!] FindResourceW failed with error: %d \n", GetLastError());
- [ ] return -1;
}
// GetHGLOBAL or the handle of the specified resource data since its required to call LockResource later on
hGlobal = LoadResource(NULL, hRsrc);
if (hGlobal == NULL) {
// in case of function failure
printf("[!] LoadResource failed with errror: %d \n", GetLastError());
return -1;
}
// Get the address of our payload in .rsrc section
pPayloadAddress = LockResource(hGlobal);
if (pPayloadAddress == NULL) {
// in case of function failure
printf("[!] LockResource failed with Error: %d \n", GetLastError());
return -1;
}
// Get the size of our payload in .rsrc section
sPayloadSize = SizeofResource(NULL, hRsrc);
if (sPayloadSize == NULL) {
// in case of function failure
printf("[!] SizeofResource failed with error: %d \n", GetLastError());
return -1;
}
// Printing pointer and size to the screen
printf("[i] pPayloadAddress var: 0x%p \n", pPayloadAddress);
printf("[i] sPayloadSize var: %ld \n", sPayloadSize);
printf("[#] Press <Enter> to quit...");
getchar();
return 0;
}
Important to note: the payload address that this code prints to the screen, is read-only memory. To edit a payload, you must allocate a buffer with the same size as the payload and copy it over. The new buffer is where changes (like decrypting a payload!) can be made. This can be done with memcpy
like this:
// Allocating memory using a HeapAlloc call.
PVOID pTmpBuffer = HeapAlloc(GetProcessHeap(), 0, sPayloadSize);
if (pTmpBuffer != NULL){
// copying the payload from the resource section to the new buffer.
memcpy(pTmpBuffer, pPayloadAddress, sPayloadSize);
}
// Printing the base address of our buffer (pTmpBuffer)
printf("[i] pTmpBuffer var: 0x%p \n", pTmpBuffer);
Module 16 - Introduction to Payload Encryption
Encrypting (parts of) the malware is almost always necessary against modern security solutions. Encryption may not be effective against f.e. runtime-analysis and heuristic-analysis. The more data that’s encrypted within a file, the higher the entropy of the file. The most widely used encryption algorithms in malware development are:
- XOR
- AES
- RC4
Payload Encryption - XOR
Exclusive or or XOR encryption is faster than AEC and RC4 and does not require additional libraries or the Windows API. It is also a bidirectional encryption algorithm.
Example of an XOR encryption function:
/*
- pShellcode: Base address of the payload to encrypt.
- sShellcodeSize: Size of the payload.
- bKey: A single arbitrary byte representing the key for encrypting the payload.
*/
VOID XorByOneKey(IN PBYTE pShellcode, IN SIZE_T sShellcodeSize, IN BYTE bKey) {
for (size_t i = 0; i < sShellcodeSize; i++){
pShellcode[i] = pShellcode[i] ^ bKey;
}
}