Гайд Декомпиляция AutoHotKey

#Rin

Известный
Автор темы
Всефорумный модератор
1,214
1,035
Декомпиляция AutoHotKey
Код скриптов на AutoHotKey при компиляции пакуется как есть (Исключение: IronAHK), из него вырезаются только комментарии и другие небольшие изменения.
Следовательно процесс декомпиляции легок, но при криптовании и обфускации этот процесс усложняется.

Содержание
  1. Примечание
  2. Краткий ввод в использование отладчика
  3. Определение версии и упаковщиков
  4. Общие сходства декомпиляции
  5. AHK v.1.0 (Basic, Classic, Vanilla)
  6. AHK v.1.1 (AHK_L)
  7. AHK v.1.1/2.0 (AHK_N, AHK_H, AHK_H-alpha)
  8. AHK v.1.1 (AHK_Guard)
  9. AHK v.1.1 (AHK_Protector, AHK_Protector_Mini)
  10. IronAHK
  11. Снятие UPX/MPRESS
  12. Снятие дампа
  13. Распаковка с учетом протекторов
  14. Проблемы обфускации
  15. Ссылки


Примечание
Информация приведена в учебных целях и в целях проверки скриптов.
Все примеры приведены на отладчике по имении x64dbg (x32dbg).
Благодарю @asdzxcjqwe за помощь в конце 2017 и начале 2018 года.
 
Последнее редактирование:

#Rin

Известный
Автор темы
Всефорумный модератор
1,214
1,035


Краткий ввод в использование отладчика
Документация по отладчику

Клавиши которые вам потребуются на данный момент:
  1. F2 - Переключить точку останову.
  2. F7 - Шаг с заходом.
  3. F8 - Шаг с обходом.
  4. F9 - Выполнить.
  5. Ctrl + F9 - Выполнить до возврата.
  6. Ctrl + I - Открыть модуль Scylla (Нужна для снятия дампа).
Эти действия можно выполнять не только клавишами, но и через меню вверху окна:
ysgueFQ.png


Модули:
p9Uvvz7.png


Так же есть меню на ПКМ:
eB6In8o.png


Немного ответов на возможные вопросы:
Q: Что значить шаг?
A: Это выполнение ровно одной инструкции процессора.

Q: Что означает шаг с обходом?
A: Шаг с обходом выполняет вызов функции, но не заходит в него.

Q: Что означает шаг с заходом?
A: Шаг с заходом позволяет зайти в вызов функции.

Немного терминов:
Entry Point (EP) - Точка входа (Место куда передается управление в программе, после инициализацией ОС).
Original Entry Point (OEP) - Оригинальная точка входа (Тоже самое что EP, но так как упаковщики/протекторы меняют EP под себя, оригинальная точка входа прячется и ее надо найти).
Регистры - блок ячеек памяти, образующий сверхбыструю оперативную память (СОЗУ) внутри процессора.
Трассировка - процесс пошагового выполнения программы. Т.е нажатие F8 в нашем случае.


Определение версии и упаковщиков
Версию AHK можно определить в свойствах, так как большинство людей даже не изменяют информацию приведенную здесь:
l79QuEG.png


Если эта информация изменена, то можно попытаться найти функцию, которая возвращать версию AHK:
C++:
VarSizeType BIV_AhkVersion(LPTSTR aBuf, LPTSTR aVarName)
{
    if (aBuf)
        _tcscpy(aBuf, T_AHK_VERSION);
    return (VarSizeType)_tcslen(T_AHK_VERSION);
}
Как же ее найти в скомпилированном файле?
Если в исходниках AHK помотать несколько строк выше, можно найти функцию в которой используются строки, по которым как раз таки можно ориентироваться. В нашем случае этой строкой будет "Logoff", перейдя по адресу где она используется (о поиске строк чуть позже) и помотав несколько раз вниз, как раз можно найти эту функцию:
jGAvnUU.png


Что бы обнаружить протектор или упаковщик можно воспользоваться Detect It Easy 2.0 и подобными программами.
yhqmyue.png




Общие сходства декомпиляции
Все версии AHK подгружают код из ресурсов.
Исключением является только IronAHK, так как он компилируется в байт код, эта версия не подлежит декомпиляции, так же исключением является AHK Classic, в ней код находился так же в файле, но чтобы его прочитать, интерпретатор открывал себя в виде ZIP архива и читал оттуда.
Есть два варианта как найти место где происходить загрузка кода:
В обоих случаях для начала нужно запустить скрипт через отладчик. Это можно сделать перекидыванием иконки скрипта на иконку отладчика, или через меню отладчика.
Вариант 1:
В данном случае мы просто ищем по строке названия ресурса.
  1. Переходим во вкладку "Отладочные символы" и выбираем в списке название нашего скрипта, и жмем дважды на него.
    vf0kmfS.png
  2. Нажимаем на кнопку поиска строк "Az".
    EYv6y5t.png
  3. Если файл запакован упаковщиком, смотри Снятие UPX/MPRESS, если протектором, перейди в Распаковка с учетом протекторов
  4. Ищем следующие вариации ">AUTOHOTKEY SCRIPT<", ">AHK WITH ICON<", "E4847ED08866458F8DD35F94B37001C0", "AHK_SCRIPT".
  5. Переходим по адресу где используется эта строка, под нею должен быть ряд функций с LoadResouce. SizeOfResource, LockResource (Исключение: Некоторые сборки AHK_H).
