R0 CREW

Gray Hat Python: Глава 3 - Создание отладчика под Windows (Перевод: Prosper-H)

Перейти к содержанию

Intro

Теперь, когда мы рассмотрели основы, пришло время применить знания, полученные в предыдущих главах книги. Когда Microsoft разработала Windows, она добавила поразительное множество отладочных функций, что бы помочь разработчикам и специалистам по обеспечению качества. В этой главе, мы будем интенсивно использовать эти функции, для создания собственного отладчика на Python’e. Важно отметить здесь и то, что мы, по сути, совершаем углубленное изучение PyDbg (Педрама Амини), поскольку, на данный момент, это наиболее чистый Windows-отладчик, написанный на Python (прим. пер. есть еще один стремительно развивающийся WinAppDbg).

3.1 Debuggee, Where Art Thou?

Для того что бы выполнить отладку процесса, у вас должна быть возможность ассоциировать отладчик с отлаживаемым процессом. Следовательно, наш отладчик должен иметь возможность либо открыть исполняемый файл и запустить его, либо присоединиться к уже запущенному процессу. Windows debugging API – предоставляет простой способ сделать и то и другое.

Существуют некоторые различия между открытием процесса и присоединением к нему. Преимуществом открытия процесса есть то, что у вас есть возможность получить контроль над процессом еще до того, как он сможет запустить какой бы то ни было код. Это может быть удобно при анализе вредоносных программ или других типов вредоносного кода. При присоединении вы вклиниваетесь в уже работающий процесс, что позволяет пропускать часть кода и анализировать специфические области, в которых вы заинтересованы. В зависимости от того, что отлаживается и то какой анализ нужно проделать - вы выбираете, какой подход вам использовать.

Первый способ, получения процесса выполняющегося под отладчиком – это запустить исполняемый файл испод самого отладчика. Для создания процесса в Windows, вам нужно вызвать функцию CreateProcessA() Главы 2, мы реализуем: программные точки останова, аппаратные точки останова и точки останова на память. Так же мы разработаем специальные обработчики для каждого типа брейкпойнтов и покажем, как корректно возобновить работу процесса после останова.

3.4.1 Программные брейкпойнты

Для того, что бы установить программный брейкпойнт (soft breakpoint), мы должны уметь читать и писать в память процесса. Делается это с помощью функций ReadProcessMemory() Главе 2, поэтому давайте перейдем к деталям реализации. Самое важное, при взаимодействии с аппаратными брэйкпоинтами, это отслеживание какой из четырех доступных отладочных регистров свободен для использования, а какой уже занят и используется. Мы должны быть уверены, что всегда используем свободный слот, иначе у нас могут возникнуть проблемы, когда брейкпойнт не сработает, в то время когда мы ожидаем этого.

Давайте начнем с перечисления всех потоков в процессе, затем получим запись контекста для каждого из них. Используя полученную запись контекста, модифицируем один из регистров, между DR0 и DR3 (в зависимости от того какой из них свободен) для того, что бы поместить адрес требуемого брейкпойнта. Затем зеркально отразим соответствующие биты в регистре DR7, для включения брейкпойнта, а так же установки его типа и длины.

После того, как мы создали подпрограмму, что бы установить брейкпойнт, следующее, что нам нужно это изменить главный цикл обработки отладочных событий, чтобы мы могли правильно обработать исключение, которые выскочит на аппаратном брейкпойнте. Мы знаем, аппаратная точка останова срабатывает на INT 1, поэтому мы просто добавим еще один обработчик исключений в наш отладочный цикл. Давайте начнем с установки брейкпойнта.

Example 7: my_debugger.py

