27 October 2011

Anti - PasswordSpy - przechwytywanie procedury okna

Istnieje kilka sposobów na zabezpieczenie programów przed PasswordSpy, który jest "ojcem" wszelakiej maści softu do podglądania kontrolek edycyjnych z hasłem. Poniżej przedstawiam własny sposób na zabezpieczenie kontrolki klasy "Edit". Jest to proste rozwiązanie wymagające przejęcia procedury okna z hasłem.
Programy tego typu wykorzystują IPC wysyłając do okna edycyjnego z hałem komunikat WM_GETTEXT przy pomocy SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
Trzeci parametr funkcji (wParam) to rozmiar bufora do którego ma się zmieścić odkryte hasło. Jest to słaby punkt, który wykorzystałem do zabezpieczenia okna edycyjnego z hasłem. Przejmując procedurę okna PassEdit mam możliwość sprawdzenia jak duży jest bufor. Jeśli długość bufora intruza, będzie różna on tej którą zaprogramowaliśmy w naszej aplikacji, WM_GETTEXT dla okna nie wykona się poprawnie.
Poniższy kod pozwala odczytać zawartość okna z hasłem tylko jeśli podamy prawidłową długość bufora, dlatego należy zadeklarować jednakową długość we wszystkich odwołaniach do danych z okna PassEdit. Słabą stroną rozwiązania jest możliwość wykorzystania przez intruza takiej samej długości bufora (w przypadku PasswordSpy jest to 128 znaków), ale prawdopodobieństwo jest małe.
Inną ciekawostką jest to, że PasswordSpy jest rozpoznawany jako zagrożenie przez Symantec AntiVirus, jednak nie metoda heurystyczna, a zwykła sygnatura decyduje o zagrożeniu - dlatego aby go wykorzystać, należy samemu skompilować kod lub oprzeć swojego Spy'a na dostępnym kodzie.
Podczas ruchu myszką nad oknem głównym następuje przepisywanie danych z PassEdit.
Kod metody zabezpieczenia powstał w DevC++
// copyright "Copyright © 2011, www.FiveWithFour.blogspot.com"

#include < windows.h >

// zmienne
HINSTANCE      hInstance;
HWND             hPassEdit, hEdit;
LONG              lEditProc;
DWORD           dwBuffSize = 200;  // długość bufora na hasło
char                szClassName[ ] = "WindowsApp";

// definicje funkcji
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK EditWndProc (HWND, UINT, WPARAM, LPARAM);

// okno główne
int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil)
    {
    HWND hwnd;
    MSG messages;
    WNDCLASSEX wincl;

    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;
    wincl.style = CS_DBLCLKS;
    wincl.cbSize = sizeof (WNDCLASSEX);
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;
    wincl.cbClsExtra = 0;
    wincl.cbWndExtra = 0;
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    if (!RegisterClassEx (&wincl))
        return 0;

    hwnd = CreateWindowEx(0, szClassName, "5w4 Anti-PasswordSpy example", WS_OVERLAPPEDWINDOW,
           CW_USEDEFAULT,
           CW_USEDEFAULT,
           300, 240,
           HWND_DESKTOP, NULL,
           hThisInstance, NULL);

    ShowWindow (hwnd, nFunsterStil);
    while (GetMessage (&messages, NULL, 0, 0))
    {
        TranslateMessage(&messages);
        DispatchMessage(&messages);
    }
    return messages.wParam;
    }
    
// procedura okna głównego
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
   {
   char          sBuff[dwBuffSize];

   switch (message)
      {
      case WM_CREATE:
         hPassEdit = CreateWindowEx(WS_EX_CLIENTEDGE,
                                      "Edit","",
                                      WS_CHILD|WS_VISIBLE|ES_PASSWORD,
                                      20,20,250,20,
                                      hwnd,NULL,hInstance,NULL);
         hEdit = CreateWindowEx(WS_EX_CLIENTEDGE,
                                     "Edit","",
                                     WS_CHILD|WS_VISIBLE,
                                     20,50,250,20,
                                     hwnd,NULL,hInstance,NULL);
         lEditProc = SetWindowLong(hPassEdit, GWL_WNDPROC, (LONG)EditWndProc); // przejęcie procedury PassEdit 
         break;
      case WM_COMMAND:
         break;
      case WM_MOUSEMOVE: // przepisywanie danych podczas ruchu myszki
         SendMessage(hPassEdit, WM_GETTEXT, sizeof(sBuff), (LPARAM)sBuff);           
         SendMessage(hEdit, WM_SETTEXT, sizeof(sBuff), (LPARAM)sBuff);
         break;         
      case WM_DESTROY:
         PostQuitMessage(0);
         break;
      default:
         return DefWindowProc (hwnd, message, wParam, lParam);
      }
   return 0;
   }
   
// nowa procedura okna PassEdit
LRESULT CALLBACK EditWndProc (HWND hEdit, UINT uMsg, WPARAM wParam, LPARAM lParam)
   {
   switch (uMsg)
      {
      case WM_GETTEXT:
         if ((DWORD)wParam == dwBuffSize)
            return CallWindowProc((WNDPROC)lEditProc, hPassEdit,uMsg, wParam, lParam);
         break;         
      default:
         return CallWindowProc((WNDPROC)lEditProc, hPassEdit,uMsg, wParam, lParam);
         break;
      }
   return 0;
   }