Вариант 2:
  1. Внизу отладчика находиться консоль.
  2. Вводим в нее команду "bp FindResourceA" или "bp FindResourceW" (бряк на функцию).
    M0ECpJQ.png
  3. Нажимаем F9, пока не увидим в консоле следующие сообщение.
    wKFdGU2.png
  4. Нажимаем Ctrl + F9, затем F8, и мы попадаем на то место с подгрузкой кода.
    8nodMTf.png
Общее:
  1. Жмем F8 пока не доберемся до SizeOfResource, еще раз F8.
  2. Перенаправляем свой взял на окно с регистрами, а именно регистр eax.
    fxN4gZs.png
  3. ПКМ -> "Копировать значение", и где-нибудь записываем его (Например в блокноте).
    FLbNyMS.png
  4. То что мы сохранили это размер ресурса (т.е ахк кода).
  5. Трассируем до функции LockResource, и выполняем ее.
  6. Жмем ПКМ -> "Перейти к дампу".
    21ml23X.png
Дамп кода:
  1. Перебрасываем взор на окно снизу.
    qGyOQYh.png
  2. В примере код маленький, но в случае с большим кодом мотать вниз в маленьком окне отладчике проблематично, так что просто выделяем первый символ и мотаем до конца дампа, где жмем Shift, в итоге код выделиться.
  3. Жмем ПКМ -> "Двоичные операции" -> "Сохранить в файл".
    EqIQVoa.png
  4. Открываем файл в HxD или в другом удобном HEX редакторе.
  5. Жмем Ctrl + G и вводим как раз то значение, что запоминали в шаге 7.
  6. Выделяем от этого смещения до конца файла, и удаляем это.
    RDLK5DA.png
  7. Ctrl + S и файл сохранен.



AHK v.1.0 (Basic, Classic, Vanilla)
Самая первая версия AHK, некоторые предпочитают компилировать компилятором этой версии, так как он позволяет указать пароль для декомпиляции или вообще запретить декомпиляцию (естественно это лишь программное ограничение).
  1. Используем первый вариант поиска места загрузки кода из главы общие сходства декомпиляции.
    03c7QDY.png
  2. Трассируем, пока визуально не замечаем что здесь цикл с расшифровкой кода.
    6Hf68ej.png
  3. Смотрим дамп регистра edi.
  4. Можем тыкать F8 и визуально наблюдать как код потихоньку дешифруется, либо же поставить бряк на первую инструкцию после цикла и нажать F9.
  5. Дальше дампим код как и в главе общие сходства декомпиляции, в подгруппе дамп кода.



AHK v.1.1 (AHK_L)
Процесс декомпиляции точно такой же как описанный в главе общие сходства декомпиляции.
С++ код подгрузки ахк кода:
C++:
// ...
    TextMem::Buffer textbuf(NULL, 0, false);

    HRSRC hRes;
    HGLOBAL hResData;

#ifdef _DEBUG
    if (hRes = FindResource(NULL, _T("AHK"), RT_RCDATA))
#else
    if (hRes = FindResource(NULL, _T(">AUTOHOTKEY SCRIPT<"), RT_RCDATA))
#endif
    {}
    else if (hRes = FindResource(NULL, _T(">AHK WITH ICON<"), RT_RCDATA))
    {}
 
    if ( !( hRes
            && (textbuf.mLength = SizeofResource(NULL, hRes))
            && (hResData = LoadResource(NULL, hRes))
            && (textbuf.mBuffer = LockResource(hResData)) ) )
    {
        MsgBox(_T("Could not extract script from EXE."), 0, aFileSpec);
        return FAIL;
    }

    TextMem tmem, *fp = &tmem;
    // NOTE: Ahk2Exe strips off the UTF-8 BOM.
    tmem.Open(textbuf, TextStream::READ | TextStream::EOL_CRLF | TextStream::EOL_ORPHAN_CR, CP_UTF8);
// ...
 
Последнее редактирование:

#Rin

Известный
Автор темы
Всефорумный модератор
1,214
1,035


