Kali-KM_Security Study

메모리에서 특정 코드로 시작하는 스레드를 찾는 코드이다. 주로 코드 인젝션을 통해 다른 프로세스에 코드가 인젝션 되었는지 확인할 때 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#include <Windows.h>
#include <iostream>
#include <TlHelp32.h>
 
#define ThreadQuerySetWin32StartAddress 9  
 
using namespace std;
typedef NTSTATUS(WINAPI *NtQueryInformationThreadT)(HANDLE ThreadHandle, ULONG ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);
 
BOOL GetThreadStartAddress(DWORD tid, PVOID *EntryPoint);
BOOL CompareBinary(CHAR Buffer[]);
 
int main()
{
    BOOL result;
    THREADENTRY32 t32;
    PVOID EntryPoint;
    HANDLE hProc;
    CHAR Buffer[0x100];
    DWORD NumberofByteRead;
    HANDLE hSnap;
 
    printf("\n[*] Memory Detection - Reverse_L01.exe\n");
 
    /* Create a Snapshot Handle */
    hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    if (hSnap == INVALID_HANDLE_VALUE)
        return 0;
 
    /* if you don't initialize THREADENTRY32.dwSize, Thread32First API fails */
    t32.dwSize = sizeof(THREADENTRY32);
    if (Thread32First(hSnap, &t32))
    {
        do
        {
            result = GetThreadStartAddress(t32.th32ThreadID, &EntryPoint);
            if ((DWORD)EntryPoint == 0x1)
            {
                continue;
            }
            hProc = OpenProcess(PROCESS_VM_READ, 0, t32.th32OwnerProcessID);
            ReadProcessMemory(hProc, EntryPoint, Buffer, 0x100&NumberofByteRead);
 
            if (CompareBinary(Buffer))
            {
                printf("\t+ Detect It, Process ID : %d, Thread ID : %d, EntryPoint :0x%X\n", t32.th32OwnerProcessID, t32.th32ThreadID, EntryPoint);
            }
        } while (Thread32Next(hSnap, &t32));
    }
    CloseHandle(hSnap);
    
    return 0;
}
 
BOOL GetThreadStartAddress(DWORD tid, PVOID *EntryPoint)
{
    PVOID ThreadInfo;
    ULONG ThreadInfoLength;
    PULONG ReturnLength;
 
    HMODULE hNtdll = LoadLibrary("ntdll.dll");
    NtQueryInformationThreadT NtQueryInformationThread = (NtQueryInformationThreadT)GetProcAddress(hNtdll, "NtQueryInformationThread");
    
    if (!NtQueryInformationThread)
        return FALSE;
 
    /* if NtQueryInformationThread's THREADINFOCALSS is a ThreadQurtySetWin32StartAddress, return start address of thread */
    HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, 0, tid);
    NTSTATUS NtStat = NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, &ThreadInfo, sizeof(ThreadInfo), NULL);
    
    *EntryPoint = ThreadInfo;
    return TRUE;
}
 
