PowerBASIC Forums
  PowerBASIC for Windows
  Intro to Alternate Data Streams (virtually invisible files)

Post New Topic  Post A Reply
profile | register | preferences | faq | search

UBBFriend: Email This Page to Someone! next newest topic | next oldest topic
Author Topic:   Intro to Alternate Data Streams (virtually invisible files)
Wayne Diamond
Member
posted April 16, 2001 02:19 PM     Click Here to See the Profile for Wayne Diamond     Edit/Delete Message   Reply w/Quote
NTFS Alternate Data Streams (ADS, first introduced in WinNT 3.1, and available on WinNT4 and Win2K) is a little known and mostly undocumented NTFS feature that provides a means of modifying file data without actually changing the original image of the file - that is, you can save multiple images of the one file, using just the one filename. In effect it is almost like hiding multiple files behind one parent file.

The following demo creates three files, although only one is actually visible (even using command prompt "dir" listing). The first file created is the actual parent file, called "stream.txt" in this demo. This is the visible file. The two other files that get created with it are ADS files, and as such they are - for all intensive purposes - completely invisible, even though they hold unique data. Even anti-virus scanners don't (yet) scan in this area (although ones that scan files on disk-writes should detect them).

How do you create an ADS stream file? Easy - just open it with ":StreamIdentifier" appended to the end of the parent filename (where "StreamIdentifier" can be any unique alphanumeric string). If you open an ADS file that doesn't have a parent, the parent will be created as a 0-byte file.

Please don't abuse this. It is a fantastic feature to use for protecting your own programs and data files - for example, it makes it very hard for a trojan to corrupt your program files if the files are ADS files, as unless it knows the specific parent filename _AND_ StreamIdentifier the trojan won't be able to find your files. Also, it's kind of cute to look in your program directory and only see the exe and no other files! Another great use of ADS files is multiple backups! You could even write your own file-protection system such as the one used by Win2K - if a file is changed, you just go back to a previous ADS stream and restore that image. It's just too good to abuse! (but also too easy )

Enjoy!



'ADS_STREAMS.BAS - NTFS Alternate Data Streams demo, by Wayne Diamond
'Requires NT3.51, NT4, Win2K or higher.

#COMPILE EXE "streams.exe"

$STREAMDIR = "C:\Streams"

FUNCTION PBMAIN() AS LONG
ON ERROR RESUME NEXT
DIM TempStr AS STRING * 6
'// Create and change directory to $STREAMDIR
MKDIR $STREAMDIR
CHDRIVE LEFT$($STREAMDIR,1)
CHDIR "\"
CHDIR RIGHT$($STREAMDIR, LEN($STREAMDIR) - 2)

'// Create the PARENT file
TempStr = "Parent"
OPEN $STREAMDIR & "\stream.txt" FOR BINARY ACCESS WRITE LOCK SHARED AS #1
PUT #1, 1, TempStr
CLOSE #1

'// Create first STREAM file 'under' the parent file
TempStr = "File 1"
OPEN $STREAMDIR & "\stream.txt:s1" FOR BINARY ACCESS WRITE LOCK SHARED AS #1
PUT #1, 1, TempStr
CLOSE #1

'// Create second stream file
TempStr = "File 2"
OPEN $STREAMDIR & "\stream.txt:s2" FOR BINARY ACCESS WRITE LOCK SHARED AS #1
PUT #1, 1, TempStr
CLOSE #1

'// Now READ the data from our 3 files (note that only 1 of these files is visible in directory listings)
OPEN $STREAMDIR & "\stream.txt" FOR BINARY ACCESS READ LOCK SHARED AS #1
GET #1, 1, TempStr
CLOSE #1
IF TempStr <> "Parent" THEN
STDOUT "This machine does not support NTFS Alternate Data Streams!"
EXIT FUNCTION
END IF
STDOUT "Data in parent file: " & TempStr

OPEN $STREAMDIR & "\stream.txt:s1" FOR BINARY ACCESS READ LOCK SHARED AS #1
GET #1, 1, TempStr
CLOSE #1
STDOUT "Data in 1st stream file: " & TempStr