AHK v.1.1 (AHK_N, AHK_H, AHK_H-alpha)
Эта ветка версий была намерено и относительно сложно защищена от декомпиляции. По мимо шифрования кода и затирания памяти, здесь есть и неплохая антиотладка.
С++ код подгрузки ахк кода:
C++:
// ...
    HRSRC hRes;

#ifdef _DEBUG
    hRes = FindResource(NULL, _T("AHK"), RT_RCDATA);
#else
    hRes = FindResource(NULL, _T("E4847ED08866458F8DD35F94B37001C0"), RT_RCDATA);
#endif
    HGLOBAL hResData;
    if ( !( hRes
            && (textbuf.mLength = SizeofResource(NULL, hRes))
            && (hResData = LoadResource(NULL, hRes))
            && (textbuf.mBuffer = LockResource(hResData)) ) )
    {
        MsgBox(_T("Could not extract script from EXE."), 0, aFileSpec);
        return FAIL;
    }
    if (*(unsigned int*)textbuf.mBuffer == 0x04034b50)
    {
#ifndef _USRDLL
        if (!AHKModule())
            return FAIL;
#endif
        LPVOID aDataBuf;
        for (int i = 0; i < 10; i++)
            *g_default_pwd[i] = i + 1;
        aSizeDeCompressed = DecompressBuffer(textbuf.mBuffer, aDataBuf, textbuf.mLength, g_default_pwd);
        if (aSizeDeCompressed)
        {
            AUTO_MALLOCA(buff, LPVOID, aSizeDeCompressed + 2); // +2 for terminator, will be freed when function returns
            memcpy(buff, aDataBuf, aSizeDeCompressed);
            g_memset((char*)buff + aSizeDeCompressed, 0, 2);
            g_memset(aDataBuf, 0, aSizeDeCompressed);
            free(aDataBuf);
            textbuf.mLength = aSizeDeCompressed + 2;
            textbuf.mBuffer = buff;
#ifndef _USRDLL
            if (!AHKModule())
                return FAIL;
            MemoryFreeLibrary(g_hNTDLL);
#endif
        }
    }
    fp = &tmem;
    // NOTE: Ahk2Exe strips off the UTF-8 BOM.
    tmem.Open(textbuf, TextStream::READ | TextStream::EOL_CRLF | TextStream::EOL_ORPHAN_CR, CP_UTF8);
    // ...
