2024年7月20日土曜日

windows の リンクファルの解析


winとlubuntuの dual 環境にして結構たつ。lubuntuで足りないものは Qt と PyQt で自力でプログラムを書きためて、最近はほぼwinを使わない生活になってきた。
のだが、PyQtで書いたファイラでwinのショートカットを開けないのが不便になってきて、なんとか自力で解析して開けるようにしたので備忘録として残しておく

pythonでの解析はこんな感じ

 import os,struct

# パスを指定
lnk_file_path = 'ここに .lnk のフルパス'

with open(lnk_file_path, 'rb') as f:
    data = f.read()

# ヘッダ情報の解析
header_size = 0x4c  # 常にこの値
ShellItemIdList_size = struct.unpack('H', data[header_size : header_size + 2])[0] + 2
LinkInfoOffset = header_size + ShellItemIdList_size
basePathOffset = struct.unpack('I', data[LinkInfoOffset + 16 : LinkInfoOffset + 20])[0] + LinkInfoOffset
Offset_end=0
for n in range(basePathOffset , data.__sizeof__() ) :         # 終端0(2バイト連続で0)を探す
    if struct.unpack('H', data[n : n + 2 ])[0] == 0 : 
        Offset_end = n
        break

basepath = data[basePathOffset : Offset_end].decode('shift-jis')

print(basepath)

これで取得できるのはあくまでwinのパスなので、lubuntu用にパスの変換は必要。
リンクファイルの構造は結構複雑なのでこのコードで取得できない場合もあると思うが私の環境では今のところ取得できてます。
リンクファイルのフォーマットについては公式ページからpdfで落とせるので興味があれば直接見たほうが良いと思うが、簡単な考え方は以下になります。

A リンクファイルの構造
 1. Header  
 2. Shell Item Id List  
 3. Link Info  
 4〜8 関係ないので省略

B 上記 1〜3 の構造

 1. Header (先頭)
   4 bytes ヘッダのサイズ 必ず 0x0000004C になる これが次のデータの先頭アドレスになる
  16 bytes リンクファイルのクラスID 必ず 00021401-0000-0000-C000-000000000046 になる
   4 bytes LinkFlags 詳細省略
   4 bytes FileAttributes 
  24 bytes Fileのタイムスタンプ 3 種類(8 × 3) 
  24 bytes その他詳細省略
  合計 76 bytes ⇒ 0x4c  (1)

 2. Shell Item Id List  
  2 bytes   次の IDList のサイズ
  variable IDList
  合計 2 + サイズ(アドレス 0x4c の 2bytes の数値) (2)

 3. Link Info  
   4 bytes LinkInfoSize
   4 bytes LinkInfoHeaderSize 7種類のオフセットの場合 0x1c(28)オプションがある場合 0x24 以下
   4 bytes LinkInfoFlags
   4 bytes VolumeIDOffset
   (合計 ここまで 16 bytes )
   4 bytes LocalBasePathOffset   (3) これがパスの位置 Link Info の先頭からのオフセット

    ファイルの先頭から (1) + (2) + (3)  の位置にパスの最初の文字がある
    パスの長さはデータとして与えられていないが、NULL–terminated string なので 0000 で終わるまで探せばわかる

バイナリを扱うのならcの方が簡単な気もするけど、python の struct は使いやすいね。
linuxでバイナリエディタを探してるけどGUIではなかなか見つからなくて、自作しようと思ってる。で、今のところは vim を使ってる。vim の凄さは少しづつわかりかけてるが、どうも敷居が高いというか、慣れません (-_-;)
---------------
7/20  コードちょっと修正した 4bytes の basePathOffset は I ですね しかしフォーマット的に無駄じゃないかな 2bytes で十分なような?

0 件のコメント:

コメントを投稿