Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 2 августа!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

ПРОГРАММИРОВАНИЕ НА C и С++



Возможности языков семейства Си по истине безграничны, однако, в этой свободе кроются и недостатки: всегда нужно программисту держать ухо востро и контроллировать "переполнение буфера", чтобы потом программа не вылетала в "синий экран" на массе разнообразных версий Windows и железа у пользователей. Те же крэкеры и реверсеры специально ищут в коде программ на Си уязвимости, куда можно подсадить любой вирусный код, об этом более подробно автор рассказывал в своём видеокурсе здесь. Я там многое узнал и теперь мой код стал значительно более безопасный.

Написание экстра-маленьких Win32 приложений на С++ от 1 КБ используя лишь API, на примере программы Windows Hider

KMiNT21 (mailto:kmint21@mail.ru).
[c] Copyright 2002. Украина, Запорожье.
Special thanks: Dr.Golova[uinC], NiFi[uinC].
uinC Member, [c] uinC

Введение

Натыкаясь в Интернете на довольно интересные программы, я часто не решался их закачивать после того, как узнавал их размер. Какую ни возьми - все огромные. Да и ресурсов системных потребляют немало. В этой статье я расскажу о том, как сделать программу в среднем в 10 - 100 раз меньше размером, чем попадаются аналогичные.

Цель

Написать очень быструю и маленькую программу, скрывающую по CTRL+F12 заданные окна. При нажатии комбинации CTRL+F10 она должна показать спрятанные окна. Входные данные:

 
 TXT Файл вида
 
 ------------
 
 Internet Explorer
 
 The Bat!
 
 Visual C++
 
 911
 
 ------------
 
 

Если будут найдены окна, содержащие в своем заголовке указанные строки, они будут спрятаны.
В вышеуказанном примере будут спрятаны все окна IE, окно Microsoft Visual C++, окно почтовой программы "The Bat!" и все окна, в заголовках которых содержится комбинация символов "911".

Итак, писать будем на чистом Win32 API. Создадим окно, привяжем к нему горячие клавиши. По требованию будем осуществлять перебор видимых окон в системе и в заголовке каждого будем искать заданные комбинации символов.

Опции линкера

Если ничего не предпринимать, то нам не удастся получить в итоге файл менее 32 КБ(примерно). Поэтому пишем:

 
 #pragma comment(linker,"/MERGE:.rdata=.text")
 
 #pragma comment(linker,"/FILEALIGN:512 /SECTION:.text,EWRX
 
                         /IGNORE:4078")
 
 #pragma comment(linker,"/ENTRY:New_WinMain")
 
 #pragma comment(linker,"/NODEFAULTLIB")
 
 

На что теперь стоит обратить особое внимание? Обычно точка входа в программу выглядит так:

 
 int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR szCmdLine,int nCmdShow)
 
 

(кстати, для Win32 приложений второй параметр всегда NULL)

Но(!)... Так как мы отключили "Runtime library", нам теперь передается в этих параметрах разный мусор. Поэтому называем точку входа не WinMain а New_WinMain, которую объявим, как void New_WinMain(void), чтобы не забыть о том, что нам ничего не передается. А параметр HINSTANCE получаем функцией GetModuleHandle(NULL). Ах да, и выходить из программы будем функцией ExitProcess.

Теперь если собрать нашу пустую программку, которая ничего делать не будет, размер ее будет 1 Кб. Но нам нужно еще дописать 3 Кб кода. Продолжим.

Чтобы все дальнейшее было понятно даже новичку в программировании под Windows, я прокомментирую все.

Объявим кое-какие константы

Это понадобится для регистрации "горячих" клавиш функцией RegisterHotKey.

 
 #define HOTKEYHIDE 1
 
 #define HOTKEYSHOW 2
 
 

Размер буффера, куда будет считываться заголовок окна функцией GetWindowText.

 
 #define SSZZ 256
 
 

