## 개요 LNK 파일은 링크파일이라고 한다. 쉽게 이해하자면 바로가기라고 볼 수 있다. 어떤 프로그램의 링크 파일을 누르게 된다면 원본 파일에 “링크”되어 그 프로그램을 실행할 수 있는 편의기능이다. 그렇다면 의문이 들 수 있다. 바로가기 파일은 프로그램을 설치하거나 사용자가 편리하게 사용하기 위해 임의로 생성하는 것인데, 어떻게 포렌식 분석에 사용한다는 것인가? 눈치가 빠르다면 알았겠지만, 사용자만이 LNK 파일을 생성하는 것이 아니기 때문이다. 어떤 기능이 아티팩트로써 의미를 가진다는 것은 사용자의 행위를 시스템이 기록하여 해당 사용자의 행위를 추적할 수 있기 때문이라고 생각한다. 오늘은 아티팩트로써 LNK 파일을 어떻게 활용할 수 있는지, 그 구조는 어떻게 이루어져 있는지 알아볼 것이다. ## 파일 경로 사용자가 생성한 LNK 파일은 시스템 내 어디에나 존재할 수 있다. 따라서 LNK 파일은 어디에나 있을 수 있지만 시스템이 생성하는 LNK 파일의 경우 특정 경로에 생성되기 때문에 이를 알아둘 필요가 있다. 주로 생성되는 것은 최근 사용한 파일을 원본 프로그램과 링크하여 특정 경로에 생성하는 파일이다. ``` [Windows 7 and After] - C:\Users\<username>\AppData\Roaming\Microsoft\Windows\Recent - C:\Users\%USERNAME%\AppData\Roaming\Microsoft\Office\Recent\ [Windows XP] - C:\Documents and Settings\<username>\Recent ``` > [!NOTE] > 운영체제 유형에 따라 그 경로가 다르기 때문에 항상 아티팩트를 확인하기 전에 호스트 운영체제가 뭔지 파악할 필요가 있다. > ## 구조 LNK 파일은 구조가 매우 복잡한 편이라고 생각한다. 플래그 설정 값에 따라 어떤 구조체를 사용하는지가 달라지기 때문이다. 기본적으로 ShellLinkHeader가 존재하고, 데이터 플래그 값에 따라 `HasLinkTargetIDList` 가 설정되어 있다면 LinkTargetIDList가, `HasLinkInfo` 플래그가 활성화되어 있다면 LinkInfo 구조체에 정보가 저장되는 식으로 사용한다. 일반적으로 LNK 파일을 분석할 때 가장 흔하게 접해볼 수 있는 것은 LinkInfo 구조체이다. 따라서 ShellLinkHeader와 LinkInfo에 대해서 어떤 정보를 담고 있는지 알아보고 간단하게 실습을 진행해 보도록 하자. ### ShellLinkHeader | Offset | Size | | | ------ | ---- | ------------------------------------------------ | | 0x00 | 4 | 헤더 크기 (**항상 : 0x0000004C**) | | 0x04 | 16 | LNK GUID: {00021401-0000-0000-c000-000000000046} | | 0x14 | 4 | | | 0x18 | 4 | | | 0x1C | 8 | 생성 시각 (FILETIME 형식) | | 0x24 | 8 | 마지막 접근 시각 (FILETIME 형식) | | 0x2C | 8 | 마지막 수정 시각 (FILETIME 형식) | | 0x34 | 4 | 파일 크기 | | 0x38 | 4 | 아이콘 인덱스 값 | | 0x3C | 4 | | | 0x40 | 2 | | | 0x42 | 2 | Unknown (Reserved) | | 0x44 | 4 | Unknown (Reserved) | | 0x48 | 4 | Unknown (Reserved) | | | | | - 데이터 플래그 | Value | Identifier | Description | | ---------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 0x00000001 | HasTargetIDList | The LNK file contains a link target identifier | | 0x00000002 | HasLinkInfo | The LNK file contains location information | | 0x00000004 | HasName | The LNK file contains a description data string | | 0x00000008 | HasRelativePath | The LNK file contains a relative path data string | | 0x00000010 | HasWorkingDir | The LNK file contains a working directory data string | | 0x00000020 | HasArguments | The LNK file contains a command line arguments data string | | 0x00000040 | HasIconLocation | The LNK file contains a custom icon location | | 0x00000080 | IsUnicode | The data strings in the LNK file are stored in Unicode (UTF-16 little-endian) instead of ASCII | | 0x00000100 | ForceNoLinkInfo | The location information is ignored | | 0x00000200 | HasExpString | The LNK file contains environment variables location data block | | 0x00000400 | RunInSeparateProcess | A 16-bit target application is run in a separate virtual machine. | | 0x00000800 | | Unknown (Reserved) | | 0x00001000 | HasDarwinID | The LNK file contains a Darwin (Mac OS-X) properties data block | | 0x00002000 | RunAsUser | The target application is run as a different user. | | 0x00004000 | HasExpIcon | The LNK file contains an icon location data block | | 0x00008000 | NoPidlAlias | The file system location is represented in the shell namespace when the path to an item is parsed into the link target identifiersContains a known folder location data block? | | 0x00020000 | RunWithShimLayer | The target application is run with the shim layer. The LNK file contains shim layer properties data block. | | 0x00040000 | ForceNoLinkTrack | The LNK does not contain a distributed link tracking data block | | 0x00080000 | EnableTargetMetadata | The LNK file contains a metadata property store data block | | 0x00100000 | DisableLinkPathTracking | The environment variables location block should be ignored | | 0x00200000 | DisableKnownFolderTracking | Unknown | | 0x00400000 | DisableKnownFolderAlias | Unknown | | 0x00800000 | AllowLinkToLink | Unknown | | 0x01000000 | UnaliasOnSave | Unknown | | 0x02000000 | PreferEnvironmentPath | Unknown | | 0x04000000 | KeepLocalIDListForUNCTarget | Unknown | | | | | - 파일 속성 플래그 | Value | Identifier | Description | | ---------- | ---------------------------------- | ------------------------------------------------------------------------------------------------------- | | 0x00000001 | FILE_ATTRIBUTE_READONLY | Is read-only | | 0x00000002 | FILE_ATTRIBUTE_HIDDEN | Is hidden | | 0x00000004 | FILE_ATTRIBUTE_SYSTEM | Is a system file or directory | | 0x00000008 | | Reserved, not used by the LNK formatIs a volume label | | 0x00000010 | FILE_ATTRIBUTE_DIRECTORY | Is a directory | | 0x00000020 | FILE_ATTRIBUTE_ARCHIVE | Should be archived | | 0x00000040 | FILE_ATTRIBUTE_DEVICE | Reserved, not used by the LNK formatIs a device | | 0x00000080 | FILE_ATTRIBUTE_NORMAL | Is normalNone of the other flags should be set | | 0x00000100 | FILE_ATTRIBUTE_TEMPORARY | Is temporary | | 0x00000200 | FILE_ATTRIBUTE_SPARSE_FILE | Is a sparse file | | 0x00000400 | FILE_ATTRIBUTE_REPARSE_POINT | Is a reparse point or symbolic link | | 0x00000800 | FILE_ATTRIBUTE_COMPRESSED | Is compressed | | 0x00001000 | FILE_ATTRIBUTE_OFFLINE | Is offlineThe data of the file is stored on an offline storage. | | 0x00002000 | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | Do not index contentThe content of the file or directory should not be indexed by the indexing service. | | 0x00004000 | FILE_ATTRIBUTE_ENCRYPTED | Is encrypted | | 0x00008000 | | Unknown (seen on Windows 95 FAT) | | 0x00010000 | FILE_ATTRIBUTE_VIRTUAL | Currently reserved for future use, not used by the LNK formatIs virtual | | | | | - 실행후 윈도우 창 동작 |Value|ID|Description| |---|---|---| |0|SW_HIDE|Hides the window and activates another window.| |1|SW_NORMALSW_SHOWNORMAL|Activates and displays the window. The window is restored to its original size and position if the window is minimized or maximized.| |2|SW_SHOWMINIMIZED|Activates and minimizes the window.| |3|SW_MAXIMIZESW_SHOWMAXIMIZED|Activates and maximizes the window.| |4|SW_SHOWNOACTIVATE|Display the window in its most recent position and size without activating it.| |5|SW_SHOW|Activates the window and displays it in its current size and position.| |6|SW_MINIMIZE|Minimizes the window and activates the next top-level windows (in order of depth (Z order))| |7|SW_SHOWMINNOACTIVE|Display the window as minimized without activating it.| |8|SW_SHOWNA|Display the window in its current size and position without activating it.| |9|SW_RESTORE|Activates and displays the window. The window is restored to its original size and position if the window is minimized or maximized.| |10|SW_SHOWDEFAULT|Set the show state based on the ShowWindow values specified during the creation of the process.| |11|SW_FORCEMINIMIZE|Minimizes a window, even if the thread that owns the window is not responding.| - 핫키 (단축키) **Upper Bytes** |Value|Identifier|Description| |---|---|---| |0x01|HOTKEYF_SHIFT|The shift key| |0x02|HOTKEYF_CONTROL|The control key| |0x04|HOTKEYF_ALT|The alt key| **Lower Bytes** |Value|Identifier|Description| |---|---|---| |0x30 – 0x39||Numeric keys 0 - 9| |||| |0x41 – 0x5a||Upper case alphabetical keys A- Z| |||| |0x70|VK_F1|Function key 1| |0x71|VK_F2|Function key 2| |0x72|VK_F3|Function key 3| |0x73|VK_F4|Function key 4| |0x74|VK_F5|Function key 5| |0x75|VK_F6|Function key 6| |0x76|VK_F7|Function key 7| |0x77|VK_F8|Function key 8| |0x78|VK_F9|Function key 9| |0x79|VK_F10|Function key 10| |0x7a|VK_F11|Function key 11| |0x7b|VK_F12|Function key 12| |0x7c|VK_F13|Function key 13| |0x7d|VK_F14|Function key 14| |0x7e|VK_F15|Function key 15| |0x7f|VK_F16|Function key 16| |0x80|VK_F17|Function key 17| |0x81|VK_F18|Function key 18| |0x82|VK_F19|Function key 19| |0x83|VK_F20|Function key 20| |0x84|VK_F21|Function key 21| |0x85|VK_F22|Function key 22| |0x86|VK_F23|Function key 23| |0x87|VK_F24|Function key 24| |||| |0x90|VK_NUMLOCK|Num lock key| |0x91|VK_SCROLL|Scroll lock key| ### LinkInfo |Offset|Size|Description| |---|---|---| |0|4|LinkInfo 크기| |4|4|헤더 크기| |8|4|| |12|4|볼륨 정보 오프셋| |16|4|로컬 경로 오프셋| |20|4|네트워크 공유 오프셋| |24|4|일반 경로 오프셋| `if header size ≥ 0x1C` |Offset|Size|Value|Description| |---|---|---|---| ||||로컬 경로 오프셋 (Unicode)| |…​|…​||Volume Information| |…​|…​||로컬 경로 문자열 (ASCII) (Local Information)| |…​|…​||공공 경로 (ASCII) ASCII string terminated by an end-of-string character (Network Share)| `if header size ≥ 0x20` |Offset|Size|Value|Description| |---|---|---|---| |…​|…​||공공 경로 오프셋 (Unicode)| |​|…​||Volume Information| |…​|…​||로컬 경로 문자열 (ASCII)| |…​|…​||공공 경로 (UTF-16 LE)| - 위치 정보 플래그 |Value|Identifier|Description| |---|---|---| |0x0001|VolumeIDAndLocalBasePath|The linked file is on a volumeIf set the volume information and the local path contain data| |0x0002|CommonNetworkRelativeLinkAndPathSuffix|The linked file is on a network shareIf set the network share information and common path contain data| ## 데이터 어떤 데이터를 담고 있는지는 구조를 자세히 보았다면 알 수 있을 것이다. 하지만 표만 봐도 좀 복잡하고 정보가 많기 때문에 주요한 정보만 자세히 알아보도록 하자. ### 시간 값 어떤 파일이 언제 어떻게 사용되었는지 확인하는 것이 LNK 파일 아티팩트의 본질이다. 즉 LNK파일이 언제 생성되었고, 언제 수정되었는지를 통해서 1차적으로 정보를 얻어갈 수 있고, LNK 파일에 링크된 파일 (원본 파일)의 시간 정보를 통해서도 파일의 실행 여부를 확인해볼 수 있다. ### 절대 경로 & 디바이스 정보 실행된 파일의 이름과 절대 경로를 파악할 수 있다. 다만 주의사항에 적혀있는 것처럼 인코딩에 유의하여 분석해야 한다. 또한 실행된 디바이스에 대한 Volume Information을 얻어갈 수 있기 때문에 로컬 장치인지, 외부 장치인지 식별해낼 수 있다. > [!Warning] 주의사항 > > **데이터를 봤을 때 인코딩이 다들 제각각이었던 것을 확인할 수 있다. 헤더 크기에 따라 Unicode(UTF-8), ASCII, Unicode(UTF-16LE) 등 하나의 파일 안에서 다양한 인코딩을 이용해 데이터를 처리하게 된다. 이런 이유 때문에 도구를 사용하는 경우 인코딩이 깨진 상태로 결과가 출력되는 일이 잦을 것이므로, 도구를 탓하지 말고 인코딩을 변경해보자. > ## 분석 도구 - 010-editor ( [LNK format](https://www.sweetscape.com/010editor/repository/files/LNK.bt) ) - LNK parser ( [download](http://forensic.korea.ac.kr/tools/20170327_LinkParser.exe) ) ## 결론 LNK 파일은 많이 쓰이긴 하면서도… 어떻게 보면 그렇게 많이 참조하지는 않을 수도 있다. 때로는 등잔 밑이 어두울 수도 있기 때문에 해결해야하는 문제의 유형에 따라 알맞게 사용할 수 있다면 그것만으로 충분할 것이다. 간단한 구조에 대해서도 알아보았고, 작은 파일 크기에 비해 들어있는 정보가 생각보다 많다는 것을 느꼈을 것이다. 너무 분석 도구에만 의존하지 말고, 구조에 맞게 직접 도구를 간단하게 만들어 보는 것도 좋은 연습이라고 생각한다 :)