Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

partial fix for #1680, rewrite proc enum #1686

Merged
merged 3 commits into from
Aug 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 63 additions & 28 deletions analyzer/windows/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@
import sys
import timeit
import traceback
from ctypes import POINTER, byref, c_int, c_ulong, c_void_p, cast, create_string_buffer, create_unicode_buffer, sizeof
from ctypes import (
byref,
c_buffer,
c_int,
create_string_buffer,
sizeof,
wintypes,
)
from pathlib import Path
from shutil import copy
from threading import Lock
Expand All @@ -34,7 +41,15 @@
SHUTDOWN_MUTEX,
TERMINATE_EVENT,
)
from lib.common.defines import ADVAPI32, EVENT_MODIFY_STATE, KERNEL32, NTDLL, SYSTEM_PROCESS_INFORMATION
from lib.common.defines import (
ADVAPI32,
EVENT_MODIFY_STATE,
KERNEL32,
PSAPI,
SHELL32,
MAX_PATH,
PROCESS_QUERY_LIMITED_INFORMATION,
)
from lib.common.exceptions import CuckooError, CuckooPackageError
from lib.common.hashing import hash_file
from lib.common.results import upload_to_host
Expand Down Expand Up @@ -82,6 +97,43 @@ def pid_from_service_name(servicename):
return thepid


def pids_from_image_names(suffixlist):
"""Get PIDs for processes whose image name ends with one of the given suffixes.

Matches are not sensitive to casing; both the suffixes and process
image names are normalized prior to comparison.
"""
retpids = []
arr = wintypes.DWORD * 10000 # arbitrary - is this enough?
lpid_process_ptr = arr()
num_bytes = wintypes.DWORD()
image_name = c_buffer(MAX_PATH)
ok = PSAPI.EnumProcesses(byref(lpid_process_ptr), sizeof(lpid_process_ptr), byref(num_bytes))
if not ok:
log.debug("psapi.EnumProcesses failed")
return retpids

suffixlist = tuple([x.lower() for x in suffixlist])
num_processes = int(num_bytes.value / sizeof(wintypes.DWORD))
pids = lpid_process_ptr[:num_processes]

for pid in pids:
h_process = KERNEL32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
if not h_process:
log.debug("kernel.OpenProcess failed for PID: %d", pid)
continue
n = PSAPI.GetProcessImageFileNameA(h_process, image_name, MAX_PATH)
KERNEL32.CloseHandle(h_process)
if not n:
log.debug("psapi.GetProcessImageFileNameA failed for PID: %d", pid)
continue
image_name_pystr = image_name.value.decode().lower()
# e.g., image name: "\device\harddiskvolume4\windows\system32\services.exe"
if image_name_pystr.endswith(suffixlist):
retpids.append(pid)
return retpids


def in_protected_path(fname):
"""Checks file name against some protected names."""
if not fname:
Expand Down Expand Up @@ -186,30 +238,6 @@ def get_pipe_path(self, name):
return f"\\\\.\\PIPE\\{name}"
return f"\\??\\PIPE\\{name}"

def pids_from_process_name_list(self, namelist):
proclist = []
pidlist = []
buf = create_unicode_buffer(1024 * 1024)
p = cast(buf, c_void_p)
retlen = c_ulong(0)
retval = NTDLL.NtQuerySystemInformation(5, buf, 1024 * 1024, byref(retlen))
if retval:
return []
proc = cast(p, POINTER(SYSTEM_PROCESS_INFORMATION)).contents
while proc.NextEntryOffset:
p.value += proc.NextEntryOffset
proc = cast(p, POINTER(SYSTEM_PROCESS_INFORMATION)).contents
# proclist.append((proc.ImageName.Buffer[:proc.ImageName.Length // 2], proc.UniqueProcessId))
proclist.append((proc.ImageName.Buffer, proc.UniqueProcessId))

for proc in proclist:
lowerproc = proc[0].lower()
for name in namelist:
if lowerproc == name:
pidlist.append(proc[1])
break
return pidlist

def prepare(self):
"""Prepare env for analysis."""
global MONITOR_DLL, MONITOR_DLL_64, HIDE_PIDS
Expand Down Expand Up @@ -255,13 +283,15 @@ def prepare(self):
MONITOR_DLL_64 = self.options.get("dll_64")

# get PID for services.exe for monitoring services
svcpid = self.pids_from_process_name_list(["services.exe"])
svcpid = pids_from_image_names(["services.exe"])
if svcpid:
if len(svcpid) > 1:
log.debug("found %d services.exe processes", len(svcpid))
self.SERVICES_PID = svcpid[0]
self.config.services_pid = svcpid[0]
self.CRITICAL_PROCESS_LIST.append(int(svcpid[0]))

HIDE_PIDS = set(self.pids_from_process_name_list(self.files.PROTECTED_NAMES))
HIDE_PIDS = set(pids_from_image_names(self.files.PROTECTED_NAMES))

# Initialize and start the Pipe Servers. This is going to be used for
# communicating with the injected and monitored processes.
Expand Down Expand Up @@ -330,6 +360,11 @@ def run(self):
log.debug("Pipe server name: %s", PIPE)
log.debug("Python path: %s", os.path.dirname(sys.executable))

if SHELL32.IsUserAnAdmin():
log.info("analysis running as an admin")
else:
log.info("analysis running as a normal user")

# If no analysis package was specified at submission, we try to select one automatically.
if not self.config.package:
log.debug("No analysis package specified, trying to detect it automagically")
Expand Down
19 changes: 3 additions & 16 deletions analyzer/windows/lib/common/defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
USER32 = windll.user32
SHELL32 = windll.shell32
PDH = windll.pdh
PSAPI = windll.psapi

BYTE = c_ubyte
USHORT = c_ushort
Expand Down Expand Up @@ -134,6 +135,8 @@
TRUNCATE_EXISTING = 5
CREATE_NO_WINDOW = 0x08000000

MAX_PATH = 260


class STARTUPINFO(Structure):
_fields_ = [
Expand Down Expand Up @@ -253,22 +256,6 @@ class UNICODE_STRING(Structure):
]


class SYSTEM_PROCESS_INFORMATION(Structure):
_fields_ = [
("NextEntryOffset", ULONG),
("NumberOfThreads", ULONG),
("Reserved0", UINT64),
("Reserved1", UINT64),
("Reserved2", UINT64),
("CreateTime", UINT64),
("UserTime", UINT64),
("KernelTime", UINT64),
("ImageName", UNICODE_STRING),
("BasePriority", ULONG),
("UniqueProcessId", PVOID),
]


class SECURITY_DESCRIPTOR(Structure):
_pack_ = 1
_fields_ = [
Expand Down