Bilgus Posted May 9, 2016 Share Posted May 9, 2016 (edited) Here is a preliminary Shellbag parser for desktop icons I used information found at http://www.williballenthin.com/forensics/shellbags/, http://forensicswiki.org/wiki/Shell_Item#Format, and https://github.com/libyal/libfwsi/blob/master/documentation/Windows%20Shell%20Item%20format.asciidoc So far This only works with the desktop ShItem Specs although, I admit this is the only one I tried it with. The code makes a file called data.txt containing the data from the registry key and a file with the name 'RegKeyValNameXXXXxyyyy'_FileData for each ItemPos Value found The Information is returned in an INI file With [SHITEM#] as the section name This Code is not commented very well yet nor is it a final version Please let me know what kind of errors you find and Post the improvements you make. Sample Entry For Computer: [SHITEM0] FileStart= 0x0000000000000010 FileEnd= 0x000000000000002C RecordLen= 28 FilePtr= 16 SHIconX= 36 SHIconY= -1 Size= 20 Flags= 501F GUID= 20D04FE0-3AEA-1069-A2D8-08002B30309D Shortname= Computer Longname= Computer Sample Entry for Google Chrome [SHITEM3] FileStart= 0x00000000000000C4 FileEnd= 0x000000000000013A RecordLen= 118 FilePtr= 196 SHIconX= 36 SHIconY= 368 Size= 108 Flags= 003A FileSize= 2193 ModifiedDate= 02/19/2016 ModifiedTime= 22:56.10 FileAttribs= 8224 Shortname= GOOGLE~1.LNK ExtSize= 80 ExtVer= 8 ExtSIG= 0xBEEF0004 CreatedDate= 09/26/2014 CreatedTime= 03:09.38 AccessDate= 09/26/2014 AccessTime= 03:09.38 Unknown2= 42 64FileRef= 000300000000F689 Unknown3= 0 LongNameSize= 0 Unknown4= 0 LongName= Google Chrome.lnk LongNameAddl= Unknown5= 0024001C The Code expandcollapse popup#include <WinAPIFiles.au3> ;FileOpen Opt("MustDeclareVars", 1) Local $sSubKey = "" Local $aRegKeys[30] Local $iRegCount = 0 For $i = 1 To 30 $sSubKey = RegEnumVal("HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Bags\1\Desktop\", $i) If @error = -1 Then ExitLoop If StringInStr($sSubKey, "ItemPos", 0) Then $aRegKeys[$iRegCount + 1] = $sSubKey $iRegCount += 1 EndIf Next $aRegKeys[0] = $iRegCount Local $sFilePath = "" Local $PathResultsIni = "" Local $sRegEntry = "" For $i = 1 To $aRegKeys[0] $sSubKey = $aRegKeys[$i] $sRegEntry = RegRead("HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Bags\1\Desktop", $sSubKey) If Not (@extended = 3) Then MsgBox(0, "Incorrect Key Type", $sSubKey & @CRLF & "Key Not REG_BINARY ") ;Const $REG_BINARY = 3 EndIf If Not (StringLeft($sRegEntry, 34) = "0x00000000000000000000000000000000") Then MsgBox(0, "Incorrect File Header", $sSubKey & @CRLF & "Header does not match ShItem Header " & @CRLF & StringLeft($sRegEntry, 32) & @CRLF & "0x00000000000000000000000000000000") EndIf $sFilePath = @ScriptDir & "\RegData.txt" $PathResultsIni = @ScriptDir & "\" & $sSubKey & "_" & "FileData.Ini" FileDelete($PathResultsIni) FileDelete($sFilePath) If Not FileWrite($sFilePath, $sRegEntry) Then ; Create a file to read data from. MsgBox("0", @ScriptName, "An error occurred while writing the temporary data file.") EndIf $sRegEntry = "" $iRegCount = ParseSH_ItemToIni($sFilePath, $PathResultsIni) $aRegKeys[$i] = $iRegCount & " Entries saved to:" & @CRLF & $PathResultsIni Next $sSubKey = "" For $i = 1 To $aRegKeys[0] $sSubKey &= $aRegKeys[$i] & @CRLF Next MsgBox(0, @ScriptName & " Finished Parsing", $sSubKey) ;------------------------------------------------------------------- Func ParseSH_ItemToIni($sFilePath, $PathResultsIni) Local Const $hFile = FileOpen($sFilePath, $FO_BINARY + $FO_FULLFILE_DETECT) Local $iFilePos0 = 0 ;File Pointer FileSetPos($hFile, 0, 2) Local $iFileLast = FileGetPos($hFile) ;End Of File FileSetPos($hFile, $iFilePos0, 0) Local $iFileStart = 0, $FileEnd = 0, $iRecordStart = 0, $iRecordNumber = 0, $iRecordEnd Local $iSHIconX = 0, $iSHIconY = 0 Local $iSize = 0, $iFlags = 0, $iSortOrderIndex = 0 ;added Local $sGUID = 0 Local $iFileSize = 0 Local $sModDate = "", $sModTime = "" Local $iFileAttribs = 0 Local $sShortname = "" Local $iExtSize = 0, $iExtVer = 0 Local $iExtSig = 0 Local $iUnknown0 = 0, $iUnknown1 = 0 Local $sCreDate = 0, $sCreTime = 0 Local $sAccDate = 0, $sAccTime = 0 Local $iUnknown2 = 0 Local $i64FileRef = 0 Local $iUnknown3 = 0 Local $iLongNameSize = 0 Local $iUnknown4 = 0 Local $sLongName = "", $sLongNameAddl = "" Local $iUnknown5 = 0 For $iPos = 0 To $iFileLast - 2 ;Step 2 Removed causes the loss of the last item ;Set all fields to default Values $iFileStart = 0 $FileEnd = 0 $iRecordStart = 0 $iSHIconX = 0 $iSHIconY = 0 $iSize = 0 $iFlags = 0 $iSortOrderIndex = 0 ;added $sGUID = 0 $iFileSize = 0 $sModDate = "" $sModTime = "" $iFileAttribs = 0 $sShortname = "" $iExtSize = 0 $iExtVer = 0 $iExtSig = 0 $sCreDate = 0 $sCreTime = 0 $sAccDate = 0 $sAccTime = 0 $iUnknown2 = 0 $i64FileRef = 0 $iUnknown3 = 0 $iLongNameSize = 0 $iUnknown4 = 0 $sLongName = "" $sLongNameAddl = "" $iUnknown5 = 0 ;$iUnknown0 = 0 Replaced By $iExtSig ;$iUnknown1 = 0 Replaced By $iExtSig $iFileStart = FileGetPos($hFile) $iSHIconX = ReadRegHexStringValue($hFile, 32, "INT") $iSHIconY = ReadRegHexStringValue($hFile, 32, "INT") $iRecordStart = FileGetPos($hFile) $iSize = ReadRegHexStringValue($hFile, 16, "INT") $iFlags = ReadRegHexStringValue($hFile, 8, "INT") ;16 changed to 8 $iSortOrderIndex = ReadRegHexStringValue($hFile, 8, "INT") ;Added If $iSize <= 21 Then If Not (BitAND($iFlags, 0x00FF) = 0x1F) Then $iFilePos0 += 2 FileSetPos($hFile, $iFilePos0, 0) ContinueLoop ElseIf $iSize > 15 And $iSortOrderIndex > 0 Then $sGUID = ReadRegHexStringValue($hFile, 128, "GUID") $sShortname = RegRead("HKEY_CLASSES_ROOT\CLSID\{" & $sGUID & "}", "") If Not (@error) Then $iRecordEnd = FileGetPos($hFile) ConsoleWrite(@CRLF & "[SHITEM" & $iRecordNumber & "] " & $sShortname & ":" & $iExtSig & ",") FileWrite($PathResultsIni, _ @CRLF & "[SHITEM" & $iRecordNumber & "]" & _ @CRLF & "FileStart= 0x" & Hex($iFileStart, 16) & _ @CRLF & "FileEnd= 0x" & Hex($iRecordEnd, 16) & _ @CRLF & "RecordLen= " & ($iRecordEnd - $iFileStart) & _ @CRLF & "FilePtr= " & ($iFilePos0) & _ ; the second part of FilePtr is SortOrderIndex @CRLF & "SHIconX= " & $iSHIconX & _ @CRLF & "SHIconY= " & $iSHIconY & _ @CRLF & "Size= " & $iSize & _ @CRLF & "Flags= " & Hex($iFlags, 2) & _ @CRLF & "SortOrderIndex= " & $iSortOrderIndex & _ @CRLF & "GUID= " & $sGUID & _ @CRLF & "Shortname= " & $sShortname & _ @CRLF & "Longname= " & $sShortname & @CRLF & @CRLF) $iRecordNumber += 1 $iFilePos0 = FileGetPos($hFile) Else $iFilePos0 += 2 FileSetPos($hFile, $iFilePos0, 0) MsgBox(0, "INVALID GUID", "Invalid GUID encountered", 10) EndIf ContinueLoop EndIf EndIf $iFileSize = ReadRegHexStringValue($hFile, 32, "INT") $sModDate = ReadRegHexStringValue($hFile, 16, "DOSDATE") $sModTime = ReadRegHexStringValue($hFile, 16, "DOSTIME") $iFileAttribs = ReadRegHexStringValue($hFile, 16, "INT") $sShortname = ReadRegHexStringValue($hFile, $iSize, "String") $iExtSize = ReadRegHexStringValue($hFile, 16, "INT") $iExtVer = ReadRegHexStringValue($hFile, 16, "INT") $iExtSig = "0x" & Hex(ReadRegHexStringValue($hFile, 32, "INT")) ;msgbox(0,"",$iExtSig & " " & HEX(BitAND($iExtSig, 0xFFFF0000))) If ($iSize <= 21 Or Not (BitAND($iExtSig, 0xFFFF0000) = 0xBEEF0000) Or Not (BitAND($iFlags, 0x70)) = 0x30) Then $iFilePos0 += 2 FileSetPos($hFile, $iFilePos0, 0) ContinueLoop EndIf ;FileRead($hFile,2) If $iExtVer >= 0x0003 Then ;$iUnknown0 = ReadRegHexStringValue($hFile, 16, "INT") Replaced By $iExtSig ;$iUnknown1 = ReadRegHexStringValue($hFile, 16, "INT") Replaced By $iExtSig $sCreDate = ReadRegHexStringValue($hFile, 16, "DOSDATE") $sCreTime = ReadRegHexStringValue($hFile, 16, "DOSTIME") $sAccDate = ReadRegHexStringValue($hFile, 16, "DOSDATE") $sAccTime = ReadRegHexStringValue($hFile, 16, "DOSTIME") $iUnknown2 = ReadRegHexStringValue($hFile, 32, "INT") EndIf If $iExtVer >= 0x0007 Then $i64FileRef = Hex(ReadRegHexStringValue($hFile, 64, "INT"), 16) $iUnknown3 = ReadRegHexStringValue($hFile, 64, "INT") $iLongNameSize = ReadRegHexStringValue($hFile, 16, "INT") If $iExtVer <= 0x0008 Then ;Changed to <= from >= Not working right otherwise $iUnknown4 = ReadRegHexStringValue($hFile, 32, "INT") EndIf $sLongName = ReadRegHexStringValue($hFile, 0, "WSTRING") If $iLongNameSize > 0 Then ;MsgBox(0, "$iLongNameSize", $iLongNameSize) $sLongNameAddl = ReadRegHexStringValue($hFile, $iLongNameSize + 2, "WString") EndIf ElseIf $iExtVer >= 0x0003 Then $iUnknown5 = ReadRegHexStringValue($hFile, 16, "INT") EndIf $iUnknown5 = ReadRegHexStringValue($hFile, 32, "HEX") $iRecordEnd = FileGetPos($hFile) If Not (($iRecordEnd - $iRecordStart) = ($iSize + 2)) Then $iFilePos0 += 2 FileSetPos($hFile, $iFilePos0, 0) ContinueLoop ElseIf ($iFilePos0 > $iFileLast - 4) Then ExitLoop ElseIf $iSize < $iFileLast Then ConsoleWrite(@CRLF & "[SHITEM" & $iRecordNumber & "] " & $sLongName & ":" & $iExtSig & ",") EndIf FileWrite($PathResultsIni, _ @CRLF & "[SHITEM" & $iRecordNumber & "]" & _ @CRLF & "FileStart= 0x" & Hex($iFileStart, 16) & @CRLF & "FileEnd= 0x" & Hex($iRecordEnd, 16) & _ @CRLF & "RecordLen= " & ($iRecordEnd - $iFileStart) & _ @CRLF & "FilePtr= " & ($iFilePos0) & _ @CRLF & "SHIconX= " & $iSHIconX & @CRLF & "SHIconY= " & $iSHIconY & _ @CRLF & "Size= " & $iSize & _ @CRLF & "Flags= " & Hex($iFlags, 2) & _ @CRLF & "SortOrderIndex= " & $iSortOrderIndex & _ ;added @CRLF & "FileSize= " & $iFileSize & _ @CRLF & "ModifiedDate= " & $sModDate & @CRLF & "ModifiedTime= " & $sModTime & _ @CRLF & "FileAttribs= " & $iFileAttribs & _ @CRLF & "Shortname= " & $sShortname & _ @CRLF & "ExtSize= " & $iExtSize & _ @CRLF & "ExtVer= " & $iExtVer & _ @CRLF & "ExtSIG= " & ($iExtSig) & _ @CRLF & "CreatedDate= " & $sCreDate & @CRLF & "CreatedTime= " & $sCreTime & _ @CRLF & "AccessDate= " & $sAccDate & @CRLF & "AccessTime= " & $sAccTime & _ @CRLF & "Unknown2= " & $iUnknown2 & _ @CRLF & "64FileRef= " & $i64FileRef & _ @CRLF & "Unknown3= " & $iUnknown3 & _ @CRLF & "LongNameSize= " & $iLongNameSize & _ @CRLF & "Unknown4= " & $iUnknown4 & _ @CRLF & "LongName= " & $sLongName & _ @CRLF & "LongNameAddl= " & $sLongNameAddl & _ @CRLF & "Unknown5= " & $iUnknown5 & @CRLF & @CRLF) ;@CRLF & "Unknown0= " & $iUnknown0 & _;@CRLF & "Unknown1= " & $iUnknown1 & _ (Replaced By $iExtSig) $iRecordNumber += 1 Next FileClose($hFile) Return $iRecordNumber EndFunc ;==>ParseSH_ItemToIni Func ReadRegHexStringValue(Const $hFile, $iLength, $sType) $sType = StringUpper($sType) Local $sData = "" If Not ($sType = "WSTRING" Or $sType = "STRING" Or $sType = "GUID") Then $iLength /= 8 $sData = FileRead($hFile, $iLength) EndIf Local $vReturn = "" Local $iCount = 0 Local $aTemp, $iTemp, $sTemp Switch $sType Case "STRING" $vReturn = "" For $i = 0 To 260 ;$iFilePos += 8 $sData = FileRead($hFile, 1) If $sData = Chr(00) Then If Not (FileRead($hFile, 1) = Chr(00)) Then FileSetPos($hFile, FileGetPos($hFile) - 1, 0) ;Sometimes Double Null Terminated ExitLoop EndIf $vReturn &= BinaryToString($sData) Next Case "WSTRING" $vReturn = "" For $i = 0 To 260 $sData = FileRead($hFile, 2) If $sData = Chr(00) & Chr(00) Then ExitLoop $vReturn &= BinaryToString($sData, $SB_UTF16LE) Next $aTemp = 0 Case "INT" $vReturn = Int($sData) Case "DOSDATE" $vReturn &= StringFormat("%02d/%02d/%04d", BitShift(BitAND($sData, 0x01e0), 5), BitAND($sData, 0x1F), BitShift($sData, 9) + 1980) Case "DOSTIME" ;UTC $vReturn &= StringFormat("%02d:%02d.%02d", BitShift($sData, 11), BitShift(BitAND($sData, 0x07e0), 5), BitAND($sData, 0x1F) * 2) Case "HEX" $vReturn = Hex(Int($sData), $iLength * 2) Case "HEXSTR" $vReturn = Hex($sData, $iLength * 2) Case "GUID" If $iLength = 128 Then $sData = FileRead($hFile, 4) $vReturn = Hex(Int($sData), 8) & "-" $sData = FileRead($hFile, 2) $vReturn &= Hex(Int($sData), 4) & "-" $sData = FileRead($hFile, 2) $vReturn &= Hex(Int($sData), 4) & "-" $sData = FileRead($hFile, 2) $vReturn &= Hex($sData, 2) & "-" $sData = FileRead($hFile, 6) $vReturn &= Hex($sData, 4) Else $vReturn = 0 EndIf #cs ;Old "GUID" ;= Hex(ReadRegHexStringValue($hFile, 32, "INT"), 8) & "-" & _ ;Hex(ReadRegHexStringValue($hFile, 16, "INT"), 4) & "-" & _ ;Hex(ReadRegHexStringValue($hFile, 16, "INT"), 4) & "-" & _ ;ReadRegHexStringValue($hFile, 16, "Hex") & "-" & _ ;ReadRegHexStringValue($hFile, 48, "HEX") #CE Case Else MsgBox(0, "ReadRegHexStringValue Error", "Datatype " & $sType & " not found.") EndSwitch Return $vReturn EndFunc ;==>ReadRegHexStringValue Edited May 9, 2016 by Bilgus Split Flags into Flags and SortOrderIndex also added mask of 0x70 on Flags for the longer shitem entry Biatu 1 Link to comment Share on other sites More sharing options...
Bilgus Posted May 9, 2016 Author Share Posted May 9, 2016 (edited) As of today this code is 1 day old. I haven't tried it on anything but Win7 x64 and only the Desktop Bags located in HKEY_CURRENT_USER\Software\Microsoft\Windows\Shell\Bags\1\Desktop. I'd consider this ALPHA and for sure not use it in any script that is anything near critical. It should be noted that this program only works by trying every position in the file until the Structure matches the data. Furthermore, it only depends on a few Items for verification. Namely: matching $iSize+2 to the record size, $iExtSig= 0xBEEF0000 , ($iFlags & 0x70) = 0x30 and That size is Greater than 20\21. For the builtin icons Flags = 0x1F, $iSortOrderIndex > 0 and GUID entry from the registry, This is probably pretty robust but I had to rearrange the GUID data BEndian and LEndian from the Reg Entry so maybe there is some kind of format to it or it won't work on every system IDK. I am not sure how robust any of this will be so hopefully someone has an epiphany Edited May 9, 2016 by Bilgus added info Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now