preprocessor

Discussion in 'LANGS.C' started by osox, Feb 22, 2011.

  1. osox

    osox New Member

    Blog Posts:
    0
    Всех приветсвую.
    написал оболочку для импорта по хешу все будет выглядеть так
    как будто юзаеш "настоящее апи" ничего лишнего не передаеш но на самом деле вызовы идут косвенно через раскодировщик хеша и парсинг таблицы импорта

    все шаблоны в релизе компилятор выкинет
    никакой рантайм с собой не тянет

    Code (Text):
    1. #include <boost/preprocessor.hpp>
    2. #include <boost/typeof/typeof.hpp>
    3. #include <stdio.h>
    4. #include <windows.h>
    5.  
    6.  
    7. template<class t>
    8. struct hhh
    9. {
    10.     t v;
    11.     hhh(t v):v(v){}
    12.     template<class xi>
    13.     operator xi()
    14.     {
    15.         return (xi)v;
    16.     }
    17. };
    18.  
    19. template<class u>
    20. hhh<u> fgh(u p)
    21. {
    22.     return hhh<u>(p);
    23. }
    24.  
    25. #define MYM(z, i, e) fgh(e##i)
    26.  
    27. #define NUM_ARGS 1
    28.  
    29. template
    30. <
    31.     class ret_t,
    32.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class f),
    33.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class t)
    34. >
    35.  
    36. ret_t callable(ret_t(__cdecl*par)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, f)),
    37.                BOOST_PP_ENUM_BINARY_PARAMS(NUM_ARGS, t, p))
    38. {
    39.     return par(
    40.     BOOST_PP_ENUM(NUM_ARGS, MYM, p)
    41.     );
    42. }
    43.  
    44. template
    45. <
    46.     class ret_t,
    47.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class f),
    48.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class t)
    49. >
    50.  
    51. ret_t callable(ret_t(__stdcall*par)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, f)),
    52.                BOOST_PP_ENUM_BINARY_PARAMS(NUM_ARGS, t, p))
    53. {
    54.     return par(
    55.     BOOST_PP_ENUM(NUM_ARGS, MYM, p)
    56.     );
    57. }
    58.  
    59. #undef NUM_ARGS
    60. #define NUM_ARGS 2
    61.  
    62. template
    63. <
    64.     class ret_t,
    65.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class f),
    66.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class t)
    67. >
    68.  
    69. ret_t callable(ret_t(__cdecl*par)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, f)),
    70.                BOOST_PP_ENUM_BINARY_PARAMS(NUM_ARGS, t, p))
    71. {
    72.     return par(
    73.     BOOST_PP_ENUM(NUM_ARGS, MYM, p)
    74.     );
    75. }
    76.  
    77. template
    78. <
    79.     class ret_t,
    80.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class f),
    81.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class t)
    82. >
    83.  
    84. ret_t callable(ret_t(__stdcall*par)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, f)),
    85.                BOOST_PP_ENUM_BINARY_PARAMS(NUM_ARGS, t, p))
    86. {
    87.     return par(
    88.     BOOST_PP_ENUM(NUM_ARGS, MYM, p)
    89.     );
    90. }
    91.  
    92. #undef NUM_ARGS
    93. #define NUM_ARGS 3
    94.  
    95. template
    96. <
    97.     class ret_t,
    98.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class f),
    99.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class t)
    100. >
    101.  
    102. ret_t callable(ret_t(__cdecl*par)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, f)),
    103.                BOOST_PP_ENUM_BINARY_PARAMS(NUM_ARGS, t, p))
    104. {
    105.     return par(
    106.     BOOST_PP_ENUM(NUM_ARGS, MYM, p)
    107.     );
    108. }
    109.  
    110. template
    111. <
    112.     class ret_t,
    113.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class f),
    114.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class t)
    115. >
    116.  
    117. ret_t callable(ret_t(__stdcall*par)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, f)),
    118.                BOOST_PP_ENUM_BINARY_PARAMS(NUM_ARGS, t, p))
    119. {
    120.     return par(
    121.     BOOST_PP_ENUM(NUM_ARGS, MYM, p)
    122.     );
    123. }
    124.  
    125. #undef NUM_ARGS
    126. #define NUM_ARGS 4
    127.  
    128. template
    129. <
    130.     class ret_t,
    131.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class f),
    132.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class t)
    133. >
    134.  
    135. ret_t callable(ret_t(__cdecl*par)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, f)),
    136.                BOOST_PP_ENUM_BINARY_PARAMS(NUM_ARGS, t, p))
    137. {
    138.     return par(
    139.     BOOST_PP_ENUM(NUM_ARGS, MYM, p)
    140.     );
    141. }
    142.  
    143. template
    144. <
    145.     class ret_t,
    146.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class f),
    147.     BOOST_PP_ENUM_PARAMS(NUM_ARGS, class t)
    148. >
    149.  
    150. ret_t callable(ret_t(__stdcall*par)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, f)),
    151.                BOOST_PP_ENUM_BINARY_PARAMS(NUM_ARGS, t, p))
    152. {
    153.     return par(
    154.     BOOST_PP_ENUM(NUM_ARGS, MYM, p)
    155.     );
    156. }
    .............
    пару слов о генерации файла заголовка
    если при анализе таблицы экспорта находим функции в двух версиях генерим
    такой стаб
    Code (Text):
    1. #undef MessageBox
    2. #ifdef _UNICODE
    3. #  define MessageBox(...) callable(MessageBoxW,__VA_ARGS__)
    4. #else
    5. #  define MessageBox(...) callable(MessageBoxA,__VA_ARGS__)
    6. #endif
    если только в одной верии то такой
    Code (Text):
    1. #define fread(...) callable(fread,__VA_ARGS__)
    но только не оба вместе
    при генерации заголовка подставляем хеш первым
    пареметром
    Code (Text):
    1. #undef MessageBox
    2. #ifdef _UNICODE
    3. #  define MessageBox(...) callable(0xcccccccc,MessageBoxW,__VA_ARGS__)
    4. #else
    5. #  define MessageBox(...) callable(0xcccccccc,MessageBoxA,__VA_ARGS__)
    6. #endif
    или так
    Code (Text):
    1. #undef MessageBox
    2. #ifdef _UNICODE
    3. #  define MessageBox(...) callable<0xcccccccc>(MessageBoxW,__VA_ARGS__)
    4. #else
    5. #  define MessageBox(...) callable<0xcccccccc>(MessageBoxA,__VA_ARGS__)
    6. #endif
    внутри можно так
    Code (Text):
    1. ret_t callable(ret_t(__stdcall*par)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, f)),
    2.                BOOST_PP_ENUM_BINARY_PARAMS(NUM_ARGS, t, p))
    3. {
    4.         _hash_ = ...decode...
    5.     return ((typeof(par))_hash_)(BOOST_PP_ENUM(NUM_ARGS, MYM, p));
    6. }
    а указатель лишний в релизе компилятор выкинет
    вот код релиза
    Code (Text):
    1.  
    2.     size_t st = MessageBox(0, 0, 0, 0);
    3. 00401011 56               push        esi  
    4. 00401012 56               push        esi  
    5. 00401013 56               push        esi  
    6. 00401014 8B F8            mov         edi,eax
    7. 00401016 56               push        esi  
    8. 00401017 33 C0            xor         eax,eax
    9. 00401019 FF D0            call        eax
    тоесть компилятор все выкинул
    только осталось подставить раскодировщик хеша

    все это дело юзается проедельно удобно
    calling convention и подходящий тип
    возвращаемого значения разруливают шаблоны
    Code (Text):
    1. int main()
    2. {
    3.     size_t rec = fread(0, 0, 0, 0);
    4.     size_t st = MessageBox(0, 0, 0, 0);
    5.     printf("", st, rec);
    6. }
    надо только написать генератор файла заголовка это несложно
     
  2. Rel

    Rel Well-Known Member

    Blog Posts:
    2
    баян))) примерно тоже самое было по-моему в 80 номере ксакепа (еще начало 2000-ых), только без использования тяжеловесного буста, и без таблицы импорта (с таблицой импорта нет абсолютно никакого смысла делать вызовы по хешу)... советую ознакомиться... сейчас кстати ищу возможность прикрутить подобное под линуксами, там немного по-другому все...
     
  3. osox

    osox New Member

    Blog Posts:
    0
    таблица импорта тут не юзается а тяжеловесный
    препроцессор скрывает очень много ручного копипаста
    он выкидывается компилятором под чистую
    в release
     
  4. Rel

    Rel Well-Known Member

    Blog Posts:
    2
    как это не юзается? а откуда например образуется адрес MessageBoxW?
     
  5. max7C4

    max7C4 New Member

    Blog Posts:
    0
    osox
    osox
    а зачем тогда ее парсить?
     
  6. osox

    osox New Member

    Blog Posts:
    0
    тут парсится таблица экспорта
    адрес берется из хеша имена апи функций
    использованы для выведения типов
    в шаблонах
     
  7. Rel

    Rel Well-Known Member

    Blog Posts:
    2
    где тут парсится таблица экспорта? ткните меня носом в этот кусок кода...

    по-моему у вас какое-то странное понятие хеша имени функции... как адрес получается из хеша?

    ЗЫ собирите бинарник без всех стандартных библиотек (и без user32.lib, что характерно), но с вашим импортом по хешу:
    Code (Text):
    1. void main() { MessageBox(NULL, "Test", "Test", MB_OK); }
    и залейте сюда...
     
  8. osox

    osox New Member

    Blog Posts:
    0
    хотел написать таблицы экспорта ошибся
     
  9. osox

    osox New Member

    Blog Posts:
    0
    загугли алгоритм из имени хеш храним в нашем бинарике
    потом парсинг таблицы экпорта
    снова хешируем и сравниваем с нашим
    в статье я этот код не привел его в гугле полно
    извини сейчас писать ничего не смогу написался уже
     
  10. osox

    osox New Member

    Blog Posts:
    0
    Code (Text):
    1. ret_t callable(ret_t(__stdcall*par)(BOOST_PP_ENUM_PARAMS(NUM_ARGS, f)),
    2.                BOOST_PP_ENUM_BINARY_PARAMS(NUM_ARGS, t, p))
    3. {
    4.         _hash_ = ...decode...
    5.     return ((typeof(par))_hash_)(BOOST_PP_ENUM(NUM_ARGS, MYM, p));
    6. }
    вот тут я специально комментарий вставил что надо в этом месте втавить декодер хеша код просто не стал приводить
     
  11. Rel

    Rel Well-Known Member

    Blog Posts:
    2
    хеш нельзя декодировать, что за детский сад?)))

    вся соль в этом коде, а тех решений, что вы привели можно кучу наворотить и на макросах и на темплейтах...

    Code (Text):
    1. #undef MessageBox
    2. #ifdef _UNICODE
    3. #  define MessageBox(...) callable(0xcccccccc,MessageBoxW,__VA_ARGS__)
    4. #else
    5. #  define MessageBox(...) callable(0xcccccccc,MessageBoxA,__VA_ARGS__)
    6. #endif
    что такое MessagBoxW и MessageBoxA? не понимаю, как работают эти макросы...

    ЗЫ еще раз... зачем вы изобретаете велосипед? все можно сделать проще и красивей, например как в статье в ксакепе:
    http://www.xakep.ru/magazine/xa/080/116/1.asp

    ЗЗЫ давайте лучше поговорим, как под никсами это сделать?)))
     
  12. osox

    osox New Member

    Blog Posts:
    0
    2Rel в архиве пример
    пользовать так
    приведу код бинарика в архиве
    Code (Text):
    1. #include <windows.h>
    2. #include <shlwapi.h>
    3.  
    4. #include "api_hash.h"
    5. #include "api_gate.h"
    6.  
    7. int print(const char *fmt, ...)
    8. {
    9.     va_list va;
    10.     va_start(va, fmt);
    11.     char buf[512];
    12.     int rec = wvnsprintf(buf, _countof(buf), fmt, va);
    13.     WriteConsole(GetStdHandle(-11), buf, rec, 0, 0);
    14.     va_end(va);
    15.     return rec;
    16. }
    17.  
    18.  
    19. int main()
    20. {
    21.     startup_code();
    22.     bool succ = false;
    23.     SetConsoleTitle("load yahoo homepage");
    24.     WSADATA ws;
    25.     if (!WSAStartup(0x0202, &ws))
    26.     {
    27.         struct sockaddr_in addr;
    28.         struct hostent *d_addr;
    29.         SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
    30.         if (INVALID_SOCKET != s)
    31.         {
    32.             d_addr = gethostbyname("www.yahoo.ru");
    33.             if (d_addr)
    34.             {
    35.                 addr.sin_family = AF_INET;
    36.                 addr.sin_addr.s_addr = *((unsigned long*)d_addr->h_addr);
    37.                 addr.sin_port = htons(80);
    38.                 if (!connect(s, (sockaddr*)&addr, sizeof(addr)))
    39.                 {
    40.                     char buf[]="GET / HTTP/1.0\r\nHost: yahoo.com\r\n\r\n";
    41.                     send(s, buf, lstrlen(buf), 0);
    42.                     char dup[256];
    43.                     int rec = recv(s, dup, sizeof(dup), 0);
    44.                     if (rec && SOCKET_ERROR != rec)
    45.                         print("%s\n", dup), succ = true;
    46.                 }
    47.             }
    48.             closesocket(s);
    49.         }
    50.         WSACleanup();
    51.     }
    52.     if (!succ)
    53.         print("not expected error occured\n");
    54.     print("press any key . . . ");
    55.     INPUT_RECORD in;
    56.     DWORD re;
    57.     while (ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &in, 1, &re))
    58.         if (in.EventType == KEY_EVENT)
    59.             break;
    60.     ExitProcess(0);
    61. }
     
  13. osox

    osox New Member

    Blog Posts:
    0
    архив
    генериш файл заголовка с нужными библами и пишеш без импорта даже имена апи функций в коде менять не надо они просто подменятся
    файлы в архиве обновил
    + добавил типобезопасный интерфейс
     
  14. Rel

    Rel Well-Known Member

    Blog Posts:
    2
    я понял смысл, но все равно это кажется не очень красивым... круто было бы для этого использовать variadic template, но далеко не все компиляторы поддерживают c++0x в достаточной для этого степени... единственным решением - плодить темплейты для всех количеств параметров, но было бы круто написать типа того (вызов функции в конструкторе объекта и перегруженный каст оператор):
    Code (Text):
    1. struct DynImport
    2. {
    3. template <uint32_t dllhash, uint32_t funchash, typename ... Args> DynImport(Args ... args)
    4. {
    5. typedef void* (WINAPI* PFunc)(args...);
    6. PFunc pFunc = GetProcAddressByHash(dllhash, funchash);
    7. if(pFunc) { this->Result = pFunc(args...); }
    8. else { this->Result = (void*)-1; }
    9. }
    10. template <typename RetType> operator RetType() { return (RetType)this->Result; }
    11. void* Result;
    12. };
    еще интересный момент, что возвращать в том случае, если адрес функции не был найден... то есть в винде для функций из не ntdll.dll лучшим вариантом будет возвращать 0 (так как они в основном возвращают HANDLE или BOOL или указатель на что-то), а для функций из ntdll.dll 0 - NT_SUCCESS... как разруливать такие ситуации? надо в темплейте перегруженного каст оператора каким-то образом грамотно выделить тип возвращаемого значения...

    по поводу апи-стафф (подробнее посмотрю завтра на работе)... в качестве pfnLoadLibrary лучше использовать LdrLoadLibrary, так как не во всех случаях kernel32.dll будет находится в адресном пространстве, а вот ntdll.dll есть всегда... и опять же kernel32.dll не обязательно будет там, где вы ее захардкодили, лучше динимически находить ее базе данных загрузщика и сравнивать ее название с хешом... относительно GetProcAddressEx косяк в том, что не учитываются редиректы (как например HeapAlloc из kernel32.dll редиректит на RtlHeapAlloc из ntdll.dll)...
     
  15. Rel

    Rel Well-Known Member

    Blog Posts:
    2
    вот довольно удобная реализация (но работает только с компиляторами, поддерживающими variadic templates, а это вроде только gcc >= 4.4 и MinGW):
    Code (Text):
    1. //-----------------------------------------//
    2. // Динамический вызов апи-функций по хешам //
    3. //-----------------------------------------//
    4. template <uint32_t Mod, uint32_t Func> struct DynApi
    5. {
    6.     //-------------------------------------------------------//
    7.     // Вызов функции с любым числом параметров по хешу имени //
    8.     // функции и имени библиотеки                            //
    9.     //-------------------------------------------------------//
    10.     template <typename ... Args> inline DynApi(Args ... args)
    11.     {
    12.         typedef void* (__stdcall* PFunc)(Args ...);
    13.         PFunc pFunc = (PFunc)GetProcAddressHash(Mod, Func);
    14.         if(pFunc != NULL) { this->Result = pFunc(args ...); }
    15.         else              { this->Result = NULL; }
    16.     }
    17.  
    18.     //----------------------------------------------------//
    19.     // Совместимость вызова со стандартными типами данных //
    20.     //----------------------------------------------------//
    21.     template <typename RetType> inline operator RetType() { return (RetType)this->Result; }
    22.  
    23.     //------------------------------//
    24.     // Результат выполнения функции //
    25.     //------------------------------//
    26.     void* Result;
    27. };
    но надо подумать, как удобнее будет рулить calling convention'ом и возвратом в том случае, если функция не была найдена, без использования буста... кто что думает по этому поводу? и кстати сделал аналогичную тему в линуксах, там поиск соответственно идет по link_map'ам, только одна проблема - не знаю пока, как определить базовый адрес elf-образа, пока что захардкодил на 0x08048000, есть ли какие идеи (парсинг /proc/self/maps не катит, так как слишком надуманный способ)?
     
  16. osox

    osox New Member

    Blog Posts:
    0
    провел тут как то эксперимент взял sdbot'а исходник скомпилил проверил на
    вирустотатале 20/45 отвязал импорт скомпилил проверил снова
    11/45 половина в том числе касперский др веб нод32 микрософт антивир замолчали потом
    накатал утилитку зашифровал секцию .rdata импорта нет удобно
    (фактически там только внутренние строки бота остались) целиком ее
    ксориш проверил снова 6/45 из тех кто остались известные это
    avast и mcafee
     
  17. Rel

    Rel Well-Known Member

    Blog Posts:
    2
    osox
    да... строки частенько в сигнатуры попадают, последовательности вызовов апи тоже...