CyberArmy University | Open Source Institute | CyberArmy Intelligence & Security | CyberArmy Services & Projects

[Asm] Scanning for Alternative Data Streams


[Reply] [View by Thread] [Help]
[Back To Article Discussion Forum]

Posted by Author sefo On 2007-06-15 20:09:18




View and vote on the article here: Scanning for Alternative Data Streams


Scanning for Alternative Data Streams

Category
Asm
Summary
"Alternate data streams allow files to be associated with more than one data stream." (Wikipedia)

The code I'm going to demonstrate here is high-level enough to be ported to other languages easily.
Body
The way to scan ADS we are going to see uses the undocumented API:
NtQueryInformationFile

Its definition is:
NtQueryInformationFile(
  IN HANDLE FileHandle,
  OUT PIO_STATUS_BLOCK IoStatusBlock,
  OUT PVOID FileInformation,
  IN ULONG Length,
  IN FILE_INFORMATION_CLASS FileInformationClass ); 
FileHandle is simply the DWORD resulting from a call to CreateFile.

IoStatusBlock is a structure containing the status of the call to NtQueryInformationFile. (status code + information message)

FileInformation is a buffer that will contain all the information relative to a specific file after the call to NtQueryInformationFile.

Length is the length of the FileInformation buffer.

FileInformationClass is a list of informations you can get from the NtQueryInformationFile function like file name, file size, stream names...etc.

Declaring the variables and structures

In an include file, we now need to declare all these buffers and other handles.

First we need to declare a DataStream structure that will help us manipulate each stream using the 'object syntax':
MyStream.StreamSize
MyStream.cStreamName
You can declare this structure as follows:
WIN32_FIND_STREAM_DATA Struct
	StreamSize qword ?
	cStreamName dword 296 DUP (?)
WIN32_FIND_STREAM_DATA ENDS
Although all of the following is not always necessary, we are going to declare all of the values we can pass to NtQueryInformationFile. The FileInformationClass is just a list of constants representing the data you want to get from the API call:
FileDirectoryInformation equ 1
FileFullDirectoryInformation equ 2
FileBothDirectoryInformation equ 3
FileBasicInformation equ 4
FileStandardInformation equ 5
FileInternalInformation equ 6
FileEaInformation equ 7
FileAccessInformation equ 8
FileNameInformation equ 9
FileRenameInformation equ 10
FileLinkInformation equ 11
FileNamesInformation equ 12
FileDispositionInformation equ 13
FilePositionInformation equ 14
FileFullEaInformation equ 15
FileModeInformation equ 16
FileAlignmentInformation equ 17
FileAllInformation equ 18
FileAllocationInformation equ 19
FileEndOfFileInformation equ 20
FileAlternateNameInformation equ 21
FileStreamInformation equ 22
FilePipeInformation equ 23
FilePipeLocalInformation equ 24
FilePipeRemoteInformation equ 25
FileMailslotQueryInformation equ 26
FileMailslotSetInformation equ 27
FileCompressionInformation equ 28
FileCopyOnWriteInformation equ 29
FileCompletionInformation equ 30
FileMoveClusterInformation equ 31
FileQuotaInformation equ 32
FileReparsePointInformation equ 33
FileNetworkOpenInformation equ 34
FileObjectIdInformation equ 35
FileTrackingInformation equ 36
FileOleDirectoryInformation equ 37
FileContentIndexInformation equ 38
FileInheritContentIndexInformation equ 39
FileOleInformation equ 40
FileMaximumInformation equ 41
The one we're going to use as first parameter is number 22, FileStreanInformation.
(IN FILE_INFORMATION_CLASS FileInformationClass)

The next very simple structure to declare is the IoStatus one.
IO_STATUS_BLOCK STRUCT
	Status dword ?
	Information dword ?
IO_STATUS_BLOCK ENDS
It will contain the result of the call to the function, so we can see if the function worked.

The last structure is the most important one. After the call to NtQueryInformationFile (with parameter 22),
the file stream information structure will be filled will all the details about the streams for that particular file.
FILE_STREAM_INFORMATION struct
	NextEntry dword ?
	NameLength dword ?
	xSize qword ?
	AllocationSize qword ?
	xName byte ?
FILE_STREAM_INFORMATION ENDS
Now we will move to the .data segment. Since NtQueryInformationFile is an undocumented function, we need to 'hook' it to be able to use it, which we can do by using a combination of LoadLibrary and GetProcAddress.

