preprocessor

Тема в разделе "LANGS.C", создана пользователем osox, 22 фев 2011.

  1. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    Всех приветсвую.
    написал оболочку для импорта по хешу все будет выглядеть так
    как будто юзаеш "настоящее апи" ничего лишнего не передаеш но на самом деле вызовы идут косвенно через раскодировщик хеша и парсинг таблицы импорта

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

    Код (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. }
    .............
    пару слов о генерации файла заголовка
    если при анализе таблицы экспорта находим функции в двух версиях генерим
    такой стаб
    Код (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
    если только в одной верии то такой
    Код (Text):
    1. #define fread(...) callable(fread,__VA_ARGS__)
    но только не оба вместе
    при генерации заголовка подставляем хеш первым
    пареметром
    Код (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
    или так
    Код (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
    внутри можно так
    Код (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. }
    а указатель лишний в релизе компилятор выкинет
    вот код релиза
    Код (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 и подходящий тип
    возвращаемого значения разруливают шаблоны
    Код (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

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

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    таблица импорта тут не юзается а тяжеловесный
    препроцессор скрывает очень много ручного копипаста
    он выкидывается компилятором под чистую
    в release
     
  4. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    как это не юзается? а откуда например образуется адрес MessageBoxW?
     
  5. max7C4

    max7C4 New Member

    Публикаций:
    0
    Регистрация:
    17 мар 2008
    Сообщения:
    1.203
    osox
    osox
    а зачем тогда ее парсить?
     
  6. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    тут парсится таблица экспорта
    адрес берется из хеша имена апи функций
    использованы для выведения типов
    в шаблонах
     
  7. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    где тут парсится таблица экспорта? ткните меня носом в этот кусок кода...

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

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

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    хотел написать таблицы экспорта ошибся
     
  9. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    загугли алгоритм из имени хеш храним в нашем бинарике
    потом парсинг таблицы экпорта
    снова хешируем и сравниваем с нашим
    в статье я этот код не привел его в гугле полно
    извини сейчас писать ничего не смогу написался уже
     
  10. osox

    osox New Member

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    Код (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

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    хеш нельзя декодировать, что за детский сад?)))

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

    Код (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

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    2Rel в архиве пример
    пользовать так
    приведу код бинарика в архиве
    Код (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

    Публикаций:
    0
    Регистрация:
    13 ноя 2009
    Сообщения:
    280
    архив
    генериш файл заголовка с нужными библами и пишеш без импорта даже имена апи функций в коде менять не надо они просто подменятся
    файлы в архиве обновил
    + добавил типобезопасный интерфейс
     
  14. Rel

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    я понял смысл, но все равно это кажется не очень красивым... круто было бы для этого использовать variadic template, но далеко не все компиляторы поддерживают c++0x в достаточной для этого степени... единственным решением - плодить темплейты для всех количеств параметров, но было бы круто написать типа того (вызов функции в конструкторе объекта и перегруженный каст оператор):
    Код (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

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    вот довольно удобная реализация (но работает только с компиляторами, поддерживающими variadic templates, а это вроде только gcc >= 4.4 и MinGW):
    Код (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

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

    Rel Well-Known Member

    Публикаций:
    2
    Регистрация:
    11 дек 2008
    Сообщения:
    5.323
    osox
    да... строки частенько в сигнатуры попадают, последовательности вызовов апи тоже...