TlsCallBack:
C++:
// The TLS callback is called before the process entry point executes, and is executed before the debugger breaks
// This allows you to perform anti-debugging checks before the debugger can do anything
// Therefore, TLS callback is a very powerful anti-debugging technique
void WINAPI TlsCallback(PVOID Module, DWORD Reason, PVOID Context)
{
    PBOOLEAN BeingDebugged;
    HANDLE DebugPort = NULL;
 
    // Execute only if A_IsCompiled
#ifdef _DEBUG
    g_TlsDoExecute = true;
    return;
#endif
#ifndef AUTOHOTKEYSC
    if (!FindResource(NULL, _T("E4847ED08866458F8DD35F94B37001C0"), MAKEINTRESOURCE(RT_RCDATA)))
    {
        g_TlsDoExecute = true;
        return;
    }
#endif
    Sleep(20);
#ifdef _M_IX86 // compiles for x86
    BeingDebugged = (PBOOLEAN)__readfsdword(0x30) + 2;
#elif _M_AMD64 // compiles for x64
    BeingDebugged = (PBOOLEAN)__readgsqword(0x60) + 2; //0x60 because offset is doubled in 64bit
#endif
    if (*BeingDebugged) // Read the PEB
        return;
    char filename[MAX_PATH];
    HFILE fp;
    HMODULE hModule = GetModuleHandleA("ntdll.dll");
    unsigned char* data = (unsigned char*)GlobalAlloc(NULL, 0x300000); // 3MB should be sufficient
    GetModuleFileNameA(hModule, filename, MAX_PATH);
    g_hNTDLL = MemoryLoadLibrary(data, _lread(fp = _lopen(filename, OF_READ), data, 0x300000));
    _lclose(fp);
    GlobalFree(data);
    MyNtQueryInformationProcess _NtQueryInformationProcess = (MyNtQueryInformationProcess)MemoryGetProcAddress(g_hNTDLL, "NtQueryInformationProcess");
    if (!_NtQueryInformationProcess(NtCurrentProcess(), 7, &DebugPort, sizeof(HANDLE), NULL) && DebugPort)
        return;
#ifdef _WIN64
    MyNtSetInformationThread _NtSetInformationThread = (MyNtSetInformationThread)MemoryGetProcAddress(g_hNTDLL, "NtSetInformationThread");
#else
    MyNtSetInformationThread _NtSetInformationThread = (MyNtSetInformationThread)GetProcAddress(hModule, "NtSetInformationThread");
#endif
    _NtSetInformationThread(GetCurrentThread(), 0x11, 0, 0);
    BOOL _BeingDebugged;
    CheckRemoteDebuggerPresent(GetCurrentProcess(), &_BeingDebugged);
    if (_BeingDebugged)
        return;
    FILETIME SystemTime, CreationTime = { 0 };
    FILETIME ExitTime, KernelTime, UserTime;
    GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime);
    GetSystemTimeAsFileTime(&SystemTime);
    TCHAR timerbuf[MAX_INTEGER_LENGTH];
    _i64tot((((((ULONGLONG)SystemTime.dwHighDateTime) << 32) + SystemTime.dwLowDateTime) - ((((ULONGLONG)CreationTime.dwHighDateTime) << 32) + CreationTime.dwLowDateTime)), timerbuf, 10);
    ULONGLONG time = ((((((ULONGLONG)SystemTime.dwHighDateTime) << 32) + SystemTime.dwLowDateTime) - ((((ULONGLONG)CreationTime.dwHighDateTime) << 32) + CreationTime.dwLowDateTime)));
    if (time > 20000000 || time < 1000)
        return;
    ((_QueryPerformanceCounter)MemoryGetProcAddress(g_hNTDLL, "RtlQueryPerformanceFrequency"))((LARGE_INTEGER*)&g_QPCfreq);
    (g_QPC = (_QueryPerformanceCounter)MemoryGetProcAddress(g_hNTDLL, "RtlQueryPerformanceCounter"))((LARGE_INTEGER*)&g_QPCtimer);
    g_TlsDoExecute = true;
}
void WINAPI TlsCallbackCall(PVOID Module, DWORD Reason, PVOID Context);
__declspec(allocate(".CRT$XLB")) PIMAGE_TLS_CALLBACK CallbackAddress[] = { TlsCallbackCall, NULL, TlsCallback }; // Put the TLS callback address into a null terminated array of the .CRT$XLB section
void WINAPI TlsCallbackCall(PVOID Module, DWORD Reason, PVOID Context)
{
    VirtualProtect(CallbackAddress + sizeof(UINT_PTR), sizeof(UINT_PTR), PAGE_EXECUTE_READWRITE, &g_TlsOldProtect);
    for (int i = 0; i < sizeof(UINT_PTR); i++)
        *((BYTE*)&CallbackAddress[1] + i) = ((BYTE*)&CallbackAddress[2])[i];
    CallbackAddress[2] = NULL;
    VirtualProtect(CallbackAddress + sizeof(UINT_PTR), sizeof(UINT_PTR), g_TlsOldProtect, &g_TlsOldProtect);
}
Функция AHKModule:
C++:
#ifndef _USRDLL
bool AHKModule()
{
#ifndef _DEBUG
    double aQPCtimer;
    g_QPC((LARGE_INTEGER*)&aQPCtimer);
    double time = (aQPCtimer - g_QPCtimer) / g_QPCfreq;
    g_QPCtimer = aQPCtimer;
    TCHAR buf[MAX_INTEGER_LENGTH];
    FTOA(time, buf, MAX_INTEGER_LENGTH);
    if (time > 1 || time < 0.00001)
        return false;
#endif
    return true;
}
#endif
Обратите внимание на функцию AHKModule(), ведь она участвует в антиотладке. Поэтому эту функцию нужно занопить.
DecompressBuffer() - эта функция дешифрует AES-256, а также распаковывает из памяти код по технологиям LiteZip, именно в этой функции находиться пароль дешифрации, по умолчанию это AutoHotkey, но другие люди могут его изменить, так же есть протектор от @asdzxcjqwe который позволяет менять этот пароль без перекомпиляции интерпретатора, пароль при этом фиксированной длины.
Пароль для дешифрации передается 2-ым аргументом в функцию CryptHashData().
  1. Обходим антиотладку, сделать это довольно легко. В интерпретаторе есть глобальная переменная
    C++:
    bool g_TlsDoExecute;
    , после выполнения TlsCallback (функция, которая вызывается задолго до того как точка входа получить управление), этой переменной присваивается true, во всех остальных случаях идет return, а следовательно переменная так и остается со значением false, следовательно в WinMain не пройдет проверка
    C++:
        if (!g_TlsDoExecute)
            return 0;
    и программа завершится.
  2. Ищем текст "RtlQueryPerformanceCounter", переходим по адресу где она используется и чуть ниже мы как раз и находим нужную переменную.
    5ITGtsx.png
  3. Изменяем ее значение на 1.
    b6lrSHO.png
  4. Ищем строки начинающиеся на E4, либо же такой же бряк на FindResource.
    GWbjuat.png
  5. Заходим в анти-отладочную функцию, на скриншоте выше то что с комментарием. Выделяем все до инструкции ret и заполняем функцию нопами. ПКМ по выделенному -> "Двоичные операции" -> "Заполнить командами NOP".
    leWbVDQ.png
  6. Ищем строчку "Parameter #2 required".
  7. Помотав ниже, находим те WinCrypt функции.
    wS77gyT.png
  8. В конце цикла в одном из регистре (в разных версиях могло быть по разному, в нашем случае в ecx, но бывает и в eax) мы можем обнаружить пароль для дешифрации.
    HlXHyU5.png