...
class debugger():
    def __init__(self):
        self.h_process = None
        self.pid = None
        self.debugger_active = False
        self.h_thread = None
        self.context = None
        self.breakpoints = {}
        self.first_breakpoint= True
        self.hardware_breakpoints = {}
    ...
    def bp_set_hw(self, address, length, condition):

        # Check for a valid length value
        if length not in (1, 2, 4):
            return False
        else:
            length -= 1

        # Check for a valid condition
        if condition not in (HW_ACCESS, HW_EXECUTE, HW_WRITE):
            return False

        # Check for available slots
        if not self.hardware_breakpoints.has_key(0):
            available = 0
        elif not self.hardware_breakpoints.has_key(1):
            available = 1
        elif not self.hardware_breakpoints.has_key(2):
            available = 2
        elif not self.hardware_breakpoints.has_key(3):
            available = 3
        else:
            return False

        # We want to set the debug register in every thread
        for thread_id in self.enumerate_threads():
            context = self.get_thread_context(thread_id=thread_id)

            # Enable the appropriate flag in the DR7
            # register to set the breakpoint
            context.Dr7 |= 1 << (available * 2)

            # Save the address of the breakpoint in the
            # free register that we found
            if available == 0:
                context.Dr0 = address
            elif available == 1:
                context.Dr1 = address
            elif available == 2:
                context.Dr2 = address
            elif available == 3:
                context.Dr3 = address

            # Set the breakpoint condition
                context.Dr7 |= condition << ((available * 4) + 16)

            # Set the length
                context.Dr7 |= length << ((available * 4) + 18)

            # Set thread context with the break set
            h_thread = self.open_thread(thread_id)
            kernel32.SetThreadContext(h_thread,byref(context))

        # update the internal hardware breakpoint array at the used
        # slot index.
        self.hardware_breakpoints[available] = (address,length,condition)

        return True

Вы видите, что мы выбираем свободный слот, для хранения брейкпойнта, проверяя глобальный словарь hardware_breakpoints. После того, как мы получили свободный слот, нам нужно установить адрес брейкпойнта в слот и обновить в регистре DR7 соответствующие флаги, которые позволяют включить этот самый брейкпойнт. Теперь, когда у нас есть механизм поддержки, для установки аппаратных брейкпойнтов, давайте обновим цикл обработки отладочных событий и добавим обработчик исключений поддерживающий прерывание INT 1.

Example 7: my_debugger.py

...
class debugger():
...
    def get_debug_event(self):

        if self.exception == EXCEPTION_ACCESS_VIOLATION:
            print "Access Violation Detected."
        elif self.exception == EXCEPTION_BREAKPOINT:
            continue_status = self.exception_handler_breakpoint()
        elif self.exception == EXCEPTION_GUARD_PAGE:
            print "Guard Page Access Detected."
        elif self.exception == EXCEPTION_SINGLE_STEP:
            self.exception_handler_single_step()
        ...
    def exception_handler_single_step(self):

        # Comment from PyDbg:
        # determine if this single step event occurred in reaction to a
        # hardware breakpoint and grab the hit breakpoint.
        # according to the Intel docs, we should be able to check for
        # the BS flag in Dr6. but it appears that Windows
        # isn't properly propagating that flag down to us.
        if self.context.Dr6 & 0x1 and self.hardware_breakpoints.has_key(0):
            slot = 0
        elif self.context.Dr6 & 0x2 and self.hardware_breakpoints.has_key(1):
            slot = 1
        elif self.context.Dr6 & 0x4 and self.hardware_breakpoints.has_key(2):
            slot = 2
        elif self.context.Dr6 & 0x8 and self.hardware_breakpoints.has_key(3):
            slot = 3
        else:
            # This wasn't an INT1 generated by a hw breakpoint
            continue_status = DBG_EXCEPTION_NOT_HANDLED

        # Now let's remove the breakpoint from the list
        if self.bp_del_hw(slot):
            continue_status = DBG_CONTINUE

        print "[*] Hardware breakpoint removed."
        return continue_status

    def bp_del_hw(self,slot):

        # Disable the breakpoint for all active threads
        for thread_id in self.enumerate_threads():

            context = self.get_thread_context(thread_id=thread_id)

            # Reset the flags to remove the breakpoint
            context.Dr7 &= ~(1 << (slot * 2))

            # Zero out the address
            if slot == 0:
                context.Dr0 = 0x00000000
            elif slot == 1:
                context.Dr1 = 0x00000000
            elif slot == 2:
                context.Dr2 = 0x00000000
            elif slot == 3:
                context.Dr3 = 0x00000000

            # Remove the condition flag
            context.Dr7 &= ~(3 << ((slot * 4) + 16))

            # Remove the length flag
            context.Dr7 &= ~(3 << ((slot * 4) + 18))

            # Reset the thread's context with the breakpoint removed
            h_thread = self.open_thread(thread_id)
            kernel32.SetThreadContext(h_thread,byref(context))

        # remove the breakpoint from the internal list.
        del self.hardware_breakpoints[slot]
        return True

