Symulacja klawiatury w usłudze

0

Cześć, mam kod, który ma symulować wciskanie klawiszy. Kod jest prosty:

  for I := 1 to length(str) do
  begin
    keybd_event(vkKeyScan(str[i]), 0, 0, 0);
    keybd_event(vkKeyScan(str[i]), 0, KEYEVENTF_KEYUP, 0);
  end;

i działa w aplikacji okienkowej. Gdy aktywny jest np. Notatnik, ładnie do niego tekst jest wpisywany. Natomiast kod nie chce w ogóle działać w aplikacji serwisowej. Nie ważne, czy ustawię współdziałanie z pulpitem, czy nie. Otwieram sobie notatnika i tam powinny lecieć pewne teksty, ale nic się nie dzieje. Mam pewność, że problem leży w tym miejscu, bo to sprawdzałem (zaraz przed tym kodem zapisuję log do pliku tekstowego). Czemu to nie działa w usłudze? Co zrobić, żeby działało? Problem dotyczy Windows 7, jeśli to istotne.

Próbowałem też to zrobić assemblerem, ale Windows chyba blokuje przerwanie 16h. Czy tak może być? A może moja funkcja jest źle napisana? (w asmie mam właściwie zerowe doświadczenie):

function SendChar(VirtualCode: word): boolean; assembler;
asm
  push CX
  push AX
  mov CX, VirtualCode  
  mov AH, 05h  {wywołaj funkcję 5 - czyli wysłanie na bufor klawiatury}

  int 16h       {wywołanie przerwania - tu dostaję access violation: read of adress 0xffffffff}

  pop AX
  pop CX

  mov @Result, AL
end;

wywołuję tak:

SendChar(vkKeyScan('B'));

dodanie znacznika <code class="delphi"> - fp

1

Do symulowania klawiszy proponował bym raczej użyć takiego kodu, jak poniżej:

procedure KeyDownUp(KeyToSend : Byte; KeyDown : boolean);
const
  DownUp_Flags_Arr : array[boolean] of DWORD = (0, KEYEVENTF_KEYUP);
  Extended_Flags_Arr : array[boolean] of DWORD = (0, KEYEVENTF_EXTENDEDKEY);
var
  Input : TagINPUT;
  KeyExtended : boolean;
begin
  Input.Itype := INPUT_KEYBOARD;
  Input.ki.wVK := KeyToSend;
  Input.ki.wScan := MapVirtualKey(KeyToSend, 0);
  KeyExtended := KeyToSend in
    [VK_CONTROL, VK_LCONTROL, VK_RCONTROL,
    VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT,
    VK_HOME, VK_END, VK_PRIOR, VK_NEXT,
    VK_INSERT, VK_DELETE, VK_MENU];
  Input.ki.dwFlags := DownUp_Flags_Arr[not KeyDown] or Extended_Flags_Arr[KeyExtended];
  Input.ki.time := 0;
  SendInput(1, Input, SizeOf(Input));
end;

A co do aplikacji serwisowej, rozumiem że masz na myśli usługę? Jeżeli tak, to mogę się mylić, ale w usługa za pewne ze względów bezpieczeństwa nie można zakładać na przykład Hooków. Nie udało mi się też pokazać na przykład MessageBoxa. Także być może i symulacja klawiszy nie zadziała.

Przed chwilą sprawdzałem usługę, którą napisałem na bazie przykładu znalezionego w Google w C++ i WinAPI. I normalnie zapisuje on do pliku loga na dysku, co ustaloną liczbę milisekund ilość dostepnej pamięci. I teraz też ją normalnie zapisuje, ale już powyższy kod z symulowaniem wciskania klawisza A, nie zaskutkował. Ale może ktoś tutaj jeszcze coś więcej Tobie doradzi. Bo kto wie, może jest jednak jakiś sposób.

4

Usługi działają w zupełnie innej sesji niż zwykłe aplikacje, dlatego symulowanie klawiszy w taki sposób nie przejdzie. Można to częściowo obejść uruchamiając osobny program w sesji użytkownika, ale to zależy co konkretnie chcesz zrobić. W podobny sposób zrobione jest to w antywirusach tzn. silnik działa jako usługa, a GUI jako zwykła aplikacja. W menadżerze zadań w zakładce "procesy" można dodać kolumnę z informacją o identyfikatorze sesji w jakiej działa dany proces.

