Skip to content

Commit bcbb0af

Browse files
1.8.3 Fix Registry and Deprecate INF
Registry was broken due to use of __cdecl *printf family functions, those are no more needed with this commit. INF is also removed, now we do sc create with a script instead.
1 parent ff22ae9 commit bcbb0af

12 files changed

Lines changed: 154 additions & 180 deletions

.vs/TimeDefuser-VS2026.vcxproj

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@
119119
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
120120
<OmitDefaultLibName>true</OmitDefaultLibName>
121121
<CallingConvention>StdCall</CallingConvention>
122+
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
123+
<IntrinsicFunctions>false</IntrinsicFunctions>
122124
</ClCompile>
123125
<Link>
124126
<SubSystem>Native</SubSystem>
@@ -175,6 +177,7 @@
175177
<ControlFlowGuard>false</ControlFlowGuard>
176178
<FunctionLevelLinking>false</FunctionLevelLinking>
177179
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
180+
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
178181
</ClCompile>
179182
<Link>
180183
<SubSystem>Native</SubSystem>
@@ -216,9 +219,6 @@
216219
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
217220
</Link>
218221
</ItemDefinitionGroup>
219-
<ItemGroup>
220-
<None Include="..\src\TimeDefuser.inf" />
221-
</ItemGroup>
222222
<ItemGroup>
223223
<ResourceCompile Include="..\src\TimeDefuser.rc" />
224224
</ItemGroup>
@@ -230,6 +230,10 @@
230230
<ClInclude Include="..\src\resource.h" />
231231
<ClInclude Include="..\src\TimeDefuser.h" />
232232
</ItemGroup>
233+
<ItemGroup>
234+
<None Include="..\README.md" />
235+
<None Include="..\TimeDefuser-Research.md" />
236+
</ItemGroup>
233237
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
234238
<ImportGroup Label="ExtensionTargets">
235239
</ImportGroup>

.vs/TimeDefuser-VS2026.vcxproj.filters

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
1414
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
1515
</Filter>
16-
</ItemGroup>
17-
<ItemGroup>
18-
<None Include="..\src\TimeDefuser.inf" />
16+
<Filter Include="Documentation">
17+
<UniqueIdentifier>{042252ca-9640-435a-957f-683ba38dd0d3}</UniqueIdentifier>
18+
</Filter>
1919
</ItemGroup>
2020
<ItemGroup>
2121
<ResourceCompile Include="..\src\TimeDefuser.rc">
@@ -38,4 +38,12 @@
3838
<Filter>Header Files</Filter>
3939
</ClInclude>
4040
</ItemGroup>
41+
<ItemGroup>
42+
<None Include="..\README.md">
43+
<Filter>Documentation</Filter>
44+
</None>
45+
<None Include="..\TimeDefuser-Research.md">
46+
<Filter>Documentation</Filter>
47+
</None>
48+
</ItemGroup>
4149
</Project>

Installer.bat

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
rem ====================================================
2+
rem TimeDefuser Service Installer Script
3+
rem
4+
rem https://github.com/NevermindExpress/TimeDefuser
5+
rem ====================================================
6+
7+
net session >nul 2>&1
8+
if %errorlevel% equ 0 (
9+
copy TimeDefuser-%processor_architecture%.sys %windir%\System32\Drivers\TimeDefuser.sys
10+
sc create TimeDefuser type= kernel start= auto binPath= %windir%\System32\Drivers\TimeDefuser.sys
11+
sc start TimeDefuser
12+
) else (
13+
echo Administrator rights are required.
14+
)
15+
pause

README.md

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
# TimeDefuser
2-
TimeDefuser is a kernel-mode Windows driver that patches the kernel to neutralize the expiration date (a.k.a. timebomb),
3-
which is seen on most prerelease builds that has been ever compiled.
42