We need to pass the string "ntdll" as a parameter to LoadLibrary and "NtQueryInformationFile" to GetProcAddress.
.DATA
Ntdll           BYTE "ntdll.dll",0
QueryInfo       BYTE "NtQueryInformationFile",0
In the same data segment, we need to declare a pointer to the IOSTATUSBLOCK structure:
ioStatus        IO_STATUS_BLOCK <>
Finally, in the uninitialised data segment we are going to add some handles:
hFile           DWORD ? ;handle for CreateFile
hNtdll          DWORD ? ;handle to NtDll after LoadLibrary
hNqif           DWORD ? ;handle to NtQueryInformationFile after GetProcAddress
and a buffer that will contain all the stream information and a dword representing the size of the data:
cbStreamInfo    DWORD   ? ;pointer to size
pStreamInfo     DWORD   ? ;pointer to buffer
Now that the hardest part is done we can start coding.

The code

The first thing to do is to 'hook' NtQueryInformationFile.

a/ LoadLibrary to get a handle to ntdll.dll
push offset Ntdll ;this is the string we declared earlier
call LoadLibrary
or eax,eax ;EAX should contain the handle or 0 if an error occurred.
je Error
mov hNtdll,eax ;save the handle for future use
Just below this, we can now hook the function in ntdll.dll
push offset QueryInfo ;string "NtQueryInformationFile"
push eax ;handle of ntdll
call GetProcAddress
or eax,eax ;EAX should contain the handle or 0 if error
je Error
mov hNqif,eax ;save the handle
Now that we have the handle to the function, we can call it, but before we can call it, we need to allocate bytes for the result buffer.
mov dword ptr[cbStreamInfo],16384 ;size
invoke VirtualAlloc,NULL,[cbStreamInfo],MEM_COMMIT,PAGE_READWRITE
mov dword ptr[pStreamInfo],eax ;pointer to buffer
/b The call to NtQueryInformationFile
push FileStreamInformation
push dword ptr[cbStreamInfo]
push dword ptr[pStreamInfo]
push offset ioStatus
push hFile
call hNqif
or eax,eax
jne Error
The first parameter is the famous 22 pointed by the 'variable' FileStreamInformation.(Note: we could have just pushed 22 directly)

The second parameter is the size pointed to by cbStreamInfo, and in the third parameter we pass the pointer to the result buffer. The last two parameters are the pointers to the status structure and the handle to the file you opened previously with CreateFile.

Now it's time to check if everything went well by looking at the status.
cmp dword ptr[ioStatus.Information],7FFFFFFFh
ja Error
From there, we should see that everything went well and we have the information we need in our buffer. Now we just need to parse it to find what it is we are looking for (stream names in particular):
xor ebx,ebx
mov eax,dword ptr[pStreamInfo] ;pointer to the buffer containing the streams info
add ebx,eax
With this code, both EAX and EBX point to the result buffer (with all our information). Looking at the FILE_STREAM_INFORMATION structure, we can deduce that, from the beginning of each entry, adding 18 (FILE_STREAM_INFORMATION.xName) will bring us to the stream name of the next entry, and adding the value of NextEntry to EBX or EAX (beginning of each entry) will bring us to the beginning of the next entry.
@@:
    mov ecx,dword ptr[ebx+FILE_STREAM_INFORMATION.NameLength] ;ECX containing the length will be used as index for a loop
    mov eax,ebx
    add eax,[FILE_STREAM_INFORMATION.xName] ;eax points to the string "::$DATA" if default stream or ":stream_name:$DATA"
    pushad
        invoke WideCharToMultiByte,CP_ACP,0,eax,-1,offset StreamName,260,0,0 ;stream name is in UNICODE, we need to convert it
        push offset StreamName
        push offset Stream
        push offset Result
        call wsprintf
        add esp,0Ch ;wsprintf has a dynamic number of parameters
	;The stream name is now in the buffer pointed by Result (you can use a 256 bytes buffer)
	;you can print it out in your program or simply MessageBox it
    popad
    cmp dword ptr[ebx+FILE_STREAM_INFORMATION.NextEntry],0 ;is there a next entry?
    jz @F ;forward jump
    add ebx,dword ptr[ebx+FILE_STREAM_INFORMATION.NextEntry] ;ebx=begining of entry
    jmp @B ;backward jump
@@:
invoke VirtualFree,dword ptr[pStreamInfo],NULL,MEM_RELEASE ;free the memory
invoke CloseHandle,dword ptr[hFile] ;free the file handle





There are no replies to this post yet.



Guest:
Subject:
Message:
Signature:
Optional Image Link:
http://

CyberArmy::Forum v0.6
Generated In 0.02918 seconds


About Us | Privacy Policy | Mission Statement | Help