OPEN $STREAMDIR & "\stream.txt:s2" FOR BINARY ACCESS READ LOCK SHARED AS #1
GET #1, 1, TempStr
CLOSE #1
STDOUT "Data in 2nd stream file: " & TempStr

STDOUT "---"
STDOUT "Press any key to delete all 3 files ... ";
WAITKEY$
KILL $STREAMDIR & "\stream.txt"
'As this is just a demo and we don't want to leave behind any rubbish, we'll tidy up after ourselves..
'As the ADS files are invisible they can't really be deleted any other way
KILL $STREAMDIR & "\stream.txt:s1"
KILL $STREAMDIR & "\stream.txt:s2"
END FUNCTION

[This message has been edited by Wayne Diamond (edited April 16, 2001).]

IP: Logged

Wayne Diamond
Member
posted April 16, 2001 02:27 PM     Click Here to See the Profile for Wayne Diamond     Edit/Delete Message   Reply w/Quote
Here is a simple demo that basically returns a yes or no as to whether or not NTFS Alternate Data Streaming is supported by your system


#COMPILE EXE "ads-test.exe"

$ADS_STREAM_FILE = "c:\autoexec.bat:ADSTest" 'uses c:\autoexec.bat, as that is likely to exist

FUNCTION PBMAIN() AS LONG
ON ERROR RESUME NEXT
DIM strTemp AS STRING
OPEN $ADS_STREAM_FILE FOR OUTPUT AS #1
PRINT #1, "ADS"
CLOSE #1
OPEN $ADS_STREAM_FILE FOR INPUT AS #1
LINE INPUT #1, strTemp
CLOSE #1
KILL $ADS_STREAM_FILE
IF LEFT$(strTemp,3) = "ADS" THEN
STDOUT "YES! NTFS Alternate Data Streams is working"
ELSE
STDOUT "Nope - NTFS Alternate Data Streams is not supported by this machine"
END IF
END FUNCTION

------------------

IP: Logged

Eric Pearson
Member
posted April 16, 2001 02:41 PM     Click Here to See the Profile for Eric Pearson     Edit/Delete Message   Reply w/Quote
I get "Nope" on my NT4, 2000, and 98 test systems.

[Added later]

Oops never mind... all of those systems use FAT, not NTFS.

-- Eric

------------------
Perfect Sync Development Tools
Perfect Sync Web Site
Contact Us: support@perfectsync.com

[This message has been edited by Eric Pearson (edited April 16, 2001).]

IP: Logged

Wayne Diamond
Member
posted April 16, 2001 02:55 PM     Click Here to See the Profile for Wayne Diamond     Edit/Delete Message   Reply w/Quote
Kevin,
Yep it's for NTFS only sorry It works on my NT4 workstation, NT4 server, NT4 SBS server, and Win2K workstation -- not the Win98 box though, so naturally if youre going to use ADS streams to hide your data, make sure you initiate a quick test to make sure you can read/write from a stream file first - if you cant, then proceed as you normally would, otherwise just point at the streamed images instead

Cheers,
Wayne

[This message has been edited by Wayne Diamond (edited April 16, 2001).]

IP: Logged

Wayne Diamond
Member
posted April 16, 2001 03:23 PM     Click Here to See the Profile for Wayne Diamond     Edit/Delete Message   Reply w/Quote
Last example - putting it to more practical use. Most programs have supporting datafiles - such as configuration files, database files etc, so although you have a standalone program, after running it you may have a config file or two so that your settings are remembered.

This demo uses ADS streams to hide its config files -- and cheekily uses its own executable as the parent file, so no other files need to be created
If it fails to open the ADS stream file, it will revert to using the normal filenames, so it is FAT and NTFS (and OS) friendly, even though it will only hide its config files on NTFS.

The crux of it is this:


'---
OPEN ExeName & ":" & $CONFIGFILE FOR OUTPUT AS #1 'Try to open ADS stream file
IF ERR <> 0 THEN OPEN $CONFIGFILE FOR OUTPUT AS #1 'If it failed, simply use normal filename instead - too easy!
'---