5-
This patch patches the timebomb code itself in the kernel so it is the most effective and versatile way to neutralize it, instead of activation patching (i.e. policy files or registry editing) which is not available in many builds.
3+
TimeDefuser is a Windows kernel security research project on enforcement of expiration dates (a.k.a. a "*timebomb*") on prerelease Windows builds, how to patch them for gaining arbitrary code execution,
4+
and a proof-of-concept (shared for education and research purposes) that removes expiration date enforcement from the kernel.
5+
The PoC driver in this repository patches the timebomb code itself in the kernel, which differs from widespread "activation-based" patches (policy files, registry edits, etc.).
6+
Thus, it is the most effective and versatile way to neutralize it, unlike activation-based patching methods which are not available in many builds.
67

7-
All builds are theoretically supported but not all builds are tested, see the notes for more info, or the end of this readme for screenshots.
8+
All builds are *theoretically* supported, but not all builds are tested. See the notes below and screenshots at the end of this document.
9+
10+
Full whitepaper and technical analysis is located [here](/TimeDefuser-Research.md). The rest of this document is about the PoC driver that removes expiration date enforcement from the system.
11+
12+
# TimeDefuser PoC Driver
813

914
> [!WARNING]
1015
> This driver is intended to remove the **Windows builds'** expiration date only
@@ -13,41 +18,43 @@ It will not remove the expiration date of
1318
- Your abusive relationship
1419
- 100-minute Minecraft demo
1520
- The Pepsi can from 1956 that is inside your fridge for whatever reason
16-
- Aceyware "Tracey" Operating System version 0.1.3 (or whatever its name ends up being)
21+
- Aceyware "Tracey" Operating System version 0.1.3 (or whatever it ends up being called)
1722
- ???
18-
- Evaluation retail Windows builds. While it theoretically should work, such configuration is not supported and any bug reports regarding to them will be closed without any further action.
23+
- Evaluation retail Windows builds. While it *may* work, this configuration is unsupported and any related bug reports will be closed.
1924

