analyzer/windows/lib/api/process.py
# Copyright (C) 2010-2012 Cuckoo Sandbox Developers.
# This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
# See the file 'docs/LICENSE' for copying permission.
import os
import string
import random
import logging
from time import time
from ctypes import *
from shutil import copy
from lib.common.defines import *
from lib.common.paths import PATHS
from lib.core.errors import get_error_string
log = logging.getLogger(__name__)
def randomize_dll(dll_path):
"""Randomize DLL name.
@return: new DLL path.
"""
new_dll_name = "".join(random.choice(string.ascii_letters) for x in range(6))
new_dll_path = os.path.join("dll", "%s.dll" % new_dll_name)
try:
copy(dll_path, new_dll_path)
return new_dll_path
except:
return dll_path
class Process:
"""Windows process."""
def __init__(self, pid=0, h_process=0, thread_id=0, h_thread=0):
"""@param pid: PID.
@param h_process: process handle.
@param thread_id: thread id.
@param h_thread: thread handle.
"""
self.pid = pid
self.h_process = h_process
self.thread_id = thread_id
self.h_thread = h_thread
self.suspended = False
def get_system_info(self):
"""Get system information."""
self.system_info = SYSTEM_INFO()
KERNEL32.GetSystemInfo(byref(self.system_info))
def open(self):
"""Open a process.
@return: operation status.
"""
if self.pid == 0:
return False
self.h_process = KERNEL32.OpenProcess(PROCESS_ALL_ACCESS,
False,
int(self.pid))
return True
def exit_code(self):
"""Get process exit code.
@return: exit code value.
"""
if not self.h_process:
self.open()
exit_code = c_ulong(0)
KERNEL32.GetExitCodeProcess(self.h_process, byref(exit_code))
return exit_code.value
def is_alive(self):
"""Process is alive?
@return: process status.
"""
if self.exit_code() == STILL_ACTIVE:
return True
else:
return False
def execute(self, path=None, args=None, suspended=False):
"""Execute sample process.
@param path: sample path.
@param args: process args.
@param suspended: is suspended.
@return: operation status.
"""
if not os.access(path, os.X_OK):
log.info("Problem Accessing to %s." % path)
return False
startup_info = STARTUPINFO()
startup_info.cb = sizeof(startup_info)
process_info = PROCESS_INFORMATION()
if args:
arguments = "\"" + path + "\" " + args
else:
arguments = None
creation_flags = CREATE_NEW_CONSOLE
if suspended:
self.suspended = True
creation_flags += CREATE_SUSPENDED
created = KERNEL32.CreateProcessA(path,
arguments,
None,
None,
None,
creation_flags,
None,
None,
byref(startup_info),
byref(process_info))
if created:
self.pid = process_info.dwProcessId
self.h_thread = process_info.hThread
log.info("Successfully executed process from path \"%s\" with arguments \"%s\" with pid %d"
% (path, args, self.pid))
return True
else:
log.error("Failed to execute process from path \"%s\" with arguments \"%s\"" % (path, args))
return False
def resume(self):
"""Resume a suspended thread.
@return: operation status.
"""
if not self.suspended:
log.warning("The process with pid %d was not suspended at creation" % self.pid)
return False
if self.h_thread == 0:
return False
KERNEL32.Sleep(2000)
if KERNEL32.ResumeThread(self.h_thread) != -1:
log.info("Successfully resumed process with pid %d" % self.pid)
return True
else:
log.error("Failed to resume process with pid %d" % self.pid)
return False
def terminate(self):
"""Terminate process.
@return: operation status.
"""
if self.h_process == 0:
self.open()
if KERNEL32.TerminateProcess(self.h_process, 1):
log.info("Successfully terminated process with pid %d" % self.pid)
return True
else:
log.error("Failed to terminate process with pid %d" % self.pid)
return False
def inject(self, dll=os.path.join("dll", "cuckoomon.dll"), apc=False):
"""Cuckoo DLL injection.
@param dll: Cuckoo DLL path.
@param apc: APC use.
"""
if self.pid == 0:
log.warning("No valid pid specified, injection aborted")
return False
if not self.is_alive():
log.warning("The process with pid %d is not alive, injection aborted" % self.pid)
return False
dll = randomize_dll(dll)
if not dll or not os.path.exists(dll):
log.warning("No valid DLL specified to be injected in process with pid %d, injection aborted" % self.pid)
return False
KERNEL32.Sleep(2000)
arg = KERNEL32.VirtualAllocEx(self.h_process,
None,
len(dll) + 1,
MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE)
if not arg:
log.error("VirtualAllocEx failed when injecting process with pid %d, injection aborted (Error: %s)"
% (self.pid, get_error_string(KERNEL32.GetLastError())))
return False
bytes_written = c_int(0)
if not KERNEL32.WriteProcessMemory(self.h_process,
arg,
dll + '\x00',
len(dll) + 1,
byref(bytes_written)):
log.error("WriteProcessMemory failed when injecting process with pid %d, injection aborted (Error: %s)"
% (self.pid, get_error_string(KERNEL32.GetLastError())))
return False
kernel32_handle = KERNEL32.GetModuleHandleA("kernel32.dll")
load_library = KERNEL32.GetProcAddress(kernel32_handle,
"LoadLibraryA")
if apc or self.suspended:
log.info("Using QueueUserAPC injection")
if self.h_thread == 0:
log.info("No valid thread handle specified for injecting process with pid %d, injection aborted" % self.pid)
return False
if KERNEL32.QueueUserAPC(load_library, self.h_thread, arg) == 0:
log.error("QueueUserAPC failed when injecting process with pid %d (Error: %s)"
% (self.pid, get_error_string(KERNEL32.GetLastError())))
return False
else:
log.info("Using CreateRemoteThread injection")
new_thread_id = c_ulong(0)
if not KERNEL32.CreateRemoteThread(self.h_process,
None,
0,
load_library,
arg,
0,
byref(new_thread_id)):
log.error("CreateRemoteThread failed when injecting process with pid %d (Error: %s)"
% (self.pid, get_error_string(KERNEL32.GetLastError())))
return False
log.info("Successfully injected process with pid %d" % self.pid)
return True
def dump_memory(self):
"""Dump process memory.
@return: operation status.
"""
if self.pid == 0:
log.warning("No valid pid specified, memory dump aborted")
return False
if not self.is_alive():
log.warning("The process with pid %d is not alive, memory dump aborted" % self.pid)
return False
self.get_system_info()
page_size = self.system_info.dwPageSize
min_addr = self.system_info.lpMinimumApplicationAddress
max_addr = self.system_info.lpMaximumApplicationAddress
mem = min_addr
root = os.path.join(PATHS["memory"], str(self.pid))
root = os.path.join(root, str(int(time())))
if not os.path.exists(root):
os.makedirs(root)
while(mem < max_addr):
mbi = MEMORY_BASIC_INFORMATION()
count = c_ulong(0)
if KERNEL32.VirtualQueryEx(self.h_process, mem, byref(mbi), sizeof(mbi)) < sizeof(mbi):
mem += page_size
continue
if mbi.State == 0x1000 and mbi.Type == 0x20000:
buf = create_string_buffer(mbi.RegionSize)
if KERNEL32.ReadProcessMemory(self.h_process,
mem,
buf,
mbi.RegionSize,
byref(count)):
path = os.path.join(root, "0x%.8x.dmp" % mem)
chunk = open(path, "wb")
chunk.write(buf.raw)
chunk.close()
mem += mbi.RegionSize
else:
mem += page_size
log.info("Memory dump of process with pid %d completed" % self.pid)
return True