【问题标题】:ctypes, GetErrorCode 6, LoadLibrary and Modulehandle returning None?ctypes、GetErrorCode 6、LoadLibrary 和 Modulehandle 返回 None?
【发布时间】:2019-12-16 21:56:57
【问题描述】:

我目前正在了解更多关于ctypes 及其功能的信息,我正在尝试通过PID(process ID)WriteProcessMemory 的脚本创建到记事本中。但是,当我尝试执行我的脚本时,记事本会立即崩溃。我正在关注this 的教程,我认为它与“Gray Hat for Hacking Python”一书相同。正确的,执行的shell代码应该是创建一个消息框。

这是我的代码。

import os
import colorama
from colorama import Fore, Back, Style
import win32com.client
from ctypes import *

from ctypes import wintypes
import ctypes
from ctypes.wintypes import BOOL
from ctypes.wintypes import DWORD
from ctypes.wintypes import HANDLE
from ctypes.wintypes import LPVOID
from ctypes.wintypes import LPCVOID
from ctypes.wintypes import LPCWSTR

colorama.init()
kernel32 = ctypes.WinDLL('Kernel32', use_last_error=True)

LPCSTR = LPCTSTR = ctypes.c_char_p
LPDWORD = PDWORD = ctypes.POINTER(DWORD)
class _SECURITY_ATTRIBUTES(ctypes.Structure):
    _fields_ = [('nLength', DWORD),
                ('lpSecurityDescriptor', LPVOID),
                ('bInheritHandle', BOOL),]
SECURITY_ATTRIBUTES = _SECURITY_ATTRIBUTES
LPSECURITY_ATTRIBUTES = ctypes.POINTER(_SECURITY_ATTRIBUTES)
LPTHREAD_START_ROUTINE = LPVOID

OpenProcess = kernel32.OpenProcess
OpenProcess.restype = HANDLE
OpenProcess.argtypes = (DWORD, BOOL, DWORD)

VirtualAllocEx = kernel32.VirtualAllocEx
VirtualAllocEx.restype = LPVOID
VirtualAllocEx.argtypes = (HANDLE, LPVOID, ctypes.c_size_t, DWORD, DWORD)

ReadProcessMemory = kernel32.ReadProcessMemory
ReadProcessMemory.restype = BOOL
ReadProcessMemory.argtypes = (HANDLE, LPCVOID, LPVOID, DWORD, DWORD)

WriteProcessMemory = kernel32.WriteProcessMemory
WriteProcessMemory.restype = BOOL
WriteProcessMemory.argtypes = (HANDLE, LPVOID, LPCVOID, DWORD, ctypes.c_int)

CreateRemoteThread = kernel32.CreateRemoteThread
CreateRemoteThread.restype = HANDLE
CreateRemoteThread.argtypes = (HANDLE, LPSECURITY_ATTRIBUTES, ctypes.c_size_t , LPTHREAD_START_ROUTINE, LPVOID, DWORD, ctypes.c_ulong)

GetLastError = kernel32.GetLastError
GetLastError.restype = DWORD
GetLastError.argtypes = ()

GetModuleHandle = kernel32.GetModuleHandleA
GetModuleHandle.restype = HANDLE
GetModuleHandle.argtypes =  (LPCWSTR,)

GetProcAddress = kernel32.GetProcAddress
GetProcAddress.restype = LPVOID
GetProcAddress.argtypes = (HANDLE, LPCWSTR)

# https://www.aldeid.com/wiki/Process-Security-and-Access-Rights
PROCESS_VM_READ = 0x0010 # Required to read memory in a process using ReadProcessMemory. 
PROCESS_VM_WRITE = 0x0020
PROCESS_VM_OPERATION = 0x0008 # Required to write to memory in a process using WriteProcessMemory. 
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_CREATE_THREAD = 0x0002
PROCESS_ALL_ACCESS = (PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD) #0x1F0FFF

print(Fore.RED + 'Retrieving PIDs...')
WMI= win32com.client.GetObject('winmgmts:')
processes = WMI.ExecQuery('SELECT * from win32_process')
print(Fore.GREEN)
process_list = [i.Properties_('ProcessId').Value for i in processes] # list of available processes
for process in processes:
    print(process.Properties_('ProcessId').Value , " - " , process.Properties_('Name').Value)

PID = int(input('Enter the PID of the process '))


# https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
process_handle = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, PID) # creating the handle
if not process_handle:
    print ("Couldn't acquire a handle to PID: %s" % PID)


shellcode = "C:\\Users\\User\\Desktop\\py\\injector\\hello-world-x64.dll"


# https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex

memory_alloc = kernel32.VirtualAllocEx(process_handle,0, len(shellcode), (0x1000 | 0x2000), 0x40) # allocating memory to the process
write = kernel32.WriteProcessMemory(process_handle, memory_alloc, shellcode, len(shellcode), 0) 

ModuleHandle = kernel32.GetModuleHandleA('kernel32.dll')
LoadLibraryA = kernel32.GetProcAddress(ModuleHandle,"LoadLibraryA")

if not kernel32.CreateRemoteThread(process_handle, None, 0, LoadLibraryA, memory_alloc, 0, 0):
    print("Failed injection..")