Размер буфера, куда будет считываться файл со стоками фильтрации (используется в объявлении char FilterStrings[MAXFIL];)

 
 #define MAXFIL 1024
 
 

(Примечание: При желании можно сделать и выделение памяти динамически - найти файл, узнать его размер и выделить блок. Приблизительный пример:

 
 // .....................
 
 WIN32_FIND_DATA FindData;
 
 HANDLE hFind=FindFirstFile(szFilterStringsFile,&FindData);
 
 if (hFind!=INVALID_HANDLE_VALUE)
 
 {
 
   i=(FindData.nFileSizeHigh * MAXDWORD) + FindData.nFileSizeLow;
 
   HGLOBAL hGA=GlobalAlloc(GMEM_ZEROINIT|GMEM_MOVEABLE,i+1);
 
   // (+ end-ZERO)
 
   if (hGA!=NULL)
 
   {
 
     LPVOID lpStrings=GlobalLock(hGA);
 
     DWORD dw;
 
     if (lpStrings!=NULL) ReadFile(hFile,lpStrings,i,&dw,NULL);
 
     }
 
 
 
   }
 
   FindClose(hFind);
 
   CloseHandle(hFile);
 
 // ...............................
 
 // Но так как вряд ли файл настроек у нас будет больше одного
 
 // килобайта, я оставил статичный массив.
 
 )
 
 

Массив хендлов окон (вряд ли будет у нас более 300 окон)

 
 HWND aHwnd[300];
 
 

Кол-во инициализированных элементов в этом массиве

 
 unsigned int cHwnd=0;
 
 

Дескрипторы окон - главное и два дочерних - кнопка "Hide" и кнопка "Edit filter strings"

 
 HWND hwndMain, hwndButtonHide, hwndButtonEditFilter;
 
 

Тут будет что-то типа "c:\programs\winhider\winhider.settings.txt"

 
 char szFilterStringsFile[MAX_PATH]="(с)2002 KMiNT21";
 
 

Соответственно, хендл файла с именем "что-то типа"

 
 HANDLE hFile;
 
 

А это место, куда будем считывать все из этого файла

 
 char FilterStrings[MAXFIL];
 
 

Функции

Обработка сообщений главного окна

 
 LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
 
 

Функция, которая будет вызываться для каждого окна при переборе всех окон

 
 static BOOL FAR PASCAL my_EnumWindowsProc(HWND hwnd, DWORD lParam);
 
 

Проверка наличия строки str2 в str1

 
 BOOL Contain(char* str1, char* str2);
 
 

Скрывание с экрана очередного окна

 
 inline void HideNext(HWND hwnd){ ShowWindow(aHwnd[cHwnd++]=hwnd,SW_HIDE); }
 
 

Возврат всех спрятанных окон на экран

 
 inline void ShowAll(void) { while(cHwnd) ShowWindow(aHwnd[--cHwnd],SW_SHOW);}
 
 

Пройдемся по главным строкам функции NewWinMain

* Получим INSTANCE модуля. Это нам нужно для регистрации оконного класса

 
 HINSTANCE hInst=GetModuleHandle(NULL);
 
 