В более старых версиях, пароль был статичный и находился еще легче:
  1. Ищем строчку "close AHK_PlayMe".
  2. Мотаем чуть ниже и находим массив символов, это и есть пароль.
    d7A07o6.png
Можно воспользоваться этим софтом, записав туда этот пароль. Ну или же можно будет продолжить трассировать и сдампить код из отладчика.




AHK v.1.1 (AHK_Guard)
Криптор от @asdzxcjqwe, основан на AHK v.1.1 (AHK_L)
Работал очень просто.
C++:
// ...
for (has_continuation_section = false, in_continuation_section = 0;;)
{
            // This increment relies on the fact that this loop always has at least one iteration:
            ++phys_line_number; // Tracks phys. line number in *this* file (independent of any recursion caused by #Include).
            next_buf_length = GetLine(next_buf, LINE_SIZE - 1, in_continuation_section, fp);
            for (size_t i = 0; i < next_buf_length; i++)
                next_buf[i] += 4; // Типа крутое шифрование.
// ....
}
Ну а так же еще пару таких строчек, плюс затирание этих дешифрованных строчек, после их лексинга.
Помимо этого первые 999 строчек кода, забиты рандом символами.
Вытащить код легко, действуем так же как с AHK v.1.1 (AHK_L), но после пишем утилиту которая прочитает дамп и к каждому байту прибавит 0x4, ну или же вновь воспользоваться этим софтом.



AHK v.1.1 (AHK_Protector, AHK_Protector_Mini)
Криптор от @Rinat_Namazov, основан так же на AHK v.1.1 (AHK_L). Антиотладчик мощный, но не все продумано, относительно простой метод шифрования.
Здесь долго останавливаться не буду, но вот сам алгоритм дешифровки кода:
lGJurp8.png




IronAHK
Версия о которой мало кто знает, она заброшена, но подавала большие надежды. Ведь позволяло компилировать скрипты в байт-код вроде как виртуальной машины .NET, так же с помощью Mono оно могло быть кроссплатформенной. Качественной декомпиляции не подлежит. Разве только попробовать декомпиляторы C#.



Снятие UPX/MPRESS
  1. Как только мы закинем файл в отладчик, отладчик остановиться на системной точке остановы.
    AiMWJzJ.png
  2. Жмем F9, мы попали на точку входа.
    tLLJW7V.png
  3. Мотаем вниз пока не увидим последний JMP перед нулями.
    XtQIvv8.png
  4. Устанавливаем бряк на этот адрес, нажатием F2, после этого действия адрес инструкции будет подсвечиваться красным.
  5. Нажимаем вновь F9, отладчик выполнить код и остановиться на этом адресе.
  6. Жмем F8, мы попали на оригинальную точку входа, которая была до упаковки.
    47rJLIO.png
  7. Можно делать дамп, а после восстановить таблицу импорта, что бы получить чистый EXE, из которого после через Resource Hacker, можно будет достать код, картинки и прочие ресурсы, если они были. Смотреть главу снятия дампа.


Снятие дампа
Как только мы дошли до 7-го шага в главе снятия UPX/MPRESS, мы можем приступить к снятию дампа.
  1. Находясь на OEP, нажимаем Ctrl + I, перед нами откроется окно модуля Scylla.
    vuEU5pH.png
  2. Нажимаем "IAT Autosearch", и смотрим что бы в OEP был записан как раз-таки настоящий адрес.
  3. Нажимаем "Dump", и сохраняем файл в удобном месте.
  4. Запускаем оригинальный AHK скрипт, без отладчика, без всего.
  5. В окне Scylla выбираем процесс который мы только что запустили.
  6. Жмем "Get Imports".
  7. Жмем "Fix Dump", и выбираем дамп который мы сохранили в шаге 3.
В случае с упаковкой с помощью UPX, можно вообще распаковать с помощью штатной функции. Запускаем в консоле "upx.exe -d script.exe".
Все упаковщики примерно одинаково распаковываются, иногда можно даже наткнутся на автоматический распаковщик.




