Новости из Блогов PHP Generic Eval Unpacker

Discussion in 'Мировые новости. Обсуждения.' started by d3l3t3, 28 Apr 2012.

  1. d3l3t3

    d3l3t3 Banned

    Joined:
    3 Dec 2010
    Messages:
    1,771
    Likes Received:
    98
    Reputations:
    10
    PHP Generic Eval Unpacker



    В предыдущей статье dx рассказывал о ручной методике снятия типовой и довольно распространенной защиты PHP-скрипта. Если проанализировать наиболее часто встречающиеся типы защиты (например, в разделе запросов на расшифровку на Античате), то можно заметить, что в большинстве случаев защита построена на максимальном сохранении исходного кода скрипта и использовании функции eval в конечном счете. Снимать такую защиту очень просто, но слегка занудно, поэтому я решил написать примитивную программу, которая осуществляет сие действо автоматически.
    Чтобы пост не был унылым, я кратенько опишу, что из себя представляет анпакер. Итак, из-за своей лени я решил использовать php-cli, расширение для php (которое перехватывает eval) и сделать к этому простой GUI. Результирующая программа выглядит следующим образом:

    [​IMG]

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

    PHP:
    #define PHP_WIN32
    #define ZEND_WIN32
    #define ZTS 1
    #define ZEND_DEBUG 0

    #pragma comment(lib, "php5ts.lib")

    #include "zend_config.w32.h"
    #include "php.h"

     
    PHP_MINIT_FUNCTION(evalhook);
    PHP_MSHUTDOWN_FUNCTION(evalhook);
    PHP_MINFO_FUNCTION(evalhook);
     
     
    zend_module_entry evalhook_ext_module_entry = {
        
    STANDARD_MODULE_HEADER,
        
    "Eval Hook",
        
    NULL,
        
    PHP_MINIT(evalhook),
        
    PHP_MSHUTDOWN(evalhook),
        
    NULLNULLNULL,
        
    "1.0",
        
    STANDARD_MODULE_PROPERTIES
    };
     
    ZEND_GET_MODULE(evalhook_ext);
     
    static 
    zend_op_array *(*orig_compile_string)(zval *source_stringchar *filename TSRMLS_DC);
    static 
    zend_bool evalhook_hooked 0;
     
    static 
    zend_op_array *evalhook_compile_string(zval inputchar *filename TSRMLS_DC)
    {
        
    /* Разделитель */
        
    const unsigned char delim[] = {0xDE0xAD0xBE0xEF};
     
        if (
    Z_TYPE_P(input) != IS_STRING)
        {
            return 
    orig_compile_string(inputfilename TSRMLS_CC);
        }
     
        
    /* Записываем содержимое, переданное в eval, в stdout */
        
    fwrite(input->value.str.val1input->value.str.lenstdout);
        
    /* Добавляем разделитель, чтобы была возможность разделения кода, относящегося к разным eval'ам */
        
    fwrite(delim1sizeof(delim), stdout);
     
     
        return 
    orig_compile_string(inputfilename TSRMLS_CC);
    }
     
    /* Функция, вызываемая при загрузке расширения */
    PHP_MINIT_FUNCTION(evalhook)
    {
        
    /* Отключаем буферизацию stdout */
        
    setvbuf(stdoutNULL_IONBF0);
     
        if (
    evalhook_hooked == 0)
        {
            
    evalhook_hooked 1;
            
    orig_compile_string zend_compile_string;
            
    zend_compile_string evalhook_compile_string;
        }
        return 
    SUCCESS;
    }
     
    /* Функция, вызываемая при выгрузке расширения */
    PHP_MSHUTDOWN_FUNCTION(evalhook)
    {
        if (
    evalhook_hooked == 1)
        {
            
    evalhook_hooked 0;
            
    zend_compile_string orig_compile_string;
        }
     
        return 
    SUCCESS;
    }
    Как видно из приведенного выше кода, перехват осуществляется довольно просто, если знать как. Никаких грязных методов, трамплинов и прочей лабуды. Теперь рассмотрим не менее простой GUI к этому делу, который написан на адовой смеси C/C++ и является примером того, как не следует писать программы. Код GUI приведу частями. Начнем с инклюдов и глобальных переменных:

    PHP:
    #include <vector>
    #include <string>
    #include <algorithm>
    #include <iterator>
     
    #include <Windows.h>
    #include <Shlwapi.h>
    #include <process.h>
    #include <tchar.h>
     
    #include "resource.h"
     
    #pragma comment (lib, "Shlwapi")
     
    /* Хендл основного окна */
    HWND ghWnd;
    /* Вектор для хранения данных eval'ов */
    std::vector<std::wstringeval_results;
    /* Хендлы, используемые для перенаправления stdout дочернего процесса */
    HANDLE child_read NULLchild_write NULL;
    /* Сигнатура для разделения кода, относящегося к разным eval'ам */
    const unsigned char signature[] = {0xDE0xAD0xBE0xEF};
    Несколько вспомогательных функций:

    PHP:
    /* Функция очевидного преобразования */
    std::wstring str2wstr(const std::strings)
    {
        
    std::wstring result;
        
    size_t lenslength s.length() + 1;
     
        
    len MultiByteToWideChar(CP_ACP0s.c_str(), slength00); 
        
    result.resize(len);
     
        
    MultiByteToWideChar(CP_ACP0s.c_str(), slength, &result[0], len);
     
        return 
    result;
    }
    /* Мой любимый Structured Exception Handler */
    LONG WINAPI SEH(struct _EXCEPTION_POINTERS *lpTopLevelExceptionFilter)
    {
        
    FatalAppExit(0TEXT("Необрабатываемое исключение"));
        return 
    0L;
    }
    /* Функция для включения/отключения элементов управления на основной форме */
    void enable_gui_controls(BOOL enable)
    {
        
    EnableWindow(GetDlgItem(ghWndIDC_LIST), enable);
        
    EnableWindow(GetDlgItem(ghWndIDC_UNPACK), enable);
        
    EnableWindow(GetDlgItem(ghWndIDC_CLEAR), enable);
    }
    /* Функция, отвечающая за диалог выбора файла */
    DWORD GetOpenName(TCHAR outbuf, const TCHAR filter, const TCHAR title)
    {
        
    OPENFILENAME ofn = {0};
        
    TCHAR buf[MAX_PATH 2];
     
        
    GetModuleFileName(NULLbufMAX_PATH);
     
        
    TCHAR tmp StrRChr(bufNULLL'\\');
        if(
    tmp != 0)
        {
            *
    tmp 0;
            
    ofn.lpstrInitialDir buf;
        }
     
        
    ofn.hInstance GetModuleHandle(NULL);
        
    ofn.hwndOwner ghWnd;
        
    ofn.lStructSize sizeof(OPENFILENAME);
        
    ofn.lpstrFilter filter;
        
    ofn.nFilterIndex 1;
        
    ofn.lpstrFile outbuf;
        
    ofn.lpstrFile[0] = 0;
        
    ofn.lpstrFile[1] = 0;
        
    ofn.nMaxFile MAX_PATH;
        
    ofn.lpstrTitle title;
        
    ofn.Flags OFN_EXPLORER OFN_DONTADDTORECENT OFN_FILEMUSTEXIST OFN_HIDEREADONLY OFN_LONGNAMES OFN_NONETWORKBUTTON OFN_PATHMUSTEXIST;
     
        return 
    GetOpenFileName(&ofn);
    }
    И, наконец, основные функции в порядке убывания "важности":

    PHP:
    unsigned __stdcall process_pipe(void arg)
    {
        
    BYTE buffer[1024];
        
    DWORD bytes_read 0;
        
    std::wstring temporary;
        
    std::vector<unsigned chardata;
        
    std::vector<unsigned char>::iterator beginend;
        
    std::vector<std::wstring>::const_iterator it;
     
     
        for (;;) 
        {
            
    /* Проверяем, есть ли данные в пайпе */
            
    if(!PeekNamedPipe(child_readNULL0NULL, &bytes_readNULL) && bytes_read == 0)
            {
     
                
    begin data.begin();
     
                
    /* Разделяем содержимое data на составляющие, */
                /* попутно занося результаты в глобальный вектор eval_results */
                /* и добавляя перечень в форму */
                
    while(1)
                {
                    
    end std::search(begindata.end(), signaturesignature sizeof(signature));
     
                    
    /* Преобразуем в wide-string для адекватного отображения многобайтовых кодировок */
                    
    temporary str2wstr(std::string(beginend));
                    
    eval_results.push_back(temporary);
                    
    /* Добавляем в ListBox урезанных вариант содержимого */
                    
    temporary temporary.substr(012) + L"...";
                    
    SendDlgItemMessage(ghWndIDC_LISTLB_ADDSTRINGNULLreinterpret_cast<LPARAM>(temporary.c_str()));
     
                    if(
    end == data.end())
                    {
                        break;
                    }
                    
    begin end sizeof(signature);
                }
                
    /* Активируем элементы управления на форме */
                
    enable_gui_controls(TRUE);
     
                break;
            }
     
            
    /* Читаем данные из пайпа */
            
    ReadFile(child_readbuffermin(bytes_readsizeof(buffer)), &bytes_readNULL);
            if(
    bytes_read != 0)
            {
                
    data.insert(data.end(), bufferbuffer bytes_read);
            }
        }
     
        
    CloseHandle(child_read);
     
        return 
    0;
    }
    В общем-то основную функцию мы рассмотрели, теперь остался всеми любимый DlgProc и WinMain:

    PHP:
    int DlgProc(HWND hWndUINT uMsgWPARAM wParamLPARAM lParam)
    {
        static 
    HICON ico;
        
    unsigned int selection_index;
        static 
    TCHAR file_path[MAX_PATH], cmd[MAX_PATH 2];
        
    TCHAR tmp;
     
        
    STARTUPINFO si;
        
    PROCESS_INFORMATION pi;
        
    SECURITY_ATTRIBUTES sa;
     
        
    ghWnd hWnd;
     
        switch(
    uMsg)
        {
            case 
    WM_INITDIALOG:
                
    /* Устанавливаем иконку для основного окна */
                
    ico LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON));
                
    SendMessage(hWndWM_SETICONICON_SMALL, (LPARAM)ico);
            break;
     
            case 
    WM_COMMAND:
                switch(
    LOWORD(wParam))
                {
                    case 
    IDC_LIST:
                        switch(
    HIWORD(wParam))
                        {
                            case 
    LBN_DBLCLK:
                            case 
    LBN_SELCHANGE:
                                
    selection_index SendDlgItemMessage(hWndIDC_LISTLB_GETCURSEL00);
                                if(
    selection_index eval_results.size())
                                {
                                    
    /* Выводим в IDC_DATA содержимое в соответствии с выбранным элементом из IDC_LIST */
                                    
    SetDlgItemText(hWndIDC_DATAeval_results[selection_index].c_str());
                                }
                            break;
                        }
                    break;
     
                    case 
    IDC_CLEAR:
                        
    /* Очищаем вектор и сопутствующие элементы на форме */
                        
    eval_results.clear();
     
                        
    SetDlgItemText(hWndIDC_DATAL"");
                        
    SendDlgItemMessage(hWndIDC_LISTLB_RESETCONTENT00);
                    break;
     
                    case 
    IDC_BROWSE:
                        
    /* Вызываем диалог выбора файла, результат записываем в IDC_PATH */
                        
    if(GetOpenName(file_pathTEXT("PHP (*.php)\0*.php\0Все файлы (*.*)\0*.*\0\0"), TEXT("Открыть...")))
                        {
                            
    SetDlgItemText(hWndIDC_PATHfile_path);
                        }
                    break;
     
                    case 
    IDC_UNPACK:
                        if(
    GetDlgItemText(hWndIDC_PATHfile_pathsizeof(file_path)))
                        {
                            
    /* Деактивируем некоторые элементы интерфейса */
                            /* А то будет ататат из-за потоков */
                            
    enable_gui_controls(FALSE);
     
                            
    /* Формируем аргумент командной строки для последующего запуска процесса */
                            
    _stprintf_s
                            
    (
                                
    cmd,
                                
    sizeof(cmd)/sizeof(cmd[0]),
                                
    TEXT("-f \"%s\""),
                                
    file_path
                            
    );
     
                            
    memset(&si0sizeof(STARTUPINFO));
                            
    memset(&pi0sizeof(PROCESS_INFORMATION));
                            
    memset(&sa0sizeof(SECURITY_ATTRIBUTES));
     
                            
    /* Включаем наследование дескрипторов дочерним процессом */
                            
    sa.nLength sizeof(SECURITY_ATTRIBUTES);
                            
    sa.bInheritHandle TRUE;
                            
    sa.lpSecurityDescriptor NULL;
                            
    /* Создаем пайп для перенаправления stdout */
                            
    CreatePipe(&child_read, &child_write, &sa0);
                            
    SetHandleInformation(child_readHANDLE_FLAG_INHERIT0);
     
                            
    /* Устанавливаем хендл, куда будет перенаправлен stdout дочернего процесса */
                            /* и флаги для скрытия консольного окна дочернего процесса */
                            
    si.cb sizeof(STARTUPINFO);
                            
    si.wShowWindow SW_HIDE;
                            
    si.hStdOutput child_write;
                            
    si.dwFlags STARTF_USESTDHANDLES STARTF_USESHOWWINDOW;
     
                            
    GetModuleFileName(NULLfile_pathMAX_PATH);
     
                            
    tmp StrRChr(file_pathNULLL'\\');
                            if(
    tmp != 0)
                            {
                                *
    tmp 0;
                            }
                            
    /* Путь к интерпретатору PHP */
                            
    _tcscat_s(file_pathMAX_PATHTEXT("\\php-5.3.3\\php.exe"));
     
                            if
                            (
                                
    CreateProcess
                                
    (
                                    
    file_path,
                                    
    cmd,
                                    
    NULL,
                                    
    NULL,
                                    
    TRUE,
                                    
    0,
                                    
    NULL,
                                    
    NULL,
                                    &
    si,
                                    &
    pi
                                
    ) == NULL
                            
    )
                            {
                                
    MessageBox(hWndTEXT("Ошибка создания процесса"), TEXT("Ошибка"), MB_OK MB_ICONERROR);
                                break;
                            }
     
                            
    /* Закрываем ненужные хендлы */
                            
    CloseHandle(pi.hProcess);
                            
    CloseHandle(pi.hThread);
                            
    CloseHandle(child_write);
     
                            
    _beginthreadex(NULL0, &process_pipeNULL0NULL);
                        }
                        else
                        {
                            
    MessageBox(hWndTEXT("Укажите путь к файлу"), TEXT("Ошибка"), MB_OK MB_ICONERROR);
                        }
                    break;
                }
            break;
     
            case 
    WM_CLOSE:
                if(
    child_write)
                {
                    
    CloseHandle(child_write);
                }
     
                
    DestroyIcon(ico);
                
    EndDialog(hWnd0);
            break;
     
            default:
                return 
    0;
        }
     
        return 
    1;
    }
     
    int WINAPI WinMain(HINSTANCE hInstanceHINSTANCE hPrevInstanceLPSTR lpCmdLineint nCmdShow)
    {
        
    SetUnhandledExceptionFilter(SEH);
     
        
    DialogBoxParam(hInstanceMAKEINTRESOURCE(IDD_MAIN), 0, (DLGPROCDlgProc0);
     
        return 
    0;
    }
    Вот и все. Приведенный выше код является ужасно примитивным и нелепым, но позволяет сэкономить немного времени при распаковке очередного PHP-скрипта. Но какие же скрипты может распаковать данная программа? Ну, например, подобные этим:

    http://pastebin.com/rLhMLui2

    http://pastebin.com/r8m1Vj2b

    Обратите внимание на то, что при распаковке скрипт исполняется, а также скрипт может до конца не распаковаться, если в нем существует, например, привязка к домену.

    Бинарник с PHP: скачать

    Исходный код: скачать

    автор: Kaimi
    http://kaimi.ru/
    http://kaimi.ru/2012/04/php-generic-eval-unpacker/
     
  2. Oppos

    Oppos New Member

    Joined:
    17 Apr 2012
    Messages:
    4
    Likes Received:
    0
    Reputations:
    0
    Пригодится, в закладки. Спасибо )
     
  3. phpdreamer

    phpdreamer Member

    Joined:
    26 Jul 2009
    Messages:
    522
    Likes Received:
    86
    Reputations:
    19
    Не понимаю какой смысл таким образом паковать, если все равно распакуют
     
  4. Jakeroid

    Jakeroid Member

    Joined:
    9 May 2009
    Messages:
    199
    Likes Received:
    12
    Reputations:
    1
    Защита от дураков и лентяеев :).
     
  5. DeepBlue7

    DeepBlue7 Elder - Старейшина

    Joined:
    2 Jan 2009
    Messages:
    359
    Likes Received:
    50
    Reputations:
    12
    Сталкивался точно с тем же пакером что в первом посте.. руками - не очень то кайфово. Спасибо.
     
  6. BlueMarine

    BlueMarine New Member

    Joined:
    18 Sep 2010
    Messages:
    0
    Likes Received:
    2
    Reputations:
    0
    Херит кодировку, как исправить?