Тут все довольно просто: когда происходи прерывание INT 1 мы проверяем, устанавливался ли какой-нибудь отладочный регистр с аппаратным брейкпойнтом. И если отладчик обнаруживает установленную аппаратную точку останова, соответствующую адресу исключения, он обнуляет флаги в регистре DR7 и сбрасывает регистр отладки, который содержит адрес брейкпойнта. Давайте посмотрим на этот процесс в действии. Модифицируйте скрипт my_test.py, для использования аппаратных брейкпойнтов. В качестве хомячка будем использовать нашу функцию printf().

Example 7: my_test.py

import my_debugger
from my_debugger_defines import *

debugger = my_debugger.debugger()

pid = raw_input("Enter the PID of the process to attach to: ")

debugger.attach(int(pid))

printf = debugger.func_resolve("msvcrt.dll","printf")
print "[*] Address of printf: 0x%08x" % printf

debugger.bp_set_hw(printf,1,HW_EXECUTE)
debugger.run()

В этом тесте мы просто устанавливаем брейкпойнт на функцию printf() всякий раз, как она выполняется. Длина брейкпойнта всего один байт. В данном тесте импортировали файл my_debugger_defines.py. Это было сделано для того, что бы у нас был доступ к константе HW_EXECUTE, которая придает немного ясности коду.

Когда запустите скрипт – увидите вывод, представленный в Листинг 3-4.

Листинг 3-4: Порядок событий для обработки аппаратных брейкпойнтов

Enter the PID of the process to attach to: 2504
[>] Address of printf: 0x77c4186a
Event Code: 3 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 6 Thread ID: 3704
Event Code: 2 Thread ID: 2228
Event Code: 1 Thread ID: 2228
[>] Exception address: 0x7c901230
[>] Hit the first breakpoint.
Event Code: 4 Thread ID: 2228
[B]Event Code: 1 Thread ID: 3704
[>] Hardware breakpoint removed.[/B]

Основываясь на выводе скрипта – можно видеть, что после срабатывания исключения обработчик удаляет брейкпойнт. Цикл продолжит выполняться после того, как отработает обработчик. Теперь у нас есть поддержка программных и аппаратных брейкпойнтов, давайте завершим создание нашего отладчика, добавив поддержку брейкпойнтов на память.

3.4.3 Брейкпоинты на память

Заключительная функцией, которую мы реализуем, будет брейкпойнт на память. В начале, мы просто запросим информацию о разделе памяти, что бы определить, где находится ее базовый адрес (где в виртуальной памяти начинается страница). После того, как мы определили размер страницы, нам нужно установить права доступа на эту страницу, что бы обеспечить ее охрану. Когда процессор попытается получить доступ к этой памяти, сработает исключение GUARD_PAGE_EXCEPTION. Используя специфический обработчик, для данного исключения, мы вернем оригинальный права доступа для страницы и продолжим выполнение.

Для того, что бы правильно рассчитать размер страницы, которой мы собираемся манипулировать, нужно вначале обратиться с запросом к операционной системе, что бы получить размер страницы по умолчанию. Это делается с помощью функции GetSystemInfo() [20], которая заполняет структуру SYSTEM_INFO [21]. Эта структура содержит поле dwPageSize, в котором находится правильный, для системы, размер страницы. Мы реализуем этот шаг, во время инициализации первого экземпляра класса debugger().

Example 8: my_debugger.py

...
class debugger():

    def __init__(self):
        self.h_process = None
        self.pid = None
        self.debugger_active = False
        self.h_thread = None
        self.context = None
        self.breakpoints = {}
        self.first_breakpoint= True
        self.hardware_breakpoints = {}

        # Here let's determine and store
        # the default page size for the system
        system_info = SYSTEM_INFO()
        kernel32.GetSystemInfo(byref(system_info))
        self.page_size = system_info.dwPageSize
    ...

Теперь, когда получен размер страницы по умолчанию, мы готовы обращаться к странице и манипулировать ее правами. Первый шаг, заключается в запросе страницы, на адрес которой мы хотели бы установить брейкпойнт. Это делается с помощью функции VirtualQueryEx() [22], которая заполняет структуру MEMORY_BASIC_INFORMATION [23] характерными значениями для страницы, которую мы запрашиваем. Ниже приводятся определения для функции и структуры:

SIZE_T WINAPI VirtualQuery(
    HANDLE hProcess,
    LPCVOID lpAddress,
    PMEMORY_BASIC_INFORMATION lpBuffer,
    SIZE_T dwLength
);

