Reputation: 504
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.
Upvotes: 4
Views: 1422
Reputation: 33
Building on @JosefZ's answer, I've analyzed the sequence of row values (and it's the same for column values) and found a cool pattern in the differences between consecutive terms. Starting from the third term, the differences between consecutive terms follow this sequence:
By taking the base-2 logarithm of these differences, we get the sequence of values:
What stands out is that the logarithmic value 7
appears once, 6
appears twice, 5
appears four times, and so on. In general, for a difference of (2^n), it appears (2^{7-n}) times.
To quantify this, I derived a formula for the difference between the (n)-th and ((n-1))-th terms of the sequence:
difference = math.pow(2, 7 - math.floor(math.log2(n - 2)))
Then, I created a function to generate a mapping of the sequence using this formula:
import math
def generate_mapping(n: int) -> dict:
mapping = {0: 1, 16256: 2}
tmp = 16256
for i in range(3, n + 1):
tmp += math.pow(2, 7 - math.floor(math.log2(i - 2)))
mapping[tmp] = i
return mapping
To optimize this, we can replace the expression math.pow(2, 7 - math.floor(math.log2(n - 2)))
with a bit-shift operation:
1 << (7 - (i - 2).bit_length() + 1)
This reduces computation time by avoiding floating-point operations. Here's the updated version of the function with the optimization:
def generate_mapping(n: int) -> dict:
mapping = {0: 1, 16256: 2}
tmp = 16256
for i in range(3, n + 1):
tmp += 1 << (7 - (i - 2).bit_length() + 1)
mapping[tmp] = i
return mapping
As you can see, the latter is much faster though this kind of optimization may be needless.
After applying the optimized mapping, I get the following result:
As you can see, the strange row and column values have been successfully converted into indexes.
Upvotes: 2
Reputation: 30183
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
…
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):
Upvotes: 4
Reputation: 1044
it seems that you have an binary file that must be tranformed in floats, strings, etc.
Here you have a function that I have made that uses struct unpack to read an amount of bytes and then transform then in float or int or str, etc.
# Import
import numpy as np
import struct
def ReadBytes(file, fmt, size):
"""
Function to read an amount of bytes and convert to the desired format.
Parameters
----------
file : Stream of file.
Stream from open file.
- `file = open("file.bin")`
fmt : str
String containing the format type of the object to be converted.
- <https://docs.python.org/3/library/struct.html>
size : int
How many bytes should be read.
Returns
-------
oc : object{int, float, str, ...}
Desired object read from bytes and converted to desired type.
"""
if size<0:
# If you have an array you can use size = -n indicating that the array has n positions.
# Length to be read.
bs = file.read(abs(size))
# Converting length.
size = struct.unpack('B',bs)[0]
# Format considering the len.
fmt = str(size)+fmt
# Object to be read, in bytes.
o = file.read(size)
# Converting the object to the format desired.
oc = struct.unpack(fmt, o)[0]
return oc
In a simple usage, you can just use the struct unpack itself.
The following shows how to unpack 4 bytes to int
:
struct.unpack('i', b'\x03\x01\x01\x01')
# Output-> (16843011,)
Upvotes: 1