Python - Ctype.windll.kernel32.CreateThread Значение параметра - PullRequest
0 голосов
/ 29 мая 2018

это может быть действительно простой вопрос, но почему-то я просто не могу обернуть голову вокруг ответа, и я не могу найти ни одной хорошей и связанной документации по этой теме.

Итак, я пытаюсь создать PoC с помощью модуля Python ctypes и метода CreateThread в классе ctypes.windll.kernel32 (для выполнения некоторого внедрения шелл-кода в пространстве памяти программы)

Согласно msdnдокументация CreateThread 7 параметров:

  • Указатель на атрибуты безопасности
  • Начальный размер стека
  • Указатель на начальный адрес
  • Указатель на любые параметры
  • Флаг создания
  • Указатель на значение, которое получает идентификатор потока

И все примеры использования python для вызова функций стиля c иlibs таковы:

thread_handle = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
                ctypes.c_int(0),
                ctypes.c_int(ptr),
                ctypes.c_int(0),
                ctypes.c_int(0),
                ctypes.pointer(ctypes.c_int(0)))

Может кто-нибудь объяснить, почему последний параметр был использован как ctypes.pointer (c_int0), тогда как для других параметров используется значение константы другого нулевого указателя целое число 0.(например, ctypes.c_int (0))

Обновление: вот пример кода, и эту реализацию можно увидеть по всей сети:

Строка 786 из вызов функции createThread в python

Обратите внимание на приведенную выше строку скрипта с упомянутыми комментариями:

  #   _Out_opt_ LPDWORD  lpThreadId  // NULL, so the thread identifier is not returned.

Похоже, автор может ошибаться при комментированииссылка для вызова функции CreateThread.

Предположение: Согласно комментарию в упомянутом ответе Марка, ThreadID и ThreadHandle различаются, и путем передачи в ctypes.pointer (ctypes.c_int (0)) вместо простоplain ctypes.c_int (0) (NULL) означает, что в расположении int 0 будет храниться идентификатор потока.Может ли кто-нибудь подтвердить это предположение?

1 Ответ

0 голосов
/ 29 мая 2018

Последний параметр создает целое число C (c_int(0)) и передает его как указатель.Это соответствует последнему определению параметра свободно.Это должен быть DWORD, который обычно определяется как unsigned long (c_ulong в ctypes).Использование ctypes.byref более эффективно, чем создание указателя.Параметр используется для возврата идентификатора потока в качестве выходного параметра, поэтому для хранения идентификатора необходим адрес экземпляра правильного типа C.

Вот рабочий пример, который явно определяет входы / выходы каждогофункция с ctypes.Обратите внимание, что ctypes имеет предопределенные типы Windows в wintypes:

import ctypes as c
from ctypes import wintypes as w

LPTHREAD_START_ROUTINE = c.WINFUNCTYPE(w.DWORD,w.LPVOID)
SIZE_T = c.c_size_t

k32 = c.WinDLL('kernel32')
test = c.WinDLL('test')

CreateThread = k32.CreateThread
CreateThread.argtypes = w.LPVOID,SIZE_T,LPTHREAD_START_ROUTINE,w.LPVOID,w.DWORD,w.LPDWORD
CreateThread.restype = w.HANDLE

WaitForSingleObject = k32.WaitForSingleObject
WaitForSingleObject.argtypes = w.HANDLE,w.DWORD
WaitForSingleObject.restype = w.DWORD

sa = None  # No security specified.  None == NULL pointer.
stack = 0  # Use default stack
start = LPTHREAD_START_ROUTINE(test.func)
param = 0x12345
flags = 0 # start thread immediately
tid = w.DWORD()
h = CreateThread(sa,stack,start,param,flags,c.byref(tid))
WaitForSingleObject(h,1000) # wait for the thread to exit.

Вот код для простой функции C, выполняемой как поток:

#include <stdio.h>

__declspec(dllexport) unsigned long __stdcall func(void* p)
{
    printf("%p\n",p);
    return 0;
}

Вотвывод:

0000000000012345
...