Распаковка с учетом протекторов
Когда скрипт накрыт протектором по типу VMP, Themida и т.д. Использовать отладчик ради этой распаковки не особо целесообразно и куда проще воспользоваться следующими методами:
1. Поиск по сигнатуре, возможен если код не был сильно видоизменен протектором.
Код:
00 00 00 00 10 00 26 00 46 00 69 00 6C 00 65 00 00 00 00 00 78 FF 26 00 52 00 65 00 6C 00 6F 00 61 00 64 00 20 00 53 00 63 00 72 00 69 00 70 00 74 00 09 00 43 00 74 00 72 00 6C 00 2B 00 52 00 00 00 00 00 79 FF 26 00 45 00 64 00 69 00 74 00 20 00 53 00 63 00 72 00 69 00 70 00 74 00 09 00 43 00 74 00 72 00 6C 00 2B 00 45 00 00 00 00 00 7A FF 26 00 57 00 69 00 6E 00 64 00 6F 00 77 00 20 00 53 00 70 00 79 00 00 00 00 00 00 00 00 00 00 00 7B FF 26 00 50 00 61 00 75 00 73 00 65 00 20 00 53 00 63 00 72 00 69 00 70 00 74 00 09 00 50 00 61 00 75 00 73 00 65 00 00 00 00 00 7C FF 26 00 53 00 75 00 73 00 70 00 65 00 6E 00 64 00 20 00 48 00 6F 00 74 00 6B 00 65 00 79 00 73 00 00 00 00 00 00 00 00 00 80 00 7D FF 45 00 26 00 78 00 69 00 74 00 20 00 28 00 54 00 65 00 72 00 6D 00 69 00 6E 00 61 00 74 00 65 00 20 00 53 00 63 00 72 00 69 00 70 00 74 00 29 00 00 00 10 00 26 00 56 00 69 00 65 00 77 00 00 00 00 00 7E FF 26 00 4C 00 69 00 6E 00 65 00 73 00 20 00 6D 00 6F 00 73 00 74 00 20 00 72 00 65 00 63 00 65 00 6E 00 74 00 6C 00 79 00 20 00 65 00 78 00 65 00 63 00 75 00 74 00 65 00 64 00 09 00 43 00 74 00 72 00 6C 00 2B 00 4C 00 00 00 00 00 7F FF 26 00 56 00 61 00 72 00 69 00 61 00 62 00 6C 00 65 00 73 00 20 00 61 00 6E 00 64 00 20 00 74 00 68 00 65 00 69 00 72 00 20 00 63 00 6F 00 6E 00 74 00 65 00 6E 00 74 00 73 00 09 00 43 00 74 00 72 00 6C 00 2B 00 56 00 00 00 00 00 80 FF 26 00 48 00 6F 00 74 00 6B 00 65 00 79 00 73 00 20 00 61 00 6E 00 64 00 20 00 74 00 68 00 65 00 69 00 72 00 20 00 6D 00 65 00 74 00 68 00 6F 00 64 00 73 00 09 00 43 00 74 00 72 00 6C 00 2B 00 48 00 00 00 00 00 81 FF 26 00 4B 00 65 00 79 00 20 00 68 00 69 00 73 00 74 00 6F 00 72 00 79 00 20 00 61 00 6E 00 64 00 20 00 73 00 63 00 72 00 69 00 70 00 74 00 20 00 69 00 6E 00 66 00 6F 00 09 00 43 00 74 00 72 00 6C 00 2B 00 4B 00 00 00 00 00 00 00 00 00 80 00 82 FF 26 00 52 00 65 00 66 00 72 00 65 00 73 00 68 00 09 00 46 00 35 00 00 00 90 00 26 00 48 00 65 00 6C 00 70 00 00 00 00 00 83 FF 26 00 55 00 73 00 65 00 72 00 20 00 4D 00 61 00 6E 00 75 00 61 00 6C 00 09 00 46 00 31 00 00 00 80 00 84 FF 26 00 57 00 65 00 62 00 20 00 53 00 69 00 74 00 65 00 00 00 01 00 FF FF 00 00 00 00 00 00 00 00 48 0A CC 80 04 00 00 00 00 00 D2 00 53 00 00 00 00 00 44 00 69 00 61 00 6C 00 6F 00 67 00 00 00 0A 00 90 01 00 00 4D 00 53 00 20 00 53 00 68 00 65 00 6C 00 6C 00 20 00 44 00 6C 00 67 00 00 00 00 00 00 00 00 00 00 00 80 00 81 50 02 00 33 00 CF 00 0C 00 C9 00 00 00 FF FF 81 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 50 33 00 43 00 1F 00 0C 00 01 00 00 00 FF FF 80 00 4F 00 4B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 50 81 00 43 00 1F 00 0C 00 02 00 00 00 FF FF 80 00 43 00 61 00 6E 00 63 00 65 00 6C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 50 03 00 02 00 CD 00 30 00 CC 00 00 00 FF FF 82 00 50 00 72 00 6F 00 6D 00 70 00 74 00 00 00 00 00 03 00 70 00 83 FF 00 00 0B 00 48 00 80 FF 00 00 0B 00 4B 00 81 FF 00 00 0B 00 4C 00 7E FF 00 00 03 00 74 00 82 FF 00 00 0B 00 56 00 7F FF 00 00 03 00 13 00 7B FF 00 00 0B 00 45 00 79 FF 00 00 8B 00 52 00 78 FF 00 00
Или же:
Код:
3B 20 3C 43 4F 4D 50 49 4C 45 52 3A
zEE2AQR.png