#COMPILE EXE "adsprog.exe"
#INCLUDE "win32api.inc"

FUNCTION ExeName() AS STRING
ON ERROR RESUME NEXT
LOCAL hModule AS LONG
LOCAL buffer AS ASCIIZ * 256
hModule = GetModuleHandle(BYVAL 0&)
GetModuleFileName hModule, Buffer, 256
FUNCTION = Buffer
END FUNCTION

$CONFIGFILE1 = "file1.cfg"
$CONFIGFILE2 = "file2.cfg"

FUNCTION PBMAIN() AS LONG
ON ERROR RESUME NEXT
'// Dummy Config file #1
OPEN ExeName & ":" & $CONFIGFILE1 FOR OUTPUT AS #1 'Try ADS stream file
IF ERR <> 0 THEN
STDOUT "Wrote to normal file " & $CONFIGFILE1
OPEN $CONFIGFILE1 FOR OUTPUT AS #1 'If failed, use normal file
ELSE
STDOUT "Wrote to ADS stream " & ExeName & ":" & $CONFIGFILE1
END IF
CLOSE #1

'// Dummy Config file #2
OPEN ExeName & ":" & $CONFIGFILE2 FOR OUTPUT AS #1
IF ERR <> 0 THEN
STDOUT "Wrote to normal file " & $CONFIGFILE2
OPEN $CONFIGFILE2 FOR OUTPUT AS #1 'If failed, use normal file
ELSE
STDOUT "Wrote to ADS stream " & ExeName & ":" & $CONFIGFILE2
END IF
CLOSE #1
END FUNCTION

[This message has been edited by Wayne Diamond (edited April 16, 2001).]

IP: Logged

mark smit
unregistered
posted April 16, 2001 08:22 PM           Edit/Delete Message   Reply w/Quote
Wow! very nifty posibilities. I just made an ADS file and copied it to another drive and it even worked there. Where can I read up more on this little gem?

------------------
Cheers!

IP: Logged

mark smit
unregistered
posted April 16, 2001 09:09 PM           Edit/Delete Message   Reply w/Quote
One thing I just found is that kill on an ADS file files with error 75. So how do you delete an ADS file from a normal parent file then. Also, how would you get a directory listing of ADS files attached to a parent file?

------------------
Cheers!

IP: Logged

Wayne Diamond
Member
posted April 16, 2001 11:58 PM     Click Here to See the Profile for Wayne Diamond     Edit/Delete Message   Reply w/Quote
Mark,

Use www.google.com, and just do such searches as "NTFS streams" (with speechmarks).

Thanks for your sharp observation on the err75 when killing stream files.
There are some special attributes to be aware of with streams:
- You CANT use DIR$() to detect a stream file
- You CANT use KILL() to kill a stream file, you must kill it's parent.
- You CAN use FILECOPY() to copy a stream file to either a normal file, or another stream
- Killing a parent automatically kills all stream files under that parent.
- Renaming a parent automatically 'renames' all child stream files under that.
- The only known way to enumerate streams under a file is by using BackupRead() with SeBackupPrivilege enabled (needless to say, you must be logged in as Admin)

Here's another demo which demonstrates copying and deleting streams.
1. It creates a parent file "stream1.txt", with a stream under it - "stream1.txt:Stream".
2. It then does a simply, standard call to FILECOPY "stream1.txt", "stream2.txt" -- doing this automatically copies the streams of stream1.txt over to stream2.txt, so after this call we have four files:
stream1.txt, stream1.txt:Stream, stream2.txt, stream2.txt:Stream
3. It then calls KILL "stream1.txt", which automatically kills stream1.txt and all of it's child streams (which is just "stream1.txt:Stream" in this case)
4. It then reads from "stream2.txt:Stream" and displays that data


#COMPILE EXE
#INCLUDE "win32api.inc"

$PARENTFILE = "stream1.txt"