BOOL CompareBinary(CHAR Buffer[])
{
    char CmpCode[0x67= { '\x6A''\x00''\x68''\x00''\x20''\x40''\x00''\x68''\x12''\x20''\x40''\x00',
        '\x6A''\x00''\xE8''\x4E''\x00''\x00''\x00''\x68''\x94''\x20''\x40''\x00''\xE8''\x38',
        '\x00''\x00''\x00''\x46''\x48''\xEB''\x00''\x46''\x46''\x48''\x3B''\xC6''\x74''\x15',
        '\x6A''\x00''\x68''\x35''\x20''\x40''\x00''\x68''\x3B''\x20''\x40''\x00''\x6A''\x00',
        '\xE8''\x26''\x00''\x00''\x00''\xEB''\x13''\x6A''\x00''\x68''\x5E''\x20''\x40''\x00',
        '\x68''\x64''\x20''\x40''\x00''\x6A''\x00''\xE8''\x11''\x00''\x00''\x00''\xE8''\x06',
        '\x00''\x00''\x00''\xFF''\x25''\x50''\x30''\x40''\x00''\xFF''\x25''\x54''\x30''\x40',
        '\x00''\xFF''\x25''\x5C''\x30''\x40''\x00' };
 
    for (int i = 0; i < sizeof(CmpCode); i++)
    {
        if ((BYTE)CmpCode[i] != (BYTE)Buffer[i])
        {
            return FALSE;
        }
    }
    return TRUE;
}
 
cs




Comment +0

아주 기초적인 DLL 파일로 지정된 경로(코드에선 Kali\Dekstop)에 아무런 내용이 없는 txt 파일을 만드는 코드이다.

아래 코드는 DLL 을 로드하기 위한 EXE 파일의 코드이다.


Comment +0

지정한 핸들의 파일이 PE 파일인지 확인하는 코드이다. MZ 헤더와 PE 시그니쳐를 확인하며, 실제 파일의 크기가 PE 구조에 나타난 크기보다 작지는 않는지 확인한다. PE 구조를 파싱하는 부분은 아래와 동일하므로 조금씩 수정하면 다른 부분들도 확인할 수 있다.


Comment +0

인자로 지정한 경로에 존재하는 파일을 탐색하는 코드이다. 만약 폴더를 탐색하면 재귀를 통해 다시 그 안에 파일들까지 탐색한다. 컴파일러에 따라 "_CRT_SECURE_NO_WARNINGS"를 추가해주어야 하거나, "char * 형식의 인수가 LPCWSTR 형식의 매개 변수와 호환되지 않습니다" 라는 오류로 인해 유니코드 문자 집합 사용 설정을 해제해주어야 한다.



Comment +0


'Programming > Python' 카테고리의 다른 글

Python - PE Parser  (1) 2016.05.16
Python - Yara Launcher  (2) 2016.03.06
Pytsk3를 통한 MFT 추출  (2) 2016.02.23
Python - Simple Extract File  (0) 2016.02.17
Python - MFT Path Parsing  (0) 2016.02.04
Prefetch Parser 제작기  (2) 2015.12.26

Comment +1



'Programming > Python' 카테고리의 다른 글

Python - PE Parser  (1) 2016.05.16
Python - Yara Launcher  (2) 2016.03.06
Pytsk3를 통한 MFT 추출  (2) 2016.02.23
Python - Simple Extract File  (0) 2016.02.17
Python - MFT Path Parsing  (0) 2016.02.04
Prefetch Parser 제작기  (2) 2015.12.26

Comment +2

  • 감동 2016.11.22 17:32

    yara를 위한 소스코드 잘봤습니다.
    그런데, def Filter_string(matched): 에서 fil_1과 fil_2 변수는 어디에서 사용되는지요? 소스를 찾아봐도 빈 선언문과 조건문밖에 보이지 않아 뭔가 더 필요해 보입니다.

    • 감동 2016.11.22 17:37

      아.. 확인했습니다..

      제가 필요한 매칭을 넣어서 결과중에서 가져오는 용도로 사용하는 것이군요.

      감사합니다..



'Programming > Python' 카테고리의 다른 글

Python - PE Parser  (1) 2016.05.16
Python - Yara Launcher  (2) 2016.03.06
Pytsk3를 통한 MFT 추출  (2) 2016.02.23
Python - Simple Extract File  (0) 2016.02.17
Python - MFT Path Parsing  (0) 2016.02.04
Prefetch Parser 제작기  (2) 2015.12.26

Comment +2

  • Kali-KM 2016.05.09 08:57

    19번 라인에 wirte > write 로 수정해야함

  • 구르마 2016.06.13 17:12

    pytsk를 사용하기 위해 찾던 중에 포스트 내용 잘 보았습니다. 공부중인데 많은 도움이 되었어요.
    질문이 있는데, 혹시 분할된 이미지 파일을 Open하는 방법도 알고 계신가요?
    전송 목적으로 분할해서 이미징을 하기도 하는데 이 이미지 파일들을 열어보고 싶거든요.
    C버전에서는 열 수 있었는데 혹시 pytsk3에서도 가능한가요?



'Programming > Python' 카테고리의 다른 글

Python - Yara Launcher  (2) 2016.03.06
Pytsk3를 통한 MFT 추출  (2) 2016.02.23
Python - Simple Extract File  (0) 2016.02.17
Python - MFT Path Parsing  (0) 2016.02.04
Prefetch Parser 제작기  (2) 2015.12.26
Windows Timestamp Convert 64bit  (0) 2015.12.12

Comment +0

$MFT Path Parsing


  추출된 $MFT에서 파일의 경로를 조합하기 위하여 만들어보았다. 영어의 경우 이상이 없지만, 한글의 경우 인코딩 문제로 인하여 깨짐 현상이 발생한다. 그러한 면에서는 아직 미완성이지만 $MFT에서 파일의 경로를 알아낼 때 필요하기에 만들어보았다.

+ 한글 인코딩 문제도 해결 




'Programming > Python' 카테고리의 다른 글

Pytsk3를 통한 MFT 추출  (2) 2016.02.23
Python - Simple Extract File  (0) 2016.02.17
Python - MFT Path Parsing  (0) 2016.02.04
Prefetch Parser 제작기  (2) 2015.12.26
Windows Timestamp Convert 64bit  (0) 2015.12.12
hex viewer의 일대기  (1) 2015.11.04

Comment +0


prefetch_parser.exe


1. 개요


1.1 동기

Prefetch 분석 도구를 제작하고자 현재 프리패치와 관련된 툴로 WinPrefetchView를 사용하고 있지만 불편한 점을 느꼈다. 실행 시 프리패치 파일에 대하여 상세히 분석을 해주지만 현재 실행시킨 시스템에서의 파일만 분석을 한다는 것이다.

Figure 1. WinPrefetchView

 

그렇기에 다른 PC의 프리패치 파일을 분석하기 위해선 그 해당 PC에서 실행을 하거나 해야한다. 하지만 모든 상황에서 실행이 가능한 것은 아니기 때문에 최대한 흔적을 적게 남기는 것을 목표로 하고자 한번 만들게 되었다.

  

1.2 구상

우선 어떻게 만들지 구상을 해보았다. 여러 가지 고려 해야 할 사항은 매우 많지만 가장 크게 뽑는 다면 다음과 같다.

      • 제작에 사용할 프로그래밍 언어 - Python
      • 윈도우 버전 – Win10의 경우 압축이 되어있음
      • GUI vs CLI – 흔적을 최소화 하기 위하여 CLI

이와 같이 3가지 사항에 대하여 크게 고민을 하였다. 프로그래밍 언어의 경우 다룰 수 있는 것이 파이썬 밖에 없었기에 결정하는데 큰 고민이 없었지만 윈도우 버전의 경우 압축 해제 과정이 들어가야 하기에 어느 버전으로 할 것 인지 고민했다.

하지만 WinPrefetchView에서는 두 가지 모두 지원을 하고 있었다. 그러므로 하나로 통합은 힘들 테니 각 버전에 맞는 2개를 만들어보자 라는 생각을 하게 되었다.

GUI의 경우 사용에 있어 편의성을 향상 시키겠지만, 우선 내가 아직 GUI를 한번도 만들어보지 않았다는 점이 크게 작용하여 결국 CLI로 택하였다.

  

1.3 전체적인 흐름

어떻게 프로그래밍을 해야 할까 고민을 해보았다. 어떻게 동작을 해야 하나 고민을 하고 다음과 같이 생각하였다.

      1. 우선 파일을 읽어야 한다. f=open('.pf','rb'). 그렇다면 이 파일은 어떻게 지정할 것인가?
      2. sys라이브러리를 통해 인자로 받는 것이 별 힘이 안 들 것 같음
      3. 읽은 파일이 압축 된 것일 경우 압축을 해제 – 압축 해제에는 다른 분의 소스를 사용
      4. 해제된 데이터를 읽어 .pf파일의 포맷에 맞게 읽음
      5. 이름, 사이즈, 마지막 실행시간, pf파일생성시간, pf파일 수정시간, 런카운트, 실행에 포함되는 파일의 이름들
      6. 5의 사항들을 출력

이렇게 전체적인 흐름을 생각하였고 이에 맞추어 코드를 하나하나 타이핑 하였고, 하면서도 많은 부분 분노와 슬픔이 있었다.



2 코드 쓰기


2.1 경로 인자 받기 & 압축 해제

우선 파일의 경로를 인자로 받아야 한다. Sys 모듈을 사용하고 인자로 받은 pf파일이 있는 경로에서 .pf 확장자를 찾아 선별하여야 한다.

Figure 2. 인자받기 & PF확장자 찾기

위의 코드와 같이 첫 번째 줄에서 sys.argv[1]로 인자를 받는다. 그리고 그 해당 경로에서 .pf 확장자를 갖는 file을 찾는 코드이다.

 

이제 인자로 받은 파일이 XPRESS 압축이 되어 있는 경우 압축을 해제하여야 한다. 이는 francesco.picasso@gmail.com 분의 소스를 가져왔으며 원래는 인자로 2개를 받아 input file과 output file을 지정해주어야 하지만, 이를 수정하여 output을 파일이 아니라 bytearray의 형태로 압축 해제된 데이터를 반환하도록 하였다.

Figure 3. XPRESS 압축해제

  

2.2 Pf 파일 이름 읽기

이제 윈도우 10의 경우에도 압축이 해제된 데이터를 가질 수가 있다. 그러므로 포맷에 맞게 간단히 데이터를 읽기만 하면 되는 줄 알았다. F.read()의 데이터를 buf라 했을 때 Pf 파일의 이름은 buf[16:49]이다. 그대로 읽기만 하면 되는데 문제가 생겼다. 아래의 그림을 보자.

Figure 4. NULL이 포함된 이름

파일의 이름이 Unicode의 형태처럼 2바이트씩 쓰여 있는 것을 알 수가 있다. 따라서 이를 그냥 출력하면 'M E L O N . E X E'와 같이 출력이 된다. 그냥 써도 별 문제는 없겠지만 나중에 해당 파일의 이름을 복붙하는 과정에 있어 저 공백 부분을 매번 지우기가 귀찮을 것 같아 공백 없이 출력되도록 하였다.

Figure 5. NULL 없애기

함수의 인자로 buf[16:49]와 같이 주면 buf[16]부터 buf[49]까지 하나 하나 읽으며 \x00을 buf배열에서 제거한다. 그리고 \x00이 제거된 부분만 반환을 하는 부분이다.

Figure 6. 출력된 파일 이름

이렇게 성공적으로 이름이 출력되는 것을 위의 그림과 같이 확인할 수가 있다. 이제 뭔가 쉽게 잘 풀리는 것 같아서 좋다.


2.3 파일 사이즈 출력

파일 사이즈도 출력을 해보자. 파일의 사이즈는 아래의 그림과 같이 12:15에 존재하고 있다. 이 부분을 10진수로 읽어 출력을 하면 된다.

Figure 7. 파일 사이즈

16진수를 10진수로 나타내기 위해서는 해당 부분을 읽은 다음에 10진수로 변환하여 출력을 해주어야 한다. 하지만 %d로 출력을 바로 하면 아래와 같은 결과를 볼 수가 있다.

 

Figure 8. 파일 사이즈 출력 오류

Bytearray형식으로 읽었기 때문에 %d의 형식으로 출력을 할 수가 없다는 것이다. 그렇다면 %s로 출력을 하면 어떨까?

 

Figure 9. 파일 사이즈 출력 오류

위의 그림과 같이 이상하게 출력되는 것을 확인할 수가 있다. 그렇다면 어떻게 해야 할까 고민을 하던 중 아는 분이 mft와 관련하여 분석하는 Python 코드를 보았는데 리틀엔디언을 십진수로 출력해주는 코드가 있기에 가져왔다.

Figure 10. LittleEndianToInt

이 함수를 통하여 리틀엔디언으로 나타난 부분을 바로 10진수로 출력해주는 것을 확인할 수가 있다. 참 편리한 부분이다.

Figure 11. 파일 사이즈 출력

원하는 바와 같이 204910이 제대로 출력되는 것을 확인할 수가 있다. 이렇게 파일의 이름과 사이즈의 출력에 성공하였다.


2.4 마지막 실행 시간 출력

포렌식 툴인 만큼 가장 중요한 부분은 바로 시간과 관련된 부분이다. 0x80~0x88까지의 부분이 프리패치 파일이 마지막으로 실행된 시간(Last Run Time)이다.

Figure 12. 마지막 실행시간 위치

 

시간과 관련하여 꽤 시간을 소모하였다. 시간을 출력하는데 datetime 모듈을 사용하였으며 필요한 코드는 아래의 그림과 같다.

Figure 13. 시간 변환 함수

우선 리틀엔디언을 빅엔디언 10진수로 변환해주고 그 다음 time_convert라 써있는 부분의 함수를 호출하여 알맞은 형태로 출력되게 하고자 하였다. 여기서 애를 먹은 부분은 3번째와 5번째라인이다. 우선 5번째 라인의 마지막 timedelta에 hours=9를 해주므로 한국 기준 시간인 UTC 9:00을 맞출 수 있었다. 만약 저 부분을 쓰지 않는다면 한국 시간과 9시간 차이가 나는 값을 얻게 된다.

Figure 14. 시간 출력 오류

3번째 라인의 경우 쓰지 않을 경우 위와 같이 에러가 출력이 되는데, 3번째 라인을 써주므로 문자열의 형태로 만든 다음 원하는 결과를 제대로 나타낼 수 있게 해주므로 나의 코드에서는 반드시 써주어야 했다.

Figure 15. 시간 출력 성공

위의 과정들을 통하여 코드를 완성하였다. 그리고 위와 같이 올바른 값을 출력하도록 할 수 있었다.


2.5 pf파일 생성시간 & 수정시간 출력

생성시간과 수정시간의 경우 실행한 원래의 파일에 대한 것이 아니라 프리패치 파일이 생성된 시간과 수정된 시간에 관하여 출력을 나타낸다.

Figure 16. 생성시간 & 수정시간

이 둘은 datetime 모듈과 os.path의 getctime과 getmtime을 통하여 쉽게 얻을 수가 있었다. 이에 대한 출력 결과는 아래와 같다.

Figure 17. 생성시간 & 수정시간 출력 성공

  

2.6 실행 횟수 출력

프리패치 파일에는 해당 파일을 몇 번 실행했는지 또한 나타나있다. 이는 pf 파일의 0xD0에서 0xD3까지에 위치해 있다.

Figure 18. Run Count 위치

이 경우도 2.3에서 파일의 사이즈 출력과 비슷한 작업을 거쳐 출력을 해주면 된다. 우선 리틀 엔디언의 형태로 되어있으므로 빅엔디언으로 변환하고 10진수로 출력을 해주면 된다. 여기선 위와 같이 LittleEndianToInt 함수를 사용하였다.

Figure 19. Run Count 출력 성공


2.7 포함되는 파일 목록

프리패치 파일에는 해당 프로그램이 실행 될 때 로드하는 파일에 대하여 목록을 가지고 있다. 이러한 파일의 목록은 분석을 하는 입장에서도 중요한데, 만약 잘못된 위치에서 의심스러운 파일이 로드 되는 등의 행위는 분석에 있어 시간을 단축할 수 있게 해주는 중요한 단서가 된다.

Figure 20. 파일 목록 & 크기

0x64~0x68까지는 로드되는 파일의 이름이 있는 위치를 나타내며 0x68~0x6C는 파일 목록이 저장된 위치의 크기를 나타내어 준다. 따라서 offset은 0x64~0x68이며 size는 0x68~0x6C이므로 로드된 파일의 목록이 나타나는 마지막 위치는 offset+size이 된다.

Figure 21. 로드된 파일 목록 함수

우선 file list의 offset을 0x64에서 확인을 해주고 size는 0x68에서 확인을 해준다. 그리고 이를 통해 마지막 위치를 알 수가 있으니 해당 부분을 읽으면 된다. 하지만 여기서 아래의 그림을 확인하자.

Figure 22. 유니코드와 NULL 구분

표시한 부분이 바로 로드된 파일의 나누는 기준이 된다. 전체적으로 유니코드로 나타나기에 \x00\x00을 통해 파일을 나누어야 한다. 그렇기에 위의 코드에서 Unicode_split를 추가한 것이다.

이렇게 \x00\x00을 통해 각 파일들을 나눌 수가 있다. 하지만 유니코드의 형태를 그대로 읽었기에 출력을 할 경우 "V O L U M E . . ."처럼 2.2에서 파일의 이름에 공백이 추가되어있듯이 나타난다. 하지만 이번에는 bytearray의 형태가 아니라 문자열의 형태로 파일의 목록을 읽었기 때문에 다른 방식으로 접근해야 했다.

Figure 23. 로드된 파일 목록 공백 제거

"V.O.L.U.M.E …"와 같이 파일 경로를 하나 하나 인자로 remove_uni_null 함수에 주게 되면 임시로 만든 tmp배열에 \x00이 아닌 경우에만 추가된다. 그러므로 \x00은 자연히 떨어져 나가게 되며 이렇게 만들어진 새로운 tmp 배열은 join(tmp)를 통해 일반적인 문자열과 같이 출력되는 결과를 확인할 수가 있다.

Figure 24. 출력 결과

이렇게 전체적인 내용을 모두 완성하였으며 출력 결과 또한 만족스럽다.



3. 후기


일단 원하는 내용의 프로그램은 만들었기에 매우 뿌듯하다. 하지만 다 만들면서 또는 만든 후에 느끼는 점이 매우 많은데 이에 대해 정리하면 다음과 같다.

첫째, 확실히 프로그래밍은 많이 해보아야 하는 것 같다. 전체적인 구성을 하는 것보다는 유니코드에 포함된 NULL을 제거하거나 리틀엔디언을 10진수로 출력하거나 시간을 변환하고자 하는데 해당 형식은 안된다는 등의 오류를 해결하는데 오히려 훨씬 더 많은 시간을 지체하였다. 결국 오랜 시간 끝에 하나하나 해결을 하였기에 이와 유사한 프로그램을 만들 때에는 내가 만든 소스를 보므로 시간을 훨씬 단축할 수 있을 것 같다.

둘째, 원하는 내용을 만들었지만 아쉬운 점이 많이 느껴진다. 예를 들어 CLI의 형태이므로 어떠한 컬럼을 기준으로 정렬을 할 수 없다는 점이 가장 크다. 내가 만든 프로그램의 경우 지정된 폴더에 이름 순서에 따라 나타나는 정도이기에 다소 불편함이 있기는 하다.

셋째, 전체적으로 코드에 대한 이해가 부족한 느낌이 크다. 왜 굳이 bytearray를 사용했는지, 왜 bytearray는 변환이 되고 str은 안되는지 등에 대하여 자세한 이유를 모른다는 점에서 부족함을 많이 느꼈다.

넷째, 부족한 프로그램이지만 내가 만든 프로그램은 내가 애정을 가져야 하는 것 같다. 다른 사람이 더 잘 만들 수 있지만, 만들면서 많은 고민을 했다는 것 자체만으로도 큰 도움이 되는 것 같다.


참고


http://www.forensicswiki.org/wiki/Windows_Prefetch_File_Format   프리패치 파일 포맷

http://devanix.tistory.com/306  datetime 모듈

http://tt-share.blogspot.kr/2015/05/python_35.html    파일 mac 시간 구하기

https://gist.github.com/dfirfpi/113ff71274a97b489dfd#file-w10pfdecomp-py   XPRESS 압축 해제

https://gist.github.com/craSH/393155/19cb41b9f536593cb16a978af8ebeb00ffae500f 프리패치 해시 값

http://www.forensicfocus.com/Forums/viewtopic/p=6542386/#6542386


4. 첨부

완성된 코드




압축해제에 참고한 코드



'Programming > Python' 카테고리의 다른 글

Python - Simple Extract File  (0) 2016.02.17
Python - MFT Path Parsing  (0) 2016.02.04
Prefetch Parser 제작기  (2) 2015.12.26
Windows Timestamp Convert 64bit  (0) 2015.12.12
hex viewer의 일대기  (1) 2015.11.04
Upgrade - Hex_Viewer.py  (0) 2015.11.03

Comment +2

  • 2016.10.05 14:20

    비밀댓글입니다

  • 지나가는사람 2018.05.08 14:47

    안녕하세요 포스팅해주시는 글들 잘 보고있습니다. (너무 정리를 잘해주셔서 따로 안해도 될정도라 너무 감사드립니다 ㅎ)
    좀 지난 글이라 그냥 지나갈까 하다 그래도 혹시몰라 코멘트 남겨 놓습니다.
    이노무 오지랍...

    WinPrefetchView 의 경우 실행 인자로 /folder 값을 받을 수 있으며, 다른 시스템에서 추출된 프리패치를 확인 할 수 있습니다.
    이외 다른 옵션도 있으니 참고하시면 좋을 것 같습니다.
    https://www.nirsoft.net/utils/win_prefetch_view.html

    감사합니다.