add a not yet complete tool to dump the recycle bin databases

svn path=/trunk/; revision=18384
This commit is contained in:
Thomas Bluemel 2005-10-09 20:56:17 +00:00
parent be1c8d7a38
commit d49c0b4e12
2 changed files with 746 additions and 0 deletions

View file

@ -0,0 +1,733 @@
/*
*
* dumprecbin - dumps a recycle bin database
*
* Copyright (c) 2005 by Thomas Weidenmueller <w3seek@reactos.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* TODO: - Support for Vista recycle bins (read the DeleteInfo NTFS streams, also NT 5.x)
* - Support for INFO databases (win95)
*/
#include <windows.h>
#include <winternl.h>
#include <sddl.h>
#include <ntsecapi.h>
#include <stdio.h>
#include <ctype.h>
#include <tchar.h>
#ifndef NT_SUCCESS
#define NT_SUCCESS(status) ((LONG)(status) >= 0)
#endif
typedef struct _RECYCLE_BIN
{
struct _RECYCLE_BIN *Next;
PSID Sid;
WCHAR User[255];
WCHAR Path[MAX_PATH + 1];
} RECYCLE_BIN, *PRECYCLE_BIN;
typedef enum
{
ivUnknown = 0,
ivINFO2
} INFO_VERSION, *PINFO_VERSION;
typedef struct _INFO2_HEADER
{
DWORD Version;
DWORD Zero1;
DWORD Zero2;
DWORD RecordSize;
} INFO2_HEADER, *PINFO2_HEADER;
typedef struct _INFO2_RECORD
{
DWORD Unknown;
CHAR AnsiFileName[MAX_PATH];
DWORD RecordNumber;
DWORD DriveLetter;
FILETIME DeletionTime;
DWORD DeletedPhysicalSize;
WCHAR FileName[MAX_PATH - 2];
} INFO2_RECORD, *PINFO2_RECORD;
static HANDLE
OpenAndMapInfoDatabase(IN LPTSTR szFileName,
OUT PVOID *MappingBasePtr,
OUT PLARGE_INTEGER FileSize)
{
HANDLE hFile, hMapping = INVALID_HANDLE_VALUE;
hFile = CreateFile(szFileName,
FILE_READ_DATA,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
if (GetFileSizeEx(hFile,
FileSize) &&
FileSize->QuadPart >= 0xF)
{
hMapping = CreateFileMapping(hFile,
NULL,
PAGE_READONLY,
0,
0,
NULL);
if (hMapping == NULL ||
!(*MappingBasePtr = MapViewOfFile(hMapping,
FILE_MAP_READ,
0,
0,
0)))
{
if (hMapping != NULL)
{
CloseHandle(hMapping);
}
hMapping = INVALID_HANDLE_VALUE;
}
}
CloseHandle(hFile);
}
return hMapping;
}
static VOID
UnmapAndCloseDatabase(IN HANDLE hMapping,
IN PVOID MappingBasePtr)
{
UnmapViewOfFile(MappingBasePtr);
CloseHandle(hMapping);
}
static INFO_VERSION
DetectDatabaseVersion(PVOID Header)
{
PINFO2_HEADER Info2 = (PINFO2_HEADER)Header;
INFO_VERSION Version = ivUnknown;
if (Info2->Version == 5 &&
Info2->Zero1 == 0 &&
Info2->Zero2 == 0 &&
Info2->RecordSize == 0x320)
{
Version = ivINFO2;
}
return Version;
}
static BOOL
IsValidRecycleBin(IN LPTSTR szPath)
{
TCHAR szFile[MAX_PATH + 1];
TCHAR szClsId[48];
INFO_VERSION DbVersion = ivUnknown;
_stprintf(szFile,
_T("%s\\desktop.ini"),
szPath);
/* check if directory contains a valid desktop.ini for the recycle bin */
if (GetPrivateProfileString(TEXT(".ShellClassInfo"),
TEXT("CLSID"),
NULL,
szClsId,
sizeof(szClsId) / sizeof(szClsId[0]),
szFile) &&
!_tcsicmp(_T("{645FF040-5081-101B-9F08-00AA002F954E}"),
szClsId))
{
HANDLE hDb;
LARGE_INTEGER FileSize;
PVOID pDbBase = NULL;
/* open the database and check the signature */
_stprintf(szFile,
_T("%s\\INFO2"),
szPath);
hDb = OpenAndMapInfoDatabase(szFile,
&pDbBase,
&FileSize);
if (hDb != INVALID_HANDLE_VALUE)
{
DbVersion = DetectDatabaseVersion(pDbBase);
UnmapAndCloseDatabase(hDb,
pDbBase);
}
}
return DbVersion != ivUnknown;
}
static BOOL
OpenLocalLSAPolicyHandle(IN ACCESS_MASK DesiredAccess,
OUT PLSA_HANDLE PolicyHandle)
{
LSA_OBJECT_ATTRIBUTES LsaObjectAttributes = {0};
NTSTATUS Status;
Status = LsaOpenPolicy(NULL,
&LsaObjectAttributes,
DesiredAccess,
PolicyHandle);
if (!NT_SUCCESS(Status))
{
SetLastError(LsaNtStatusToWinError(Status));
return FALSE;
}
return TRUE;
}
static BOOL
ConvertSIDToAccountName(IN PSID Sid,
OUT LPWSTR User)
{
DWORD AccountNameLen = 0;
DWORD DomainNameLen = 0;
SID_NAME_USE NameUse;
DWORD Error = ERROR_SUCCESS;
LPWSTR AccountName, DomainName;
BOOL Ret = FALSE;
if (!LookupAccountSidW(NULL,
Sid,
NULL,
&AccountNameLen,
NULL,
&DomainNameLen,
&NameUse))
{
Error = GetLastError();
if (Error == ERROR_NONE_MAPPED ||
Error != ERROR_INSUFFICIENT_BUFFER)
{
/* some unexpected error occured! */
goto ConvertSID;
}
}
AccountName = (LPWSTR)HeapAlloc(GetProcessHeap(),
0,
(AccountNameLen + DomainNameLen) * sizeof(WCHAR));
if (AccountName != NULL)
{
LSA_HANDLE PolicyHandle;
DomainName = AccountName + AccountNameLen;
if (!LookupAccountSidW(NULL,
Sid,
AccountName,
&AccountNameLen,
DomainName,
&DomainNameLen,
&NameUse))
{
goto BailFreeAccountName;
}
wcscpy(User,
AccountName);
Ret = TRUE;
if (OpenLocalLSAPolicyHandle(POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
&PolicyHandle))
{
PLSA_REFERENCED_DOMAIN_LIST ReferencedDomain;
PLSA_TRANSLATED_NAME Names;
PLSA_TRUST_INFORMATION Domain;
PLSA_UNICODE_STRING DomainName;
PPOLICY_ACCOUNT_DOMAIN_INFO PolicyAccountDomainInfo = NULL;
NTSTATUS Status;
Status = LsaLookupSids(PolicyHandle,
1,
&Sid,
&ReferencedDomain,
&Names);
if (NT_SUCCESS(Status))
{
if (ReferencedDomain != NULL &&
Names->DomainIndex >= 0)
{
Domain = &ReferencedDomain->Domains[Names->DomainIndex];
DomainName = &Domain->Name;
}
else
{
Domain = NULL;
DomainName = NULL;
}
switch (Names->Use)
{
case SidTypeAlias:
if (Domain != NULL)
{
/* query the domain name for BUILTIN accounts */
Status = LsaQueryInformationPolicy(PolicyHandle,
PolicyAccountDomainInformation,
(PVOID*)&PolicyAccountDomainInfo);
if (NT_SUCCESS(Status))
{
DomainName = &PolicyAccountDomainInfo->DomainName;
}
}
/* fall through */
case SidTypeUser:
{
if (Domain != NULL)
{
WCHAR *s;
/* NOTE: LSA_UNICODE_STRINGs are not always NULL-terminated! */
wcscpy(User,
AccountName);
wcscat(User,
L" (");
s = User + wcslen(User);
CopyMemory(s,
DomainName->Buffer,
DomainName->Length);
s += DomainName->Length / sizeof(WCHAR);
*(s++) = L'\\';
CopyMemory(s,
Names->Name.Buffer,
Names->Name.Length);
s += Names->Name.Length / sizeof(WCHAR);
*(s++) = L')';
*s = L'\0';
}
break;
}
case SidTypeWellKnownGroup:
{
break;
}
default:
{
_ftprintf(stderr,
_T("Unhandled SID type: 0x%x\n"),
Names->Use);
break;
}
}
if (PolicyAccountDomainInfo != NULL)
{
LsaFreeMemory(PolicyAccountDomainInfo);
}
LsaFreeMemory(ReferencedDomain);
LsaFreeMemory(Names);
}
LsaClose(PolicyHandle);
if (!NT_SUCCESS(Status))
{
Ret = FALSE;
goto BailFreeAccountName;
}
}
else
{
BailFreeAccountName:
HeapFree(GetProcessHeap(),
0,
AccountName);
goto ConvertSID;
}
}
ConvertSID:
if (!Ret)
{
LPWSTR StrSid;
Ret = ConvertSidToStringSidW(Sid,
&StrSid);
if (Ret)
{
wcscpy(User,
StrSid);
LocalFree((HLOCAL)StrSid);
}
}
return Ret;
}
static VOID
FreeRecycleBinsList(IN OUT PRECYCLE_BIN *RecycleBinsListHead)
{
PRECYCLE_BIN CurrentBin, NextBin;
CurrentBin = *RecycleBinsListHead;
while (CurrentBin != NULL)
{
NextBin = CurrentBin->Next;
LocalFree((HLOCAL)CurrentBin->Sid);
HeapFree(GetProcessHeap(),
0,
CurrentBin);
CurrentBin = NextBin;
}
*RecycleBinsListHead = NULL;
}
static BOOL
LocateRecycleBins(IN LPWSTR szDrive,
OUT PRECYCLE_BIN *RecycleBinsListHead)
{
TCHAR szRecBinPath[MAX_PATH + 1];
HANDLE FindResult;
WIN32_FIND_DATA FindData;
PRECYCLE_BIN NewBin;
BOOL Ret = FALSE;
FreeRecycleBinsList(RecycleBinsListHead);
/*
* search for recycle bins on volumes that support file security (NTFS)
*/
_stprintf(szRecBinPath,
_T("%lS\\RECYCLER\\*"),
szDrive);
FindResult = FindFirstFile(szRecBinPath,
&FindData);
if (FindResult != INVALID_HANDLE_VALUE)
{
do
{
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
_tcscmp(FindData.cFileName,
_T("..")) &&
_tcscmp(FindData.cFileName,
_T(".")))
{
PSID Sid;
if (ConvertStringSidToSid(FindData.cFileName,
&Sid))
{
_stprintf(szRecBinPath,
_T("%s\\RECYCLER\\%s"),
szDrive,
FindData.cFileName);
if (IsValidRecycleBin(szRecBinPath))
{
NewBin = (PRECYCLE_BIN)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(RECYCLE_BIN));
if (NewBin != NULL)
{
_tcscpy(NewBin->Path,
szRecBinPath);
/* convert the SID to an account name */
ConvertSIDToAccountName(Sid,
NewBin->User);
/* append the recycle bin */
*RecycleBinsListHead = NewBin;
RecycleBinsListHead = &NewBin->Next;
Ret = TRUE;
}
else
goto ContinueFreeSid;
}
else
{
ContinueFreeSid:
LocalFree((HLOCAL)Sid);
}
}
}
} while (FindNextFile(FindResult,
&FindData));
FindClose(FindResult);
}
/*
* search for recycle bins on volumes that don't support file security (FAT)
*/
_stprintf(szRecBinPath,
_T("%s\\Recycled"),
szDrive);
FindResult = FindFirstFile(szRecBinPath,
&FindData);
if (FindResult != INVALID_HANDLE_VALUE)
{
if (IsValidRecycleBin(szRecBinPath))
{
SID_IDENTIFIER_AUTHORITY WorldSia = {SECURITY_WORLD_SID_AUTHORITY};
PSID EveryoneSid;
/* create an Everyone SID */
if (AllocateAndInitializeSid(&WorldSia,
1,
SECURITY_WORLD_RID,
0,
0,
0,
0,
0,
0,
0,
&EveryoneSid))
{
NewBin = (PRECYCLE_BIN)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(RECYCLE_BIN));
if (NewBin != NULL)
{
_tcscpy(NewBin->Path,
szRecBinPath);
/* convert the SID to an account name */
ConvertSIDToAccountName(EveryoneSid,
NewBin->User);
/* append the recycle bin */
*RecycleBinsListHead = NewBin;
RecycleBinsListHead = &NewBin->Next;
Ret = TRUE;
}
else
FreeSid(EveryoneSid);
}
}
FindClose(FindResult);
}
return Ret;
}
static VOID
DiskFileNameFromRecord(OUT LPTSTR szShortFileName,
IN DWORD RecordNumber,
IN WCHAR cDriveLetter,
IN LPWSTR szFileName)
{
LPWSTR FileExt;
FileExt = wcsrchr(szFileName,
L'.');
if (FileExt != NULL)
{
_stprintf(szShortFileName,
_T("D%lC%d%lS"),
cDriveLetter,
RecordNumber,
FileExt);
}
else
{
_stprintf(szShortFileName,
_T("D%lC%d"),
cDriveLetter,
RecordNumber);
}
}
static BOOL
DumpRecycleBin(IN PRECYCLE_BIN RecycleBin)
{
WCHAR szFile[MAX_PATH + 1];
HANDLE hDb;
LARGE_INTEGER FileSize;
PVOID pDbBase = NULL;
INFO_VERSION Version = ivUnknown;
_tprintf(_T("Dumping recycle bin of \"%lS\":\n"),
RecycleBin->User);
_tprintf(_T("Directory: %lS\n\n"),
RecycleBin->Path);
_stprintf(szFile,
_T("%s\\INFO2"),
RecycleBin->Path);
hDb = OpenAndMapInfoDatabase(szFile,
&pDbBase,
&FileSize);
if (hDb != INVALID_HANDLE_VALUE)
{
Version = DetectDatabaseVersion(pDbBase);
/* dump the INFO2 database */
switch (Version)
{
case ivINFO2:
{
DWORD nRecords;
PINFO2_HEADER Info2Header = (PINFO2_HEADER)pDbBase;
PINFO2_RECORD Info2 = (PINFO2_RECORD)(Info2Header + 1);
int i = 0;
nRecords = (FileSize.QuadPart - sizeof(INFO2_HEADER)) / Info2Header->RecordSize;
while (nRecords != 0)
{
/* if the first character of the AnsiFileName is zero, the record
is considered deleted */
if (Info2->AnsiFileName[0] != '\0')
{
_tprintf(_T(" [%d] Record: #%d \"%lS\"\n"),
++i,
Info2->RecordNumber,
Info2->FileName);
DiskFileNameFromRecord(szFile,
Info2->RecordNumber,
(WCHAR)Info2->DriveLetter + L'a',
Info2->FileName);
_tprintf(_T(" Name on disk: \"%s\"\n"),
szFile);
_tprintf(_T(" Deleted size on disk: %d KB\n"),
Info2->DeletedPhysicalSize / 1024);
}
nRecords--;
Info2++;
}
break;
}
default:
break;
}
UnmapAndCloseDatabase(hDb,
pDbBase);
}
return FALSE;
}
static BOOL
SelectRecycleBin(IN LPWSTR szDrive)
{
BOOL Ret;
PRECYCLE_BIN RecycleBinsList = NULL;
Ret = LocateRecycleBins(szDrive,
&RecycleBinsList);
if (Ret)
{
if (RecycleBinsList->Next != NULL)
{
PRECYCLE_BIN CurrentBin = RecycleBinsList;
int n = 0, i = 0;
/* if there are multiple recycle bins ask the user which one to dump */
_tprintf(_T("There are several recycle bins on this drive. Select one:\n"));
while (CurrentBin != NULL)
{
_tprintf(_T(" [%d] %lS\n"),
++i,
CurrentBin->User);
CurrentBin = CurrentBin->Next;
n++;
}
_tprintf(_T("Enter the number: "));
DisplayPrompt:
_tscanf(_T("%d"),
&i);
if (i > n || i < 1)
{
_tprintf(_T("Please enter a number between 1 and %d: "),
n);
goto DisplayPrompt;
}
/* walk to the selected recycle bin */
CurrentBin = RecycleBinsList;
while (CurrentBin != NULL && i != 1)
{
CurrentBin = CurrentBin->Next;
i--;
}
/* dump it */
Ret = DumpRecycleBin(CurrentBin);
}
else
{
/* dump the first (and only) recycle bin */
Ret = DumpRecycleBin(RecycleBinsList);
}
}
else
{
_ftprintf(stderr,
_T("No recycle bins on this volume!\n"));
}
FreeRecycleBinsList(&RecycleBinsList);
return Ret;
}
static VOID
PrintHelp(VOID)
{
_ftprintf(stderr,
_T("Usage: dumprecbin C:\n"));
}
int
main(int argc,
char *argv[])
{
if (argc != 2 ||
strlen(argv[1]) != 2 || argv[1][1] != ':' ||
toupper(argv[1][0]) < 'A' || toupper(argv[1][0]) > 'Z')
{
PrintHelp();
return 1;
}
else
{
WCHAR szDrive[3];
_stprintf(szDrive,
_T("%lC:"),
argv[1][0]);
if (!SelectRecycleBin(szDrive))
return 1;
else
return 0;
}
}

View file

@ -0,0 +1,13 @@
<module name="dumprecbin" type="win32cui" installbase="bin" installname="dumprecbin.exe">
<include base="dumprecbin">.</include>
<define name="__USE_W32API" />
<define name="UNICODE" />
<define name="_UNICODE" />
<define name="_WIN32_IE">0x0500</define>
<define name="_WIN32_WINNT">0x0600</define>
<define name="WINVER">0x0600</define>
<library>advapi32</library>
<library>kernel32</library>
<library>ntdll</library>
<file>dumprecbin.c</file>
</module>