FUNCTION PBMAIN() AS LONG
ON ERROR RESUME NEXT
DIM TempStr AS STRING
'Create a parent, and a stream under it
OPEN $PARENTFILE FOR OUTPUT AS #1
PRINT #1, "PARENT - Im the parent - kill me and you'll take my children with me!"
CLOSE #1
OPEN $PARENTFILE & ":Stream" FOR OUTPUT AS #1
PRINT #1, "CHILD - Im the stream - you cant kill me, youll have to kill my parent!"
CLOSE #1

'Now copy the parent file - stream1.txt, to stream2.txt. Note how its stream(s) are copied across with it, so "stream1.txt:Stream" is copied automatically as "stream2.txt:Stream"
FILECOPY $PARENTFILE, "stream2.txt"

'Now kill the original. By doing this, we're also killing "stream1.txt:Stream"
KILL $PARENTFILE

'But because we copied the parent across first, we can still access our newly-copied stream!
OPEN "stream2.txt:Stream" FOR INPUT AS #1
LINE INPUT #1, TempStr
STDOUT "TempStr=" & TempStr
CLOSE #1

STDOUT "Press any key to clean up..."
WAITKEY$
'Wait for user to press a key (allows them to inspect the directory for changes etc).
'Then the two parent files (which automatically kills their child streams)
KILL $PARENTFILE
KILL "stream2.txt"
END FUNCTION


------------------

IP: Logged

Wayne Diamond
Member
posted April 17, 2001 12:03 AM     Click Here to See the Profile for Wayne Diamond     Edit/Delete Message   Reply w/Quote
This is possibly the only C++ source of enumerating child streams out of a file.
It seems fairly simple, and every declaration required is already in win32api.inc, but its too C++ish for me to port -- if anyone is able to port this to PB, I would be forever grateful!


#include <windows.h>
#include <stdio.h>
#pragma hdrstop
#define err doerr( __FILE__, __LINE__ )

void doerr( const char *file, int line )
{
DWORD e;
e = GetLastError();
if ( e == 0 )
return;
printf( "%s(%d): gle = %lu\n", file, line, e );
exit( 2 );
}

void enableprivs()
{
HANDLE hToken;
byte buf[sizeof TOKEN_PRIVILEGES * 2];
TOKEN_PRIVILEGES & tkp = *( (TOKEN_PRIVILEGES *) buf );

if ( ! OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )
err;

// enable SeBackupPrivilege, SeRestorePrivilege

if ( !LookupPrivilegeValue( NULL, SE_BACKUP_NAME, &tkp.Privileges[0].Luid ) )
err;
if ( !LookupPrivilegeValue( NULL, SE_RESTORE_NAME, &tkp.Privileges[1].Luid ) )
err;
tkp.PrivilegeCount = 2;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tkp.Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp,
NULL, NULL );
}

void dumphdr( WIN32_STREAM_ID & wsi )
{
const char *p;
printf( "\nstream [%lu] \"%S\":\n", wsi.dwStreamNameSize,
wsi.dwStreamNameSize? wsi.cStreamName: L"" );
switch ( wsi.dwStreamId )
{
case BACKUP_DATA:
p = "data";
break;
case BACKUP_EA_DATA:
p = "extended attributes";
break;
case BACKUP_SECURITY_DATA:
p = "security";
break;
case BACKUP_ALTERNATE_DATA:
p = "other streams";
break;
case BACKUP_LINK:
p = "link";
break;
default:
p = "unknown";
break;
}
printf( " type: %s\n", p );
printf( " size: %I64d\n", wsi.Size.QuadPart );
}

