Link to home
Create AccountLog in
Microsoft Development

Microsoft Development

--

Questions

--

Followers

Top Experts

Avatar of Lubster
Lubster

RE-Storing a NTFS Security Descriptor in C
Hello all, I'm trying to create a utility which exports a file's security descriptor, and re-assign it on demand.

I've created a test sample, which uses GetSecurityInfo() with the DACL flag, and then try to re-assign the very same DACL with SetSecurityInfo().

Before applying SetSecurityInfo(), the descriptor's 'Control' is: 0xA004 , SE_DACL_PRESENT , SE_SACL_PROTECTED , SE_SELF_RELATIVE.

After applying SetSecurityInfo(), the descriptor's 'Control' is: 0x8404 , SE_DACL_AUTO_INHERITED , SE_DACL_PRESENT , SE_SELF_RELATIVE.

Is there a way to store a file descriptor's state, and re-store it IDENTICALLY?

Here is the sample:
#define _WIN32_WINNT 0x0501 
#define WINVER 0x0501 
 
#include <stdio.h> 
#include <windows.h> 
#include "accctrl.h" 
#include "aclapi.h" 
#include "sddl.h" 
 
int main (void) 
{ 
  PSECURITY_DESCRIPTOR PSecurityD; 
  HANDLE hFile; 
  PACL dacl; 
  int ret = 0; 
  DWORD lasterror; 
 
  hFile = CreateFile("test.txt", READ_CONTROL | ACCESS_SYSTEM_SECURITY ,  
          FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 
 
  if (hFile == INVALID_HANDLE_VALUE) { 
    fprintf(stderr,"CreateFile() failed. Error: INVALID_HANDLE_VALUE\n"); 
    return 1; 
  } 
 
  lasterror = GetSecurityInfo(hFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, 
                              NULL, NULL, &dacl, NULL, &PSecurityD); 
 
  if (lasterror != ERROR_SUCCESS) { 
    fprintf(stderr,"GetSecurityInfo() failed. Error: %lu;\n", lasterror); 
    ret = 1; 
    goto ret1; 
  } 
 
  CloseHandle(hFile); 
 
  hFile = CreateFile("test.txt",READ_CONTROL | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY ,  
                     0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); 
 
  if (hFile == INVALID_HANDLE_VALUE) { 
    fprintf(stderr,"CreateFile() failed. Error: INVALID_HANDLE_VALUE\n"); 
    ret = 2; 
    goto ret2;; 
  } 
 
  lasterror = SetSecurityInfo(hFile, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION , NULL, NULL, dacl, NULL); 
 
  if (lasterror != ERROR_SUCCESS) { 
    fprintf(stderr,"SetSecurityInfo() failed. Last error: %lu;\n", lasterror); 
    ret = 2; 
    goto ret2; 
  } 
 
  CloseHandle(hFile); 
  ret2: 
  free(dacl); 
  free(PSecurityD); 
  ret1: 
  return ret; 
}

Open in new window

Zero AI Policy

We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.


Avatar of bbaobbao🇦🇺

are the SECURITY_INFORMATION bit flags same as those for ControlFlags as per the class definition below?

class Win32_SecurityDescriptor : __SecurityDescriptor
{
  uint32        ControlFlags;
  Win32_ACE     DACL[];
  Win32_Trustee Group;
  Win32_Trustee Owner;
  Win32_ACE     SACL[];
};

do you mean that ONLY a few bits of ControlFlags are changed after re-applying the same SecurityInfo, and no ANY other changes??

Avatar of LubsterLubster

ASKER

Very appreciate your response, bbao.

Here are the steps in greater detail:

1. I create a text file using notepad.exe - "test.txt".
2. I extract "test.txt"'s security descriptor in its native state, writing it to output1.bin.
3. I try to re-write "test.txt"'s security descriptor (DACL) using SetSecurityInfo() (the code sample I originally posted).
4. I extract "test.txt"'s security descriptor in its native state, writing it to output2.bin.

5. I start comparing output1.bin and output2.bin, according to: http://msdn.microsoft.com/en-us/library/cc230366%28PROT.10%29.aspx
The first byte is REVISION, which is set to 0x01 for both.
The second byte is RM CONTROL (resource manager), which is set 0x00 for both.
The following two bytes are CONTROL, "04 A0" for output1.bin - while "04 84" for output2.bin

All the rest of the bytes are identical.

If I repeat this flow again, while re-using the current "test.txt" - output1.bin and output2.bin will be identical.

I hope I clarified it for you, if you need further details be my guest - I'm lost in the dark.

Note: I added another sample of code which I used to extract the descriptor to a file, for the purpose of comparing them.

Lubster
#define _WIN32_WINNT 0x0501
#define WINVER 0x0501

#include <stdio.h>
#include <windows.h>
#include "accctrl.h"
#include "aclapi.h"
#include "sddl.h"

int main (void)
{
  DWORD lasterror;
  PSECURITY_DESCRIPTOR PSecurityD;
  HANDLE hFile;

  PSID owner;
  PSID group;
  PACL dacl;
  PACL sacl;

  HANDLE hFile2;
  DWORD wrote = 0;
  int ret = 0;

  HANDLE	ProcessHandle =	NULL;
  TOKEN_PRIVILEGES TPrivileges;
  LUID luid;

  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &ProcessHandle)) {
    printf("OpenProcessToken failed.\n");
    return 1;
  }

  if (LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &luid) == 0) {
    lasterror = GetLastError();
    printf("LookupPrivilegeValue() error: %lu\n", lasterror); 
    return 1;
  }

  TPrivileges.PrivilegeCount = 1;
  TPrivileges.Privileges[0].Luid = luid;
  TPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

  if (AdjustTokenPrivileges(ProcessHandle, FALSE, &TPrivileges, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL) == 0) { 
    lasterror = GetLastError();
    printf("AdjustTokenPrivileges() error: %lu\n", lasterror); 
    return 1;
  } 

  lasterror = GetLastError();
  if (lasterror == ERROR_NOT_ALL_ASSIGNED) {
    printf("AdjustTokenPrivilege() failed.\n");
    return 1;
  }

  CloseHandle(ProcessHandle);

  /* --- */

  hFile = CreateFile("test.txt", READ_CONTROL | ACCESS_SYSTEM_SECURITY , 
          FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

  if (hFile == INVALID_HANDLE_VALUE) {
    fprintf(stderr,"CreateFile(test.txt) failed. Error: INVALID_HANDLE_VALUE\n");
    return 1;
  }

  lasterror = GetSecurityInfo(hFile, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | 
                              GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION ,
                              &owner, &group, &dacl, &sacl, &PSecurityD);

  if (lasterror != ERROR_SUCCESS) {
    fprintf(stderr,"GetSecurityInfo() failed. Error: %lu;\n", lasterror);
    ret = 1;
    goto ret1;
  }

  /* --- */

  hFile2 = CreateFile("output.bin", GENERIC_WRITE , 
          0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

  if (hFile == INVALID_HANDLE_VALUE) {
    fprintf(stderr,"CreateFile(output.bin) hFile2 failed. Error: INVALID_HANDLE_VALUE\n");
    return 1;
  }

  lasterror = WriteFile(hFile2, PSecurityD, GetSecurityDescriptorLength(PSecurityD), &wrote, NULL);
  if (wrote != GetSecurityDescriptorLength(PSecurityD)) {
    fprintf(stderr,"WriteFile() error");
    return 1;
  }

  CloseHandle(hFile2);

  free(owner);
  free(group);
  free(dacl);
  free(sacl);
  free(PSecurityD);
  ret1:
  CloseHandle(hFile);
  return ret;
}

Open in new window


Avatar of LubsterLubster

ASKER

@evilrix: Appreciated.

Lubster

Reward 1Reward 2Reward 3Reward 4Reward 5Reward 6

EARN REWARDS FOR ASKING, ANSWERING, AND MORE.

Earn free swag for participating on the platform.


Avatar of LubsterLubster

ASKER

@ModernMatt: Thank you.

You should be able to use BackupRead, BackupWrite. BTW: Did you enable SeSecurityPrivilege? I also had problems with a similiar approach when using only READ_CONTROL , I had to change my access mode to GENERIC_READ to get it to work in my case.
 

Avatar of LubsterLubster

ASKER

Hello egl1044,
From the little I understand of BackupRead() - It's "All In Wonder" kind of method, which don't seem to answer my need, the ability of extracting and restoring a security descriptor of a file, while not extracting the content of the file, nor any properties which are not security descriptor related.

Regards Privileges, I tried SE_RESTORE_NAME , SE_BACKUP_NAME , SE_SECURITY_NAME. (Combined, apart, and mixed)

While CreateFile()'s desired access, I pretty much followed the MSDN logic regards RESTORE and BACKUP (respectfully). The only adjustment I had to add while restoring, was adding "READ_CONTROL".

Tried your "GENERIC_READ" suggestion - it seems like it didn't affect the results.

I think this whole mess, comes down to one note over SECURITY_DESCRIPTOR data type:
http://msdn.microsoft.com/en-us/library/aa379573%28VS.85%29.aspx

"Some SECURITY_INFORMATION members work only with the SetNamedSecurityInfo function. These members are not returned in the structure returned by other security functions such as GetNamedSecurityInfo or ConvertStringSecurityDescriptorToSecurityDescriptor."

Is this for real?
"Some" members?
Which members? Why? How?

"These members are not returned by other functions"?
Which functions? Why? How?

Needless to say, this shows up ONLY at the data type. I -can not- find such suggestion on ANY of the related functions.

Any MSDN decryptors capable of decrypting this Remark?

(Maybe it wasn't my fault after all...)
Lubster

Free T-shirt

Get a FREE t-shirt when you ask your first question.

We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.


Avatar of LubsterLubster

ASKER

Well guys, after my few weeks quest, I think it's time to meet reality - the requirement is impossible to achieve using the native API functions.

Very appreciate everybody's effort to assist. If you believe my conclusion is false - feel free to correct me.

Lubster

Avatar of LubsterLubster

ASKER

evilrix, I can't thank you enough :)

Lubster

Avatar of evilrixevilrix🇬🇧

No worries... I just wish I could have done more to help you.

Reward 1Reward 2Reward 3Reward 4Reward 5Reward 6

EARN REWARDS FOR ASKING, ANSWERING, AND MORE.

Earn free swag for participating on the platform.


Forgive me for my late reply.
<< From the little I understand of BackupRead() - It's "All In Wonder" kind of method >>
Not exactly you can enumerate or seek to any of the streams to read/restore the information. If your only interested in security information seek to the BACKUP_SECURITY_DATA stream this allows you to read or restore the security information in the file you can ignore all other streams. The real data begins at BACKUP_DATA this is the real contents of the file which can be ignored along with the other streams.
Whats your output if you add LABEL_SECURITY_INFORMATION bit to GetSecurityInfo?

Avatar of LubsterLubster

ASKER

@egl1044: This is a true situation where "Better late than never" applies beautifully.

Thanks for correcting my premature assumption of BackupRead(). If your description of it's flexibility is accurate, then I suppose it suits my needs.

Unfortunately, I can't seem to find any such sample, nor can I understand how to achieve said behavior using the documentation.

The best I managed to achieve, is the attached code sample, of a basic operation for using BackupRead() and writing its buffer to a file. But I can't understand how to specify "Security Descriptor" only?

Regards your flag of GetSecurityinfo() - it actually doesn't matter, due to the fact that GetSecurityInfo() extracts the identical descriptor. The issue is actually with the SetSecurityInfo() , which actually TOGGLES the flags, therefore it can't set its default state. The default state is 0 for auto-inherit, and 0 for protected. while SetSecurityInfo() may set (1=auto-inherit AND 0=protected) , or  (0=auto-inherit AND 1=protected), but it can't set 0 for both (no such flag).

Lubster
#define _WIN32_WINNT 0x0501
#define WINVER 0x0501

#include <stdio.h>
#include <windows.h>

int SetPriv(HANDLE *ProcessHandle, LPCTSTR priv);

int SetPriv(HANDLE *ProcessHandle, LPCTSTR priv)
{
	TOKEN_PRIVILEGES TPrivileges;
	LUID luid;
  DWORD lasterror;

	if (LookupPrivilegeValue(NULL, priv, &luid) == 0) {
		lasterror = GetLastError();
		printf("Error: LookupPrivilegeValue() - %lu.\n", lasterror); 
    return 1;
  }

  TPrivileges.PrivilegeCount = 1;
  TPrivileges.Privileges[0].Luid = luid;
  TPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	if (AdjustTokenPrivileges(ProcessHandle, FALSE, &TPrivileges, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL) == 0) { 
    lasterror = GetLastError();
    printf("Error: AdjustTokenPrivileges() - %lu.\n", lasterror); 
    return 1;
  } 

  lasterror = GetLastError();
  if (lasterror == ERROR_NOT_ALL_ASSIGNED)	{
    printf("Error: AdjustTokenPrivilege().\n");
    return 1;
  }
  
  return 0;
}

int main (void)
{
  HANDLE hFile1;
  HANDLE hFile2;

  /* --- */
  HANDLE	ProcessHandle =	NULL;

  if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &ProcessHandle)) {
		printf("OpenProcessToken failed.\n");
	  return 1;
	}

  if (SetPriv(ProcessHandle, SE_RESTORE_NAME) != 0) return 1;
  if (SetPriv(ProcessHandle, SE_BACKUP_NAME) != 0) return 1;
  if (SetPriv(ProcessHandle, SE_SECURITY_NAME) != 0) return 1;

  CloseHandle(ProcessHandle);
  /* --- */

  hFile1 = CreateFile("test1.txt", GENERIC_READ | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY , 
                      0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

  if (hFile1 == INVALID_HANDLE_VALUE) {
    fprintf(stderr,"Error: CreateFile() - INVALID_HANDLE_VALUE.\n");
    return 1;
  }

  hFile2 = CreateFile("test2.txt", GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY , 
                     0, NULL, OPEN_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, NULL);

  if (hFile2 == INVALID_HANDLE_VALUE) {
    fprintf(stderr,"Error: CreateFile() - INVALID_HANDLE_VALUE.\n");
    return 1;
  }

  unsigned char buf[sizeof(WIN32_STREAM_ID) + 1];
  DWORD bytes_read;
  DWORD bytes_written;
  LPVOID context = NULL;

  while (1) {
    if (!BackupRead(hFile1, buf, sizeof(buf), &bytes_read, FALSE, TRUE, &context ) ) {
      printf("Error: BackupRead() - %d.",GetLastError());
     return 1;
    } 
    if (bytes_read == 0) {
      fprintf(stderr,"bytes_read = 0.\n");
      break;
    }
    if (!WriteFile (hFile2, buf, bytes_read, &bytes_written, NULL)) {
      fprintf(stderr,"Error: WriteFile() - %d.\n", GetLastError());
      return 1;
    }
  }

  CloseHandle(hFile1);
  CloseHandle(hFile2);
  return 0;
}

Open in new window


I had some extra time to play with your example. I replicated it and found that the bytes are indeed different from the original after applying the original to a temporary file and then later extracting the temporary raw bytes they are indeed different but the actual file security is valid. I have a strong theory that SetSecurityInfo() is ignoring specific bits for some reason I don't exactly know but I am leaning towards the following flags  SE_DACLAUTO_INHERIT_REQ, SE_SACL_AUTO_INHERIT_REQ.
I haven't been able to get the same bytes as the original but the security information is still identical and works as indeded.
Yes examples of BackupRead and BackupWrite are extremely rare and the documentation isn't the greatest. I have posted alot of information about each of them on MSDN section. I was under the impression you could implement these since you might want to end up backing up the file but if you don't want to use them it's not required.
There is an example here enumerating the streams
http://msdn.microsoft.com/en-us/magazine/cc163677.aspx

Free T-shirt

Get a FREE t-shirt when you ask your first question.

We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.


Avatar of LubsterLubster

ASKER

egl1044: For starters, let me clarify my conclusion regards the original difficulty. GetSecurifyInfo() extracts a whole raw security descriptor. While SetSecurityInfo() only assigns it's components.

From SetSecurityInfo() POV, the Control bits can be assigned passively - using it's flags.
If I want the Control to be true for X, I need to assign Y (a flag which implies setting a X).
X: SE_DACL_PRESENT, Y: DACL_SECURITY_INFORMATION.
X: SE_DACL_AUTO_INHERITED, Y: UNPROTECTED_DACL_SECURITY_INFORMATION.
X: SE_DACL_PROTECTED, Y: PROTECTED_DACL_SECURITY_INFORMATION.

X represents the actual desired Control, while Y represents the needed flag to achieve it.

Now you see the contradiction? this method of setting the Control bits, by design, won't allow me to set the default state of 0:0. It may allow me to set it either 1:0 , or 0:1.

There is no flag for 0:0. There is no flag which says: "Default state".
It's either it's AUTO_INHERITED, or it's PROTECTED (while the default state, is neither).

(I'm still trying my best to understand your last reference of BackupSeek)
Lubster

Avatar of LubsterLubster

ASKER

Unfortunately I haven't completely understood your reference,
for the mean while I found this sample:
http://win32.mvps.org/ntfs/dump_ntfs_streams.cpp

From: http://win32.mvps.org/ntfs/streams.html

Isn't your reference suggests that 20 bytes might be aligned to 24 bytes? the fact that he hardcoded '20' might be wrong?

(Should I open a new question regards the 'new' subject?)
Lubster

ASKER CERTIFIED SOLUTION
Avatar of nffvrxqgrcfqvvcnffvrxqgrcfqvvc

Link to home
membership
Log in or create a free account to see answer.
Signing up is free and takes 30 seconds. No credit card required.
Create Account

Avatar of LubsterLubster

ASKER

I will summarize my conclusion regards the original topic for future researchers:
GetSecurityInfo() is able to extract a whole Security Descriptor. Its SecurityInfo flags suggests which components you would like to retrieve.
While SetSecurityInfo() is able to restore said components. Its SecurityInfo flags are actually being used as passive toggles for the Security Descriptor's Control.

If we desire Control X, we need to use a Y flag:
X: SE_DACL_PRESENT, Y: DACL_SECURITY_INFORMATION.
X: SE_DACL_AUTO_INHERITED, Y: UNPROTECTED_DACL_SECURITY_INFORMATION.
X: SE_DACL_PROTECTED, Y: PROTECTED_DACL_SECURITY_INFORMATION.
X: SE_SACL_PRESENT, Y: SACL_SECURITY_INFORMATION.
X: SE_SACL_AUTO_INHERITED, Y: UNPROTECTED_SACL_SECURITY_INFORMATION.
X: SE_SACL_PROTECTED, Y: PROTECTED_SACL_SECURITY_INFORMATION.

It means, that by design, it can not restore an identical state of Control. A default state indicates the AUTO_INHERITED and PROTECTED to be 0. While SetSecurityInfo() have no such flag for setting a 0 - it may only toggle them. (set the requested flag to 1, while the other being set to 0)

egl1044's suggestion of BackupRead() and BackupWrite() seems capable to extract and restore a Security Descriptor's  identical state - I'm moving it to a new Question.

@egl1044: Thanks once again, I'm still on the topic - but I've decided it wouldn't be fair to EE's rules, because this is way off-topic from my initial question, so I will open a follow-up question.

@evilrix, @ModernMatt, @(and the other helpers): I can't thank you enough for your effort. I guess my requirement using the native API functions was indeed impossible by design, while egl1044's suggestion indeed answers my needs.

I will open a new Question for the new topic.
You guys are the best! :)

Lubster

Reward 1Reward 2Reward 3Reward 4Reward 5Reward 6

EARN REWARDS FOR ASKING, ANSWERING, AND MORE.

Earn free swag for participating on the platform.


I haven't encountered an individual asking a question with your amount of patients and respect on this site for ages. A final thought I want to thank you for being a well respected member of the EE community.

Avatar of evilrixevilrix🇬🇧

Lubster,

I'm so pleased you found a solution. If there is anything else I can do for you just ping me, you can find my e-mail address in my profile.

Best regards,

-Rx.

Avatar of LubsterLubster

ASKER

evilrix: Finding a solution - nice, finding egl1044 - Great!

Thanks for your suggestion, just posted the follow-up question here:
https://www.experts-exchange.com/questions/26206959/Extracting-a-NTFS-Security-Descriptor-using-BackupRead-and-BackupSeek.html

(I hope I selected the relevant zones)
Lubster

Free T-shirt

Get a FREE t-shirt when you ask your first question.

We believe in human intelligence. Our moderation policy strictly prohibits the use of LLM content in our Q&A threads.


Unfortunately I'm not much of C++  guy so I can only give you an example in VB(from my own application tinkered for your needs) if you could understand that code it would help you. I don't know if it's appropriate to posting VB code in C++ zone though.

Avatar of LubsterLubster

ASKER

egl1044: As far as I understand, the functions share the very same behavior. You're more than welcomed to share your VB code - It can't hurt :)