typedef struct MEMORY_BASIC_INFORMATION{
    PVOID BaseAddress;
    PVOID AllocationBase;
    DWORD AllocationProtect;
    SIZE_T RegionSize;
    DWORD State;
    DWORD Protect;
    DWORD Type;
}

Как только структура была заполнена, будем использовать значение в поле BaseAddress, как начальную точку для установки прав доступа на страницу. Для установки правд доступа используется функция VirtualProtectEx() [24], которая имеет следующий прототип:

BOOL WINAPI VirtualProtectEx(
    HANDLE hProcess,
    LPVOID lpAddress,
    SIZE_T dwSize,
    DWORD flNewProtect,
    PDWORD lpflOldProtect
);

Итак, давайте перейдем к коду. Мы собираемся создать глобальный список защищенных страниц, которые нам нужно явно установить, так же как и глобальный список брейкпойнтов, на адреса в памяти, которые наш обработчик исключений будет использовать, когда произойдет исключение GUARD_PAGE_EXCEPTION. Затем установим права доступа на адрес и окружающие его страницы памяти (если адрес находится между двумя или более страницами памяти).

Example 8: my_debugger.py

...
class debugger():

    def __init__(self):
        ...
        self.guarded_pages = []
        self.memory_breakpoints = {}
    ...

    def bp_set_mem (self, address, size):
        mbi = MEMORY_BASIC_INFORMATION()

        # If our VirtualQueryEx() call doesn’t return
        # a full-sized MEMORY_BASIC_INFORMATION
        # then return False

        if kernel32.VirtualQueryEx(self.h_process,
                                           address,
                                           byref(mbi),
                                           sizeof(mbi)) < sizeof(mbi):
            return False

        current_page = mbi.BaseAddress

        # We will set the permissions on all pages that are
        # affected by our memory breakpoint.
        while current_page <= address + size:

            # Add the page to the list; this will
            # differentiate our guarded pages from those
            # that were set by the OS or the debuggee process
            self.guarded_pages.append(current_page)

            old_protection = c_ulong(0)
            if not kernel32.VirtualProtectEx(self.h_process,
                                                      current_page,
                                                      size,
                                                      mbi.Protect | PAGE_GUARD,
                                                      byref(old_protection)):
                return False

            # Increase our range by the size of the
            # default system memory page size
            current_page += self.page_size

        # Add the memory breakpoint to our global list
        self.memory_breakpoints[address] = (address, size, mbi)

        return True

Теперь у вас есть возможность устанавливать брэйкпоинты на память. Если вы опробуете их, в текущем состоянии, используя нашу функцию printf() зацикленную в цикле, вы получите вывод, который будет просто говорить “Guard Page Access Detected”. Хорошо то, что когда к защищенной странице получают доступ и срабатывает исключение, операционная система на самом деле удаляет защиту, установленную на страницу памяти, и позволяет вам продолжить выполнение. Подобное действие избавляет вас от создания специфического обработчика решающего эту задачу. Однако вы могли бы создать дополнительную логику, в существующем отладочном цикле, для выполнения определенных действий, когда срабатывает брейкпойнт. Например, вы могли бы осуществить такие действия, как: восстановление брейкпойнта, чтение памяти из места, где был установлен брэйкпоинт, приготовление свежего кофе, почесывание бороды – в общем, все что угодно.

3.5 Заключение

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

Следующие шаги будут состоять в том, что бы продемонстрировать некоторые возможности, продвинутого использования, двух сформировавшихся и стабильных отладочных платформ под Windows: PyDbg и Immunity Debugger. Вы получили большое количество информации о том, как PyDbg работает “под капотом”, поэтому будете чувствовать себя комфортно, переходя к нему. Синтаксис Immunity Debugger немного отличается, но он предлагает существенно отличающийся набор функций. Понимание того, как использовать и тот и другой отладчик, для специфических задач отладки, очень важно для вас, поскольку позволит вам автоматизировать отладку. Дорогу осилит лишь тот, кто шагает! Поэтому вперед, вперед и только в перед! Переходим к PyDbg…

3.6 Дополнительные материалы от переводчика

Для самых ленивых я разбил исходные коды, приведенные в книге, на Example (Примеры). Все это добро вы можете скачать тут. Успехов =)

© Translated by Prosper-H from r0 Crew