int main( int argc, char *argv[] )
{
HANDLE fh;
if ( argc != 2 )
{
printf( "usage: dump_ntfs_streams {file}\n" );
return 1;
}
// SeBackupPrivilege is not necessary to enumerate streams --
// but it helps if you are an admin/backup-operator and need
// to scan files to which you have no permissions
enableprivs();
fh = CreateFile( argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_POSIX_SEMANTICS, NULL );
if ( fh == INVALID_HANDLE_VALUE | | fh == NULL )
err;
byte buf[4096];
DWORD numread, numtoskip;
void *ctx = NULL;
WIN32_STREAM_ID & wsi = *( (WIN32_STREAM_ID *) buf );
numtoskip = 0;
while ( 1 )
{
// we are at the start of a stream header. read it.
if ( ! BackupRead( fh, buf, 20, &numread, FALSE, TRUE, &ctx ) )
err;
if ( numread == 0 )
break;
if ( wsi.dwStreamNameSize > 0 )
{
if ( ! BackupRead( fh, buf + 20, wsi.dwStreamNameSize, &numread, FALSE, TRUE, &ctx ) )
err;
if ( numread != wsi.dwStreamNameSize )
break;
}
dumphdr( wsi );
// skip stream data
if ( wsi.Size.QuadPart > 0 )
{
DWORD lo, hi;
BackupSeek( fh, 0xffffffffL, 0x7fffffffL, &lo, &hi, &ctx );
}
}
// make NT release the context
BackupRead( fh, buf, 0, &numread, TRUE, FALSE, &ctx );
CloseHandle( fh );
return 0;
}


------------------

IP: Logged

Florent Heyworth
Member
posted April 18, 2001 01:20 AM     Click Here to See the Profile for Florent Heyworth     Edit/Delete Message   Reply w/Quote
[UPDATED 05-Jul-2001 - Added CloseHandle on the handle return from OpenProcessToken]
[UPDATED 20-Apr-2001 - For stream directories]
Here's the PB translation (made it close to the original C program)


#INCLUDE "win32api.inc"

TYPE T_TOKEN_PRIVILEGES
PrivilegeCount AS DWORD
Privileges AS BYTE PTR ' array size may vary
END TYPE

#IF NOT %DEF(%TOKEN_ADJUST_PRIVILEGES)
%TOKEN_ADJUST_PRIVILEGES = &H0020
#ENDIF

#IF NOT %DEF(%TOKEN_QUERY)
%TOKEN_QUERY = &H0008
#ENDIF

FUNCTION strFromUnicode( BYVAL dwConvert AS DWORD ) AS STRING
LOCAL lLength AS LONG
LOCAL sBuffer AS STRING

lLength = lstrlenW( BYVAL dwConvert )
sBuffer = SPACE$( lLength )

CALL WideCharToMultiByte ( 0, _
%NULL, _
BYVAL dwConvert, _
lLength, _
BYVAL STRPTR(sBuffer), _
LEN(sBuffer), _
BYVAL %NULL, _
BYVAL %NULL )

FUNCTION = sBuffer

END FUNCTION

FUNCTION enableprivs() AS LONG
LOCAL hToken AS LONG
LOCAL tToken AS T_TOKEN_PRIVILEGES

tToken.Privileges = HeapAlloc( GetProcessHeap(), %HEAP_ZERO_MEMORY, LEN(LUID_AND_ATTRIBUTES) * 2)
IF tToken.Privileges = %NULL THEN
FUNCTION = %NULL
EXIT FUNCTION
END IF

DIM tLuid(0:1) AS LUID_AND_ATTRIBUTES AT tToken.Privileges

IF ISFALSE( OpenProcessToken( GetCurrentProcess(), _
%TOKEN_ADJUST_PRIVILEGES OR %TOKEN_QUERY, _
hToken ) ) THEN
FUNCTION = %FALSE
GOTO Clean_Up
END IF

' enable SeBackupPrivilege, SeRestorePrivilege
IF ISFALSE( LookUpPrivilegeValue( BYVAL %NULL, $SE_BACKUP_NAME, BYVAL VARPTR(tLuid(0)) ) ) THEN
FUNCTION = %FALSE
GOTO Clean_Up
END IF

IF ISFALSE( LookUpPrivilegeValue( BYVAL %NULL, $SE_RESTORE_NAME, BYVAL VARPTR(tLuid(1)) ) ) THEN
FUNCTION = %FALSE
GOTO Clean_Up
END IF

tToken.PrivilegeCount = 2
tLuid(0).Attributes = %SE_PRIVILEGE_ENABLED
tLuid(1).Attributes = %SE_PRIVILEGE_ENABLED

