Skip to content

Commit

Permalink
refactor: replace pylnk3 with LnkParse3
Browse files Browse the repository at this point in the history
Signed-off-by: Jack Cherng <jfcherng@gmail.com>
  • Loading branch information
jfcherng committed Feb 16, 2024
1 parent 067ef54 commit d71887d
Show file tree
Hide file tree
Showing 63 changed files with 3,812 additions and 2,115 deletions.
1 change: 1 addition & 0 deletions plugin/libs/LnkParse3/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .lnk_file import LnkFile as lnk_file
65 changes: 65 additions & 0 deletions plugin/libs/LnkParse3/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from datetime import datetime
from datetime import timezone
from struct import unpack
import functools
import sys
import warnings

from .utils import parse_uuid, parse_packed_uuid, parse_filetime, parse_dostime


def must_be(expected):
def outer(func):
@functools.wraps(func)
def inner(self, *args, **kwargs):
result = func(self, *args, **kwargs)

if result != expected:
msg = "%s must be %s: %s" % (func.__name__, expected, result)
warnings.warn(msg)

return result

return inner

return outer


def uuid(func):
@functools.wraps(func)
def inner(self, *args, **kwargs):
binary = func(self, *args, **kwargs)

return parse_uuid(binary)

return inner


def packed_uuid(func):
@functools.wraps(func)
def inner(self, *args, **kwargs):
text = func(self, *args, **kwargs)

return parse_packed_uuid(text)

return inner


def filetime(func):
@functools.wraps(func)
def inner(self, *args, **kwargs):
binary = func(self, *args, **kwargs)

return parse_filetime(binary)

return inner


def dostime(func):
@functools.wraps(func)
def inner(self, *args, **kwargs):
binary = func(self, *args, **kwargs)

return parse_dostime(binary)

return inner
2 changes: 2 additions & 0 deletions plugin/libs/LnkParse3/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class LnkParserError(Exception):
...
File renamed without changes.
28 changes: 28 additions & 0 deletions plugin/libs/LnkParse3/extra/code_page.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from struct import unpack
from ..extra.lnk_extra_base import LnkExtraBase

"""
------------------------------------------------------------------
| 0-7b | 8-15b | 16-23b | 24-31b |
------------------------------------------------------------------
| <u_int32> BlockSize == 0x0000000C |
------------------------------------------------------------------
| <u_int32> BlockSignature == 0xA0000004 |
------------------------------------------------------------------
| <u_int32> CodePage |
------------------------------------------------------------------
"""


class CodePage(LnkExtraBase):
def name(self):
return "CONSOLE_CODEPAGE_BLOCK"

def code_page(self):
start, end = 8, 12
return unpack("<I", self._raw[start:end])[0]

def as_dict(self):
tmp = super().as_dict()
tmp["code_page"] = self.code_page()
return tmp
169 changes: 169 additions & 0 deletions plugin/libs/LnkParse3/extra/console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
from struct import unpack
from ..extra.lnk_extra_base import LnkExtraBase

"""
------------------------------------------------------------------
| 0-7b | 8-15b | 16-23b | 24-31b |
------------------------------------------------------------------
| <u_int32> BlockSize == 0x000000CC |
------------------------------------------------------------------
| <u_int32> BlockSignature == 0xA0000002 |
-----------------------------------------------------------------|
| <u_int16> FillAttributes |<u_int16> PopupFillAttributes |
------------------------------------------------------------------
| <int16> ScreenBufferSizeX | <int16> ScreenBufferSizeY |
------------------------------------------------------------------
| <int16> WindowSizeX | <int16> WindowSizeY |
------------------------------------------------------------------
| <int16> WindowOriginX | <int16> WindowOriginY |
------------------------------------------------------------------
| Unused1 |
------------------------------------------------------------------
| Unused2 |
------------------------------------------------------------------
| <u_int32> FontSize |
------------------------------------------------------------------
| <u_int32> FontFamily |
------------------------------------------------------------------
| <u_int32> FontWeight |
------------------------------------------------------------------
| <unicode_str> Face Name |
| 64 B |
------------------------------------------------------------------
| <u_int32> CursorSize |
------------------------------------------------------------------
| <u_int32> FullScreen |
------------------------------------------------------------------
| <u_int32> QuickEdit |
------------------------------------------------------------------
| <u_int32> InsertMode |
------------------------------------------------------------------
| <u_int32> AutoPosition |
------------------------------------------------------------------
| <u_int32> HistoryBufferSize |
------------------------------------------------------------------
| <u_int32> NumberOfHistoryBuffers |
------------------------------------------------------------------
| <u_int32> HistoryNoDup |
------------------------------------------------------------------
| <vector<u_int32>> ColorTable |
| 64 B |
------------------------------------------------------------------
"""