Народ у меня тут поползла ошибка не могу понять что еще передать в качестве параметра:(

Enter the PID of the process to attach to: 4032
Traceback (most recent call last):
  File "C:\Documents and Settings\Администратор\workspace\myFirstDebbuger\src\my_test.py", line 21, in <module>
    thread_context = debugger.get_thread_context(thread)
AttributeError: debugger instance has no attribute 'get_thread_context'

Сам исходник:

'''
Created on 11.05.2012

@author: aspidites
'''

import my_debugger

debugger = my_debugger.debugger()

pid = raw_input("Enter the PID of the process to attach to: ")

debugger.attach(int(pid))

list = debugger.enumerate_threads()

# For each thread in the list we want to
# grab the value of each of the registers

for thread in list:
     thread_context = debugger.get_thread_context(thread)

    # Now let's output the contents of some of the registers
    print "[*] Dumping registers for thread ID: 0x%08x" % thread
    print "[**] EIP: 0x%08x" % thread_context.Eip
    print "[**] ESP: 0x%08x" % thread_context.Esp
    print "[**] EBP: 0x%08x" % thread_context.Ebp
    print "[**] EAX: 0x%08x" % thread_context.Eax
    print "[**] EBX: 0x%08x" % thread_context.Ebx
    print "[**] ECX: 0x%08x" % thread_context.Ecx
    print "[**] EDX: 0x%08x" % thread_context.Edx
    print "[*] END DUMP"

debugger.detach()

Все работает на виртуалке WinXP SP3 чистая, код пишется в Eclipse с PyDev.

Почти разобрался, у меня ошибка была в my_debugger.py, только не понял пока где:(

В этом фрагменте:

Была опечатка с отступами (в книге), которую я забыл исправить на правильный вариант:

Должно работать.

да, спасибо я уже сам разобрался:) Вот за что я люблю C подобный синтаксис, так за фигурные скобки, все просто и ясно, где какой блок. Но и к этому я думаю привыкну)

В Win7 64 bits все регистры пустые.
Пока не разбирался плотно, но похоже, что надо пользоваться WOW64 функциями для получения контекста и не только.

Ты бы еще на Линуксе затестил. Эта книга расчитана только на Windows XP.

Ну, а вообще, если исходить из того, что поддержка Windows XP заканчивается 8 апреля 2014, то самый комфортный переход с WinXP на ГуаноN (т.е Windows N) будет с 2015-2016 года. Но переходить надо буде не на Windows 7, а скорее всего на какую-нибудь Windows 9. Ну или как минимум на Windows 8 SPN.

А пока мой совет: все что можно запустить на Windows XP нужно запускать на Windows XP (в плане реверсинга, конечно).

Надеюсь, что с XP будем все-таки переходить на ReactOS Beta, а не на очередное блудотворение убийц негров Африки.
А если не на ReactOS, то лучше уж хотя бы на полностью свободный дистриб GNU/Linux (безо всего проприетарного. Да-да, даже так)

Это понятно. У самого на работе одни редхаты. Но сейчас почти все ноуты мало мальски производительные идут с 7-кой и почти всегда 64 бита ибо 4гб - уже стандарт. Так что осваивать wow64 все равно придется, как ни крути, ведь реверсить хочется реальные программки, а они уже на win 7 64 ориентируются.

Но конечно понятно, что книжка рассчитана на win xp 32, а тренироваться надо на кошках (с) :slight_smile:
Пошел грызть бинарный код дальше.
Спасибо большое за перевод книги. Помогает жить.

P.S. Я сделал epub для ebook-а свеого. Epub читать на небольшом экране удобнее. Если кому надо - пишите, вышлю.

Та ты что, сплюнь. ReactOS если и будет кому-то интересен, то только с академической точки зрения. Надо быть мазохистом, чтобы им пользоваться. Бум надеяться, что к тому времени, когда XP отойдет в мир иной и мы начнем переход на WinN, появятся 16x ядерные процессоры и мега-адские размеры планок ОЗУ (хотя мне и сейчас моих 24Гб с головой хватает). Вот тогда будет шик. Я вообще давно хочу купить себе серверный ящик, а лучше стоечку с несколькими серверами, с мощными процессорами и кучей гигабайтов ОЗУ на борту. Я бы тогда был самым счастливым человеком на земле. Правда пока цены кусаются, но к 2016-му я думаю уже обзаведусь этаким чудом техники.

Я уже давно перешел ))) И хотя на основной системе у меня установлен исключительно Debian - это сложно назвать полным переходом, поскольку я все время сижу в XP, но только из под ВМвари. Должен сказать, что благодаря мощности современных процессоров чувствую себя очень даже комфортно, еще лучше чем было до перехода (правда тогда у меня и компик был другой, но сути это особой не меняет).