FUNCTION = AdjustTokenPrivileges( hToken, %FALSE, BYVAL VARPTR(tToken), 4 + (LEN(LUID_AND_ATTRIBUTES) * 2), _
BYVAL %NULL, BYVAL %NULL )
Clean_Up:
CALL HeapFree( GetProcessHeap(), 0, BYVAL tToken.Privileges )
IF hToken THEN
CALL CloseHandle( hToken )
END IF

END FUNCTION

SUB dumphdr( BYREF wsi AS WIN32_STREAM_ID )
LOCAL p AS STRING
LOCAL quadpart AS QUAD PTR

STDOUT $CRLF + "stream " + FORMAT$(wsi.dwStreamNameSize) + ": ";
IF wsi.dwStreamNameSize THEN
STDOUT "'" + strFromUnicode( VARPTR(wsi.cStreamName) ) + "'"
ELSE
STDOUT "''"
END IF

SELECT CASE wsi.dwStreamId
CASE %BACKUP_DATA
p = "data"
CASE %BACKUP_EA_DATA
p = "extended attributes"
CASE %BACKUP_SECURITY_DATA
p = "security"
CASE %BACKUP_ALTERNATE_DATA
p = "other streams"
CASE %BACKUP_LINK
p = "link"
CASE ELSE
p = "unknown"
END SELECT

STDOUT " type: " + p
quadpart = VARPTR(wsi.qSize)
STDOUT " size: " + FORMAT$(@quadpart)

END SUB

FUNCTION GetCommandPath( szCmdPath AS ASCIIZ, szFileName AS ASCIIZ ) AS LONG
'returns Directory in szCmdPath and strips Filename to szFileName
LOCAL lPosDir AS LONG

