Shredder-Files-Folders
An example of the irrevocable deletion of files and entire folders.
- Active · last change 7-19-2026
- ★ 2 on GitHub
README
:computer: Shredder-Files-Folders:


A file shredder is a software tool that securely deletes files and folders by overwriting their data multiple times, making them unrecoverable by standard file recovery tools. This process protects sensitive information, such as financial or personal documents, by ensuring that deleted content is permanently erased from the hard drive rather than just removed from the file table. Many file shredders allow users to select specific files, folders, or even entire hard drives for deletion, often with features like drag-and-drop functionality and the ability to schedule shredding tasks.
:wrench: Features:
- Shredder File
- Shredder entire Folder, with all subfolders
- Batch Shredder
- Manipulate Attributes
- Overwrite Directory Seperators
- Overwrite Block Mode
- Overwrite Bit Mode
- Overwrite Bit random Mode
:wrench: Shredder Modes
Block Overwriting
When deleting a file, the pointer is removed without any changes to the blocks. But the list of blocks are marked free to be used whenever data is written. Considering no overwritten data. In this case, the block sectors are simply overwritten with a zero. Consider a fragmented filesystem like ext4 on a 512 Byte physical sector sized disk. File Recovery is pretty much useless when a deleted file's list of blocks cannot be known. If such thing is possible maybe it gives the disk little more life.
In this case, the optimal buffer size is 4096 kb.
Bits Overwriting
This function identifies all the bits of a file and replaces each one with binary data. This is significantly more thorough than a simple process, but consequently takes more time for the hard drive.
Random Overwriting
This function uses random number generators (often cryptographic random numbers) to fill the exact block on the hard drive with an insignificant sequence of zeros and ones. This takes place block by block; the program collects the blocks individually and overwrites them. The buffer size for this operation can be set separately.
⚙️: Functions
Overwriting:
Unlike simple deletion, which only removes file references, a file shredder replaces the file's data with random bits of information.
Multiple Passes:
Advanced file shredders use government-approved algorithms, such as the US Dod 5220.22-M method, to overwrite the data multiple times, further increasing security.
Data Recovery Prevention:
By thoroughly overwriting the data, file shredders prevent professional data recovery software from restoring the original content.
Secure Deletion:
The primary function is to permanently delete files, ensuring sensitive data cannot be recovered.
Batch Processing:
Many tools allow users to shred multiple files or folders at once.
Scheduling:
Some file shredders enable users to schedule tasks to automatically delete files at specific times or intervals.
Free Disk Space Cleaning:
Some programs offer features to securely clean free disk space, which may contain remnants of previously deleted but un-shredded files.
Support for Various Devices:
File shredders can often delete data from hard drives, SSDs, USB drives, and other storage media.
Restoring deleted data is usually possible, since deleting a file simply records in the file system that the corresponding data area is now free. However, the data itself remains physically on the hard drive until the area is overwritten with new data.
Once deleted, files can, under certain circumstances, be recovered using special programs. In addition, specialized data recovery and IT forensics companies offer their services to recover supposedly lost files.
So-called erasers are designed to "securely delete" files, preventing deleted data from being recovered through specialized interventions. Preventing such recovery requires overwriting the area on the data storage device freed up by the previous deletion. How often and in what form the relevant areas must be overwritten is highly controversial.
Original File:
After Shredder:
Directory Seperator:
In computing, a directory structure is the way an operating system arranges files that are accessible to the user. Files are typically displayed in a hierarchical tree structure.
A filename is a string used to uniquely identify a file stored on this structure. Before the advent of 32-bit operating systems, file names were typically limited to short names (6 to 14 characters in size). Modern operating systems now typically allow much longer filenames (more than 250 characters per pathname element).
File Attributes:
File attributes are a type of metadata that describe and may modify how files and/or directories in a filesystem behave. Typical file attributes may, for example, indicate or specify whether a file is visible, modifiable, compressed, or encrypted. The availability of most file attributes depends on support by the underlying filesystem (such as FAT, NTFS, ext4) where attribute data must be stored along with other control structures. Each attribute can have one of two states: set and cleared. Attributes are considered distinct from other metadata, such as dates and times, filename extensions or file system permissions. In addition to files, folders, volumes and other file system objects may have attributes.
Common File Attributes and Their Functions:
| Attribute | Description |
|---|---|
| Read-only (R): | Prevents a file from being modified or deleted. |
| Hidden (H): | Hides the file from standard views in File Explorer, making it invisible during normal searches. |
| System (S): | Marks the file as a crucial operating system file, often hidden by default to prevent accidental changes. |
| Archive (A): | A flag used by backup systems to indicate that the file has been created or modified and may need to be backed up. |
| Normal (N): | Indicates the file has no special attributes. |
| Compressed (C): | Saves disk space by compressing the file. |
| Encrypted (E): | Secures the file's contents to prevent unauthorized access. |
| Directory (D): | A flag specific to folders. |
The attrib command allows you to view and change attributes. For example,
attrib +h <filename>
hides a file, and
attrib -h <filename>
unhides it.
Folder Access Rights
Request folder permissions if they are not already present. Files with specific attributes in folders that require permissions may be rejected for deletion. To prevent this, specific rights can be granted to the folder beforehand to allow shredding.
To grant these rights, the string "Jeder" in the next code section must be renamed to the respective system language, e.g., the term "Anyone" for English.
if CheckBox3.Checked = true then
begin
AddAccessRights(PWideChar(Dir), 'Jeder', $FFFFFFFF);
end;function AddAccessRights(lpszFileName : PChar; lpszAccountName : PChar;
dwAccessMask : DWORD) : boolean;
const
HEAP_ZERO_MEMORY = $00000008;
ACL_REVISION = 2;
ACL_REVISION2 = 2;
INHERITED_ACE = $10;
type
ACE_HEADER = Record
AceType,
AceFlags : BYTE;
AceSize : WORD;
end;
PACE_HEADER = ^ACE_HEADER;
ACCESS_ALLOWED_ACE = Record
Header : ACE_HEADER;
Mask : ACCESS_MASK;
SidStart : DWORD;
end;
PACCESS_ALLOWED_ACE = ^ACCESS_ALLOWED_ACE;
ACL_SIZE_INFORMATION = Record
AceCount,
AclBytesInUse,
AclBytesFree : DWORD;
end;
SetSecurityDescriptorControlFnPtr = function (pSecurityDescriptor : PSecurityDescriptor;
ControlBitsOfInterest : SECURITY_DESCRIPTOR_CONTROL;
ControlBitsToSet : SECURITY_DESCRIPTOR_CONTROL) : boolean; stdcall;
var
// SID variables.
snuType : SID_NAME_USE;
szDomain : PChar;
cbDomain : DWORD;
pUserSID : Pointer;
cbUserSID : DWORD;
// File SD variables.
pFileSD : PSecurityDescriptor;
cbFileSD : DWORD;
// New SD variables.
newSD : TSecurityDescriptor;
// ACL variables.
ptrACL : PACL;
fDaclPresent,
fDaclDefaulted : BOOL;
AclInfo : ACL_SIZE_INFORMATION;
// New ACL variables.
pNewACL : PACL;
cbNewACL : DWORD;
// Temporary ACE.
pTempAce : Pointer;
CurrentAceIndex,
newAceIndex : UINT;
// Assume function will fail.
fResult,
fAPISuccess : boolean;
secInfo : SECURITY_INFORMATION;
// New APIs available only in Windows 2000 and above for setting
// SD control
_SetSecurityDescriptorControl : SetSecurityDescriptorControlFnPtr;
controlBitsOfInterest,
controlBitsToSet,
oldControlBits : SECURITY_DESCRIPTOR_CONTROL;
dwRevision : DWORD;
AceFlags : BYTE;
function myheapalloc(x : integer) : Pointer;
begin
Result := HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, x);
end;
function myheapfree(x : Pointer) : boolean;
begin
Result := HeapFree(GetProcessHeap(), 0, x);
end;
function SetFileSecurityRecursive(lpFileName: PChar; SecurityInformation: SECURITY_INFORMATION;
pSecurityDescriptor: PSecurityDescriptor): BOOL;
var
sr : TSearchRec;
begin
Result := SetFileSecurity(lpFileName, SecurityInformation, pSecurityDescriptor);
if Not Result then
Exit;
if (FileGetAttr(lpFileName) AND faDirectory) = faDirectory then
begin
// Rekursion beginnt
if FindFirst(IncludeTrailingPathDelimiter(lpFileName) + '*', $EFFF, sr) = 0 then
begin
Repeat
// msp 07.10.2004
// if ((sr.Attr and faDirectory) = faDirectory) AND (sr.Name <> '.') AND (sr.Name <> '..') then
if (sr.Name <> '.') AND (sr.Name <> '..') then
SetFileSecurityRecursive(PChar(IncludeTrailingPathDelimiter(lpFileName) + sr.Name),
SecurityInformation, pSecurityDescriptor);
until FindNext(sr) <> 0;
FindClose(sr);
end;
end;
end;
begin
// Init
szDomain := nil;
cbDomain := 0;
pUserSID := nil;
cbUserSID := 0;
// File SD variables.
pFileSD := nil;
cbFileSD := 0;
// ACL variables.
ptrACL := nil;
// New ACL variables.
pNewACL := nil;
cbNewACL := 0;
// Temporary ACE.
pTempAce := nil;
CurrentAceIndex := 0;
newAceIndex := 0;
// Assume function will fail.
fResult := FALSE;
secInfo := DACL_SECURITY_INFORMATION;
// New APIs available only in Windows 2000 and above for setting
// SD control
_SetSecurityDescriptorControl := nil;
// Delphi-Result
Result := FALSE;
try
//
// STEP 1: Get SID of the account name specified.
//
fAPISuccess := LookupAccountName(nil, lpszAccountName,
pUserSID, cbUserSID, szDomain, cbDomain, snuType);
// API should have failed with insufficient buffer.
if (Not fAPISuccess) AND (GetLastError() <> ERROR_INSUFFICIENT_BUFFER) then
raise Exception.Create('LookupAccountName Error=' + IntToStr(GetLastError()));
pUserSID := myheapalloc(cbUserSID);
if pUserSID = nil then
raise Exception.Create('myheapalloc Error=' + IntToStr(GetLastError()));
szDomain := PChar(myheapalloc(cbDomain * sizeof(PChar)));
if szDomain = nil then
raise Exception.Create('myheapalloc Error=' + IntToStr(GetLastError()));
fAPISuccess := LookupAccountName(nil, lpszAccountName,
pUserSID, cbUserSID, szDomain, cbDomain, snuType);
if Not fAPISuccess then
raise Exception.Create('LookupAccountName Error=' + IntToStr(GetLastError()));
//
// STEP 2: Get security descriptor (SD) of the file specified.
//
fAPISuccess := GetFileSecurity(lpszFileName,
secInfo, pFileSD, 0, cbFileSD);
// API should have failed with insufficient buffer.
if (Not fAPISuccess) AND (GetLastError() <> ERROR_INSUFFICIENT_BUFFER) then
raise Exception.Create('GetFileSecurity Error=' + IntToStr(GetLastError()));
pFileSD := myheapalloc(cbFileSD);
if pFileSD = nil then
raise Exception.Create('myheapalloc Error=' + IntToStr(GetLastError()));
fAPISuccess := GetFileSecurity(lpszFileName,
secInfo, pFileSD, cbFileSD, cbFileSD);
if Not fAPISuccess then
raise Exception.Create('GetFileSecurity Error=' + IntToStr(GetLastError()));
//
// STEP 3: Initialize new SD.
//
if Not InitializeSecurityDescriptor(@newSD,
SECURITY_DESCRIPTOR_REVISION) then
raise Exception.Create('InitializeSecurityDescriptor Error=' + IntToStr(GetLastError()));
//
// STEP 4: Get DACL from the old SD.
//
if Not GetSecurityDescriptorDacl(pFileSD, fDaclPresent, ptrACL,
fDaclDefaulted) then
raise Exception.Create('GetSecurityDescriptorDacl Error=' + IntToStr(GetLastError()));
//
// STEP 5: Get size information for DACL.
//
AclInfo.AceCount := 0; // Assume NULL DACL.
AclInfo.AclBytesFree := 0;
AclInfo.AclBytesInUse := sizeof(ACL);
if ptrACL = nil then
fDaclPresent := FALSE;
// If not NULL DACL, gather size information from DACL.
if Not fDaclPresent then
if Not GetAclInformation(ptrACL^, @AclInfo,
sizeof(ACL_SIZE_INFORMATION), AclSizeInformation) then
raise Exception.Create('GetAclInformation ' + IntToStr(GetLastError()));
//
// STEP 6: Compute size needed for the new ACL.
//
cbNewACL := AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE)
+ GetLengthSid(pUserSID) - sizeof(DWORD);
//
// STEP 7: Allocate memory for new ACL.
//
pNewACL := PACL(myheapalloc(cbNewACL));
if pNewACL = nil then
raise Exception.Create('myheapalloc ' + IntToStr(GetLastError()));
//
// STEP 8: Initialize the new ACL.
//
if Not InitializeAcl(pNewACL^, cbNewACL, ACL_REVISION2) then
raise Exception.Create('InitializeAcl ' + IntToStr(GetLastError()));
//
// STEP 9 If DACL is present, copy all the ACEs from the old DACL
// to the new DACL.
//
// The following code assumes that the old DACL is
// already in Windows 2000 preferred order. To conform
// to the new Windows 2000 preferred order, first we will
// copy all non-inherited ACEs from the old DACL to the
// new DACL, irrespective of the ACE type.
//
newAceIndex := 0;
if (fDaclPresent) AND (AclInfo.AceCount > 0) then
begin
for CurrentAceIndex := 0 to AclInfo.AceCount - 1 do
begin
//
// STEP 10: Get an ACE.
//
if Not GetAce(ptrACL^, CurrentAceIndex, pTempAce) then
raise Exception.Create('GetAce ' + IntToStr(GetLastError()));
//
// STEP 11: Check if it is a non-inherited ACE.
// If it is an inherited ACE, break from the loop so
// that the new access allowed non-inherited ACE can
// be added in the correct position, immediately after
// all non-inherited ACEs.
//
if PACCESS_ALLOWED_ACE(pTempAce)^.Header.AceFlags AND INHERITED_ACE > 0 then
break;
//
// STEP 12: Skip adding the ACE, if the SID matches
// with the account specified, as we are going to
// add an access allowed ACE with a different access
// mask.
//
if EqualSid(pUserSID, @(PACCESS_ALLOWED_ACE(pTempAce)^.SidStart)) then
continue;
//
// STEP 13: Add the ACE to the new ACL.
//
if Not AddAce(pNewACL^, ACL_REVISION, MAXDWORD, pTempAce,
PACE_HEADER(pTempAce)^.AceSize) then
raise Exception.Create('AddAce ' + IntToStr(GetLastError()));
Inc(newAceIndex);
end;
end;
//
// STEP 14: Add the access-allowed ACE to the new DACL.
// The new ACE added here will be in the correct position,
// immediately after all existing non-inherited ACEs.
//
AceFlags := $1 (* OBJECT_INHERIT_ACE *)
OR $2 (* CONTAINER_INHERIT_ACE *)
OR $10 (* INHERITED_ACE*);
if Not AddAccessAllowedAceEx(pNewACL^, ACL_REVISION2, AceFlags, dwAccessMask,
pUserSID) then
raise Exception.Create('AddAccessAllowedAce ' + IntToStr(GetLastError()));
//
// STEP 15: To conform to the new Windows 2000 preferred order,
// we will now copy the rest of inherited ACEs from the
// old DACL to the new DACL.
//
if (fDaclPresent) AND (AclInfo.AceCount > 0) then
begin
while CurrentAceIndex < AclInfo.AceCount do
begin
//
// STEP 16: Get an ACE.
//
if Not GetAce(ptrACL^, CurrentAceIndex, pTempAce) then
raise Exception.Create('GetAce ' + IntToStr(GetLastError()));
//
// STEP 17: Add the ACE to the new ACL.
//
if Not AddAce(pNewACL^, ACL_REVISION, MAXDWORD, pTempAce,
PACE_HEADER(pTempAce)^.AceSize) then
raise Exception.Create('AddAce ' + IntToStr(GetLastError()));
end;
Inc(CurrentAceIndex);
end;
//
// STEP 18: Set the new DACL to the new SD.
//
if Not SetSecurityDescriptorDacl(@newSD, TRUE, pNewACL, FALSE) then
raise Exception.Create('SetSecurityDescriptorDacl ' + IntToStr(GetLastError()));
//
// STEP 19: Copy the old security descriptor control flags
// regarding DACL automatic inheritance for Windows 2000 or
// later where SetSecurityDescriptorControl() API is available
// in advapi32.dll.
//
_SetSecurityDescriptorControl := SetSecurityDescriptorControlFnPtr(
GetProcAddress(GetModuleHandle('advapi32.dll'),
'SetSecurityDescriptorControl'));
if @_SetSecurityDescriptorControl <> nil then
begin
controlBitsOfInterest := 0;
controlBitsToSet := 0;
oldControlBits := 0;
dwRevision := 0;
if Not GetSecurityDescriptorControl(pFileSD, oldControlBits,
dwRevision) then
raise Exception.Create('GetSecurityDescriptorControl ' + IntToStr(GetLastError()));
if (oldControlBits AND SE_DACL_AUTO_INHERITED) <> 0 then
begin
controlBitsOfInterest := SE_DACL_AUTO_INHERIT_REQ OR SE_DACL_AUTO_INHERITED;
controlBitsToSet := controlBitsOfInterest;
end
else if (oldControlBits AND SE_DACL_PROTECTED) <> 0 then
begin
controlBitsOfInterest := SE_DACL_PROTECTED;
controlBitsToSet := controlBitsOfInterest;
end;
if controlBitsOfInterest <> 0 then
if Not _SetSecurityDescriptorControl(@newSD, controlBitsOfInterest, controlBitsToSet) then
raise Exception.Create('SetSecurityDescriptorControl ' + IntToStr(GetLastError()));
end;
//
// STEP 20: Set the new SD to the File.
//
// msp 07.09.2004: Set to all objects including subdirectories
// if Not SetFileSecurity(lpszFileName, secInfo, @newSD) then
if Not SetFileSecurityRecursive(lpszFileName, secInfo, @newSD) then
raise Exception.Create('SetFileSecurity ' + IntToStr(GetLastError()));
except
on E: Exception do
begin
MessageDlg(E.Message, mtError, [mbAbort], -1);
// WriteLog(ltError, Format('AddAccessRights: Beim Ändern der Rechte auf dem Verzeichnis ''%s'' für ''%s'' ist ein Fehler aufgetreten. %s', [lpszFileName, lpszAccountName, E.Message]), []);
Exit;
end;
end;
//
// STEP 21: Free allocated memory
//
if pUserSID <> nil then
myheapfree(pUserSID);
if szDomain <> nil then
myheapfree(szDomain);
if pFileSD <> nil then
myheapfree(pFileSD);
if pNewACL <> nil then
myheapfree(pNewACL);
fResult := TRUE;
end;