Сейчас у меня в среднем запущено 3-4 виртуалки, в особо напряженные моменты 5-6 - и я просто в восторге. В одной я “кодю”, в другой лажу в инет по доверенным сайтам и читаю книги, в третей лажу в инет по всем сайтам, четвертая у меня green-зона для работы c web-манями и прочим, в пятой я перевожу статьи для форума, в шестой дебажу чистые программки, в седьмой дебажу всякую дребедень по типу малвари, в восьмой у меня web-мастерская (для тестов каких-то web-скриптов). Как я жил без этого раньше - ума не приложу. И что самое ценное, любую виртуалку можно всегда за саспендидь (suspend) и вернуть ее в боевую готовность в течении нескольких секунд, с полностью рабочим окружением. Ну, а если вдруг с одной из виртуалкой случись что (окромя девелоперской), так всегда можно восстановить из резервной (чистой) копии, заблаговременно для этого подготовленной (благо HDD в наше время очень дешевые и ±пару сотен гигабайт не играют ни какой роли). А если понадобилась еще одна виртуалка, так это вообще плевое дело - поставил на клонирование, из чистой копии, и через 2-5 минуты у тебя уже полностью готова новая боевая среда. В общем сказка ^,^

Это уже фанатизм. Работа за ПК должна быть комфортной и если этот комфорт обеспечивает проприетарное ПО, то я без всякого угрызения совести пользую его.

Никогда не брал ноуты с предустановленной виндой. Зря выкинутые деньги на ветер. Не знаю насчет всех, но большинство ноутов идут в двух комплектация с Win7, DOS’ом или Linux’ом на борту, либо вообще только с DOS’ом или Linux’ом (учитывая пост-советские реалии). ИМХО, единственное оправдание обладателей семерки - это игры. Во всем же остальном можно обойтись поднятой по верх линукса виртуалкой с Windows XP. Хотя, не спорю, для некоторых случаев, таких как “мего-девелоперские проекты” или “дизайн ориентированные среды” все же подходит только чистая Win7 или MacOS. Но как показывает практика, дизайнеры сидят только под MacOS, а мего-девелоперские проекты дальше офисов своей разработки так и не выходят. Поэтому проблем использовать Линукс, кроме как проблем с “поиграть в какой-то новый пожиратель времени” не вижу. Тем более с учетом современных “мало мальски производительных ноутов”. У меня на одном моем старом ноуте с забыченым intel dual core процессором и 2Гб оперативки, даже под WinXP вполне себе нормально уживалось 2 две виртуалки WinXP. Я уже не говорю о махинах которые выпускают сейчас.

В общем, будущее за Линуксом, по крайней я так для себя решил. Тем более учитывая анти-пиратскую политику* и те “клеточные” интерфейсы (в смысле, интерфейсы для одноклеточных организмов), которые толкает Майкрософт.

*Анти-пиратская политика Майкрософт - это вообще отдельный разговор… Недавно покупал знакомому ноут и ставил на него Win7. Пока разобрался во всех способах активации этого Гуано-поделия, чуть было дубом не рухнул. Если мелко мягкие будут двигаться в этом же духе дальше, то я не удивлюсь, если скоро все начнут переходить с Масдаевских Пираток под Линукс. Для успеха Линуксу в основном не хватает Гейм-дейв’a, правильного маркетинга и грамотно составленных дистров. Я не знаю, что там курят в Canonical (те что с Убунты), но будь у меня финансовая поддержка я бы за несколько лет взорвал рынок настольных ОС. Тут как говорится, было бы желание, грамотный маркетинг, деньги и идеи.

В общем, как то так.

Скинь мне в ЛС или запость в эту тему я прикреплю к основному посту.

Развеем миф о том, что вы ничего не сможете создать например под Windows7 64 bit на python3.
Описывать и комментить мне влом(не все я сам понимаю). Вся суть сводится к тому, что вам необходимо взять контекст для 64 битной версии(там и регистры слегка отличаются) и добавить пару новых типов данных, а старую структуру Context временно закомментить, ну и слегка переписать метод получения контекста нити, и само собой вызов. Разобраться в изменениях вы сможете, если уж добрались сюда. Короче:
[INDENT]В my_debugger_defines.py:[/INDENT]
[INDENT][INDENT]Закомментировать вот это:[/INDENT][/INDENT]
[INDENT][INDENT][INDENT]

