S. |
If you're observant enough, you'll also notice that when it
happens, your [roaming] profile will not be updated and the recent
changes will consequently be lost. The problem is recognized by
Microsoft, and several attempts to work it around were actually
made, but none of them was successful. The problem is that Win32
Subsystem server, CSRSS.EXE, (well, rather one of DLLs
dynamically loaded to its address space) leaks handle[s] to your
personal registry hive, which makes it improssible to unload the
latter. So we do it the hard way:-) In order to catch the moment of
logout, I intercept call to UnloadUserProfile in
WINLOGON.EXE (yes, this means that module in question
is to be pre-loaded to WINLOGON.EXE). Upon call to this
function, I list processes to identify my Win32 Subsystem server,
"attach" to it, then "steal" the handles to my
registry hive and just close them. Needless to mention that it
requires DLL_PRELOAD module loaded.
 |
WINLOGOUT.c
|
/*
* Copyright (c) 2003 Andy Polyakov
*
* Build with:
*
* cl -Ox -GD -GF -Zl -MD -LD WINLOGOUT.c advapi32.lib kernel32.lib ntdll.lib
*
* See http://fy.chalmers.se/~appro/nt/DLL_PRELOAD/ for further details.
*
*/
#ifndef _DLL
#error "_DLL is not defined."
#endif
#pragma comment(linker,"/entry:DllMain@12")
#pragma comment(linker,"/section:.text,erw")
#pragma comment(linker,"/merge:.rdata=.text")
#pragma comment(linker,"/merge:.data=.text")
#if defined(WIN32) && !defined(_WIN32)
#define _WIN32
#endif
#define _WIN32_WINNT 0x0500
#include
#include
#include
#include
/* M$ headers are *complete* mess!!! */
#ifndef STATUS_INFO_LENGTH_MISMATCH
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#endif
#define NtQuerySystemInformation _NtQuerySystemInformation
#include
#undef NtQuerySystemInformation
#define SystemHandleInformation 16
typedef LONG KPRIORITY;
typedef struct {
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER SpareLi1;
LARGE_INTEGER SpareLi2;
LARGE_INTEGER SpareLi3;
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
KPRIORITY BasePriority;
HANDLE UniqueProcessId;
HANDLE InheritedFromUniqueProcessId;
ULONG HandleCount;
ULONG SessionId;
ULONG SpareUl3;
SIZE_T PeakVirtualSize;
SIZE_T VirtualSize;
ULONG PageFaultCount;
ULONG PeakWorkingSetSize;
ULONG WorkingSetSize;
SIZE_T QuotaPeakPagedPoolUsage;
SIZE_T QuotaPagedPoolUsage;
SIZE_T QuotaPeakNonPagedPoolUsage;
SIZE_T QuotaNonPagedPoolUsage;
SIZE_T PagefileUsage;
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER ReadOperationCount;
LARGE_INTEGER WriteOperationCount;
LARGE_INTEGER OtherOperationCount;
LARGE_INTEGER ReadTransferCount;
LARGE_INTEGER WriteTransferCount;
LARGE_INTEGER OtherTransferCount;
} MORE_SYSTEM_PROCESS_INFORMATION;
typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO {
USHORT UniqueProcessId;
USHORT CreatorBackTraceIndex;
UCHAR ObjectTypeIndex;
UCHAR HandleAttributes;
USHORT HandleValue;
PVOID Object;
ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;
typedef struct _SYSTEM_HANDLE_INFORMATION {
ULONG NumberOfHandles;
SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[ 1 ];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
NTSTATUS NTAPI NtQuerySystemInformation (
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
typedef struct _OBJECT_TYPE_INFORMATION {
UNICODE_STRING TypeName;
ULONG TotalNumberOfObjects;
ULONG TotalNumberOfHandles;
ULONG TotalPagedPoolUsage;
ULONG TotalNonPagedPoolUsage;
ULONG TotalNamePoolUsage;
ULONG TotalHandleTableUsage;
ULONG HighWaterNumberOfObjects;
ULONG HighWaterNumberOfHandles;
ULONG HighWaterPagedPoolUsage;
ULONG HighWaterNonPagedPoolUsage;
ULONG HighWaterNamePoolUsage;
ULONG HighWaterHandleTableUsage;
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccessMask;
BOOLEAN SecurityRequired;
BOOLEAN MaintainHandleCount;
ULONG PoolType;
ULONG DefaultPagedPoolCharge;
ULONG DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
typedef struct _OBJECT_ALL_TYPES_INFORMATION {
ULONG NumberOfTypes;
OBJECT_TYPE_INFORMATION TypeInformation[1];
} OBJECT_ALL_TYPES_INFORMATION, *POBJECT_ALL_TYPES_INFORMATION;
typedef enum _OBJECT_INFORMATION_CLASS {
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation,
ObjectAllTypesInformation,
ObjectHandleInformation
} OBJECT_INFORMATION_CLASS;
NTSTATUS NTAPI NtQueryObject (
IN HANDLE ObjectHandle,
IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
OUT PVOID ObjectInformation,
IN ULONG ObjectInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
static DWORD find_csrpid()
{ SIZE_T RequiredLength;
NTSTATUS s;
static DWORD csrpid=0;
if (csrpid) return csrpid;
csrpid=(DWORD)-1;
/* Dump processes and find this session's CSRSS.EXE */
{ MORE_SYSTEM_PROCESS_INFORMATION *ProcTable,*Proc;
DWORD sess;
RequiredLength=64*1024; /* a bit large for ws, but ok for ts */
ProcTable = VirtualAlloc(NULL,RequiredLength,MEM_COMMIT,PAGE_READWRITE);
if (ProcTable == NULL) return -1;
while ((s=NtQuerySystemInformation(SystemProcessInformation,
ProcTable,
(ULONG)RequiredLength,NULL))
== STATUS_INFO_LENGTH_MISMATCH)
{ RequiredLength += 16*1024;
VirtualFree(ProcTable,0,MEM_RELEASE);
ProcTable = VirtualAlloc (NULL,RequiredLength,
MEM_COMMIT,PAGE_READWRITE);
if (ProcTable == NULL) return -1;
}
if (s) return s;
ProcessIdToSessionId (GetCurrentProcessId(),&sess);
Proc=ProcTable;
do {
if (Proc->SessionId == sess &&
Proc->ImageName.Length==18 &&
!_wcsnicmp(Proc->ImageName.Buffer,L"CSRSS.EXE",9)
)
{ csrpid=(DWORD)Proc->UniqueProcessId; break; }
Proc=(MORE_SYSTEM_PROCESS_INFORMATION *)
((char *)Proc+Proc->NextEntryOffset);
} while (Proc->NextEntryOffset);
VirtualFree (ProcTable,0,MEM_RELEASE);
}
return csrpid;
}
static NTSTATUS list_keys(DWORD csrpid,void (*cb)(DWORD,void *),void *cbdata)
{ SIZE_T RequiredLength;
UCHAR KeyType=0;
SYSTEM_HANDLE_INFORMATION *HandleTable=NULL;
NTSTATUS s;
int i;
/* Retrieve Object Type table and find Key's index */
{ OBJECT_ALL_TYPES_INFORMATION *Types;
OBJECT_TYPE_INFORMATION *t;
s=NtQueryObject(NULL,ObjectAllTypesInformation,NULL,0,&RequiredLength);
if (s==STATUS_INFO_LENGTH_MISMATCH)
{ Types = VirtualAlloc(NULL,RequiredLength,MEM_COMMIT,PAGE_READWRITE);
if (Types == NULL) return -1;
if (s=NtQueryObject(NULL,ObjectAllTypesInformation,
Types,RequiredLength,&RequiredLength))
{ VirtualFree (Types,0,MEM_RELEASE);
return s;
}
for (t=Types->TypeInformation,i=0;iNumberOfTypes;i++)
{ if (t->TypeName.Length==6 &&
!_wcsnicmp(t->TypeName.Buffer,L"Key",3))
{ KeyType = i+1; break; }
t=(OBJECT_TYPE_INFORMATION *)(
(char *)t->TypeName.Buffer +
((t->TypeName.MaximumLength+3)&~3) );
}
}
else return s;
VirtualFree (Types,0,MEM_RELEASE);
}
if (KeyType==0) return -1;
/* Retrieve Handle Table */
{ SYSTEM_HANDLE_INFORMATION Handle;
s=NtQuerySystemInformation(SystemHandleInformation,
&Handle,sizeof(Handle),(ULONG *)&RequiredLength);
while (s==STATUS_INFO_LENGTH_MISMATCH)
{ RequiredLength += 4*1024;
if (HandleTable) VirtualFree (HandleTable,0,MEM_RELEASE);
HandleTable = VirtualAlloc(NULL,RequiredLength,MEM_COMMIT,PAGE_READWRITE);
if (HandleTable == NULL)
return -1;
s=NtQuerySystemInformation(SystemHandleInformation,
HandleTable,(ULONG)RequiredLength,(ULONG *)&RequiredLength);
}
}
if (s)
{ VirtualFree (HandleTable,0,MEM_RELEASE);
return s;
}
for (i=0;iNumberOfHandles;i++)
{ if (HandleTable->Handles[i].UniqueProcessId == csrpid)
{ if (HandleTable->Handles[i].ObjectTypeIndex==KeyType)
(*cb)(HandleTable->Handles[i].HandleValue,cbdata);
}
}
VirtualFree (HandleTable,0,MEM_RELEASE);
return 0;
}
typedef struct { HANDLE csr,log; } CB_DATA;
static void closeremotehandle (DWORD handle,CB_DATA *d)
{ HANDLE hcopy=NULL,me=GetCurrentProcess();
NTSTATUS s;
struct { UNICODE_STRING Name; wchar_t Buffer[96]; } NameQuery;
while (d->csr)
{ if (DuplicateHandle (d->csr,(HANDLE)handle,me,&hcopy,0,0,DUPLICATE_SAME_ACCESS) == 0)
break;
s=NtQueryObject(hcopy,ObjectNameInformation,&NameQuery,sizeof(NameQuery),NULL);
CloseHandle(hcopy);
if (s && s!=STATUS_INFO_LENGTH_MISMATCH) break;
if (NameQuery.Name.Length>=34 &&
!_wcsnicmp(NameQuery.Name.Buffer,L"\\REGISTRY\\User\\S-",17))
{ DuplicateHandle (d->csr,(HANDLE)handle,GetCurrentProcess(),&hcopy,0,0,
DUPLICATE_CLOSE_SOURCE|DUPLICATE_SAME_ACCESS);
CloseHandle(hcopy);
#if 0
{ TCHAR str[256],*arr[]={str};
_snprintf(str,sizeof(str)/sizeof(str[0])-1,
"WINLOGOUT.dll: Closing %x, %*S",
handle,NameQuery.Name.Length,NameQuery.Name.Buffer);
str[sizeof(str)/sizeof(str[0])-1]=0;
ReportEvent(d->log,EVENTLOG_INFORMATION_TYPE,0,1000,NULL,1,0,arr,NULL);
}
#endif
}
break;
}
}
typedef BOOL (WINAPI *UnloadUserProfile_T) (
IN HANDLE Token,
IN HANDLE Profile
);
static UnloadUserProfile_T _UnloadUserProfile=NULL,*__UnloadUserProfile=NULL;
static BOOL WINAPI UnloadUserProfile_ (
IN HANDLE Token,
IN HANDLE Profile
)
{ static int once=1;
if (once)
{ HANDLE csr,applog;
CB_DATA d;
once=0;
applog=OpenEventLog(NULL,_T("Application"));
if (csr=OpenProcess(PROCESS_DUP_HANDLE,0,find_csrpid()))
{ d.csr=csr;
d.log=applog;
list_keys(find_csrpid(),closeremotehandle,&d);
CloseHandle(csr);
}
else
{ TCHAR str[64],*arr[]={str};
_snprintf(str,sizeof(str)/sizeof(str[0])-1,
"WINLOGOUT.dll: Unable to OpenProcess(%d)",find_csrpid());
str[sizeof(str)/sizeof(str[0])-1]=0;
ReportEvent(applog,EVENTLOG_ERROR_TYPE,0,1000,NULL,1,0,arr,NULL);
}
CloseEventLog (applog);
}
return (*_UnloadUserProfile)(
Token,
Profile);
}
static int _lstricmp(const char *s1, const char *s2)
{ char c1,c2;
int ret;
while (c1=*s1, c2=*s2, c1&&c2)
{ c1|=0x20, c2|=0x20; /* lower the case */
if (ret=c1-c2) return ret;
s1++, s2++;
}
return c1-c2;
}
BOOL WINAPI DllMain (HINSTANCE h, DWORD reason, LPVOID junk)
{ DWORD acc;
HMODULE hmod;
IMAGE_DOS_HEADER *dos_header;
IMAGE_NT_HEADERS *nt_headers;
IMAGE_DATA_DIRECTORY *dir;
IMAGE_IMPORT_DESCRIPTOR *idesc;
IMAGE_THUNK_DATA *thunk;
switch (reason)
{ case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(h);
if (!(hmod=GetModuleHandle(_T("USERENV.DLL"))))
{ OutputDebugString(_T("USERENV.DLL not found?"));
return FALSE;
}
_UnloadUserProfile=(UnloadUserProfile_T)GetProcAddress(hmod,"UnloadUserProfile");
if (!(hmod=GetModuleHandle(NULL)))
{ OutputDebugString(_T("NULL not found?"));
return FALSE;
}
dos_header = (IMAGE_DOS_HEADER *)hmod;
nt_headers = (IMAGE_NT_HEADERS *)((char *)hmod + dos_header->e_lfanew);
dir=&nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
idesc=(IMAGE_IMPORT_DESCRIPTOR *)((char *)hmod + dir->VirtualAddress);
while (idesc->Name)
{ if (!_lstricmp((char *)hmod+idesc->Name,"USERENV.DLL")) break;
idesc++;
}
if (!idesc->Name)
{ OutputDebugString(_T("Can't locate USERENV.DLL import descriptor"));
return FALSE;
}
for (thunk=(IMAGE_THUNK_DATA *)((char *)hmod+idesc->FirstThunk);
thunk->u1.Function && thunk->u1.Function!=(ULONG_PTR)_UnloadUserProfile;
thunk++) ;
__UnloadUserProfile=(UnloadUserProfile_T *)thunk;
if (!VirtualProtect (__UnloadUserProfile,sizeof(void*),PAGE_EXECUTE_READWRITE,&acc))
{ OutputDebugString(_T("Unable to unlock Thunk Table"));
return FALSE;
}
*__UnloadUserProfile=UnloadUserProfile_;
VirtualProtect (__UnloadUserProfile,sizeof(void*),acc,&acc);
break;
case DLL_PROCESS_DETACH:
VirtualProtect (__UnloadUserProfile,sizeof(void*),PAGE_EXECUTE_READWRITE,&acc);
*__UnloadUserProfile=_UnloadUserProfile;
VirtualProtect (__UnloadUserProfile,sizeof(void*),acc,&acc);
break;
}
return TRUE;
}
|
|
|