* Зарегистрируем оконный класс

 
  WNDCLASS wc;
 
   wc.style = CS_HREDRAW|CS_VREDRAW ;
 
   wc.lpfnWndProc = (WNDPROC)MainWndProc;
 
   wc.hInstance = hInst;
 
   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
 
   wc.lpszClassName = "CKMINT21WINDOWSHIDERPRO";
 
   wc.hCursor = LoadCursor(NULL,IDC_ARROW);
 
   wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
 
   wc.lpszMenuName=NULL;
 
   wc.cbClsExtra=0;
 
   wc.cbWndExtra=0;
 
   if (!RegisterClass(&wc)) MessageBox(0,"I can't register window
 
                class.","Error:",0), ExitProcess(0);
 
 

* Создаем главное окно приложения

 
 hwndMain=CreateWindow(wc.lpszClassName,"Small windows hider!",
 
          WS_BORDER|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,
 
          CW_USEDEFAULT,0,291,180, NULL, NULL, hInst, NULL);
 
 

И помещаем на него две кнопки. Как видим, кнопки имеют класс "BUTTON". Они являются дочерними окну hwndMain.

 
 hwndButtonHide=CreateWindow("BUTTON","Hide!", WS_VISIBLE | WS_CHILD ,
 
                10,10,261,90, hwndMain, NULL, hInst, NULL);
 
 ShowWindow(hwndButtonHide,SW_SHOW), UpdateWindow(hwndButtonHide);
 
 hwndButtonEditFilter=CreateWindow("BUTTON","Edit filters",
 
                      WS_VISIBLE|WS_CHILD|WS_BORDER|WS_TABSTOP ,
 
                      10,110,261,30, hwndMain, NULL, hInst, NULL);
 
 ShowWindow(hwndButtonEditFilter,SW_SHOW), UpdateWindow(hwndButtonEditFilter);
 
 

Наконец, показываем главное окно

 
 ShowWindow(hwndMain,SW_SHOW), UpdateWindow(hwndMain);
 
 

Примечание: Так как кто-то этого может не знать, хочу отметить, что в языке С++ есть "операция следования" - запятая. Т.е. просто последовательно выполнятся обе функции ShowWindow и UpdateWindow (как отдельный блок). В вышеуказанной строке можно было бы и просто поставить ";", а вообще иногда это помогает избавиться от огромного количества фигурных скобок {}, в тексте программы.

* Затем регистрируем в системе HotKeys. Они будут привязаны к главному окну, которому будут передаватся сообщения WM_HOTKEY.

 
 RegisterHotKey(hwndMain,HOTKEYHIDE,MOD_CONTROL,VK_F12)
 
 RegisterHotKey(hwndMain,HOTKEYSHOW,MOD_CONTROL,VK_F10)
 
 

* Затем считываем настройки из файла и запускаем главный цикл обработки оконных сообщений для текущего процесса.

 
 MSG msg;
 
 while(GetMessage(&msg,NULL,0,0)) TranslateMessage(&msg), DispatchMessage(&msg);
 
 

Оконная процедура

 
 // Тут все довольно стандартно. Делаем  switch (msg).
 
 // ...
 
 case WM_HOTKEY:
 
     if (HOTKEYSHOW == (int)wParam)
 
     // показываем все, что мы до этого прятали, а так же главное
 
     // окно программы
 
       ShowAll(), ShowWindow(hwnd,SW_SHOW);
 
 
 
     if (HOTKEYHIDE == (int)wParam)
 
     // Скрываем наше главное окно и запускаем перебор всех окон в
 
     // системе - EnumWindows. Теперь будет вызываться функция
 
     // my_EnumWindowsProc для каждого обнаруженного в системе окна.
 
       ShowWindow(hwnd,SW_HIDE), EnumWindows((int (__stdcall *)(struct
 
                                HWND__ *,long))my_EnumWindowsProc, 0);
 
     break;
 
 // ...
 
 
 
 // Если программу пытаются минимизировать, просто скрываем ее
 
 // .........................
 
   case WM_SYSCOMMAND:
 
     if(SC_MINIMIZE == wParam) { ShowWindow(hwnd,SW_HIDE); return 0; }
 
     break;
 
     // Внимание, после ShowWindow(hwnd,SW_HIDE) мы пишем return 0,
 
     // вместо break. Почему? Да потому что не хотим, чтобы это
 
     // сообщение пошло дальше в систему. Мы его уже обработали
 
     // по-своему.
 
 // ...
 
 // А затем обрабатываем нажатия на кнопки.
 
     case BN_CLICKED:
 
       if (hwndButtonHide==(HWND)lParam)ShowWindow(hwndMain,SW_HIDE);
 
       if (hwndButtonEditFilter==(HWND)lParam)ShellExecute(NULL,"open",
 
                       szFilterStringsFile,NULL,NULL,SW_SHOWMAXIMIZED);
 
       break;
 
 

Рассмотрим функцию my_EnumWindowsProc

Пропустим все невидимые окна

 
 if (!IsWindowVisible(hwnd)) return TRUE;
 
 

Получим TITLE очередного окна

 
 GetWindowText(hwnd, szWindowsTitle, SSZZ)
 
 

Затем перебираем все стоки из файла настроек

 
   for(i=0;i<MAXFIL;i++)
 
     if (FilterStrings[i]) // если это начало строки, то
 
     {
 
      if (Contain(szWindowsTitle, FilterStrings+i)) HideNext(hwnd);
 
      // скроем окно, если эта строка содержится в szWindowsTitle
 
      while(FilterStrings[i]) i++;
 
      // сместим указатель на следующий 0
 
     }
 
 

Продолжаем дальнейший перебор окон

 
 return TRUE;
 
 

(Если бы было return FALSE, перебор бы закончился.)

В остальных функциях особо описывать нечего.

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

Q: Почему программа не линкуется?

A: Попробуйте собрать не debug, а release версию. А если вам нужна возможность отладки, воспользуйтесь обычными #define. И все-таки есть еще одни вариант. В отладочной версии линкер не может собрать файл потому, что не находит "__chkesp", которая содержится в "CHKESP.OBJ". Что мы можем сделать? Да взять и заменить тот obj на свой, который будет меньше размером и не будет содержать ненужный нам код.

Q: Как теперь получить переданную командную строку?

A: Ну тут все просто. Пользуйтесь стандартными API. То же самое и для Instance приложения. Вот они - GetCommandLine, GetModuleHandle.

Q: А какой минимальный align возможен?

A: Для того, чтобы ваша программа запускалась нормально в любой версии Windows, используйте 512 байт.

Q: А можно ли делать такими маленькими DLL?

A: Да. Назначьте свою точку входа вместо _DllMainCRTStartup. Вот пример.

Q: А почему пропали функции strcmp, strlen и т.п.?

A: Так как они были реализованы в RTL, теперь вы не можете их использовать. Но это не беда. В модуле kernel есть отличная замена этим функциям. Названия те же, но с буквой "l" вначале. Например - lstrlen, lstrcmp, lstrcat.

Q: А теперь стали недоступны функции работы с памятью - memset, CopyMemory?

A: RTL сам предоставляет интефейс для работы с памятью. Во-первых, чтобы соблюдать снандарт, во-вторых, чтобы упростить работу с памятью в среде Win32. Вот посмотрите на функцию CopyMemory - она на самом деле не является настоящей API функцией. Попробуйте слинковать проект без RTL, в котором используется эта функция. Результат - неудачная попытка линковки - ссылка на _memcpy. Еще один пример - функция new. В среде Win32 вы должы воспользоваться функциями GlobalAlloc, GlobalLoc и т.п. Однако вы можете просто заменить RTL функции своими. В файле add.txt вы можете взять уже готовые функции, если не хотите писать их сами.

Q: У меня есть один вопрос, которого нет в этом FAQ, что делать?

A: В таком случае вы можете задать вопрос на нашем форуме. www.uinc.ru/forum/

Ссылки:

  • Полный исходник программы и готовый exe можно скачать здесь.
  • Пример написания микро-DLL - dll.zip.
  • Набор c++ функций, которыми можно заменить RTL аналоги - add.txt.
  • Постоянное место статьи: (Скачано 5989 раз)
  • Программа "Windows Hider Pro", примерный скелет которой был тут приведен.


<< ВЕРНУТЬСЯ В ПОДРАЗДЕЛ

<< ВЕРНУТЬСЯ В ОГЛАВЛЕНИЕ




Материалы находятся на сайте https://exelab.ru/pro/



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS