Долой импорт 2

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by greki_hoy, 2 Oct 2011.

  1. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    для тех кто "не в теме" вводная статья
    http://www.xakep.ru/magazine/xa/080/116/1.asp
    с нее все начиналось

    утилита для удобного импорта по хешу

    правила

    файл x3j11.h всегда подключать последним пример
    #include <string.h>
    #include <stdlib.h>
    #include <conio.h>
    #include <windows.h>
    #include <shlwapi.h>
    #include "x3j11.h"
    если вы это забудете миллион ошибок гарантированны также помимо генерации
    файла x3j11.h не забывайте подключать заголовки к соответствующим библиоткам
    полтому что он их не подключает

    дальше хм если вы получите такую ошибку
    error C2914: 'boost::type_of::encode_start'
    значит вы нарвались на перегруженную функцию типа
    const char *strstr(const char *_Str, const char *_SubStr)
    char *strstr(char *_Str, const char *_SubStr)
    вообще лучше их избегать всегда в WINAPI налог есть
    но если код уже написан то открываем x3j11.h делаем
    quick search по символу strstr и видим такое
    Code:
    X3J11_CallFunction(strstr,0x4e5cf9ed,0x967c28ff,__VA_ARGS__)
    
    надо заменить на
    Code:
    X3J11_CallOverload(PCH(PCCH,PCCH),0x4e5cf9ed,0x967c28ff,__VA_ARGS__)
    
    дальше хм утилита генератор заголовка обрабатывает библиотеки
    так как в списке это значит что если первым идет ntdll.dll
    и в нем есть функция strstr а следом идет msvcrt.dll и в нем
    тоже есть strstr то импорт будет из ntdll.dll таким образом
    можно меняя порядок библиотек "тюнить" что откуда "брать"

    дальше пример переделки реальной программы sdbot'a
    компилим оптимизируем по размеру выходит 24.5 кб в списке импорта
    WS2_32.DLL, MSVCRT.DLL, WININET.DLL, KERNEL32.DLL,
    ADVAPI32.DLL, SHELL32.DLL
    запускаем утилиту пишем так же через запятую делаем
    двойной клик и видим два файла x3j11.h и x3j11.cpp
    добавляем их в проект пробуем скомпилировать ошибка
    error C2914: 'boost::type_of::encode_start' делаем клик по ошибке и видим
    что как раз на функции strstr проделываем то что обсуждалось в самом начале
    и пробуем скомпилить снова все успешно собралось смотри осталось только
    MSVCRT.DLL импортирующая одну функцию memset это неявный вызов компилятором
    для обнуления как правило массивов или переменных типа структур у нас это
    PROCESSENTRY32 pe32 = {0} такой код в самом начале WinMain правим на
    memset(&pe32, 0, sizeof(pe32)) компилим теперь таблица импорта вообще изчезла
    и .exe стал чуть меньше 24 кб но главное теперь весь импорт по хешу

    пример
    Code:
    #include <windows.h>
    #include "x3j11.h"
    
    int main()
    {
    	X3J11_Startup();
    	MessageBox(0,0,0,0);
    }
    
    минимальный exe 2 кб получается из за включения кода поддержки выше будет расти только от явно написанного кода

    да кстати все это дело тестировалось на Visual C/C++ 2008 Professional
    Windows XP SP2 x86 так что если 2008 у вас есть пробовать лучше на нем

    Code:
    static volatile LONG lTreeLock;
    
    PVOID __stdcall X3J11_Hash2Ptr(DWORD dwFuncHash, DWORD dwModHash)
    {
    004017E0 55               push        ebp  
    004017E1 8B EC            mov         ebp,esp 
    004017E3 51               push        ecx  
    	while (_InterlockedExchange(&lTreeLock, TRUE) == TRUE)
    004017E4 B8 01 00 00 00   mov         eax,1 
    004017E9 B9 48 4F 40 00   mov         ecx,404F48h 
    004017EE 87 01            xchg        eax,dword ptr [ecx] 
    004017F0 83 F8 01         cmp         eax,1 
    004017F3 75 08            jne         X3J11_Hash2Ptr+1Dh (4017FDh) 
    		FuncPtr._SwitchToThread();
    004017F5 FF 15 78 4F 40 00 call        dword ptr ds:[404F78h] 
    004017FB EB E7            jmp         X3J11_Hash2Ptr+4 (4017E4h) 
    	
    	PVOID pFunc = Tree.Find(dwFuncHash);
    004017FD 8B 55 08         mov         edx,dword ptr [dwFuncHash] 
    00401800 52               push        edx  
    00401801 B9 00 30 40 00   mov         ecx,403000h 
    00401806 E8 15 FB FF FF   call        CHashMap::Find (401320h) 
    0040180B 89 45 FC         mov         dword ptr [pFunc],eax 
    	
    	if (!pFunc)
    0040180E 83 7D FC 00      cmp         dword ptr [pFunc],0 
    00401812 75 25            jne         X3J11_Hash2Ptr+59h (401839h) 
    	{
    		pFunc = ParseExport(dwFuncHash, dwModHash);  
    00401814 8B 45 0C         mov         eax,dword ptr [dwModHash] 
    00401817 50               push        eax  
    00401818 8B 4D 08         mov         ecx,dword ptr [dwFuncHash] 
    0040181B 51               push        ecx  
    0040181C E8 0F FF FF FF   call        ParseExport (401730h) 
    00401821 83 C4 08         add         esp,8 
    00401824 89 45 FC         mov         dword ptr [pFunc],eax 
    		Tree.Add(dwFuncHash, pFunc);
    00401827 8B 55 FC         mov         edx,dword ptr [pFunc] 
    0040182A 52               push        edx  
    0040182B 8B 45 08         mov         eax,dword ptr [dwFuncHash] 
    0040182E 50               push        eax  
    0040182F B9 00 30 40 00   mov         ecx,403000h 
    00401834 E8 17 FB FF FF   call        CHashMap::Add (401350h) 
    	}
    	_InterlockedExchange(&lTreeLock, FALSE);
    00401839 33 C9            xor         ecx,ecx 
    0040183B BA 48 4F 40 00   mov         edx,404F48h 
    00401840 87 0A            xchg        ecx,dword ptr [edx] 
    
    	return pFunc;
    00401842 8B 45 FC         mov         eax,dword ptr [pFunc] 
    }
    
    сейчас поиск в "хранилище" адреса лочится с помощью интринсика <intrin.h> _InterlockedExchange есть еще вариант от которого я отказался даем каждому потоку по "хранилищу" (AVL дерево) для этого досточно объявить как __declspec(thread) CHashMap Tree это работает быстрее так как нет локов (если конечно не создавать и тут же прибивать потоки) но в общем случае такая скорость маппинга адреса api не нужна потому что проседает обычно во время выполнения вызова отправка по сети рисование и прочee

    есть еще вариант юзать динамический TLS во время первого обращения к "хранилищу" любым потоком выделять память под дерево HeapAlloc TlsSetValue и вызывать RegisterWaitForSingleObject с хендлом этого потока во время его завершения вызовется каллбек и освободит память это вариант можно юзать чтоб отказаться от статический TLS так как он создает секцию .tls 15 кб размером

    но опять же если потоки создаются и тут же прибиваются то лучше юзать локи так как при использовании TLS будут постоянно резольвиться новые адреса для каждого потока

    поэтому я оставил самый простой и универсальный вариант

    чуть не забыл вызовите X3J11_Startup перед вызовами любых других функций

    библиотеки буста необходимые для сборки (1мб)
    http://slil.ru/31798581
    утилита и примеры
    http://zalil.ru/31898084
     
    #1 greki_hoy, 2 Oct 2011
    Last edited: 19 Oct 2011
    1 person likes this.
  2. greki_hoy

    greki_hoy Member

    Joined:
    4 Mar 2010
    Messages:
    326
    Likes Received:
    57
    Reputations:
    41
    update