2025
> [!IMPORTANT]
2126
> This driver will **not** patch Windows Product Activation or any other similar mechanism. These other mechanisms can be preferred as well in supported builds but here is not their place.
2227
23-
# Notes Per Version
28+
# Notes
29+
- A good amount of x64 builds can detect this via PatchGuard (basically a mechanism in Windows kernel that detects unauthorized modifications to kernel code, does not exist in x86).
30+
Getting over it will weaponize this already versatile patch, so disabling PatchGuard will never be implemented. But as an user, you still have workarounds:
31+
- Force enable kernel debugger at boot, which will disable PatchGuard
32+
- Patch the kernel image itself with offline patcher, instead of runtime patching with driver.
33+
- This patch can technically be ported to ARM, ARM64 and Itanium hosts but due to lack of an environment to run and debug Windows on these platforms, this is not possible at the moment.
34+
35+
## Notes Per Version
36+
2437
### Windows 2000/XP
25-
- Use legacy version with those.
26-
- Also note that alternative methods such as registry edits are available for those.
27-
- **I KNOW that they do, so don't come to say me "muh set GracePeriod to 0" or "muh use TweakNT"**. This tweak for NT 5.x exists more as proof of concept, and both this patch or other tweaks will do the work.
38+
- **I KNOW there are "easier" methods, so don't come to say me "muh set GracePeriod to 0" or "muh use TweakNT"**. This tweak for NT 5.x exists more as proof of concept, and both this patch or other tweaks will do the work.
2839
### Post-reset Windows Vista & Early 7
29-
- They suck. Avoid using these versions at all. After build expires, buggy WPA breaks the timebomb which makes this patch not get applied anyway, and shows the "Activate Windows" dialog which logs you off if you say no; considering that those builds can skip the windeploy and boot to OOBE/desktop at all in the first place (https://github.com/NevermindExpress/TimeDefuser/issues/3). See https://github.com/NevermindExpress/TimeDefuser/issues/2 and https://github.com/NevermindExpress/TimeDefuser/issues/2#issuecomment-2970226626 for more info.
40+
- They suck. Avoid using these versions at all. After build expires, buggy WPA breaks the timebomb which makes this patch not get applied anyway, and shows the "Activate Windows" dialog which logs you off if you say no; considering that those builds can successfully finish the windeploy and boot to OOBE/desktop at all in the first place (https://github.com/NevermindExpress/TimeDefuser/issues/3). See https://github.com/NevermindExpress/TimeDefuser/issues/2 and https://github.com/NevermindExpress/TimeDefuser/issues/2#issuecomment-2970226626 for more info.
3041
- These builds are *wontfix* because there is nothing to fix/can be fixed in the first place. Blame Microsoft.
31-
- Alternative patch methods should be used for those. See https://github.com/NevermindExpress/TimeDefuser/issues/2#issuecomment-2904890597
3242
### Later Windows 7 (at least 67xx and later)
33-
- Since TimeDefuser 1.7.1 they are now working working without hitting into page fault (see #3), though they are still subject to PatchGuard detections, an active investigation is going for them at #8.
43+
- Since TimeDefuser 1.7.1 they are now working working without hitting into page fault (see #3), though they are still subject to PatchGuard detections.
3444
### Windows 8
35-
- Some builds such as 7880 has a partially broken timebomb that effectively gets disabled if you install at current date instead of rolling it back to pre-expiration before install. See https://github.com/NevermindExpress/TimeDefuser/issues/5
36-
- Certain builds such as aforementioned are also subject to crashes by PatchGuard, while others such as the ones with the screenshots below are not. See https://github.com/NevermindExpress/TimeDefuser/issues/5#issuecomment-3369399950
37-
- Few builds can be patched with policy/spp files replacement. **Again, I KNOW 'THEY' CAN BE PATCHED**. "MUH FBL builds can be patched by doing X/can be used at current date without doing anything" well, my thing can patch **ALL** versions (except ones that have superior PatchGuard) while your method can only fix a few builds.
45+
- Some builds such as 7880 has a partially broken timebomb that effectively gets disabled if you install at current date instead of setting it to pre-expiration before install. See https://github.com/NevermindExpress/TimeDefuser/issues/5
46+
- **Again, I KNOW 'THEY' CAN BE PATCHED WITH POLICY/SPP FILES REPLACEMENT**. "MUH FBL builds can be patched by doing X/can be used at current date without doing anything" well, my thing can patch **ALL** versions (except ones that have superior PatchGuard) while your method can only fix a few builds.
3847
### Windows 10/11
3948
> [!IMPORTANT]
4049
> Windows 10 builds are also subject to flight signing, which are code signatures that gets invalid after expiration date, thus preventing system from booting or to be used properly.
4150
> Getting over this requires additional work (resigning all binaries and disabling integrity checks, or patching bootloader & ci.dll) which is not covered by this project.
4251
- Works on pre-RTM, post-RTM ("insider") builds are untested but they likely are same as pre-RTM unless KASLR is enabled, which is not supported by this driver.
4352

4453
# Usage
45-
1. Enable test-signing (disabling driver signature enforcement might also be necessary.)
46-
2. Download the latest release and obtain "devcon" utility (available in WDK and also in some .cab files).
47-
3. Execute `devcon install C:\Path\to\TimeDefuser.inf Root\TimeDefuser`
48-
4. Allow the installition and wait for "Driver Installition Complete" message
49-
5. If your system didn't crash so far, check expiration date from "winver", if it's not there that means that it worked.
50-
6. If you want to/need to uninstall, execute `devcon remove Root\TimeDefuser` and reboot (or just delete the .sys file).
54+
Since TimeDefuser 1.8.3, INF file is deprecated and the driver is instead installed as a service with `sc.exe`. A script for installing named `Installer.bat` will be bundled with subsequent releases.
55+
- If your system didn't crash after installition, check expiration date from "winver". Absence of the expiration date means that driver has worked.
56+
- **(x64 systems only)** Wait for several minutes, the system might crash after a few minutes of installition with a `0x109 CRITICAL_STRUCTURE_CORRUPTION` bugcheck. See notes about more info.
57+
- If you need to remove driver, simply execute `sc delete TimeDefuser` and reboot.
5158

5259
# Testing and Bug Reporting
5360
The driver can either work correctly, crash the system, fail or work but not enough to fully patch the currently working system.

src/Driver.c

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
#include "TimeDefuser.h"
22

3+
#ifdef _M_IX86
4+
void* __cdecl memset(void* dest, int c, size_t count) {
5+
unsigned char* p = (unsigned char*)dest;
6+
while (count--) {
7+
*p++ = (unsigned char)c;
8+
}
9+
return dest;
10+
}
11+
#endif // _M_IX86
12+
313
BOOLEAN PatchExGetExpirationDate(void* pExGetExpirationDate) {
414
PMDL mdl = NULL;
515
unsigned char* map = NULL;
@@ -41,12 +51,13 @@ NTSTATUS DriverEntry(PDRIVER_OBEJCT DriverObject, PUNICODE_STRING RegistryPath)
4151
RTL_PROCESS_MODULES ModuleInfo = { 0 }; // Structure used for getting kernel base address
4252
unsigned long long* KernelBase = NULL; // Kernel Base address
4353
ULONG KernelSize = 0; // Kernel image size
44-
//HANDLE hKey = OpenRegistryKey(RegistryPath);
45-
HANDLE hKey = 0;
54+
HANDLE hKey = OpenRegistryKey(RegistryPath);
55+
//HANDLE hKey = 0;
4656
unsigned int KernelSize2 = 0; // Var used in loops as a max value
4757
PAGESections ps[5] = { 0 }; // PE sections that name starts with "PAGE"
4858
unsigned char* PotentialTimestamp = NULL;// Potential address of ExNtExpirationDate/a
4959
BOOLEAN Legacy = FALSE;
60+
int verMajor = 0;
5061

5162
// Unrefence unused variables.
5263
UNREFERENCED_PARAMETER(DriverObject);
@@ -56,6 +67,8 @@ NTSTATUS DriverEntry(PDRIVER_OBEJCT DriverObject, PUNICODE_STRING RegistryPath)
5667
"| Compiled on " __DATE__ " " __TIME__ " "
5768
"| https://github.com/NevermindExpress/TimeDefuser\n");
5869

70+
//TDPrint("[*] TimeDefuser: RegistryPath: %ws\n",RegistryPath->Buffer);
71+
5972
// Get SystemExpirationDate
6073
TimebombStamp = li->QuadPart;
6174
if (!TimebombStamp) {
@@ -66,7 +79,6 @@ NTSTATUS DriverEntry(PDRIVER_OBEJCT DriverObject, PUNICODE_STRING RegistryPath)
6679

6780
// Determine if we are running in a legacy system
6881
{
69-
int verMajor = 0;
7082
PsGetVersion(&verMajor, 0, 0, 0);
7183
if (verMajor == 5) {
7284
TDPrint("[*] TimeDefuser: Legacy system detected.\n");
@@ -113,6 +125,7 @@ NTSTATUS DriverEntry(PDRIVER_OBEJCT DriverObject, PUNICODE_STRING RegistryPath)
113125
*(unsigned long long*)((char*)KernelBase + Stamp2 + 8) = 0;
114126
}
115127

128+
// ExGetExpirationDate Function if not legacy.
116129
if (!Legacy) {
117130
int Function = RegReadValue(hKey, L"Function", NULL, 0);
118131
TDPrint("[+] TimeDefuser: Cached ExGetExpirationDate function address 0x%p is used.\n", (char*)KernelBase + Function);
@@ -197,13 +210,13 @@ NTSTATUS DriverEntry(PDRIVER_OBEJCT DriverObject, PUNICODE_STRING RegistryPath)
197210

198211
if (occurance) {
199212
pExpNtExpirationDate = &PotentialTimestamp[i];
200-
//RegWriteDword(hKey, L"Stamp2", (ULONG)(&PotentialTimestamp[i] - (unsigned char*)KernelBase));
213+
RegWriteDword(hKey, L"Stamp2", (ULONG)(&PotentialTimestamp[i] - (unsigned char*)KernelBase));
201214
occurance = 2;
202215
break;
203216
}
204217
else {
205218
occurance = 1;
206-
//RegWriteDword(hKey, L"Stamp1", (ULONG)((unsigned char*)&PotentialTimestamp[i] - (unsigned char*)KernelBase));
219+
RegWriteDword(hKey, L"Stamp1", (ULONG)((unsigned char*)&PotentialTimestamp[i] - (unsigned char*)KernelBase));
207220
}
208221
}
209222
}
@@ -225,7 +238,7 @@ NTSTATUS DriverEntry(PDRIVER_OBEJCT DriverObject, PUNICODE_STRING RegistryPath)
225238
// Search for PAGE section at PE sections. This section or one of the next three sections is where the
226239
// "ExpTimeRefreshWork" function is located at, which later calls a function named "ExGetExpirationDate".
227240
// Due to it's variable being, we will search the PAGE section and next three sections.
228-
241+
229242
for (size_t i = 0; i < 768; i++) {
230243
if (KernelBase[i] == sectNamePAGELK) { // Check if we found the PAGELK\0\0 section name.
231244
int* temp = (int*)&KernelBase[i + 1];
@@ -266,7 +279,7 @@ NTSTATUS DriverEntry(PDRIVER_OBEJCT DriverObject, PUNICODE_STRING RegistryPath)
266279
TDPrint("[+] TimeDefuser: CALL instruction found at 0x%p\n", &PotentialTimeRef[i - j]);
267280
unsigned char* pExGetExpirationDate = &PotentialTimeRef[i - j + 5];
268281
pExGetExpirationDate += *(unsigned int*)&PotentialTimeRef[i - j + 1]; // Next 4 bytes are relative address to our current location.
269-
//RegWriteDword(hKey, L"Function", (ULONG)(pExGetExpirationDate - (unsigned char*)KernelBase));
282+
270283
// Check if we are running at one of shit builds, refer to ExGetExpirationDateShim.
271284
if (!MmIsAddressValid(pExGetExpirationDate)) {
272285
TDPrint("[*] TimeDefuser: Invalid address, skipping this one...\n", pExGetExpirationDate);
@@ -276,14 +289,17 @@ NTSTATUS DriverEntry(PDRIVER_OBEJCT DriverObject, PUNICODE_STRING RegistryPath)
276289
TDPrint("[+] TimeDefuser: ExGetExpirationDate found at 0x%p\n", pExGetExpirationDate);
277290
#ifdef _M_IX86
278291
// Caller is patched on x86
279-
if (!PatchExGetExpirationDate(&PotentialTimeRef[i - j]))
292+
if (PatchExGetExpirationDate(&PotentialTimeRef[i - j])) {
293+
RegWriteDword(hKey, L"Function", (ULONG)(&PotentialTimeRef[i - j] - (unsigned char*)KernelBase));
280294
#else
281-
if (!PatchExGetExpirationDate(pExGetExpirationDate))
295+
if (PatchExGetExpirationDate(pExGetExpirationDate)) {
296+
RegWriteDword(hKey, L"Function", (ULONG)(pExGetExpirationDate - (unsigned char*)KernelBase));
282297
#endif
283-
goto patchFail;
284-
else {
285298
goto patchOK;
286299
}
300+
else {
301+
goto patchFail;
302+
}
287303

288304

289305
}

src/Registry.c

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
#include "TimeDefuser.h"
22

3+
typedef struct {
4+
ULONG Major;
5+
ULONG Minor;
6+
ULONG Build;
7+
} tdkernelVersion;
8+
39
HANDLE OpenRegistryKey(PUNICODE_STRING KeyPath){
410
HANDLE ret = 0;
511
OBJECT_ATTRIBUTES oa;
@@ -71,7 +77,17 @@ ULONG RegReadValue(_In_ HANDLE KeyHandle, _In_ PCWSTR ValueName, _Out_ PVOID Val
7177
else {
7278
if (kvpi->DataLength < ValueOutputSz) {
7379
ret = kvpi->DataLength;
74-
RtlCopyMemory(ValueOutput, kvpi->Data, ret);
80+
//RtlCopyMemory(ValueOutput, kvpi->Data, ret);
81+
// ^^^ this shit fucks up sooo here is a poor man's memcpy
82+
char* Data = kvpi->Data;
83+
while (kvpi->DataLength >= 8) {
84+
*(__int64*)ValueOutput = *(__int64*)Data; Data += 8;
85+
(char*)ValueOutput += 8; kvpi->DataLength -= 8;
86+
}
87+
while (kvpi->DataLength) {
88+
*(char*)ValueOutput = Data[0]; Data++;
89+
(char*)ValueOutput += 1; kvpi->DataLength--;
90+
}
7591
}
7692
else status = STATUS_INVALID_PARAMETER;
7793
}
@@ -85,25 +101,24 @@ ULONG RegReadValue(_In_ HANDLE KeyHandle, _In_ PCWSTR ValueName, _Out_ PVOID Val
85101
NTSTATUS SaveKernelVersion(_In_ HANDLE hKey) {
86102
UNICODE_STRING valName;
87103
NTSTATUS status;
104+
ULONG major, minor, build;
105+
tdkernelVersion ver;
88106

89107
RtlInitUnicodeString(&valName, L"KernelVersion");
90108

91-
ULONG major, minor, build;
92109
PsGetVersion(&major, &minor, &build, NULL);
93110

94-
WCHAR kernelVersionString[64] = L"";
95-
swprintf(kernelVersionString, 64, L"%u.%u.%u", major, minor, build);
96-
97-
98-
SIZE_T len = (wcslen(kernelVersionString) + 1) * sizeof(WCHAR);
111+
ver.Major = major;
112+
ver.Minor = minor;
113+
ver.Build = build;
99114

100115
status = ZwSetValueKey(
101116
hKey,
102117
&valName,
103118
0,
104-
REG_SZ,
105-
(PVOID)kernelVersionString,
106-
(ULONG)len
119+
REG_BINARY,
120+
&ver,
121+
sizeof(ver)
107122
);
108123

109124
return status;
@@ -112,14 +127,18 @@ NTSTATUS SaveKernelVersion(_In_ HANDLE hKey) {
112127
BOOLEAN CompareKernelVersion(_In_ HANDLE hKey) {
113128
// Firstly we will read the version of current kernel and make it a string.
114129
ULONG major, minor, build;
130+
tdkernelVersion currentVer = { 0 }, regVer = { 0 };
115131
PsGetVersion(&major, &minor, &build, NULL);
116-
WCHAR kernelVersionCurrent[64] = L"";
117-
swprintf(kernelVersionCurrent, 64, L"%u.%u.%u", major, minor, build);
132+
133+
currentVer.Major = major;
134+
currentVer.Minor = minor;
135+
currentVer.Build = build;
118136

119137
// Then we will get the value from registry.
120-
WCHAR kernelVersionReg[64] = L"";
121-
if (!RegReadValue(hKey, L"KernelVersion", kernelVersionReg, 64)) return FALSE;
138+
if (!RegReadValue(hKey, L"KernelVersion", &regVer, sizeof(regVer))) return FALSE;
122139

123140
// Lastly we will compare and return.
124-
return wcscmp(kernelVersionCurrent, kernelVersionReg) == 0;
141+
return currentVer.Major == regVer.Major &&
142+
currentVer.Minor == regVer.Minor &&
143+
currentVer.Build == regVer.Build;
125144
}

src/TimeDefuser.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include "tdwdm/wdm.h"
55

66
/// Definitions
7-
#define td_version "1.8.2"
7+
#define td_version "1.8.3"
88

99
#define SystemModuleInformation 11
1010
#define PEheader 0x5a4d // MZ

0 commit comments

Comments
 (0)