Вы используете устаревший браузер. Этот и другие сайты могут отображаться в нём некорректно. Вам необходимо обновить браузер или попробовать использовать другой.
Декомпиляция AutoHotKey
Код скриптов на AutoHotKey при компиляции пакуется как есть (Исключение: IronAHK), из него вырезаются только комментарии и другие небольшие изменения.
Следовательно процесс декомпиляции легок, но при криптовании и обфускации этот процесс усложняется.
Примечание Информация приведена в учебных целях и в целях проверки скриптов.
Все примеры приведены на отладчике по имении x64dbg (x32dbg).
Благодарю @asdzxcjqwe за помощь в конце 2017 и начале 2018 года.
Ctrl + I - Открыть модуль Scylla (Нужна для снятия дампа).
Эти действия можно выполнять не только клавишами, но и через меню вверху окна:
Модули:
Так же есть меню на ПКМ:
Немного ответов на возможные вопросы:
Q: Что значить шаг?
A: Это выполнение ровно одной инструкции процессора.
Q: Что означает шаг с обходом?
A: Шаг с обходом выполняет вызов функции, но не заходит в него.
Q: Что означает шаг с заходом?
A: Шаг с заходом позволяет зайти в вызов функции.
Немного терминов: Entry Point (EP) - Точка входа (Место куда передается управление в программе, после инициализацией ОС). Original Entry Point (OEP) - Оригинальная точка входа (Тоже самое что EP, но так как упаковщики/протекторы меняют EP под себя, оригинальная точка входа прячется и ее надо найти). Регистры - блок ячеек памяти, образующий сверхбыструю оперативную память (СОЗУ) внутри процессора. Трассировка - процесс пошагового выполнения программы. Т.е нажатие F8 в нашем случае.
Как же ее найти в скомпилированном файле?
Если в исходниках AHK помотать несколько строк выше, можно найти функцию в которой используются строки, по которым как раз таки можно ориентироваться. В нашем случае этой строкой будет "Logoff", перейдя по адресу где она используется (о поиске строк чуть позже) и помотав несколько раз вниз, как раз можно найти эту функцию:
Общие сходства декомпиляции
Все версии AHK подгружают код из ресурсов.
Исключением является только IronAHK, так как он компилируется в байт код, эта версия не подлежит декомпиляции, так же исключением является AHK Classic, в ней код находился так же в файле, но чтобы его прочитать, интерпретатор открывал себя в виде ZIP архива и читал оттуда.
Есть два варианта как найти место где происходить загрузка кода:
В обоих случаях для начала нужно запустить скрипт через отладчик. Это можно сделать перекидыванием иконки скрипта на иконку отладчика, или через меню отладчика.
Вариант 1:
В данном случае мы просто ищем по строке названия ресурса.
Переходим во вкладку "Отладочные символы" и выбираем в списке название нашего скрипта, и жмем дважды на него.
Ищем следующие вариации ">AUTOHOTKEY SCRIPT<", ">AHK WITH ICON<", "E4847ED08866458F8DD35F94B37001C0", "AHK_SCRIPT".
Переходим по адресу где используется эта строка, под нею должен быть ряд функций с LoadResouce. SizeOfResource, LockResource (Исключение: Некоторые сборки AHK_H).
Вариант 2:
Внизу отладчика находиться консоль.
Вводим в нее команду "bp FindResourceA" или "bp FindResourceW" (бряк на функцию).
Нажимаем F9, пока не увидим в консоле следующие сообщение.
Нажимаем Ctrl + F9, затем F8, и мы попадаем на то место с подгрузкой кода.
Общее:
Жмем F8 пока не доберемся до SizeOfResource, еще раз F8.
Перенаправляем свой взял на окно с регистрами, а именно регистр eax.
ПКМ -> "Копировать значение", и где-нибудь записываем его (Например в блокноте).
То что мы сохранили это размер ресурса (т.е ахк кода).
Трассируем до функции LockResource, и выполняем ее.
Жмем ПКМ -> "Перейти к дампу".
Дамп кода:
Перебрасываем взор на окно снизу.
В примере код маленький, но в случае с большим кодом мотать вниз в маленьком окне отладчике проблематично, так что просто выделяем первый символ и мотаем до конца дампа, где жмем Shift, в итоге код выделиться.
Жмем ПКМ -> "Двоичные операции" -> "Сохранить в файл".
Открываем файл в HxD или в другом удобном HEX редакторе.
Жмем Ctrl + G и вводим как раз то значение, что запоминали в шаге 7.
Выделяем от этого смещения до конца файла, и удаляем это.
AHK v.1.0 (Basic, Classic, Vanilla)
Самая первая версия AHK, некоторые предпочитают компилировать компилятором этой версии, так как он позволяет указать пароль для декомпиляции или вообще запретить декомпиляцию (естественно это лишь программное ограничение).
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(), ведь она участвует в антиотладке. Поэтому эту функцию нужно занопить.
DecompressBuffer() - эта функция дешифрует AES-256, а также распаковывает из памяти код по технологиям LiteZip, именно в этой функции находиться пароль дешифрации, по умолчанию это AutoHotkey, но другие люди могут его изменить, так же есть протектор от @asdzxcjqwe который позволяет менять этот пароль без перекомпиляции интерпретатора, пароль при этом фиксированной длины.
Пароль для дешифрации передается 2-ым аргументом в функцию CryptHashData().
Обходим антиотладку, сделать это довольно легко. В интерпретаторе есть глобальная переменная
C++:
bool g_TlsDoExecute;
, после выполнения TlsCallback (функция, которая вызывается задолго до того как точка входа получить управление), этой переменной присваивается true, во всех остальных случаях идет return, а следовательно переменная так и остается со значением false, следовательно в WinMain не пройдет проверка
C++:
if (!g_TlsDoExecute)
return 0;
и программа завершится.
Ищем текст "RtlQueryPerformanceCounter", переходим по адресу где она используется и чуть ниже мы как раз и находим нужную переменную.
Изменяем ее значение на 1.
Ищем строки начинающиеся на E4, либо же такой же бряк на FindResource.
Заходим в анти-отладочную функцию, на скриншоте выше то что с комментарием. Выделяем все до инструкции ret и заполняем функцию нопами. ПКМ по выделенному -> "Двоичные операции" -> "Заполнить командами NOP".
Ищем строчку "Parameter #2 required".
Помотав ниже, находим те WinCrypt функции.
В конце цикла в одном из регистре (в разных версиях могло быть по разному, в нашем случае в ecx, но бывает и в eax) мы можем обнаружить пароль для дешифрации.
В более старых версиях, пароль был статичный и находился еще легче:
Ищем строчку "close AHK_PlayMe".
Мотаем чуть ниже и находим массив символов, это и есть пароль.
Можно воспользоваться этим софтом, записав туда этот пароль. Ну или же можно будет продолжить трассировать и сдампить код из отладчика.
// ...
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). Антиотладчик мощный, но не все продумано, относительно простой метод шифрования.
Здесь долго останавливаться не буду, но вот сам алгоритм дешифровки кода:
IronAHK
Версия о которой мало кто знает, она заброшена, но подавала большие надежды. Ведь позволяло компилировать скрипты в байт-код вроде как виртуальной машины .NET, так же с помощью Mono оно могло быть кроссплатформенной. Качественной декомпиляции не подлежит. Разве только попробовать декомпиляторы C#.
Как только мы закинем файл в отладчик, отладчик остановиться на системной точке остановы.
Жмем F9, мы попали на точку входа.
Мотаем вниз пока не увидим последний JMP перед нулями.
Устанавливаем бряк на этот адрес, нажатием F2, после этого действия адрес инструкции будет подсвечиваться красным.
Нажимаем вновь F9, отладчик выполнить код и остановиться на этом адресе.
Жмем F8, мы попали на оригинальную точку входа, которая была до упаковки.
Можно делать дамп, а после восстановить таблицу импорта, что бы получить чистый EXE, из которого после через Resource Hacker, можно будет достать код, картинки и прочие ресурсы, если они были. Смотреть главу снятия дампа.
Снятие дампа
Как только мы дошли до 7-го шага в главе снятия UPX/MPRESS, мы можем приступить к снятию дампа.
Находясь на OEP, нажимаем Ctrl + I, перед нами откроется окно модуля Scylla.
Нажимаем "IAT Autosearch", и смотрим что бы в OEP был записан как раз-таки настоящий адрес.
Нажимаем "Dump", и сохраняем файл в удобном месте.
Запускаем оригинальный AHK скрипт, без отладчика, без всего.
В окне Scylla выбираем процесс который мы только что запустили.
Жмем "Get Imports".
Жмем "Fix Dump", и выбираем дамп который мы сохранили в шаге 3.
В случае с упаковкой с помощью UPX, можно вообще распаковать с помощью штатной функции. Запускаем в консоле "upx.exe -d script.exe".
Все упаковщики примерно одинаково распаковываются, иногда можно даже наткнутся на автоматический распаковщик.
Распаковка с учетом протекторов
Когда скрипт накрыт протектором по типу VMP, Themida и т.д. Использовать отладчик ради этой распаковки не особо целесообразно и куда проще воспользоваться следующими методами:
1. Поиск по сигнатуре, возможен если код не был сильно видоизменен протектором.
Проблемы обфускации
Обфускатор написанный для AHK не сложный, в теории для него можно написать деобфускатор, так как по сути нужна только свертка констант и переименование функций/переменных в более читабельных вид.
Декомпиляция AutoHotKey
Код скриптов на AutoHotKey при компиляции пакуется как есть (Исключение: IronAHK), из него вырезаются только комментарии и другие небольшие изменения.
Следовательно процесс декомпиляции легок, но при криптовании и обфускации этот процесс усложняется. Содержание
Примечание
Информация приведена в учебных целях и в целях проверки скриптов.
Все примеры приведены на отладчике по имении x64dbg (x32dbg).
Благодарю @asdzxcjqwe за помощь в конце 2017 и начале 2018 года.
При снятие MPRESS протектора достаточно нажать F9 запустить данный скрипт и тыкнуть пробел по сценарию и он найдет OEP точный. А дальше просто делаете импорт таблиц и подобное