Więcej info o sesjach:
http://www.brianbondy.com/blog/id/100/

0

Czyli nie ma opcji, żeby z poziomu usługi wysłać na bufor klawiatury? Nawet asmem? (Robiłem taką rzecz, ale w Windows XP i Delphi 2005 i myślałem, że nie będzie problemu. Ale tamten kod nie działa w Windows 7.)

0

jak masz usluge i tam funkcje uruchamiajaca, to wez nie uruchamiaj kodu ktory dal olesio bezposrednio, ale uruchamiaj timera z tym kodem.
byc moze pomoze.

1

@Tajiri: bardzo Tobie dziękuję. Twoja odpowiedź naprowadziła mnie na fantastyczne rozwiązanie. Teraz już umiem odpalać programy z pod usługi, bo trochę pogooglowałem na ten temat. A co do tego, że w Windows XP działało, to dziwne, bo raczej usługi działały od pewnego momentu podobnie. A oczywiście uwaga o timerze poprzednika, ma się jak pięść do oka. Trzeba mieć minimalne pojęcia o usługach. Jak by się dało robić w nich więcej rzeczy, to byłby to sposób niestety do wykorzystanie również jako malware. Bo z usługi uruchaomić inny proces można poprzez CreateProcessAsUser, ale już wykonać kod z innej dllki, który albo operuje na klawiaturze albo pokazuje MessageBox już się nie da, a przynamniej mi to nie wychodzi. Za pewne jest to związane nie tylko z bezpieczeństwem, ale i specyfiką usług. Które jak wiemy w normalnych warunkach mają służyć do wykonywania operacji w tle, bez pokazywania GUI na ógół i nie ingerując za bardzo w to co użytkownik robi, czyli na przykład nie powinny symulować naciśnięc klawiatury aby to mu nie przeszkadzało. Tak mi się przynajmniej zdaje.

0
olesio napisał(a):

A co do tego, że w Windows XP działało, to dziwne, bo raczej usługi działały od pewnego momentu podobnie.

Z linka Tajiri:

Windows Vista and above started to put user Sessions separate from NT service Sessions. It also made sure that Session 0 was not interactive.

U siebie na XP patrzę i faktycznie - wszystkie procesy są w sesji 0. Czyli zmiana weszła w Viście.

zamiana niepoprawnych znaczników [quote] na <quote> - fp

0

Separacja usług i zwykłych aplikacji została wprowadzona(od Visty), żeby chronić system przed wykonywaniem kodu korzystając z nieuprawnionego podwyższenia uprawnień za pomocą wysyłania odpowiednio spreparowanych wiadomości do kolejki komunikatów tzw. "Shatter Attacks" Polecam opis odkrywcy błędu:
http://web.archive.org/web/20060904080018/http://security.tombom.co.uk/shatter.html

Wracając do tematu, korzystanie z CreateProcessAsUser i WTSEnumerateSessions może powodować kilka problemów:

  • z określeniem aktywnej sesji (w danym momencie może być zalogowanych kilku użytkowników),
  • z czasem uruchomienia aplikacji (sesja może nie być w pełni zainicjalizowana, może być w stanie wyłączania, etc),
    pewnie znalazło by się jeszcze kilka ;)

Korzystanie z tych funkcji nie jest polecane. Lepszym rozwiązaniem jest stworzenie małego klienta, który będzie uruchamiał się automatycznie wraz ze startem każdej sesji np. przez dodanie odpowiedniego wpisu do rejestru. Potem zwykła komunikacja za pomocą pipeów lub socketów załatwia sprawę.

http://msdn.microsoft.com/en-us/library/ms683502%28VS.85%29.aspx
http://www.codeproject.com/Articles/35773/Subverting-Vista-UAC-in-Both-32-and-64-bit-Archite

1 użytkowników online, w tym zalogowanych: 0, gości: 1