print("ModuleHandle : ", ModuleHandle)
print("LoadLibrary : ", LoadLibraryA)
print("process handle : ", process_handle)
print("VirtualAllocEx : ",memory_alloc)
print("WriteProcessMemory : ",write)

print(ctypes.GetLastError())

我已经尝试打印返回值,显然给我错误的是ModuleHandleLoadLibrary,它返回的是 None 值。但据微软称:

如果函数成功,返回值非零。 如果函数失败,则返回值为 0(零)。要获取扩展错误信息,请调用 GetLastError。如果请求的写入操作进入进程中不可访问的区域,则该函数将失败。

我还尝试了返回 6 的 GetLastError() 方法,在谷歌搜索时,它指的是“无效的处理程序”。

如果有帮助,我的操作系统、记事本、VScode(my ide)、python(3.6.8) 都是 64 位的。 对于混乱的代码,我深表歉意,请随时纠正我,因为我是这方面的初学者。

编辑

这是我打印输出的图像。我也试过LoadLibraryWGetModuleHandleW 但也没用,我的记事本崩溃了。我使用的 dll 是一个通用 DLL 文件,它只会生成一个消息框,上面写着“Hello World”

【问题讨论】:

    标签: python windows kernel ctypes pywin32


    【解决方案1】:

    好的,我在编写这个脚本时犯了很多错误,@eryksun 在评论部分给出了许多精彩的答案。我写这个答案是为了巩固我学到的东西。

    1) 我应该使用kernel32 = ctypes.WinDLL('Kernel32', use_last_error=True) 而不是kernel32 = ctypes.windll.kernel32。这样可以避免与使用windll 的其他模块发生冲突。它还可以保护线程的LastErrorValue。在这种情况下,请使用ctypes.get_last_error()ctypes.set_last_error(err),而不是直接调用WinAPI GetLastErrorSetLastError

    2) WriteProcessMemory.argtypes = (HANDLE, LPVOID, LPCVOID, DWORD, ctypes.c_int) 设置错误。根据文档的 argtypes,应该是:

    BOOL WriteProcessMemory(
      HANDLE  hProcess,
      LPVOID  lpBaseAddress,
      LPCVOID lpBuffer,
      SIZE_T  nSize,
      SIZE_T  *lpNumberOfBytesWritten
    );
    

    最后两个参数应该是[HANDLE, LPVOID, LPCVOID, SIZE_T, PSIZE_T]

    3) PROCESS_ALL_ACCESS 应该只获得CreateRemoteThreadWriteProcessMemory 所需的必要权限,即PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION

    4) 我传递的字节大小不应分配为len(shellcode),而应为(len(shellcode) + 1) * WCHAR_SIZE

    一般来说,复制字符串时应考虑空终止符,例如使用 len(dll_path) + 1。在这种情况下,您将提交一个新的内存页面(在 x86 和 x64 系统上为 4 KiB),最初全为零。

    如果您使用的是 Python 3,则 DLL 路径作为宽字符、以空字符结尾的字符串传递,这说明需要加 1(尾随空字符)和乘以 WCHAR_SIZE(两个每个字符的字节数)。

    WriteProcessMemory 只是将路径作为字符串写入目标进程的地址空间。这作为参数传递给 LoadLibraryW(宽字符 Unicode 字符串的本机“W”版本),在新远程线程上的目标进程中调用。请注意,LoadLibraryW 与我们当前进程在目标进程中的地址相同,因为 kernel32.dll 始终映射到其首选基地址,并且始终加载到 Windows 进程中。大多数其他 DLL 不一定是这种情况。

    5) GetModuleHandleALoadLibraryA 应替换为各自的 W 函数。此外,GetModuleHandleWGetProcAddress 步骤是不必要的。 ctypes 已经为您做到了。只需使用 kernel32.LoadLibraryW。这取决于 kernel32.dll 在每个进程中始终映射到相同的基地址,我认为这对于现有版本的 Windows 是正确的。

    DOS 和 Windows 9x 使用代码页对给定区域设置的字符串(即序数值和字符之间的映射)进行编码,例如美国的 437、西欧的 850 和两个地区的 Windows 中的 1252。 OTOH,Windows NT (1993) 基于 Unicode(即支持所有书面语言的字符集),它需要提供与不支持 Unicode 的 Windows 9x 应用程序的兼容性。

    NT 为每个区域定义了两个代码页,一个 OEM (DOS) 代码页和一个 ANSI (Windows) 代码页。函数有一个 [W]ide 字符版本(例如 GetModuleHandleW)和一个包装的 [A]NSI 版本(例如 GetModuleHandleA)。包装器在 ANSI 和 Unicode 之间进行转换并调用宽字符函数。在 API 级别,标头定义了一个或另一个,这取决于是否定义了 UNICODE。此外,该系统定义了 TCHAR 字符串类型,如 LPTSTR,映射到窄类型(如 LPSTR)或宽字符类型(如 LPWSTR)

    自 Windows XP 以来,所有受支持的 Windows 版本都基于 Windows NT,并且添加到 API 中的许多新功能仅在宽字符版本中可用(例如 GetLocaleInfoEx;请注意,此处缺少“W”后缀案例,因为只有一个版本)。旧的 ANSI API 和 TCHAR 类型现在已成为遗留物。我建议只使用带有 Unicode 字符串的原生宽字符 API。

    感谢@eryksun 的帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-06
      • 2017-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多