pythonwindowsregistrybinaryfiles

How to read the values of IconLayouts REG_BINARY registry file


I want to make a program that gets the positions of the icons on the screen. And with some research I found out that the values I needed were in a registry binary file called IconLayouts (Located in HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\Shell\Bags\1\Desktop) I used python to get the positions using the winreg module. And succeeded on getting the values.

from winreg import *

aReg = ConnectRegistry(None, HKEY_CURRENT_USER)
aKey = OpenKey(aReg, r"Software\Microsoft\Windows\Shell\Bags\1\Desktop", REG_BINARY)

name, value, type_ = EnumValue(aKey, 9)
value = value.replace(b'\x00', b'')

This is the code I have. But the problem is I don't know what to do with these values. The program returns something like:

b'\x03\x01\x01\x01\x04,::{20D04FE0-3AEA-1069-A2D8-08002B30309D}>  ,::{645FF040-5081-101B-9F08-00AA002F954E}>  \x13Timetables.jpeg>  \nfolder>\\ \x01\x02\x01\x01\x02\x01\x0c\x04\x01\x04\x80?\x01@\x020A\x03'

I would appreciate if you would help me decipher this output and get the positions from it.


Solution

  • The following code snippet could help. It's very, very simplified code combined from Windows Shellbag Forensics article and shellbags.py script (the latter is written in Python 2.7 hence unserviceable for me).

    import struct
    from winreg import *
    
    aReg = ConnectRegistry(None, HKEY_CURRENT_USER)
    aKey = OpenKey(aReg, r"Software\Microsoft\Windows\Shell\Bags\1\Desktop", REG_BINARY)
    
    name, value, type_ = EnumValue(aKey, 9)
    
    offset = 0x10
    
    head = [struct.unpack_from("<H", value[offset:],0)[0],
            struct.unpack_from("<H", value[offset:],2)[0],
            struct.unpack_from("<H", value[offset:],4)[0],
            struct.unpack_from("<H", value[offset:],6)[0]]
    number_of_items = struct.unpack_from("<I", value[offset:],8)[0] # 4 bytes (dword)
    offset += 12
    for x in range( number_of_items):
        uint16_size     = struct.unpack_from("<H", value[offset:],0)[0];
        uint16_flags    = struct.unpack_from("<H", value[offset:],2)[0];
        uint32_filesize = struct.unpack_from("<I", value[offset:],4)[0];
        dosdate_date    = struct.unpack_from("<H", value[offset:],8)[0];
        dostime_time    = struct.unpack_from("<H", value[offset:],10)[0];
        fileattr16_     = struct.unpack_from("<H", value[offset:],12)[0];
        offset += 12
        entry_name      = value[offset:(offset + (2 * uint32_filesize - 8))].decode('utf-16-le')
        offset += (2 * uint32_filesize - 4 )
        if offset % 2:
            offset += 1
        print( x, uint32_filesize, entry_name)
    
    print( '\nThere is', (len(value) - offset), 'bytes left' )
    

    Output (truncated): .\SO\70039190.py

    0 44 ::{59031A47-3F72-44A7-89C5-5595FE6B30EE}
    1 44 ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}
    2 44 ::{5399E694-6CE5-4D6C-8FCE-1D8870FDCBA0}
    3 31 Software602 Form Filler.lnk
    4 8 test
    5 32 Virtual Russian Keyboard.lnk
    …
    49 22 WTerminalAdmin.lnk
    50 22 AVG Secure VPN.lnk
    51 44 ::{645FF040-5081-101B-9F08-00AA002F954E}
    52 29 powershell - Shortcut.lnk
    
    There is 1176 bytes left
    

    Honestly, I don't fully comprehend offset around entry_name

    Edit

    I have found (partial) structure for the rest of value. There are two tables containing row and column along with an index to desktop_items list for each desktop icon.

    Current row and column assignment is in the second table (see the picture below). The first table supposedly contains default assignments for automatic sort by (from desktop context menu).

    Unfortunately, I have no clue for interpretation of row and column values (e.g. 16256, 16384) to icon indexes (3rd row, 2nd column).

    import struct
    import winreg
    import pprint
    
    aReg = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER)
    aKey = winreg.OpenKey(aReg,
              r"Software\Microsoft\Windows\Shell\Bags\1\Desktop",
              winreg.REG_BINARY)
    name, value, type_ = winreg.EnumValue(aKey, 9)
    aKey.Close()
    aReg.Close()
    
    offset = 0x10
    head = [struct.unpack_from("<H", value[offset:],0)[0], # 2 bytes (word)
            struct.unpack_from("<H", value[offset:],2)[0],
            struct.unpack_from("<H", value[offset:],4)[0],
            struct.unpack_from("<H", value[offset:],6)[0],
            struct.unpack_from("<I", value[offset:],8)[0]  # 4 bytes (dword)
           ]
    number_of_items = head[-1]
    offset += 12
    desktop_items = []
    for x in range( number_of_items):
        uint16_size     = struct.unpack_from("<H", value[offset:],0)[0];
        uint16_flags    = struct.unpack_from("<H", value[offset:],2)[0];
        uint32_filesize = struct.unpack_from("<I", value[offset:],4)[0];
        dosdate_date    = struct.unpack_from("<H", value[offset:],8)[0];
        dostime_time    = struct.unpack_from("<H", value[offset:],10)[0];
        fileattr16_     = struct.unpack_from("<H", value[offset:],12)[0];
        offset += 12
        entry_name      = value[offset:(offset + (2 * uint32_filesize - 8))].decode('utf-16-le')
        offset += (2 * uint32_filesize - 4 )
        # uint16_size = location
        #        0x20 = %PUBLIC%\Desktop
        #        0x7c = %USERPROFILE%\Desktop
        desktop_items.append([x,
              '{:04x}'.format(uint16_size),
              0, 0,
              '{:04x}'.format(fileattr16_),
              entry_name])
        print('{:2}'.format(x),
              '{:04x}'.format(uint16_size),
              # '{:04x}'.format(uint16_flags), # always zero
              # '{:04x}'.format(dosdate_date), # always zero
              # '{:04x}'.format(dostime_time), # always zero
              '{:04x}'.format(fileattr16_),
              entry_name)
    
    print( '\nThere is', (len(value) - offset), 'bytes left' )
    print('head (12 bytes):', head)
    
    offs = offset
    head2 = []
    for x in range( 32):
        head2.append(struct.unpack_from("<H", value[offs:],2*x)[0])
    
    offs += 64
    print( 'head2 (64 bytes):', head2)
    for x in range( number_of_items):
        item_list = [
            struct.unpack_from("<H", value[offs:],0)[0],  # 0
            struct.unpack_from("<H", value[offs:],2)[0],  # column
            struct.unpack_from("<H", value[offs:],4)[0],  # 0
            struct.unpack_from("<H", value[offs:],6)[0],  # row 
            struct.unpack_from("<H", value[offs:],8)[0] ] # index to desktop_items
        # print( x, item_list)
        desktop_items[item_list[-1]][2] = int( item_list[1])
        desktop_items[item_list[-1]][3] = int( item_list[3])
        offs += 10
    
    print(len(value), offset, offs, (offs - offset), '1st table, from start:')
    table_1st = desktop_items
    table_1st.sort(key=lambda k: (k[2], k[3]))
    pprint.pprint(table_1st)
    #pprint.pprint(desktop_items)
    
    # 2nd table from behind
    offs = len(value)
    for x in range( number_of_items):
        offs -= 10 
        item_list = [
            struct.unpack_from("<H", value[offs:],0)[0],  # 0
            struct.unpack_from("<H", value[offs:],2)[0],  # column
            struct.unpack_from("<H", value[offs:],4)[0],  # 0
            struct.unpack_from("<H", value[offs:],6)[0],  # row 
            struct.unpack_from("<H", value[offs:],8)[0] ] # index to desktop_items
        # print(item_list)
        desktop_items[item_list[-1]][2] = int( item_list[1])
        desktop_items[item_list[-1]][3] = int( item_list[3])
    
    print(len(value), offset, offs, (offs - offset), '2nd table, from behind:')
    table_2nd = desktop_items
    table_2nd.sort(key=lambda k: (k[2], k[3]))
    pprint.pprint(table_2nd)
    # pprint.pprint(desktop_items)
    

    Result (an illustrative picture):

    result