Lubster

I can do that ..  C++ devs might go bonkers if they see VB code in C++ area. :)
Long = DWORD in C, I put alot of comments so that if you have trouble you might be able to make it work in C

Public Type WIN32_STREAM_ID
dwStreamId As Long
dwStreamAttributes As Long
Size As LARGE_INTEGER
dwStreamNameSize As Long
'cStreamName As Byte
End Type

Public Sub CreateFileSecurityCopy()

  Dim header As WIN32_STREAM_ID
  Dim Buff() As Byte '// variable buffer
  Dim hFile1 As Long '// source
  Dim hFile2 As Long '// copy
  Dim dwCtxRead As Long '// read context
  Dim dwCtxWrite As Long '// write context
  Dim dwNumberOfBytesRead As Long
  Dim dwNumberOfBytesWritten As Long
  Dim dwLowBytesSeeked As Long
  Dim dwHighBytesSeeked As Long
  
  '//   The source file.
  hFile1 = CreateFileW(StrPtr("\\?\e:\test.txt"), GENERIC_READ Or ACCESS_SYSTEM_SECURITY, 0, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)
  
  '//   The exact copy of source file with only security data.
  hFile2 = CreateFileW(StrPtr("\\?\e:\test_sec.txt"), GENERIC_WRITE Or WRITE_DAC Or WRITE_OWNER Or ACCESS_SYSTEM_SECURITY, 0, 0, CREATE_ALWAYS, FILE_FLAG_BACKUP_SEMANTICS, 0)
  
  '//   Read WIN32_STREAM_ID (header)
  Do While BackupRead(hFile1, VarPtr(header), 20, dwNumberOfBytesRead, 0, 1, dwCtxRead)
    
    If dwNumberOfBytesRead = 0 Then
      Exit Do ' _leave
    End If
    
    If header.dwStreamNameSize > 0 Then
    '//   Can't directly seek over a stream name using BackupSeek(), you must use BackupRead()
    '//   to move the the file position over the stream name before you can use BackupSeek()
    '//   Buff() is ignored and this call is only used to advance the file pointer position to prepare
    '//   for BackupSeek if the file contains ALTERNATE_DATA_STREAM.
      Call BackupRead(hFile1, VarPtr(Buff(0)), header.dwStreamNameSize, dwNumberOfBytesRead, 0, 1, dwCtxRead)
    End If
    
    If header.dwStreamId = StreamType.SecurityData Then
    '//   We found the SECURITY stream
      ReDim Buff(header.Size.LoPart) As Byte '// allocate buffer
    '//   Read the security data bytes
      Call BackupRead(hFile1, VarPtr(Buff(0)), header.Size.LoPart, dwNumberOfBytesRead, 0, 1, dwCtxRead)
    '//   Write the security data header
      BackupWrite hFile2, VarPtr(header), 20, dwNumberOfBytesWritten, 0, 1, dwCtxWrite
    '//   Write the security data bytes
      BackupWrite hFile2, VarPtr(Buff(0)), header.Size.LoPart, dwNumberOfBytesWritten, 0, 1, dwCtxWrite
    '// Just bail we completed the security copy.
      Exit Do ' _leave
    End If
    '//   If we haven't found the correct stream seek to the next stream header.
    '//   The dwNumberOfBytesRead and dwHighBytesSeeked ignored. Using a large seek
    '//   value will always place the position at the next stream header because you
    '//   can't seek over a stream header or ALTERNATE_DATA_STREAM name.
    BackupSeek hFile1, &HFFFFFFFF, &H7FFFFFFF, dwLowBytesSeeked, dwHighBytesSeeked, dwCtxRead
    
  Loop
  
  '//   CleanUp
  BackupRead hFile1, 0, 0, dwNumberOfBytesRead, 1, 0, dwCtxRead
  BackupWrite hFile2, 0, 0, dwNumberOfBytesWritten, 1, 0, dwCtxWrite
  
  CloseHandle hFile1
  CloseHandle hFile2
 
End Sub

Open in new window


Reward 1Reward 2Reward 3Reward 4Reward 5Reward 6

EARN REWARDS FOR ASKING, ANSWERING, AND MORE.

Earn free swag for participating on the platform.


Microsoft Development

Microsoft Development

--

Questions

--

Followers

Top Experts

Most development for the Microsoft platform is done utilizing the technologies supported by the.NET framework. Other development is done using Visual Basic for Applications (VBA) for programs like Access, Excel, Word and Outlook, with PowerShell for scripting, or with SQL for large databases.