2. Можно написать dll плагин с хуками на определенные функции.
Пример псевдокода на C++:
C++:
CHook *FindResourceHook, *SizeOfResourceHook, *LoadResourceHook, *LockResourceHook, *CryptHashDataHook;

enum eScriptType {
    UNKNOWN
    , AHK_L
    , AHK_H
    , AHK_Guard
};

std::string resources[]        = { ">AUTOHOTKEY SCRIPT<", ">AHK WITH ICON<", "E4847ED08866458F8DD35F94B37001C0", "AHK_SCRIPT" };
HRSRC scriptResInfo            = NULL;
HGLOBAL scriptResData        = NULL;
DWORD scriptSize            = 0;
TCHAR *scriptPassword        = (TCHAR *)"";
eScriptType scriptType        = UNKNOWN;
std::string scriptStrType    = "";
std::string scriptCode        = "";

void WaitPass(void*)
{
    while (true)
    {
        if (scriptPassword == "")
        {
            Sleep(50);
            continue;
        }

        LPVOID aDataBuf;
        DWORD aSizeDeCompressed = DecompressBuffer((LPVOID)scriptCode.c_str(), aDataBuf, scriptCode.length(), (TCHAR**)scriptPassword);
        if (aSizeDeCompressed)
        {
            void *buff = new LPVOID[aSizeDeCompressed + 2];
            memcpy(buff, aDataBuf, aSizeDeCompressed);
            memset((char*)buff + aSizeDeCompressed, 0, 2);
            memset(aDataBuf, 0, aSizeDeCompressed);
            free(aDataBuf);
            scriptCode = (char*)buff;
            delete buff;
        }
    }
}

HRSRC WINAPI Hook_FindResource(HMODULE hModule, LPCSTR lpName, LPCSTR lpType)
{
    HRSRC retVal = ((HRSRC(WINAPI *)(HMODULE, LPCSTR, LPCTSTR))(FindResourceHook->getTrampoline()))(hModule, lpName, lpType);
    for (int i = 0; i < 4; i++)
    {
        if (lpName == resources[i])
        {
            scriptType = (i == 0 || i == 1 ? AHK_L : (i == 2 ? AHK_H : (i == 3 ? AHK_Guard : UNKNOWN)));
            scriptResInfo = retVal;
        }
    }
    return retVal;
}

DWORD WINAPI Hook_SizeOfResource(HMODULE hModule, HRSRC hResInfo)
{
    DWORD retVal = ((DWORD(WINAPI *)(HMODULE, HRSRC))(SizeOfResourceHook->getTrampoline()))(hModule, hResInfo);
    if (hResInfo == scriptResInfo)
        scriptSize = retVal;
    return retVal;
}

HGLOBAL WINAPI Hook_LoadResource(HMODULE hModule, HRSRC hResInfo)
{
    HGLOBAL retVal = ((HGLOBAL(WINAPI *)(HMODULE, HRSRC))(LoadResourceHook->getTrampoline()))(hModule, hResInfo);
    if (hResInfo == scriptResInfo)
        scriptResData = retVal;
    return retVal;
}