class CONTEXT(Structure):
    _fields_ = [
    
        ("ContextFlags", DWORD),
        ("Dr0", DWORD),
        ("Dr1", DWORD),
        ("Dr2", DWORD),
        ("Dr3", DWORD),
        ("Dr6", DWORD),
        ("Dr7", DWORD),
        ("FloatSave", FLOATING_SAVE_AREA),
        ("SegGs", DWORD),
        ("SegFs", DWORD),
        ("SegEs", DWORD),
        ("SegDs", DWORD),
        ("Edi", DWORD),
        ("Esi", DWORD),
        ("Ebx", DWORD),
        ("Edx", DWORD),
        ("Ecx", DWORD),
        ("Eax", DWORD),
        ("Ebp", DWORD),
        ("Eip", DWORD),
        ("SegCs", DWORD),
        ("EFlags", DWORD),
        ("Esp", DWORD),
        ("SegSs", DWORD),
        ("ExtendedRegisters", BYTE * 512),
]

[/INDENT][/INDENT][/INDENT]
[INDENT][INDENT]Вместо него вставить вот это:[/INDENT][/INDENT]
[INDENT][INDENT][INDENT]

class M128A(Structure):
    _fields_ = [("Low",DWORD64), ("High",DWORD64)]

class XMM_SAVE_AREA32(Structure):
    _pack_ = 1
    _fields_ = [
                ('ControlWord', WORD),
                ('StatusWord', WORD),
                ('TagWord', BYTE),
                ('Reserved1', BYTE),
                ('ErrorOpcode', WORD),
                ('ErrorOffset', DWORD),
                ('ErrorSelector', WORD),
                ('Reserved2', WORD),
                ('DataOffset', DWORD),
                ('DataSelector', WORD),
                ('Reserved3', WORD),
                ('MxCsr', DWORD),
                ('MxCsr_Mask', DWORD),
                ('FloatRegisters', M128A * 8),
                ('XmmRegisters', M128A * 16),
                ('Reserved4', BYTE * 96)
                ]

class DUMMYSTRUCTNAME(Structure):
    _fields_=[
              ("Header", M128A * 2),
              ("Legacy", M128A * 8),
              ("Xmm0", M128A),
              ("Xmm1", M128A),
              ("Xmm2", M128A),
              ("Xmm3", M128A),
              ("Xmm4", M128A),
              ("Xmm5", M128A),
              ("Xmm6", M128A),
              ("Xmm7", M128A),
              ("Xmm8", M128A),
              ("Xmm9", M128A),
              ("Xmm10", M128A),
              ("Xmm11", M128A),
              ("Xmm12", M128A),
              ("Xmm13", M128A),
              ("Xmm14", M128A),
              ("Xmm15", M128A)
              ]


class DUMMYUNIONNAME(Union):
    _fields_=[
              ("FltSave", XMM_SAVE_AREA32),
              ("DummyStruct", DUMMYSTRUCTNAME)
              ]

class CONTEXT64(Structure):
    _pack_ = 16
    _fields_ = [
                ("P1Home", DWORD64),
                ("P2Home", DWORD64),
                ("P3Home", DWORD64),
                ("P4Home", DWORD64),
                ("P5Home", DWORD64),
                ("P6Home", DWORD64),
                ("ContextFlags", DWORD),
                ("MxCsr", DWORD),
                ("SegCs", WORD),
                ("SegDs", WORD),
                ("SegEs", WORD),
                ("SegFs", WORD),
                ("SegGs", WORD),
                ("SegSs", WORD),
                ("EFlags", DWORD),
                ("Dr0", DWORD64),
                ("Dr1", DWORD64),
                ("Dr2", DWORD64),
                ("Dr3", DWORD64),
                ("Dr6", DWORD64),
                ("Dr7", DWORD64),
                ("Rax", DWORD64),
                ("Rcx", DWORD64),
                ("Rdx", DWORD64),
                ("Rbx", DWORD64),
                ("Rsp", DWORD64),
                ("Rbp", DWORD64),
                ("Rsi", DWORD64),
                ("Rdi", DWORD64),
                ("R8", DWORD64),
                ("R9", DWORD64),
                ("R10", DWORD64),
                ("R11", DWORD64),
                ("R12", DWORD64),
                ("R13", DWORD64),
                ("R14", DWORD64),
                ("R15", DWORD64),
                ("Rip", DWORD64),
                ("DebugControl", DWORD64),
                ("LastBranchToRip", DWORD64),
                ("LastBranchFromRip", DWORD64),
                ("LastExceptionToRip", DWORD64),
                ("LastExceptionFromRip", DWORD64),
                ("DUMMYUNIONNAME", DUMMYUNIONNAME),
                ("VectorRegister", M128A * 26),
                ("VectorControl", DWORD64)
]