class Console(LnkExtraBase):
def name(self):
return "CONSOLE_PROPERTIES_BLOCK"

def fill_attributes(self):
start, end = 8, 10
return unpack("<H", self._raw[start:end])[0]

def popup_fill_attributes(self):
start, end = 10, 12
return unpack("<H", self._raw[start:end])[0]

def screen_buffer_size_x(self):
start, end = 12, 14
return unpack("<h", self._raw[start:end])[0]

def screen_buffer_size_y(self):
start, end = 14, 16
return unpack("<h", self._raw[start:end])[0]

def window_size_x(self):
start, end = 16, 18
return unpack("<h", self._raw[start:end])[0]

def window_size_y(self):
start, end = 18, 20
return unpack("<h", self._raw[start:end])[0]

def window_origin_x(self):
start, end = 20, 22
return unpack("<h", self._raw[start:end])[0]

def window_origin_y(self):
start, end = 22, 24
return unpack("<h", self._raw[start:end])[0]

def font_size(self):
start, end = 32, 36
return unpack("<I", self._raw[start:end])[0]

def font_family(self):
start, end = 36, 40
return unpack("<I", self._raw[start:end])[0]

def font_weight(self):
start, end = 40, 44
return unpack("<I", self._raw[start:end])[0]

def face_name(self):
start = 44
end = start + 64
binary = self._raw[start:end]
text = self.text_processor.read_unicode_string(binary)
return text

def cursor_size(self):
start, end = 108, 112
return unpack("<I", self._raw[start:end])[0]

def full_screen(self):
start, end = 112, 116
return unpack("<I", self._raw[start:end])[0]

def quick_edit(self):
start, end = 116, 120
return unpack("<I", self._raw[start:end])[0]

def insert_mode(self):
start, end = 120, 124
return unpack("<I", self._raw[start:end])[0]

def auto_position(self):
start, end = 124, 128
return unpack("<I", self._raw[start:end])[0]

def history_buffer_size(self):
start, end = 128, 132
return unpack("<I", self._raw[start:end])[0]

def number_of_history_buffers(self):
start, end = 132, 136
return unpack("<I", self._raw[start:end])[0]

def history_no_dup(self):
start, end = 136, 140
return unpack("<I", self._raw[start:end])[0]

def color_table(self):
start, end = 140, 144
return unpack("<I", self._raw[start:end])[0]