LPVOID WINAPI Hook_LockResource(HGLOBAL hResData)
{
    LPVOID retVal = ((LPVOID(WINAPI *)(HGLOBAL))(LockResourceHook->getTrampoline()))(hResData);
    if (hResData == scriptResData)
    {
        scriptCode = (TCHAR*)retVal;
        if (scriptType == AHK_Guard)
        {
            std::string tempCode;
            for (int i = 0, line = 0; ; i++)
            {
                BYTE oneByte = (BYTE)(scriptCode.c_str()[i]);
                if (oneByte == 0xA)
                    line++;
                if (line > 0x3E7)
                    tempCode[i] = scriptCode[i];
            }
            scriptCode.clear();
            bool uc = false;
            for (size_t i = 0; i < tempCode.length(); i++)
            {
                if (uc)
                {
                    uc = false;
                    continue;
                }
                BYTE firstByte = (BYTE)(tempCode.c_str()[i]);
                BYTE secondByte = (BYTE)(tempCode.c_str()[i + 1]);
                if (firstByte == 0xA)
                    scriptCode.append("\n");
                else if (firstByte == 0xC2 && secondByte == 0xA4)
                {
                    scriptCode.append("Ё");
                    uc = true;
                }
                else if (firstByte == 0xD2 && secondByte == 0x91)
                {
                    scriptCode.append("ё");
                    uc = true;
                }
                else if (firstByte == 0xD0)
                {
                    if (secondByte == 0x85)
                        scriptCode.append("Б");
                    if (secondByte >= 0x90 && secondByte <= 0xBF)
                        scriptCode.append((char*)(secondByte + 0x34));
                    uc = true;
                }
                else if (firstByte == 0xD1)
                {
                    if (secondByte == 0x98)
                        scriptCode.append("А");
                    else if (secondByte == 0x95)
                        scriptCode.append("В");
                    else if (secondByte == 0x97)
                        scriptCode.append("Г");
                    else if (secondByte >= 0x80 && secondByte <= 0x8B)
                        scriptCode.append((char*)(secondByte + 0x74));
                    uc = true;
                }
                else
                    scriptCode.append((char*)(firstByte + 0x4));
            }
        }
        else if (scriptType == AHK_H)
        {
            if (*(unsigned int*)scriptCode.c_str() == 0x04034b50)
                _beginthread(WaitPass, NULL, NULL);
        }
        MessageBox(NULL, scriptCode.c_str(), "Test", NULL);
    }
    return retVal;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReasonForCall, LPVOID lpReserved)
{
    switch (dwReasonForCall)
    {
    case DLL_PROCESS_ATTACH:
    {
        // Ставим хуки.
        FindResourceHook    = new CHook("kernel32.dll", "FindResource", Hook_FindResource);
        SizeOfResourceHook    = new CHook("kernel32.dll", "SizeOfResource", Hook_SizeOfResource);
        LoadResourceHook    = new CHook("kernel32.dll", "LoadResource", Hook_LoadResource);
        LockResourceHook    = new CHook("kernel32.dll", "LockResource", Hook_LockResource);
        LoadLibrary("Advapi32.dll");
        CryptHashDataHook    = new CHook("Advapi32.dll", "CryptHashData", Hook_CryptHashData);
        break;
    }
    case DLL_PROCESS_DETACH:
    {
        // Снимаем хуки.
        delete FindResourceHook;
        delete SizeOfResourceHook;
        delete LoadResourceHook;
        delete LockResourceHook;
        delete CryptHashDataHook;
        break;
    }
    }
    return TRUE;
}




Проблемы обфускации
Обфускатор написанный для AHK не сложный, в теории для него можно написать деобфускатор, так как по сути нужна только свертка констант и переименование функций/переменных в более читабельных вид.




Ссылки
  1. Статья на википедии (На английском).
  2. Официальный сайт.
  3. Форум.
  4. Документация по AHK.
  5. Документация по AHK_H v.2.0.
  6. Документация по IronAHK (Архивная ссылка от того что осталось).
  7. Исходный код AHK_Classic.
  8. Исходный код AHK_L.
  9. Исходный код AHK_N.
  10. Исходный код AHK_H.
  11. Исходный код AHK_H v.2.0.
  12. Исходный код IronAHK.
  13. Mono.
  14. Исходный код компилятора на C++.
  15. Исходный код криптора.
  16. Обфускатор.
 
Последнее редактирование:

Qsany

Потрачен
464
147
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Декомпиляция AutoHotKey
Код скриптов на AutoHotKey при компиляции пакуется как есть (Исключение: IronAHK), из него вырезаются только комментарии и другие небольшие изменения.
Следовательно процесс декомпиляции легок, но при криптовании и обфускации этот процесс усложняется.


Содержание
  1. Примечание
  2. Краткий ввод в использование отладчика
  3. Определение версии и упаковщиков
  4. Общие сходства декомпиляции
  5. AHK v.1.0 (Basic, Classic, Vanilla)
  6. AHK v.1.1 (AHK_L)
  7. AHK v.1.1/2.0 (AHK_N, AHK_H, AHK_H-alpha)
  8. AHK v.1.1 (AHK_Guard)
  9. AHK v.1.1 (AHK_Protector, AHK_Protector_Mini)
  10. IronAHK
  11. Снятие UPX/MPRESS
  12. Снятие дампа
  13. Распаковка с учетом протекторов
  14. Проблемы обфускации
  15. Ссылки


Примечание
Информация приведена в учебных целях и в целях проверки скриптов.
Все примеры приведены на отладчике по имении x64dbg (x32dbg).
Благодарю @asdzxcjqwe за помощь в конце 2017 и начале 2018 года.
При снятие MPRESS протектора достаточно нажать F9 запустить данный скрипт и тыкнуть пробел по сценарию и он найдет OEP точный. А дальше просто делаете импорт таблиц и подобное
 

Вложения

  • MPRESS.txt
    500 байт · Просмотры: 17