[/INDENT][/INDENT][/INDENT]
[INDENT][INDENT]В константы(т.е. сюда):[/INDENT][/INDENT]
[INDENT][INDENT][INDENT]

BYTE      = c_ubyte
WORD      = c_ushort
DWORD     = c_ulong
LPBYTE    = POINTER(c_ubyte)
LPTSTR    = POINTER(c_char)
HANDLE    = c_void_p
PVOID     = c_void_p
LPVOID    = c_void_p
UINT_PTR  = c_ulong
SIZE_T    = c_ulong

[/INDENT][/INDENT][/INDENT]
[INDENT][INDENT]Добавить новую константу:[/INDENT][/INDENT]
[INDENT][INDENT][INDENT]

DWORD64=c_uint64

[/INDENT][/INDENT][/INDENT]
[INDENT]В my_debugger.py:[/INDENT]
[INDENT][INDENT]Вот этот метод:[/INDENT][/INDENT]
[INDENT][INDENT][INDENT]

def get_thread_context (self, thread_id=None,h_thread=None):

        context = CONTEXT()
        context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS

        # Obtain a handle to the thread
        if h_thread is None:
            self.h_thread = self.open_thread(thread_id) 

        if kernel32.GetThreadContext(h_thread, byref(context)):
            kernel32.CloseHandle(h_thread)
            return context
        else:
            return False

[/INDENT][/INDENT][/INDENT]
[INDENT][INDENT]Заменить на этот:[/INDENT][/INDENT]
[INDENT][INDENT][INDENT]

def get_thread_context(self,thread_id=None, h_thread=None):
        context=CONTEXT64()
        context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS
        if h_thread is None:
            self.h_thread = self.open_thread(thread_id)
        kernel32.SuspendThread(self.h_thread)

        if kernel32.GetThreadContext(self.h_thread, byref(context)):
            kernel32.ResumeThread(self.h_thread)
            return context
        else:
            kernel32.ResumeThread(self.h_thread)
            return False

[/INDENT][/INDENT][/INDENT]
[INDENT]Ну и в my_test.py:[/INDENT]
[INDENT][INDENT]Заменить в вызове вот это:[/INDENT][/INDENT]
[INDENT][INDENT][INDENT]

print "[**] EIP: 0x%08x" % thread_context.Eip
    print "[**] ESP: 0x%08x" % thread_context.Esp
    print "[**] EBP: 0x%08x" % thread_context.Ebp
    print "[**] EAX: 0x%08x" % thread_context.Eax
    print "[**] EBX: 0x%08x" % thread_context.Ebx
    print "[**] ECX: 0x%08x" % thread_context.Ecx
    print "[**] EDX: 0x%08x" % thread_context.Edx

[/INDENT][/INDENT][/INDENT]
[INDENT][INDENT]Вот на это:[/INDENT][/INDENT]
[INDENT][INDENT][INDENT]

        print ("[**] RIP: 0x%08x" %thread_context.Rip)
        print ("[**] RSP: 0x%08x" %thread_context.Rsp)
        print ("[**] RBP: 0x%08x" %thread_context.Rbp)
        print ("[**] RAX: 0x%08x" %thread_context.Rax)
        print ("[**] RBX: 0x%08x" %thread_context.Rbx)
        print ("[**] RCX: 0x%08x" %thread_context.Rcx)
        print ("[**] RDX: 0x%08x" %thread_context.Rdx)

[/INDENT][/INDENT][/INDENT]
Весь код рабочий. Работает как на Python3, так и на python 2.5+, только для Python 2.5+ необходимо заменить конструкции print(‘Бла-Бла’), на print ‘Бла-бла’.

Так нигде же про миф не говорилось =) К 2015-2016 году всё портируют под Win7 и мы без лишней тарабарщины перейдем, как минимум, на Win7+.

До этого еще додуматься надо было :wink:

Чтобы не править print’ы нужно подключить фичу:

from __future__ import print_function

Правда работать будет только начиная с Python 2.6+, но ведь 2.5 уже прошлый век? Так что не страшно. ИМХО.