lPosDir = INSTR(-1, szCmdPath, "\")
IF lPosDir = 0 THEN FUNCTION = %FALSE: szFileName = szCmdPath: EXIT FUNCTION

szFileName = MID$( szCmdPath, lPosDir+1 )
szCmdPath = MID$( szCmdPath, 1, lPosDir )
IF LEN(szFileName) = 0 THEN
szFileName = szCmdPath
END IF

FUNCTION = %TRUE
END FUNCTION

FUNCTION PBMAIN() AS LONG
LOCAL fh AS LONG
LOCAL szCurdir AS ASCIIZ * %MAX_PATH
LOCAL szCmdDir AS ASCIIZ * %MAX_PATH
LOCAL szCmd AS ASCIIZ * %MAX_PATH
LOCAL szFileName AS ASCIIZ * %MAX_PATH

szCmd = COMMAND$
szCurDir = CURDIR$

IF LEN(szCmd) = 0 THEN
STDOUT "usage: enum_streams <filename>"
EXIT FUNCTION
END IF

IF GetCommandPath( szCmd, szFileName ) THEN
CHDIR szCmd
END IF
' SeBackupPrivilege is not necessary to enumerate streams --
' but it helps if you are an admin/backup-operator AND need
' to scan files to which you have no permissions
IF enableprivs() THEN
LOCAL llast AS LONG

'dont see any need for %FILE_FLAG_POSIX_SEMANTICS
fh = CreateFile( szFileName, %GENERIC_READ, 0, BYVAL %NULL, %OPEN_EXISTING, _
%FILE_FLAG_BACKUP_SEMANTICS, %NULL )
IF fh = %INVALID_HANDLE_VALUE THEN
STDOUT "Could not open file: " + szFileName
EXIT FUNCTION
END IF

DIM buf(4096) AS BYTE
LOCAL numread AS LONG, numtoskip AS LONG
LOCAL wsi AS WIN32_STREAM_ID PTR

wsi = VARPTR(buf(0))
LOCAL ctx AS LONG

DO
'we are at the start of a stream header - read it
IF ISFALSE( BackUpRead( fh, BYVAL VARPTR(buf(0)), 20, numread, %FALSE, %TRUE, ctx ) ) THEN
STDOUT "Error reading file!"
CALL CloseHandle( fh )
GOTO Clean_Up
END IF

IF numread = 0 THEN EXIT DO

IF @wsi.dwStreamNameSize > 0 THEN
IF ISFALSE( BackUpRead( fh, BYVAL VARPTR(buf(0)) + 20, @wsi.dwStreamNameSize, numread, %FALSE, %TRUE, ctx ) ) THEN
CALL CloseHandle( fh )
STDOUT "Error reading file!"
GOTO Clean_Up
END IF
IF numread <> @wsi.dwStreamNameSize THEN EXIT DO
END IF

CALL dumphdr( @wsi )
'skip stream data
LOCAL quadpart AS QUAD PTR
quadpart = VARPTR(@wsi.qSize)
IF @quadpart > 0 THEN
LOCAL lo AS LONG, hi AS LONG
CALL BackUpSeek( fh, &Hffffffff&, &H7fffffff&, lo, hi, ctx )
END IF
LOOP

'make NT release the context
CALL BackUpRead( fh, BYVAL VARPTR(buf(0)), 0, numread, %TRUE, %FALSE, ctx)

CALL CloseHandle( fh )

ELSE
STDOUT "Could not enable SE_BACKUP_NAME and SE_RESTORE_NAME privileges"
END IF

Clean_Up:
'restore Current dir
CHDIR szCurDir
END FUNCTION

[This message has been edited by Florent Heyworth (edited July 05, 2001).]

IP: Logged

Wayne Diamond
Member
posted April 18, 2001 02:37 AM     Click Here to See the Profile for Wayne Diamond     Edit/Delete Message   Reply w/Quote
!!!!!!!!!!!!!!!!!!!!!!!! FLORENT!!! You are the man! -- I am forever in your debt (although I think I was before anyway )
Im absolutely lost for words -- I'll come back here and edit this post in a few hours after a few drinks. Ive been wanting to do this in PB for sooo long but even simple C++ is beyond me - your superb PB port compiled perfectly and ran beauuuutifully, identifying the streams I had set <im so stoked>
Happy Easter again!!

------------------

IP: Logged

Wayne Diamond
Member
posted April 20, 2001 07:20 AM     Click Here to See the Profile for Wayne Diamond     Edit/Delete Message   Reply w/Quote
Florent, I've been playing around with your ported source for the last couple of days now and I must say it truly is a beautiful thing! I have one last question... here's another kinky side of ADS - not only can you link NTFS ADS streams to files, but you can also actually link them to Directories. The interesting thing with this is that because you can't delete a stream without deleting the parent that it's linked to, if you link your stream to C:\ it is virtually impossible to 'delete' your stream without taking out all of C:\ ! (in theory anyway). Imagine if a trojan/virus started filling up an ADS stream that was linked to C:\ ? Im not sure what would happen, but because Explorer etc don't reflect disk availability size change from streams, I think it could be quite nasty... :-/

Example: from Windows, go Start | Run: notepad C:\:mystream
Because Notepad is ADS-friendly you can do that, and you can read/write from that ADS stream with ease. A good place to store passwords perhaps

My question - your source works amazingly well for ADS streams linked to FILES, but not for directories... any idea why? I'm thinking it may be something to do with the CreateFile call?
Anyway Florent I hope you find some time off to enjoy the weekend!

Best regards,
Wayne

------------------

IP: Logged

Florent Heyworth
Member
posted April 20, 2001 08:25 AM     Click Here to See the Profile for Florent Heyworth     Edit/Delete Message   Reply w/Quote
Hi Wayne

the reason is that the GetCommandPath function assumed it'd work
with files and not directories. I've made a small change to
the GetCommandPath function in the code - you'll need to copy
it again.

The code should handle directories as well. Simply call the
program with a string argument for a directory omitting the
last slash as in enum.exe c:\streams NOT enum.exe c:\streams\

Yes the GetCommandPath is simplistic - feel free to substitute
a more clever one .

Cheers

Florent

------------------

IP: Logged

All times are EasternTime (US)

next newest topic | next oldest topic

Administrative Options: Close Topic | Archive/Move | Delete Topic
Post New Topic  Post A Reply
Hop to:

Contact Us | PowerBASIC BASIC Compilers

Copyright © 1999-2007 PowerBASIC, Inc. All Rights Reserved.


Ultimate Bulletin Board 5.45c