def as_dict(self):
tmp = super().as_dict()
tmp["fill_attributes"] = self.fill_attributes()
tmp["popup_fill_attributes"] = self.popup_fill_attributes()
tmp["screen_buffer_size_x"] = self.screen_buffer_size_x()
tmp["screen_buffer_size_y"] = self.screen_buffer_size_y()
tmp["window_size_x"] = self.window_size_x()
tmp["window_size_y"] = self.window_size_y()
tmp["window_origin_x"] = self.window_origin_x()
tmp["window_origin_y"] = self.window_origin_y()
tmp["font_size"] = self.font_size()
tmp["font_family"] = self.font_family()
tmp["font_weight"] = self.font_weight()
tmp["face_name"] = self.face_name()
tmp["cursor_size"] = self.cursor_size()
tmp["full_screen"] = self.full_screen()
tmp["quick_edit"] = self.quick_edit()
tmp["insert_mode"] = self.insert_mode()
tmp["auto_position"] = self.auto_position()
tmp["history_buffer_size"] = self.history_buffer_size()
tmp["number_of_history_buffers"] = self.number_of_history_buffers()
tmp["history_no_dup"] = self.history_no_dup()
tmp["color_table"] = self.color_table()
return tmp
97 changes: 97 additions & 0 deletions plugin/libs/LnkParse3/extra/darwin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from ..decorators import uuid, packed_uuid
from ..extra.lnk_extra_base import LnkExtraBase

"""
------------------------------------------------------------------
| 0-7b | 8-15b | 16-23b | 24-31b |
------------------------------------------------------------------
| <u_int32> BlockSize == 0x00000314 |
------------------------------------------------------------------
| <u_int32> BlockSignature == 0xA0000006 |
------------------------------------------------------------------
| <str> DarwinDataAnsi |
| 260 B |
------------------------------------------------------------------
| <unicode_str> DarwinDataUnicode |
| 520 B |
------------------------------------------------------------------
DarwinData consists of {Product-Code, Feature Key, Component Code}. It is stored
in a compressed format, e.g. "w_1^VX!!!!!!!!!MKKSkEXCELFiles>tW{~$4Q]c@II=l2xaTO5Z",
which can results into
{91120000-0030-0000-0000-0000000ff1ce}EXCELFiles{0638c49d-bb8b-4cd1-b191-052e8f325736}.
There are four variants according to
https://community.broadcom.com/symantecenterprise/viewdocument/working-with-darwin-descriptors:
1. {compressed product code}{feature name}>{compressed component ID}
2. {compressed product code}>{compressed component ID}
3. {compressed product code}{feature name}<
4. {compressed product code}<
See http://www.laurierhodes.info/?q=node/34 or
https://metadataconsulting.blogspot.com/2019/12/CSharp-Convert-a-GUID-to-a-Darwin-Descriptor-and-back.html
or https://web.archive.org/web/20080323160816/http://support.microsoft.com/kb/243630.
"""


class Darwin(LnkExtraBase):
def name(self):
return "DARWIN_BLOCK"

def darwin_data_ansi(self):
start = 8
end = start + 260
binary = self._raw[start:end]
text = self.text_processor.read_string(binary)
return text

def darwin_data_unicode(self):
start = 268
end = start + 520
binary = self._raw[start:end]
text = self.text_processor.read_unicode_string(binary)
return text

@packed_uuid
def product_code_id(self):
data = self.darwin_data_unicode()
start, end = 0, 20
text = data[start:end]
return text

def feature_name(self):
data = self.darwin_data_unicode()
start = 20
# Search for a termiantor sign which can be either `<` or `>`.
# None of these characters should be located in a packed GUID.
# If the character is not found, `find` returns `-1`, i.e. by using max
# we want to get the one which is present.
# An absence of both characters leads to an exception.
terminator = max(data.find(">"), data.find("<"))
if terminator == start:
# If the terminator is found but is the same as the start position,
# there is no feature name.
return None
end = terminator
text = data[start:end]
return text

@packed_uuid
def component_id(self):
data = self.darwin_data_unicode()
terminator = data.find(">")
if terminator == -1:
# Set the ID to `None` if `terminator` is not found.
return None
start = terminator + 1
text = data[start:]
return text

def as_dict(self):
tmp = super().as_dict()
tmp["darwin_data_ansi"] = self.darwin_data_ansi()
tmp["darwin_data_unicode"] = self.darwin_data_unicode()
tmp["product_code_id"] = self.product_code_id()
tmp["feature_name"] = self.feature_name()
tmp["component_id"] = self.component_id()
return tmp
Loading

0 comments on commit d71887d

Please sign in to comment.