From febdef23d7517bdadbad32c67ab71d9a15b921cd Mon Sep 17 00:00:00 2001 From: ZhuHuiBeiShaDiao <493294271@qq.com> Date: Thu, 20 Oct 2016 06:14:20 +0800 Subject: [PATCH] Page Fault Hook use ept --- .vs/PFHook/v14/.suo | Bin 0 -> 59904 bytes PFHook.sln | 62 ++ PFHook/PFHook.aps | Bin 0 -> 108772 bytes PFHook/PFHook.cpp | 106 +++ PFHook/PFHook.h | 32 + PFHook/PFHook.rc | Bin 0 -> 10568 bytes PFHook/PFHook.vcxproj | 224 ++++++ PFHook/PFHook.vcxproj.filters | 63 ++ PFHook/PFHook.vcxproj.user | 4 + PFHook/PFHookDlg.cpp | 533 +++++++++++++ PFHook/PFHookDlg.h | 64 ++ PFHook/ReadMe.txt | 67 ++ PFHook/res/PFHook.ico | Bin 0 -> 67777 bytes PFHook/res/PFHook.rc2 | Bin 0 -> 668 bytes PFHook/resource.h | Bin 0 -> 1874 bytes PFHook/stdafx.cpp | 8 + PFHook/stdafx.h | 54 ++ PFHook/targetver.h | 8 + PFHook_sys/Debug/PFHook_sys.inf | 93 +++ PFHook_sys/Debug/PFHook_sys.log | 47 ++ .../PFHook_sys.tlog/PFHook_sys.lastbuildstate | 2 + .../PFHook_sys.tlog/PFHook_sys.write.1u.tlog | Bin 0 -> 182 bytes .../PFHook_sys.tlog/stampinf.command.1.tlog | Bin 0 -> 488 bytes .../PFHook_sys.tlog/stampinf.read.1.tlog | Bin 0 -> 404 bytes .../PFHook_sys.tlog/stampinf.write.1.tlog | Bin 0 -> 304 bytes .../Debug/PFHook_sys.tlog/unsuccessfulbuild | 0 PFHook_sys/Driver.cpp | 20 + PFHook_sys/PFHook_sys.inf | 90 +++ PFHook_sys/PFHook_sys.vcxproj | 201 +++++ PFHook_sys/PFHook_sys.vcxproj.filters | 69 ++ PFHook_sys/PFHook_sys.vcxproj.user | 6 + PFHook_sys/base/common.cpp | 117 +++ PFHook_sys/base/common.h | 265 +++++++ PFHook_sys/base/ept.cpp | 324 ++++++++ PFHook_sys/base/ept.h | 210 ++++++ PFHook_sys/base/exithandler.h | 713 ++++++++++++++++++ PFHook_sys/base/global.cpp | 124 +++ PFHook_sys/base/global.h | 29 + PFHook_sys/base/vtasm.asm | 563 ++++++++++++++ PFHook_sys/base/vtasm.h | 191 +++++ PFHook_sys/base/vtsystem.cpp | 388 ++++++++++ PFHook_sys/base/vtsystem.h | 412 ++++++++++ PFHook_sys/header.h | 8 + 43 files changed, 5097 insertions(+) create mode 100644 .vs/PFHook/v14/.suo create mode 100644 PFHook.sln create mode 100644 PFHook/PFHook.aps create mode 100644 PFHook/PFHook.cpp create mode 100644 PFHook/PFHook.h create mode 100644 PFHook/PFHook.rc create mode 100644 PFHook/PFHook.vcxproj create mode 100644 PFHook/PFHook.vcxproj.filters create mode 100644 PFHook/PFHook.vcxproj.user create mode 100644 PFHook/PFHookDlg.cpp create mode 100644 PFHook/PFHookDlg.h create mode 100644 PFHook/ReadMe.txt create mode 100644 PFHook/res/PFHook.ico create mode 100644 PFHook/res/PFHook.rc2 create mode 100644 PFHook/resource.h create mode 100644 PFHook/stdafx.cpp create mode 100644 PFHook/stdafx.h create mode 100644 PFHook/targetver.h create mode 100644 PFHook_sys/Debug/PFHook_sys.inf create mode 100644 PFHook_sys/Debug/PFHook_sys.log create mode 100644 PFHook_sys/Debug/PFHook_sys.tlog/PFHook_sys.lastbuildstate create mode 100644 PFHook_sys/Debug/PFHook_sys.tlog/PFHook_sys.write.1u.tlog create mode 100644 PFHook_sys/Debug/PFHook_sys.tlog/stampinf.command.1.tlog create mode 100644 PFHook_sys/Debug/PFHook_sys.tlog/stampinf.read.1.tlog create mode 100644 PFHook_sys/Debug/PFHook_sys.tlog/stampinf.write.1.tlog create mode 100644 PFHook_sys/Debug/PFHook_sys.tlog/unsuccessfulbuild create mode 100644 PFHook_sys/Driver.cpp create mode 100644 PFHook_sys/PFHook_sys.inf create mode 100644 PFHook_sys/PFHook_sys.vcxproj create mode 100644 PFHook_sys/PFHook_sys.vcxproj.filters create mode 100644 PFHook_sys/PFHook_sys.vcxproj.user create mode 100644 PFHook_sys/base/common.cpp create mode 100644 PFHook_sys/base/common.h create mode 100644 PFHook_sys/base/ept.cpp create mode 100644 PFHook_sys/base/ept.h create mode 100644 PFHook_sys/base/exithandler.h create mode 100644 PFHook_sys/base/global.cpp create mode 100644 PFHook_sys/base/global.h create mode 100644 PFHook_sys/base/vtasm.asm create mode 100644 PFHook_sys/base/vtasm.h create mode 100644 PFHook_sys/base/vtsystem.cpp create mode 100644 PFHook_sys/base/vtsystem.h create mode 100644 PFHook_sys/header.h diff --git a/.vs/PFHook/v14/.suo b/.vs/PFHook/v14/.suo new file mode 100644 index 0000000000000000000000000000000000000000..31f7f4625751c869b79304c8ab57e70cfe80cf55 GIT binary patch literal 59904 zcmeHQ34B|{nSV|~$i*3;ETteIT#YU3^Z_KG({|!3mhCu+LXag{k}XR{lCK;EQfMiK zKq=)~*j={Jg_J^R=|xKm?6zClvb3;JwuP21?b6ci@!Pii`1K(B|G&e2l65>uK5)?J z_vy`hGjG0czM1*vo0)IsegE0xUcKj$Q+_67xQ)V6;q5~!h2^T}Rq!uS?vE9O)8StV zc>BycM^IIM5};@k7UQ`f8`awnv3?I#tUS-psTe1 z>GrSGhZW!&z&fp*_IhckuUVKy|O;Cj%92Aj6-*f{H}C&=ul?+U#1FQ1^M*DuKaFUi-h;QE;C{~E531HKOU2H;--PXe9- zFs*;X^_zfi0sbBEZNSq2hJ6>;?*X0x{0HD!!1n<^05JR&T>lgBf_(o&Twjv?Kf?9L zfS2X_pWymaz^j0t0r>uFxc*%B{|~OO%YKH8FLr5G4jskx`Xaxm6T%Gt&0rI(Xfd6bnI0>K?eadDoxqi&Y%Swn>1{zc z75P60H{_>Q{DX)~{g$p9*pk58io7O)+XK#X44y)*WUVMv2BoyW2=J~Du?ymS2S@n{ z)IA?+oEvmR#j;ms2Bg^trCzd~s;RmF{9l5!-UrZ{e|7u23h!0)KS}?2H|l@++CTe$ zqg+pOT}r9{oMEKSPS=N)AH^(~)C>5~4+J$xUFlu^Q~oT)m35zPIwZI6IRu5E^U}q{ z8NMLeRB3Jv@DneS(~5sN;(AFmoCk8oFV6ywq3yFLPGE*BvtQCr^@@}P>?b9PHv&I( z1mdO>6D{y}H^P_6aSD|`^{RZ1{Ab?H0Im2d`rl!t|6v87qW_7FPh4g`H&-w^5=Y9YXDm1&*u?W8UGZ*Z$aFAj`>&et`+}HNRV?< zbcOI6Mae3~&Gygn5BunI0b22Y9C4oj(A`qy!1{<6>mNz6=SjlfhknS1UZ!9C_kzY4 z!kp-rB3RQ2A_n^)_EcV>7d@2;D=|i_ym--%xiO-YH2Ub>my!orOku6U*W&?^V@&y2V0Us`S;rD`tIFTHrY@Us7^Q^IHmQz0B9TsH9EvrXyHgtkG^;WKWRL16Y{xGycq*E62~|?7#6uAh?4eB z&`7GJ93LpN{JJ`xz4Db;ANs(Re|q>k9e18s<3$N`1eO$WN>^#6ZXvl5Lfw$+V^mc} z)T6Fnz%ufKreXXzZ@^kBtzh1|fE49!QIf`YS6ba+YbmGwvZfUOK4&&KyCdnjSC?6T zi^o4L$0%#0_I}{L-7l>U?DsAoTYvZQkH0Dk{Y-g-;?3g1!mplN^Wr17RX=YZy6rPR z*>}xxjFkCqZuxoLyyu@%U6OfhRfYPWO8=jw&{dBXd;dQzRl`F2Ui&ql{`H%`8Q<{2 zs}JqD?zh=$vMTGMx7_}x`1YG#f2Zk}TJn3(-?((=DIfgZU!L5&&5iDk>fNAXY@ z|1MWux2+>6L)o z<~1cOWc|ygqnXSv-t~#A!nYsX^y^dGuYa4%$1`|u0PVfd61>R8W>EvCUTlOG?G^3d z{E*9|vVP5{dJ&AUZ!%*%W)|<6d+N=!77O4#X;xU8J0C?D>yvGtdGAl(e&ey`-~9f~ zb-#Gv!ECgY)Be)+AF1wrCVAoMkF8pJ=8czr^;bW(zQv*8jQj_OlSk}IMs^2R>>69h z_Lok7Sh#La{hzP39{qVk^x%`fH{UNVy=C^~or(Bh(4TS!`$t2eV8X+>ceVa3PwO$; z!ja+)XUFK4ZD@=56Y*qxAfHmtPbcjYc<0l)D@kczcq;MAF+A z@x}Xnac@T~D2gkuBOVBPdp#zD(dzAVH^<{c-ibPkR}_WvgI&qVq#nSu(vlUUGgKWh z5*rZqmnpxr_Fw6N z#?1BfS$6Wn!h2pCv_JWko~~~$>AC08o;zq^H-q%NvvQUBpVH?qGxdkXIR9f4=R0Zt z<%c$@*t&2xMiS3L&?42=2CQD`?z|G@A5%D9PNo-lx-o-BYny)jPhdWoGecYp^}|=l z!Tv}%3XwRT)7t-sz z@;R=*T!aWZKq>q$lz-ZXjiL7CT8FAGGyhCqz00W$_ngt@KCtw)lI&b41wU=|y5;0D z@!Q1_tq(171XR!>m9;=kNje=C@Sl7?6_2&@|3t)9?5{HMOPJUKrJY!kd~CxV?PH~< zY6|MW|1Kl!dW>1qD|FC+LNIZ9#t`B#8}Uu+x2;C}+Rzb@nNMyZZ~ zzwC3^PIZXa(7%G5f!jNKc_5H>J}d9q>N=9_c)^8a+CsWtzcGgR=ad$u<4msY^i zeq%kP68k@{9R|P;$+98SD!df_b4F4&zZUoXusvh~Xxb^WuOz>80M=XfpVXn(0cgvA4uJM7=K*Nj^M1g3 zKsA7i8F~OGmZ^`^_QeD+0~Gw6xK;eTULfC7Pul?CRL8}DO8_>29l$ohxGr4TKIw19 zwFR&Vuo=(_XalqZD8o7dTL4`E4}dyN51<#Y70?IR2G|bR0q_EL0xkvY0$c|00r~-c zKmfoI&j5g96m=fxkK)-dAO?s7MgW%s5&*W!6krrE1{eoWbKMKr4cG%<8vAhl0N@J1 zm4L$i54oA!vHxceT@OI=hjPc94tWvYhveJB_#38Q%;F<{#$Xw1#s6Q3`#m|WLinkR z<#WV;AtF$x(~4iH5`=r?L<`|J7O=o0e&SVJK3bKq|Ayy&jrghCVuv#pxXG1Q&y!CCWaSE)zfHE$7|}?YoxV7Uv(QaFYh*dA=)U=?XbQ zCVuX}9r$|A|P8<6W)zPe$A~mP-WW#y^R%9#_OT>dWm|{}tYA#jlqC zh1$PK9AW3hU-bCn7f9}txsxn9q}coyjiA5_iy42R^5?olKF9ih93m(@$QM@$dk`Lu z0qF|k=jbN4BmQax>j7Hp|80o-sGMYB{G46N?TBB=yH@;nA~~)3=e$Zjhw*n6=f*fk zrWOA`A};%2x=i^~iu^ws=_ou96D{ypk^hI4{GTd&t@W=W{|hvbB7>E-r-{@u{#T(7Bnh0CF4nCn?X@X1t~Ph~)J+Kg&p(84cO)1MEdAIL&tx(c9M(JH(I|F8AeE%P zDHsbTB7VIyB0lgXCNDGX*da&sq!M_$S~r~Z#}m;=|8#~ddINtNa#LULv-+)d#zwOx zXlQ8MS-nHzP$x+9g@SFq82*QYv6Q~2MNj)>h5l+??ugTb6vJ7wv46naV6F27%ypKa z*}8KlOKjy*{4L{Og+VhIM7A6$*Wwk9`0$7?5lpOT_9ep}>;l5M@heV}>qPreN!dOt?;v}K87ZIx%8)7CMG z@x71fW2~QoKY(X(gmK1;ww}{_w0IiW^sY0WFAxx|wz!^4ymVfy4CliWMatRm@LkrQ zOjph`?RoJ}q2(B^51@r-x$-j2GtQx?cdixgbInL1-N{6MCK%do<;K?lDRD=Q2+-dmb<_k5hDuCz~Jn z(ZOfe-naFbR^jNUPl^6@7UYM6fIEMTSBWhs$l#I z2Fcz7?ZyDhH#cfA$E^f9rd5L&Mp$z>;A@26Df;U0Z-Rwp9l}hIBv$-85WbJP%PQf4 z7akVQjqJ7E@^Vz~JFDwyg($9ZtUc1c@zT@&xapoPXP*JHvN5`m-m^xX# zT9g*hj3%rccB2l+NI!^GuOMtvvOdm1`B$Y}i={UIr+8&!n;x2&A2}I;4C6O_SbP~d zk`$aCne=AXL-ns_>Mp-e{SN(frL|v=ux|C=Z0m0*wf%d9hJh2Fdi$x@;PPU`S)Lc1@v{p+E1kBcK4o<$+a9qH6; zwbZ#7RH~&;`B=NqUU*Kjbe7RxdEbH=dC;SGwN9yLCiOhqn4Lx4h6^H|CZX+UkX*wV0uS zSkS@^kW)MtlRM;G&_!IL)S^CPfzO7gRzMA6Qcp4Cxf}OR$bx$D?R}6DTBO!7s0G<2 zva~yuQe}m>TVW5d%oc4IihC4hGete*K)STlRs$soHG4UmNr$xiMT^vyYT<0sQfiBw zD;l)(VioG8dat?zJs$Uqur^W`&R*IVW@!aZ(GGJh-dQd@Ugf;NfZra2!RB^aZH+Z` zji&kvE?V@;#I&QN1E*I64)CWpypGVS}WLcZB$ zF&XNO<{G2R>Z-BW+?E=f(Q2$QSxsiQ+vv2{n+yv1@@XXcGRRhvNccgivA*7FZFCuG zoJJ67G&&76jV7nN1}rr=8ja=#hsC;YZ?#UR>(>Q!AzeflgP+czJ43hbJe}@L@$SKe z&5Sb|()&k7w6gxo3bhMfb3#!ZSrgS>X&!s0QC}LK~?EezmUeds zpGejR+e+xtr~AfyDQ_q%<@=W!WQShXV95AW!t|17Z=~1W(KT+^+!Tu2;MzSsVOLKG zK09x1TO77+ybd(fHiqf3^|gDt3@x@q(&De%!UtWkEj>oWc1#S8k2P%D(!+Ff$5 zh9@rH8fzKtHI4c@2QPQ~!oK=?*RXB8VR&21P`k@fUu#S_Vu3EtShFuNzB$#{-PPYS zvBllAwapTacv`myC+lMI?dvU_;mOW{wr!qxeSBMZIC1%iB|7emHMdN5`E6Ue%_d7* zZ8*MV`*8i`;f84UWM8Xu>tMLgHPo^@m0aJGvTbjUnH_$cr_Z{rw%(E$Z}koJZ+H90 zyZUQ4w>c*!yP6!a@vWB5_Ey_~y}f(fvB?_lX*INY%vQJE(P#^fIeH?F@wjC;O2*h+ zQFr%{XLQT3!!c8MI2UbeS3xzS?+aPfP#kR!2{~+dj~rH2K@cxA|LK zn{5pakF&*QcR89`Y#o04$WanyaWznC+6r59yYe8&5V_mFKp_uGs%-wezpo|7Mq2({(G;BDZ4ytKM6ZOXlb1U&a16bAL`@9;Vwg zXZ8-Frl04pV*gvQ|DF0|?V_^({WNMdM{I=o`if@yXGk>I`$~q*b7Jo+*#IxR{cd^o zzZLskev7mi+sxUwi_C5>RmfJgV!t~dw$*AYJ?$29b8EAz&3bb7>#3R-75m-7yACS$ zyR>4TO?%Z$Nm*=*oK0HPt)Z9}Iaf4j=SA`MyA9aIapBY+KK1(?+NkY+#kG0#sM0rm z_F^{{zb|uP4l8)4_#F<({`c^)X@gDEA~!6l*#Bz#{$wtj46T;Si+RCzF|A%=jH%V+*_@l+7XHTfO#7iqC0bmziu`7p z;`p5if>R!1l($n;v4PgAZ&ysOVOv`C4w8!Ltz3I6rnkKwlfh_}jXCAZ^d6EDOm8pP zvvAu-i`eXX)U^34-m;ZopsX^0os|@>UZ4#dS~V$qtzxim+-{q2#%<#c*Ve=~Q)9f( zIq8ekHZ}Ei_tn~)!hi6Q{$$#;o($Yf6GuR;2Z5U zZQj~%-`%=9Ip}cgwpqJF0pH-HJGEt~r^j698?`q0k^{lrjfM$p=VXVyeYefFeZ4Cd znj9Y=Zf~BTYD}cqm>!95;;CdQ4HX%jWDJnh2T3 z*Y{>_F%a)_{%zn&Qks+GV8y0=S}26ewTXYX>a>St%eZ5zwVe9IgYfeNM{ZP;4nc(S zJ2z*56nSjMJEBihI8}@VhI;*}?^)gn!Vpds@IQ5k_khIP*isxuc-|y*&1)Yh= zSkUW;2ZG*dHD(=7Ws2ih@O_+#8pIh+37m^M2A>zJUU8h_#C=!S!+!yuDBla@xwCzU z=R=Hs@!yN@3}t_xDfQx%C!Q+BbEDENoYL@#7i#Qr}A@ z@7ApS-2Cu~=FSoHzhRUnzaq*LY7;VT+-YzZ&ZMQLBAv_~LKwehpgtH{C!B{gXzz1A z;z>3|0pxI8)T=g&sZ+nuRrT$Rg&8AH*zWQ^0)qLyT*-swlzMlbM-~Jc7O|`$-XFT}IU-W-=c+lk1 zzD_vv3wb=TIw`kMu8U;XlIN$Dq{}m%B&6oaI=|9rf zqspyA^R2(l|Cht2XF7AwcHFq(idWhXy>dYR%y_&0rRCxgGlmz$2dhCRHxyy>6${qKdFLsjZA$6En;M8h4&SIXrx*Q?R#l3y7YvzD9)xL@N~ z;`RkA|MVH@ncQ@@`%ihT?~m0hM^1h93hP_nK&EEY2LUo*rcFR;GFaLv>qUCX?pZHp z+c~RfLW%N}A?aHQBN(|+QgGd$U)?z#@F9)do;_-(bItVyWsJ|ShR;7OO4(5&OH!1z zkSCjN4Yw9c>YRBA$#7 z;H#@NT1jk<F1&QmPk6-fn$lSk}IMs^2R>;i2`x&p>$R%-@u&zr5S^zqzst)2hFftG~S4^}o{Bz?EKW zaqDm8Y81g#DiRANckHCI4tP1~)nMgMT-7q?WcfAsd7N0Ff{x+Ad2{o5oVU vujYgvzrMSh(wTU{5R~$U3wq)rc<#c7Ht2!rV+N+Pm2890yY$yI) literal 0 HcmV?d00001 diff --git a/PFHook.sln b/PFHook.sln new file mode 100644 index 0000000..4d571aa --- /dev/null +++ b/PFHook.sln @@ -0,0 +1,62 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PFHook_sys", "PFHook_sys\PFHook_sys.vcxproj", "{00AFD5A9-6927-417C-94DC-6C2E612352C9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PFHook_exe", "PFHook\PFHook.vcxproj", "{3E420713-1E5E-4AF4-A151-2523FF1DB720}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Debug|ARM.ActiveCfg = Debug|ARM + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Debug|ARM.Build.0 = Debug|ARM + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Debug|ARM.Deploy.0 = Debug|ARM + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Debug|ARM64.Build.0 = Debug|ARM64 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Debug|x64.ActiveCfg = Debug|x64 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Debug|x64.Build.0 = Debug|x64 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Debug|x64.Deploy.0 = Debug|x64 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Debug|x86.ActiveCfg = Debug|Win32 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Debug|x86.Build.0 = Debug|Win32 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Debug|x86.Deploy.0 = Debug|Win32 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Release|ARM.ActiveCfg = Release|ARM + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Release|ARM.Build.0 = Release|ARM + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Release|ARM.Deploy.0 = Release|ARM + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Release|ARM64.ActiveCfg = Release|ARM64 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Release|ARM64.Build.0 = Release|ARM64 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Release|ARM64.Deploy.0 = Release|ARM64 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Release|x64.ActiveCfg = Release|x64 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Release|x64.Build.0 = Release|x64 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Release|x64.Deploy.0 = Release|x64 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Release|x86.ActiveCfg = Release|Win32 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Release|x86.Build.0 = Release|Win32 + {00AFD5A9-6927-417C-94DC-6C2E612352C9}.Release|x86.Deploy.0 = Release|Win32 + {3E420713-1E5E-4AF4-A151-2523FF1DB720}.Debug|ARM.ActiveCfg = Debug|Win32 + {3E420713-1E5E-4AF4-A151-2523FF1DB720}.Debug|ARM64.ActiveCfg = Debug|Win32 + {3E420713-1E5E-4AF4-A151-2523FF1DB720}.Debug|x64.ActiveCfg = Debug|x64 + {3E420713-1E5E-4AF4-A151-2523FF1DB720}.Debug|x64.Build.0 = Debug|x64 + {3E420713-1E5E-4AF4-A151-2523FF1DB720}.Debug|x86.ActiveCfg = Debug|Win32 + {3E420713-1E5E-4AF4-A151-2523FF1DB720}.Debug|x86.Build.0 = Debug|Win32 + {3E420713-1E5E-4AF4-A151-2523FF1DB720}.Release|ARM.ActiveCfg = Release|Win32 + {3E420713-1E5E-4AF4-A151-2523FF1DB720}.Release|ARM64.ActiveCfg = Release|Win32 + {3E420713-1E5E-4AF4-A151-2523FF1DB720}.Release|x64.ActiveCfg = Release|x64 + {3E420713-1E5E-4AF4-A151-2523FF1DB720}.Release|x64.Build.0 = Release|x64 + {3E420713-1E5E-4AF4-A151-2523FF1DB720}.Release|x86.ActiveCfg = Release|Win32 + {3E420713-1E5E-4AF4-A151-2523FF1DB720}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/PFHook/PFHook.aps b/PFHook/PFHook.aps new file mode 100644 index 0000000000000000000000000000000000000000..03298884f52a96756faa661b220fd331d5fd0308 GIT binary patch literal 108772 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 literal 0 HcmV?d00001 diff --git a/PFHook/PFHook.vcxproj b/PFHook/PFHook.vcxproj new file mode 100644 index 0000000..36dbc8e --- /dev/null +++ b/PFHook/PFHook.vcxproj @@ -0,0 +1,224 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {3E420713-1E5E-4AF4-A151-2523FF1DB720} + PFHook + 8.1 + MFCProj + PFHook_exe + + + + Application + true + v140 + Unicode + Static + + + Application + false + v140 + true + Unicode + Static + + + Application + true + v140 + Unicode + Static + + + Application + false + v140 + true + Unicode + Static + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level3 + Disabled + WIN32;_WINDOWS;_DEBUG;%(PreprocessorDefinitions) + true + + + Windows + RequireAdministrator + + + false + true + _DEBUG;%(PreprocessorDefinitions) + + + 0x0804 + _DEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + Use + Level3 + Disabled + _WINDOWS;_DEBUG;%(PreprocessorDefinitions) + true + + + Windows + + + false + true + _DEBUG;%(PreprocessorDefinitions) + + + 0x0804 + _DEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;_WINDOWS;NDEBUG;%(PreprocessorDefinitions) + true + + + Windows + true + true + RequireAdministrator + + + false + true + NDEBUG;%(PreprocessorDefinitions) + + + 0x0804 + NDEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + Level3 + Use + MaxSpeed + true + true + _WINDOWS;NDEBUG;%(PreprocessorDefinitions) + true + + + Windows + true + true + + + false + true + NDEBUG;%(PreprocessorDefinitions) + + + 0x0804 + NDEBUG;%(PreprocessorDefinitions) + $(IntDir);%(AdditionalIncludeDirectories) + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PFHook/PFHook.vcxproj.filters b/PFHook/PFHook.vcxproj.filters new file mode 100644 index 0000000..35c9a7b --- /dev/null +++ b/PFHook/PFHook.vcxproj.filters @@ -0,0 +1,63 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + 头文件 + + + + + 源文件 + + + 源文件 + + + 源文件 + + + + + 资源文件 + + + + + 资源文件 + + + + + 资源文件 + + + \ No newline at end of file diff --git a/PFHook/PFHook.vcxproj.user b/PFHook/PFHook.vcxproj.user new file mode 100644 index 0000000..abe8dd8 --- /dev/null +++ b/PFHook/PFHook.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/PFHook/PFHookDlg.cpp b/PFHook/PFHookDlg.cpp new file mode 100644 index 0000000..2aab4a8 --- /dev/null +++ b/PFHook/PFHookDlg.cpp @@ -0,0 +1,533 @@ + +// PFHookDlg.cpp : 实现文件 +// + +#include "stdafx.h" +#include "PFHook.h" +#include "PFHookDlg.h" +#include "afxdialogex.h" +#include +#include +#ifdef _DEBUG +#define new DEBUG_NEW +#endif + + +// CPFHookDlg 对话框 + +#define DRIVER_NAME TEXT("PFHook") +#define DRIVER_PATH TEXT("PFHook.sys") + +// 通信代码 +#define PFHOOK_CODE_CHECKVT 0x800 +#define PFHOOK_CODE_HOOK 0x801 +#define PFHOOK_CODE_UNHOOK 0x802 + + +#define PFHOOK_WELCOME_TEXT TEXT("Version:1.0a\nThe program and driver are made by Xiaobao.\nQQ:1121402724\nHave fun! O(∩_∩)O~~") + +BOOL LoadNTDriver(TCHAR* lpszDriverName, TCHAR* lpszDriverPath); +BOOL UnloadNTDriver(TCHAR* szSvrName); + +CPFHookDlg::CPFHookDlg(CWnd* pParent /*=NULL*/) + : CDialogEx(IDD_PFHOOK_DIALOG, pParent) + , m_iDriverStatus(0) +{ + m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); +} + +void CPFHookDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialogEx::DoDataExchange(pDX); + DDX_Control(pDX, IDC_COMBO_PROCESS, m_cbbProc); + DDX_Control(pDX, IDC_LIST_HOOK_LIST, m_lbHook); + DDX_Control(pDX, IDC_EDIT_HOOKED_ADDRESS2, m_editHookAddr); + DDX_Control(pDX, IDC_EDIT_JMP_ADDRESS, m_editJmpAddr); + DDX_Control(pDX, IDC_STATIC_DRIVER_STATUS, m_staticStatus); +} + +BEGIN_MESSAGE_MAP(CPFHookDlg, CDialogEx) + ON_WM_PAINT() + ON_WM_QUERYDRAGICON() + ON_WM_CLOSE() + ON_WM_CTLCOLOR() + ON_BN_CLICKED(IDC_BUTTON_REFRESH, &CPFHookDlg::OnBnClickedButtonRefresh) + ON_BN_CLICKED(IDC_BUTTON_HOOK, &CPFHookDlg::OnBnClickedButtonHook) + ON_BN_CLICKED(IDC_BUTTON_Unhook, &CPFHookDlg::OnBnClickedButtonUnhook) +END_MESSAGE_MAP() + + +// CPFHookDlg 消息处理程序 + +BOOL CPFHookDlg::OnInitDialog() +{ + CDialogEx::OnInitDialog(); + + // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 + // 执行此操作 + SetIcon(m_hIcon, TRUE); // 设置大图标 + SetIcon(m_hIcon, FALSE); // 设置小图标 + + // TODO: 在此添加额外的初始化代码 + MessageBox(PFHOOK_WELCOME_TEXT, TEXT("Welcome"), MB_OK | MB_ICONINFORMATION); + RefreshProcess(); + + BOOL bRet = LoadNTDriver(DRIVER_NAME, DRIVER_PATH); + if (!bRet) + { + m_staticStatus.SetWindowText(TEXT("Fail to load the driver!")); + m_iDriverStatus = 1; + } + else if (!CheckVT()) + { + m_staticStatus.SetWindowText(TEXT("Fail to load the VT Engine!")); + m_iDriverStatus = 2; + } + else + { + m_staticStatus.SetWindowText(TEXT("The driver is active now.")); + m_iDriverStatus = 0; + } + + return TRUE; // 除非将焦点设置到控件,否则返回 TRUE +} + +// 如果向对话框添加最小化按钮,则需要下面的代码 +// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序, +// 这将由框架自动完成。 + +void CPFHookDlg::OnPaint() +{ + if (IsIconic()) + { + CPaintDC dc(this); // 用于绘制的设备上下文 + + SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); + + // 使图标在工作区矩形中居中 + int cxIcon = GetSystemMetrics(SM_CXICON); + int cyIcon = GetSystemMetrics(SM_CYICON); + CRect rect; + GetClientRect(&rect); + int x = (rect.Width() - cxIcon + 1) / 2; + int y = (rect.Height() - cyIcon + 1) / 2; + + // 绘制图标 + dc.DrawIcon(x, y, m_hIcon); + } + else + { + CDialogEx::OnPaint(); + } +} + +//当用户拖动最小化窗口时系统调用此函数取得光标 +//显示。 +HCURSOR CPFHookDlg::OnQueryDragIcon() +{ + return static_cast(m_hIcon); +} + + + +void CPFHookDlg::OnOK() +{ + //CDialogEx::OnOK(); +} + + +BOOL CPFHookDlg::PreTranslateMessage(MSG* pMsg) +{ + // TODO: 在此添加专用代码和/或调用基类 + if (pMsg->message == WM_KEYDOWN) + { + if (pMsg->wParam == VK_ESCAPE) + { + return 0; + } + } + return CDialogEx::PreTranslateMessage(pMsg); +} + + +void CPFHookDlg::OnClose() +{ + if (m_iDriverStatus == 1) + { + CDialogEx::OnClose(); + return; + } + + UINT uResult = MessageBox(TEXT("You really wanna do this?"),TEXT("Warning"),MB_YESNO|MB_ICONQUESTION); + if (uResult == IDYES) + { + if (m_iDriverStatus == 0) + { + for (int i = 0;i < (int)m_vecHookItem.size();i++) + { + RemoveHookItem(m_vecHookItem.at(i).iHookID); + } + } + BOOL bRet = UnloadNTDriver(DRIVER_NAME); + if (!bRet) + MessageBox(TEXT("Opp! Fail to unload the driver!"), TEXT("Error"), MB_OK | MB_ICONERROR); + CDialogEx::OnClose(); + } +} + + + +BOOL LoadNTDriver(TCHAR* lpszDriverName, TCHAR* lpszDriverPath) +{ + TCHAR szDriverImagePath[256]; + //得到完整的驱动路径 + GetFullPathName(lpszDriverPath, 256, szDriverImagePath, NULL); + + BOOL bRet = FALSE; + + SC_HANDLE hServiceMgr = NULL;//SCM管理器的句柄 + SC_HANDLE hServiceDDK = NULL;//NT驱动程序的服务句柄 + + //打开服务控制管理器 + hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + + if (hServiceMgr == NULL) + { + //OpenSCManager失败 + //printf("OpenSCManager() Faild %d ! \n", GetLastError()); + bRet = FALSE; + goto BeforeLeave; + } + else + { + ////OpenSCManager成功 + //printf("OpenSCManager() ok ! \n"); + } + + //创建驱动所对应的服务 + hServiceDDK = CreateService(hServiceMgr, + lpszDriverName, //驱动程序的在注册表中的名字  + lpszDriverName, // 注册表驱动程序的 DisplayName 值  + SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限  + SERVICE_KERNEL_DRIVER,// 表示加载的服务是驱动程序  + SERVICE_DEMAND_START, // 注册表驱动程序的 Start 值  + SERVICE_ERROR_IGNORE, // 注册表驱动程序的 ErrorControl 值  + szDriverImagePath, // 注册表驱动程序的 ImagePath 值  + NULL, + NULL, + NULL, + NULL, + NULL); + + DWORD dwRtn; + //判断服务是否失败 + if (hServiceDDK == NULL) + { + dwRtn = GetLastError(); + if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS) + { + //由于其他原因创建服务失败 + //printf("CrateService() Faild %d ! \n", dwRtn); + bRet = FALSE; + goto BeforeLeave; + } + else + { + //服务创建失败,是由于服务已经创立过 + //printf("CrateService() Faild Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! \n"); + } + hServiceDDK = OpenService(hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS); + if (hServiceDDK == NULL) + { + //如果打开服务也失败,则意味错误 + dwRtn = GetLastError(); + //printf("OpenService() Faild %d ! \n", dwRtn); + bRet = FALSE; + goto BeforeLeave; + } + else + { + //printf("OpenService() ok ! \n"); + } + } + else + { + //printf("CrateService() ok ! \n"); + } + + //开启此项服务 + bRet = StartService(hServiceDDK, NULL, NULL); + if (!bRet) + { + DWORD dwRtn = GetLastError(); + if (dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING) + { + //printf("StartService() Faild %d ! \n", dwRtn); + bRet = FALSE; + goto BeforeLeave; + } + else + { + if (dwRtn == ERROR_IO_PENDING) + { + //设备被挂住 + //printf("StartService() Faild ERROR_IO_PENDING ! \n"); + bRet = FALSE; + goto BeforeLeave; + } + else + { + //服务已经开启 + //printf("StartService() Faild ERROR_SERVICE_ALREADY_RUNNING ! \n"); + bRet = TRUE; + goto BeforeLeave; + + } + } + } + bRet = TRUE; + //离开前关闭句柄 +BeforeLeave: + if (hServiceDDK) + { + CloseServiceHandle(hServiceDDK); + } + if (hServiceMgr) + { + CloseServiceHandle(hServiceMgr); + } + return bRet; +} + +//卸载驱动程序  +BOOL UnloadNTDriver(TCHAR* szSvrName) +{ + BOOL bRet = FALSE; + SC_HANDLE hServiceMgr = NULL;//SCM管理器的句柄 + SC_HANDLE hServiceDDK = NULL;//NT驱动程序的服务句柄 + SERVICE_STATUS SvrSta; + //打开SCM管理器 + hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (hServiceMgr == NULL) + { + //带开SCM管理器失败 + //printf("OpenSCManager() Faild %d ! \n", GetLastError()); + bRet = FALSE; + goto BeforeLeave; + } + else + { + //带开SCM管理器失败成功 + //printf("OpenSCManager() ok ! \n"); + } + //打开驱动所对应的服务 + hServiceDDK = OpenService(hServiceMgr, szSvrName, SERVICE_ALL_ACCESS); + + if (hServiceDDK == NULL) + { + //打开驱动所对应的服务失败 + //printf("OpenService() Faild %d ! \n", GetLastError()); + bRet = FALSE; + goto BeforeLeave; + } + else + { +// printf("OpenService() ok ! \n"); + } + //停止驱动程序,如果停止失败,只有重新启动才能,再动态加载。  + if (!ControlService(hServiceDDK, SERVICE_CONTROL_STOP, &SvrSta)) + { + //printf("ControlService() Faild %d !\n", GetLastError()); + } + else + { + //打开驱动所对应的失败 + //printf("ControlService() ok !\n"); + } + //动态卸载驱动程序。  + if (!DeleteService(hServiceDDK)) + { + //卸载失败 + //printf("DeleteSrevice() Faild %d !\n", GetLastError()); + } + else + { + //卸载成功 + //printf("DelServer:eleteSrevice() ok !\n"); + } + bRet = TRUE; +BeforeLeave: + //离开前关闭打开的句柄 + if (hServiceDDK) + { + CloseServiceHandle(hServiceDDK); + } + if (hServiceMgr) + { + CloseServiceHandle(hServiceMgr); + } + return bRet; +} + +HBRUSH CPFHookDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) +{ + HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor); + + if (pWnd->GetSafeHwnd() == m_staticStatus.GetSafeHwnd()) + { + if (m_iDriverStatus) + { + pDC->SetTextColor(RGB(255, 0, 0)); + } + else pDC->SetTextColor(RGB(0, 255, 0)); + + } + // TODO: 在此更改 DC 的任何特性 + + // TODO: 如果默认的不是所需画笔,则返回另一个画笔 + return hbr; +} + + +bool CPFHookDlg::RefreshProcess() +{ + HANDLE hSnapshot = NULL; + PROCESSENTRY32 proc; + BOOL bMore; + + ZeroMemory(&proc, sizeof(proc)); + proc.dwSize = sizeof(proc); + + hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + if (!hSnapshot) return false; + m_cbbProc.ResetContent(); + m_cbbProc.SetCurSel(-1); + + bMore = Process32First(hSnapshot, &proc); + while (bMore) + { + int i = m_cbbProc.InsertString(-1, proc.szExeFile); + m_cbbProc.SetItemData(i, proc.th32ProcessID); + + bMore = Process32Next(hSnapshot, &proc); + } + CloseHandle(hSnapshot); + return true; +} + + +void CPFHookDlg::OnBnClickedButtonRefresh() +{ + RefreshProcess(); +} + + +void CPFHookDlg::OnBnClickedButtonHook() +{ + CString strTemp,strProcName; + int iRet; + HOOKITEM item; + int iID; + DWORD dwPID,dwHookedAddress, dwJmpAddress; + + if (m_cbbProc.GetCurSel() == -1 || m_iDriverStatus) + return; + + dwPID = m_cbbProc.GetItemData(m_cbbProc.GetCurSel()); + m_editHookAddr.GetWindowText(strTemp); + dwHookedAddress = wcstol(strTemp,NULL,16); + m_editJmpAddr.GetWindowText(strTemp); + dwJmpAddress = wcstol(strTemp, NULL, 16); + + if (dwHookedAddress == 0 || dwJmpAddress == 0) + return; + + iRet = AddHookItem(dwPID, (LPVOID)dwHookedAddress,(LPVOID)dwJmpAddress); + if (iRet!=-1) + { + MessageBeep(MB_OK); + m_cbbProc.GetLBText(m_cbbProc.GetCurSel(), strProcName); + strTemp.Format(TEXT("[%d] %s[%d] 0x%X --> 0x%X"), iRet,strProcName,dwPID,dwHookedAddress,dwJmpAddress); + iID = m_lbHook.InsertString(-1, strTemp); + + item.iID = iID; + item.iHookID = iRet; + item.dwPID = dwPID; + item.lpHookedAddr = (LPVOID)dwHookedAddress; + item.lpJmpAddr = (LPVOID)dwJmpAddress; + m_vecHookItem.push_back(item); + } + else MessageBeep(MB_ICONERROR); +} + + +void CPFHookDlg::OnBnClickedButtonUnhook() +{ + HOOKITEM item; + if (m_lbHook.GetCurSel() == -1 || m_iDriverStatus) + return; + + item = m_vecHookItem.at(m_lbHook.GetCurSel()); + + RemoveHookItem(item.iHookID); + + m_vecHookItem.erase(m_vecHookItem.begin() + m_lbHook.GetCurSel()); + m_lbHook.DeleteString(m_lbHook.GetCurSel()); +} + + +int CPFHookDlg::AddHookItem(DWORD dwPID, LPVOID lpHookedAddr, LPVOID lpJmpAddr) +{ + DWORD dwRet; + if (m_iDriverStatus) + return FALSE; + + __asm + { + xor esi,esi + dec esi + mov eax, PFHOOK_CODE_HOOK + mov ebx, dwPID + mov ecx,lpHookedAddr + mov edx,lpJmpAddr + cpuid + mov dwRet, esi + } + return dwRet; +} + + +BOOL CPFHookDlg::RemoveHookItem(int iItemID) +{ + BOOL bRet; + if (m_iDriverStatus) + return FALSE; + + __asm + { + xor esi, esi + mov eax, PFHOOK_CODE_UNHOOK + mov ecx, iItemID + cpuid + mov bRet, esi + } + return bRet; +} + + +BOOL CPFHookDlg::CheckVT() +{ + BOOL bRet; + if (m_iDriverStatus) + return FALSE; + + __asm + { + xor esi, esi + mov eax, PFHOOK_CODE_CHECKVT + cpuid + mov bRet, esi + } + return bRet; +} diff --git a/PFHook/PFHookDlg.h b/PFHook/PFHookDlg.h new file mode 100644 index 0000000..7f8b5fd --- /dev/null +++ b/PFHook/PFHookDlg.h @@ -0,0 +1,64 @@ + +// PFHookDlg.h : ͷļ +// + +#pragma once +#include "afxwin.h" +#include + +using namespace std; + +typedef struct _HOOKITEM +{ + int iID; + int iHookID; + DWORD dwPID; + LPVOID lpHookedAddr; + LPVOID lpJmpAddr; +}HOOKITEM, *PHOOKITEM; + +// CPFHookDlg Ի +class CPFHookDlg : public CDialogEx +{ +// +public: + CPFHookDlg(CWnd* pParent = NULL); // ׼캯 + +// Ի +#ifdef AFX_DESIGN_TIME + enum { IDD = IDD_PFHOOK_DIALOG }; +#endif + + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV ֧ + + +// ʵ +protected: + HICON m_hIcon; + + // ɵϢӳ亯 + virtual BOOL OnInitDialog(); + afx_msg void OnPaint(); + afx_msg HCURSOR OnQueryDragIcon(); + DECLARE_MESSAGE_MAP() +public: + CComboBox m_cbbProc; + virtual void OnOK(); + virtual BOOL PreTranslateMessage(MSG* pMsg); + afx_msg void OnClose(); + afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); + int m_iDriverStatus; + bool RefreshProcess(); + afx_msg void OnBnClickedButtonRefresh(); + afx_msg void OnBnClickedButtonHook(); + afx_msg void OnBnClickedButtonUnhook(); + CListBox m_lbHook; + CEdit m_editHookAddr; + CEdit m_editJmpAddr; + int AddHookItem(DWORD dwPID, LPVOID lpHookedAddr, LPVOID lpJmpAddr); + BOOL RemoveHookItem(int iItemID); + vector m_vecHookItem; + CStatic m_staticStatus; + BOOL CheckVT(); +}; diff --git a/PFHook/ReadMe.txt b/PFHook/ReadMe.txt new file mode 100644 index 0000000..130a2e0 --- /dev/null +++ b/PFHook/ReadMe.txt @@ -0,0 +1,67 @@ +================================================================================ + MICROSOFT 基础类库 : PFHook 项目概述 +=============================================================================== + +应用程序向导已为您创建了此 PFHook 应用程序。此应用程序不仅演示 Microsoft 基础类的基本使用方法,还可作为您编写应用程序的起点。 + +本文件概要介绍组成 PFHook 应用程序的每个文件的内容。 + +PFHook.vcxproj + 这是使用应用程序向导生成的 VC++ 项目的主项目文件,其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。 + +PFHook.vcxproj.filters + 这是使用“应用程序向导”生成的 VC++ 项目筛选器文件。它包含有关项目文件与筛选器之间的关联信息。在 IDE 中,通过这种关联,在特定节点下以分组形式显示具有相似扩展名的文件。例如,“.cpp”文件与“源文件”筛选器关联。 + +PFHook.h + 这是应用程序的主头文件。 + 其中包括其他项目特定的标头(包括 Resource.h),并声明 CPFHookApp 应用程序类。 + +PFHook.cpp + 这是包含应用程序类 CPFHookApp 的主应用程序源文件。 + +PFHook.rc + 这是程序使用的所有 Microsoft Windows 资源的列表。它包括 RES 子目录中存储的图标、位图和光标。此文件可以直接在 Microsoft Visual C++ 中进行编辑。项目资源包含在 2052 中。 + +res\PFHook.ico + 这是用作应用程序图标的图标文件。此图标包括在主资源文件 PFHook.rc 中。 + +res\PFHook.rc2 + 此文件包含不在 Microsoft Visual C++ 中进行编辑的资源。您应该将不可由资源编辑器编辑的所有资源放在此文件中。 + + +///////////////////////////////////////////////////////////////////////////// + +应用程序向导创建一个对话框类: + +PFHookDlg.h、PFHookDlg.cpp - 对话框 + 这些文件包含 CPFHookDlg 类。此类定义应用程序的主对话框的行为。对话框模板包含在 PFHook.rc 中,该文件可以在 Microsoft Visual C++ 中编辑。 + +///////////////////////////////////////////////////////////////////////////// + +其他功能: + +ActiveX 控件 + 该应用程序包含对使用 ActiveX 控件的支持。 + +///////////////////////////////////////////////////////////////////////////// + +其他标准文件: + +StdAfx.h, StdAfx.cpp + 这些文件用于生成名为 PFHook.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。 + +Resource.h + 这是标准头文件,可用于定义新的资源 ID。Microsoft Visual C++ 将读取并更新此文件。 + +PFHook.manifest + Windows XP 使用应用程序清单文件来描述特定版本的并行程序集的应用程序依赖项。加载程序使用这些信息来从程序集缓存中加载相应的程序集,并保护其不被应用程序访问。应用程序清单可能会包含在内,以作为与应用程序可执行文件安装在同一文件夹中的外部 .manifest 文件进行重新分发,它还可能以资源的形式包含在可执行文件中。 +///////////////////////////////////////////////////////////////////////////// + +其他注释: + +应用程序向导使用“TODO:”来指示应添加或自定义的源代码部分。 + +如果应用程序使用共享 DLL 中的 MFC,您将需要重新分发 MFC DLL。如果应用程序所使用的语言与操作系统的区域设置不同,则还需要重新分发相应的本地化资源 mfc110XXX.DLL。 +有关上述话题的更多信息,请参见 MSDN 文档中有关重新分发 Visual C++ 应用程序的部分。 + +///////////////////////////////////////////////////////////////////////////// diff --git a/PFHook/res/PFHook.ico b/PFHook/res/PFHook.ico new file mode 100644 index 0000000000000000000000000000000000000000..d56fbcdfdf6eac0f4727c34770c26689271d96af GIT binary patch literal 67777 zcmeFYc|4U}`!{@SGetCs%!DQx%1~mPDN~e)khut%$88(;HqRkMBs9@rPAH+2l%z67 zGG~a|g#E0oy6)?`?(6>jp5NzvKkxhe@vJ)cI@em?<5=T)Oy>yz1I!>U&WdsxaG(Qt z3jln4Kg*+301ET~+qV5GV*rGi09aXnmPJvSWj!EzK=cey!~!7AOrif(W&r5hPNDx< zZbAGbgedes%MP>vy1P+_C}DsPT9X0F5wD;8_@HP9Kyc%4eyEJ-c8>q%#|K14fLBYu z`SF1R4S)#^21F4*#0D(a_@YXb@ISM)`@jC+Q6s9s<9}x87u})@5B2r+sDZrpAIN0N z9lFpzN)_ej?};A&_Zmu>-cH@_-|Ok^oNR0Y?ElulU~d=T<<(@TZuyr6db@xCdnX%@ zfB+lUKR3|DjEp$h+d0|U8L9tjpqG?EyW(x{L?p%7+apd`3IV;8prxP)(2q>U$HzxF zdD*Ksp?Z25DQjyfYXfomW?VceF2c(xz|P1LWKgw?Mm$)yXh9<>ZA@GZx{=QSKD{4pGE@ zWHQ2Oq{-gS61*$oL){BSWJDx+h{W#^5jX^IYSM>_LUJh4T}DuHt882lIe-`w5Hn(L zR}X{=5;;E9T1HyZdz6S4p6Kiq;N)cq(V=8A$z4W7%372-5>Fn8s-cDZ42p>fzR!I5D$Ot+RM~R~&gg6{w1dWGSK_(ChGBWw@vZI7i zR2x9RnFqK6k$^gj_H%a^LHq-70WopTE#57NPc#}sl)t;XyR2v8$p9SA+k0dbN5K<) zD*U}=3MGzlaL8@-@@{K!3W#upXks)W+DBF<86^&138NzkbR7km(zm>y3^t3oY*;tvdU z^fWY{KgLh#p1~(FI+EZU85!y8>q@Ekl^BSX1Y&x6dIeghzwoa_wUw1s-iY9Dcw!~u z66yc9eu=0$+B-cngW!w*%^c-uQ3;j4kyVkAY2JV4Pgzv&%5+~}@65D6|CMj})jt|PC2jbX^P(J&%9MQISB^%%R$l&< zQ=pV~^1%PgIqGPQFY0J5Cj*8|>4#j;`!oNDRvy;$3?CW;7F`tmxt5U5{O5%KIUziL zZC;dLbo)QmGk?`E41duq3|@B4PJhxU5yQj?buV=+WjOSbt`OlvBsh6FAzei8F6rjx z8bgW;K-xwfkc@U0bmxmBQ3SW05y<9;ib%Rj`;hEEH%|uGfxjX_%GzDfhd9z~=Vk8% zGDTnAq}<$aWa9Uj7_TOX4kCSbbC>oBB1epj*m(g_mK2JFEYc@25iv3LNdA5GlgW1@ z5=Y~Rao$cqEFqEHL#2I22_v}XIB(QW3Pr-yJ5?hfB4Q*Ch@qjx3K@w+jsx~~IP;UJ z9TkzB;_%&K-)uh$@x)4hByN0?lM^@lpvjRcqY1L<1R{Fk*(j6UK&QcGndy=LYOkzJFY`^0{EcTIrdJ{1mHxX747C&~ z@kbFLvT9A}qZ%@KHHb_eP(+XQ2J$k{uW|wP01Z&1rvJ&MNMVKl`M6Ld14T;wJkI}= z<5AlHHJLo4NT%3R+t4MBuHWaJ57~bIobc#SqZV|y*D~~rZvRK;{I@7V2i3;L9`T~2 z2s;< z3<(W~0d!xHM$BPPq#O{2O84*aL1{nLW^^4IXLpR>wK!~vqb(0byHhI~&&520IdBu2#l4NA!zeU)Ge)kp`@g}wr ziJ{)!an8i(Xz%2V&E5_qV!BUSB3e}>Kz=@n@j2;hPhg#FdLo58!6$m{2_&M233Y>Z zE)6|m{Rm13C7RkbPbXGF=rQ*FhNa zoOZ#r3wt5gNgN8?_Cv9!1Qhy8!%e^aQ0O6x%5qTXCl5FM4?>QM8r%+4hMl3Na3Iti z_C#BQbd(*4B{+a&iX%v0as!!sUpP*11l0^Luu1g?m4YBB2|fsSLk~e2Q4wwhD?@3B zGL%OtL1nBm+zr=)vZ$lzUK<`Hs=_4G zRZBs`?HagTxB=W^4Zth@2zE%fz#fHm*n@ouvYOA}z=`J|ru-TXXubtG{XS5z?1#ff z1Ax_g1}ev1fX2x#IA+lWdJcVHXw?lmu03Gn@d3`b^n;B@4=9@rg1X%>7@ZviZMRW4 z;XMkrKErSUHv)1uL*VdT0%+a{hhsO1png9FG#?~`QB@i^6~%%>NfJ0zW`Ji^HW+8! z23*w@IQb|GOq;KO?bB=E`urxGYrhRHuS&u9O&RzQ-huzc9yk>`0d|pN;2kv%4vCZC znf?VF(`Udldj|ZjeTA&VR0u80ftcz7NO*W1it>t~xsU*D_v7GgO(?WiCO}%#JqUgO z0P-8}LuqR*^gIrSw~Z`v}d5zPGaxnm%+u zM|T$tysU?huO7i*UpIUl=z~YCW6;zw0Xy@xM=}9{l&1-w3mvyj+|+f5l6FSN@0moUEvj06#A;*KU6P zlm7wpyV_CzfVjB0s1SvKOC0?O{BHTDT=$5U)&VJm7u_Mm&(AACq1t}%pO8Q640Lt0 zG${mPs0V(2Ng+WrtnEDJf2C00KtsEAt;)baR|^r?J#B4$ih7U~rS!5L^GlpkAi0Ox z*jQ+3X*biVqLKw7IAS_6i&EVwix0=C0! zEX=etPEJm>D=Q8*0-wbt6yQuh$r2JN-26BOV__(FtV zT$)tLtIwnKph%eoU1Ot@!|Lj)!?Dv(C-meF7#)yATP-R`ITJUFu4ONww761FSyeR( zo}~#9%pd}kCJglt8Ed0Xe&cTz<*iWCw*oY0tm0t`K@%ddpQga4&lw%lw>qXRyGDTc zH#FS5wz~2yKTAbLNePQpqpT}S3x&YR>72u9TT@HpV^&7T4p9hp{KV(2u1;Oc$zI#H zH9T9(F9Iif+p}lwZSAd%C_U_^*s$FV4HSG{_2WmGXrnc-Sk%E!d}HH7CntNGGZvOM zj>blMhomGBzNedlr=+RuObs;+MJ$>@OH0d>3KLUP<1=S$&YZD8$@VlNkX^%f|HMxg zy*=IeQ;+NOi;Ax08x40~lYb#!$N3@mhXbd9Y}Q1FNV&EaNIaZ%p2tA0LSI9yLq$A#bk59BbqFw!FmJqHSwk_J%f>qP^)em#jm zAwUUtdurIjgGiYMA~?_S6Z!M^`&d7kLBq`(*9()A2`S-WZ)acOUe6+YWB`T0)z#(C zaDeZ9y=cBS*6{O-NimDF^Y~Yb!vO)2fxkNVD;`bZC;P&Z)WyY!_T}Zp#Mg^+2#@By z*1_NL=+ND~URZdotYm2^Vt#Rc>FukfIb1*#TG+KI|2rPK*YKq)D{(6;@hi)(R_1WH zD9XZ8I`{`XK+(0L4urqG`YCCC<=ZL+kG5j1gMY-M%8t_2)laK067HbGHy0EXy@p2} z{3rYygkN1PO-rEQ!-8VNesukNOoq@<)I zMMb4Wg$1u2zW>DkG3H~G1!kdmY{7?3Ta7seX%zu$Oe~u;<4TV4;{MXo@Y)aQgpOk#( zzu@|prkAM~+x}AV=K`{U{%?E|nStbi{}bYqzx-5W0{xLFM$a!p33r$tRQE~&r>XQG zKB};#0+=evLy(~;>=ZZ-3I|RDJ5M0+*kOU!L3zzj75L?&?(o(}eyRb8;!c9ByCEF% zGl896b|7)qXU#W_R9^=%+N{VY-2zF5=sLjxsmLeIIL!m;<^pTJXqKZGWIM`3?%ALI zX94m*ueeCTRhL6>)k6}By`BoF zD$WyBuLgkrl_0o{mqY&PVW^BzM7}B(`KX$3CrtGZAN5YSHrz)(YE_&bJV-tQN^qLKH+;>KPsRy)NLq00PJ<1P+=ePXf=L{L^OGtMCeB)S5tA z;|<6i?}o!CJ_1(vDX5%y0h%Y@fR0r!oUnNd76E-=iTqIO!1v%5)DOz0gP>_Y49Cw7 zt@)ycKBHhCi2TqScsN`d4jP$J;1!+)TE$^tcr6T0T@450Vj?J)$ACsnBAlqX2<~}N zU{{_2ZnZfOSd#~O4Hb^s9<)<}&HMO@9YTiDFy7z5R_q-S42AUvks1>qC+aZ6f1FDeEn%08+(-)Ia z)-()-t&4CAz2v_CZW*ePull(2IXwCB3i^9{;p3Zn80hbVHv{jX`{Oh`Ul@fa!z<7? zI15O9!KaZ)m>3^N{^=L^Iy(&$Ulw3`ZUz<>767ULH8JpC_^BqR{ty)Zc2g_C#mOPc z_iy!-ibp4R3-j@8=i=PM!zuXh2nTJAo%#>!M7DE@BY$?=zW`aAX||w}gNF8t>FMc{ z+&mJzJlnVZlutQWNwy8lwDPnn;H3F-q3xuu2=YCVze-_{nRrlozvL#i4J?P5Y3NL? zNndvE78Vxf*A?RD*^VsGjHH9I+57jiv29RbSx;^Dc)3nkLw&cfkghO4FApch?>pEt zElU9?ZeXE3{dlGE=+Ps4b_*K_Qy3tJ^U|T-1=;!uz2cXV{qoA z^%+BZJuMMoK7bE5u1>C8RK-e3U)(Rr_Tbsm78j@bdZ+VF=M1%WBS)vdxuADtX61>T zT)H%BZ+-ggQN4qWm6he0^QVsNMRs`qwaMk48pRB?beRKNQ+&PLnD%KOvpi<0bIifs z7*GtO`0~}fg2IAib{}s~4-a<_+&X#@2@M^Tj-lwl;K#v%?(XZQVL`~+5Abkz^9b>9 zV`LE6siC2*cOGD9XmGIm=Cxb+Ak^#^`pOfvd$_q$37-AAd2{1~{msP%xA1X6xBCdM zrae7EQ73K}{@B{Nfe$x}uHGViBYa+X|9ZhQAk5Fh-ThzfXbQzw^Y7dzmHk-$_G%fm zBU9A zw({-Us;c;ovapaq@4xr|X=HTdU3K}r_?VcOvhWbx-`l^Ab#+mi<6@$tqsyYg{@xCw zUDcK4@rXP+7^QzQE`O5;$8`J~^;*T-dvP%dl#hJ=l>X0pzx2~i!T$L@>YwbCBl`~m zm&HL8Lzf5f-R5hy2)Ba@h*)8P&*@(=^S$nRu)|9qcK8{>&cKr(;e+052AqbyR?ZM$ zZw{6x10h(65rUObyj*Pq5H*;AfMVwat&I??vk{_nwm^(NJH!}mUyGq99p8##=-VN~ zoCgw5@xet4en>VKL2-0ZNVDFtX1}D_?t*MvQOI@@gBa@raLMT)Tumpd(DQ~8)XFtVjVys&IuG#FM#AFS2%FR z8`ROeD$Tg_U>Ir-hVkxjG{pmSGX20h*%#EW1RxuQ5=&Qr61)oB4mk>ULX_YhQ5o4J zs&Fq{2kKHa;by2l6os5bwv92AM<0W7WUrLR8ADCtaTGg04iyO|DAsO@V(q7)_M$D+ zWm+S<#eL0Yxl!Z-B_&>Pw;Trzmvm5k-2h(Pv_p2vd1%VNfNT{nXu9RLX0N=s<$-Jz zZ+LYd2b;Es0|Q$cFl@~P7LKdHyuBFMxQo}~>TG-^uu0%Pu!~d!2a2zA?s@=xa;31- zFcl7IJ_OTKNx;3k9{432VfUeDps3RVl4_lB=x7JX>GXoE!!wXSJ^%{FgP>~g0#prO z!BMj}pmFv&V7;Ehu?sK2#PcPnTEBxMc70%A(+)cR?O=}UfwNw{plmS&sy3ex{xckP z9$B+#j=O#a3)f*d<~a(-1INME^Anu+8$|K;K~PI3fZi` zDF{fN0;dcVm(QI89Ey>r#^*rJjW{T;4Tr8;B6QZ}K-k-Ai0rI^50B!Yza&I+mckdl?=eyQOAu6`HzV z!(hjwHJjx_{}jCMn}zPtkI+9j3QcpLU~pgn#y_KLY!qfDhGAxE6z0CpqVJnX{~S*@ z{F4y-eQO}Jed|^!+26&V`8h4&eaB69a!B*A|87vHUk7kbOQMrRGUAfq+fIo-UzcNM zqNT!8DWBc@X7RKTpU6>u3V{N%Jl%RmS|vs*3!UW`+>(3u>51?myFjscj+vR6QI(0- zQuoK~u02BH$M;e6{k7ty;tkA9YRrshY|W8@wMYBZUNL@v(wnO@ta2>*a!gjQtdAN< zNu0FP-X{WZ?*_S0g-Nd9Aam>6j@Rwa&pVyB*OmZiyG34EU^#sCkleGUfg4X4+gR(L zb1*~7@^*2a#-S^0{(in5E&(*V51=@qt|`*f{cRsVCWVCr;oRdc1baARL?txs(X_sN z|NQBGctXhg-sm}ZBBI&!YY{&6m)(h3O8&MKyW|n+>*4u(ZQ%3WlFHSZ)vu$gK2brw z|EdRgS&nL}S66*gf^dIPa1~_ohe$FRpNkLr;m>%0iRQWo>1h!WnO%Ra z2WY(Xu=`JS|KGkp|K)E}bl2}hx&Ku@E+-0`kRID;DF@rF6k)rq64GT#z;RX;xIOiN zGe94Na7G~Ic^uBWt3&irR-}hE0YwjGm{Z>6^1x-g9gyj;8?w&HBON0I`7WYR=!Se} z*S&Dva~I^DmxTiN15n^93;D=rzUg}i`Ok`Q>8vu+L#l8arwYY>=p8e9mns}<34Spq zpb~BiD$&R{j&g*Pg!7=6a28BrFMv_17o5%tf_q_yke<;+KCm8CCmey&FhjUcG)DIp zfX}dn+Dm7UziSIo#jfxuLkk*nPQvROCh#Q35?Tt~;Mom-=tDm1Y_hKY z4=C5e0o7KJQ*D96TCYLQ1nGiPgP>>j7}QVofWCPr=$(H8Cv7^wz_uUE&b|XH*IrOJ z{Rm1{!=T~t8T7oy;3SgsM-zfU^LiMVT?qrzvScu;O@}iDL@;}i4bDy1z^$zWeBYLX zfA?K5Pn`v;*eUP}{|vs-6M%~u2k+QD11_htmpmh8l+@0!%s7EVM`)C>pn@LdfW)W(T+u(G@ei%aMm=hgqM z_m{s<_W#ZQH&@{A{Pa|w(YXKZ2X}+$O-El8Kkk~d@h{CCc$|7YYsd23dt$SAC?Wn^Pmt| zR&L^MyCd4Z0S6Br!Ud){pARuJ(LAB5qm1F$CrQY=lHz(S&h(urJm;Ra%{m#b0c>H-QVTZ3vMxyQI^MlmoH_cx!G8G5Tk!rqZ))=cX`7+VUDr41&DHR<~Cg6pQ9yd#%2nlAIQ z`8UV4$dB~wI-SsXm_NHZF(WZCF=ICHNB1ECso)Eog}?rP@O>)Z#k{TXRzY$_?Sj$f z*zRK&qgC(6+%>y~b6Fg4L9ZHLdUiLBg=aR=%*g8%;>ONvv}|pTz0(!S;7Vf2$1`W88H3_gp#POu)sN6(7=6dWaFk3mElQkQ5HPz?n{AuB}HN?=DfvU*M_dqGsc>>=MEz(kOGtQUzB~yIy7`I&g&9 zR=O?D%*Mwtp2-r5WgPHLxx891eY$>fa7z4}vr3k$n!vxpBBZccRlLSpp zzCHO@H}Rbkc56)|uV#>9m52I9JR@E&=Df9!>13Tt!}FH*laV*GI9j6tY*f6uo@=#2GHJnb4o>iAMq#O2F%+h8ru1uTxt`3k>iI7v6Csw1Sj@(yWFnFA!a%IOgC);YHo?5ZG+5A zGaQUz5gKl?7|VYR3iA99G@SSIN}aqH9GTMU$nB<7Sji`Z{}?`tY+aRQO@Rp!quz{131cPD*1v zE)OP7N!5CAmCMqx^i&xvE~jjSs_VN4vgotoHt6eazDk8VW5Y*XoudJ2RBbXzke2dw z_YQ@GDh50&w&x33foWKhYcV-Kp7EpaC3G@8b?t+uK8IV44V)LfR6#8q_u^~iLoW;w zpOo?>t}#Jlbtns5KISiT%mBMu0tcN7JGu(AvuQ_Zc z04*Kq;I*&QE5#Gf8z(Ppj?LH4jo;e8laKH|(3X$pFnz8hS5a@UHZ4uK{`ScZCFP6X z3n4BZSh7zxeV4qjKlza6C(y~h9GI-u#sJ8J0}v1a6{bdyry4$ua|a~u%VJY zJw6i6`;u>DtoGCvnj5DL_Uv9&X!RLZu)!!A_|AUa6s=brU4J#<2Jn3;nPWdn6}2wq z$PAN`IwyWOKWH`bNBLJ=Ovk`}Vf=YrcHGE_i{!1bp=%C%3^I-^M==$#;tlAk-q_nF z7~`h4hK*pdE-rFOwF_Z)R&Kfr_X~KBP;oKzh;3}22;RbdTz)B7f0IRPYnn0z;!y#dz&44mWak(Il;u8EBrsD)=RwT2)=5SLQ3S<(9D=puQsp z2YFbZ@@GA0v(D7SCrvWpZS|P$FXwH*xoI)&GM}AVsq?v!Vg7*IW^37wc&O8y zP;``*)9-~ZCwk^Ng8L1aF`+VQ33zTFosYN)9`7?c#o@Fh5HGHBu5@Oc);!a2u)rwdS! zFWT|YXr2C6^JJxWr#0(`@wfPc4`N4z5?aW6lkvKdtuuLb+crHiLd?Y^&M!Z$^OF%~ zca|x|#9$_e88)``U@+BlvxWwi?Hke=QnbjL>p2IsInSBgtTChk%^uL~rQ*9sB8DYm zeh5U+J8I(3J$t+5sb=mYMn?V^-e8`DCK}hovqO5g#e-6{KPq>g(&!-8ej9TqKb*(z z9GWpt1*^-gpC1OMkeTL>Y{4^#9ivsvMstdGI8Yy&#+ zS^M?DG2y|~Ce%X=HZlEOB!#gF3H(=I{3FQ*sbgd`8pa?$#=Cig3P~7bTGHF1Y)&7cq0q- z!S20k3(|>rs_#ac_1Zf5J%Jnti3EVcN#hnU<+Lmt%E# ze3hAw1vTjM*e1`>3B%!I-NJ&wMO&^5=_k%v&~I!(k(;d-+cQkC?kIN^2yz|?7rRY#?&y3BpACohMbremJCU@jA6dWmB?k>b}x0LdXIbtULaDm)W4PY zML8?|<_pH8z^q7o`PQbY(ireTQM?*fy3Ko}1}~`iZVNX!A)KVTYr)ahUs*e&F5YiHzsvx30UZ%6e@*6r#|2lcBG7@OjnWtVu%bF-Bh#t(RI&u2Gt*6$ohe)*$f zVb^Y#SmQIi+wlVQ3)FebBh@Cf_+akwyG`_CRHK7;u?G#gV^|)GRf{mDJq@rZ5?CB2 zZSEzEJ`u%+o}jB>R^m7&#Mb!uO%t`pbwSMWiK0)K+*jZ=rDSi#dApyzeWG8svrqz4 z^rmK{zizwX4`;$1h1X(pheNtHpQ)OEt4LdZ@sm29BQRi7sNY+f8{Flla|;JI-EtXp z4pYB-qoF~AubS!YZVVg==PS<%47~GYrr8s(o+`qkR4c-+x{ydiE5~!?(fl(}jSfX3 z&AIyzjuCY8h9CmI`Y=;*%xN$=Yk1BSPKKfWK4qrnPKFy@KcYzGb#eQeBK^RwrLnfPwA<98S2P8i)a zA6qBN$@sQ5g65r*)T>Xk_~>mMny=wxy|#9U*OW@L^uWQ>`udKzU7m8n_`TP-bzr4# zv6$9mKnU#arLX6>ncmKEH$N8*%~I9I_KmWT43MB zU(F=Z-u&R|7Gl5duBQ4!^!Tqce3JDy+#dE~NPCphJ;pV$Rx~d!)6g|h@3HirQgJ!Q zvSQBr#`>v6gx2wb?<-oHu;(jXvr0o5bC-MS>R6-*@tIBY$MPiOCfr4|ZE6KDi55FTQFeY++T)zV2}^-@PU{C3`K&T%SunGUDg)wl>r$OYBm&k&>ImJYIy7?g2y5Ji zuy0859XU)qu|6bSJC`a%o9|4)-FJ^8=!Z$BLYPB`sVgekGuu?F_F3*5R=OU>+Qek( z@Zkc@z?bC=S}vcC3))|5GjiU2y{H?UB}%vO?9HXen2qa1Qy*uVOjnbXX!e@oj#O-? zS?7F)*vsluR{uB@Z$P_egkOF*(3?h}9iv)y_|6j6gALGM#-x;u1O_tpJKxr?;=gi} zg~_?K1%F4pW}iwBS`6JnqrnZ!uhJo+fLjATKbiJf@8uV4wjuX6{-h>W?fVm}dt1_q za8OK|q`v9@MY;6E<|hd(81)LQ$+1>R1@)2qUAe3-d<7ei*Rx_bVpB5kHx)vjr}-#j zd26I*Y*Xms7pRKj))%qS?3{N}r`t1$p1ttHqx0OQRldh^*(VgeFm%^Y&!7C(l?LWY zFyH?iyn2nCdOCNqAzug|lkz}sPg&%E;N_;wDkkZ7M-S}QpR;VW1LJLm!pV^jrd=$# zHXCnI0xfeZR(TD+^-9;2RKnbe9Z4U@sc{l^Kx-+u@WX1e7M51%@q7Lz>V#dyTTRrQ zI`2-WUFphVGE!8qmtdNi9lnm^l`#V9jyOSZx=h<6bw^5O1pC4y_rN;qOS!9b?T-G+ zxjgbS_fxtq=G>Ci>p8Q(}dlaO#z->>;p<0qE>4dx1OS>+MBoCDsLUzX=+gB z#M98aTkssKZQINQ+ zB5^$yN#YbQ9?VR%I0K(-?mepeFENwxXgs$#(m%^Tx1kA^1gw!of&Q}uO%{kjCqd)3A4Ms}YQGhaF{B;m7YsD}z>`C{fyLu;54QG&kl zS)S*Jd%+StK|OZ;{Y&+Ik4NRA-K1z1mGRQtecM@l%wMWhThofmPRUcT&TN_=Dw%Vb zxF$kF#syICw-0{1UfU6q?gYKe`YzO?-ts$V0`T$)qDBf17^bhwXC9fsf!;J;PNs{e z;fDQ6#!MLBBVw?&U>sj6mTyyn@VaC4EywZI`-NLIg)T>i>&t61*2;huIn9&2Zo)a!>Ps%OT+tK zy)(mn*nMHEnd8=L2dJgwsEyan%P3_Z$n9FVTHg`&q(A;#FfVPJHRG%BT-M~64YWJL zum?wpq?XceSA2uG$Jo8GrVC(a{YHO#!NA3OksV)x@`f|C=Uag9YC;0Ao~GT(Y1mPJ zn|kycDYlN;<1MzArF{apRK&T=R8ziQ6xqB}Yu`QcySaOJu{0;!dp2tB zCt8!GC$Hmq?b+ts^hL2wo7RWaT@e<$<2Tc#(#$60nrUFr0vcmy>I+9 zJZ6R2P{rh@oK2;9vql&&I3bLTFh2NfGaKL;oJ4a~>ckvbN%W1(LetpH8?IBGSTQ;W z-ACGO_1b&u+c@;Sso$AVFMI&cp~s3F-xwzKlPVZ7VjM5K_HATa=Lp-@S!Y*#W|?ge z6nm5yD7^mSj}P`EACx z7Jd${q8dF9nKyFH=|}JLOHi+*@ab_5o>7UE;9KY}i@zNi{6ajQli>_atIK}ldw7e% z$GaI>BiQr}#^$I@^-oS$Z3zALq< zSQ=AT^)EiNT0Yr;o_|wjBwn~v-ARH$0lXRU7}%X@7`01%3!y%V|&X* z!d3b8tyXSGv0-@Kv4dplXW0ps+=DTSwC4M|-dkM@R$%N2E);HVJ?LOlzu#2czXV(2 zIAGKrSyEyfq_#xwSQ42#CvoEZ)ICq#XS4!5g&*!yGXi6gA+~VqgNn#7*~z{IbKNQqn2&Vd2oAu6n0t75beRblus{1LhRf) z?PkT7GFgVh4~!DLcfB>I{W=Fcbc>{FGr9l)rddqV5_M**p$^i{GdBfR%@m>q^{LL; zOq@He{#iiC^W4d+9=Ux7mnIy~WCx$ssn>m3ue(dxp@b@1x~OK1*Rh=6jQu#q<{MAa z*n^J+j}sUk9>W|Kbfv4oQ0H(AT+w#a-@~K&K3$Ehy#03n4d@>`Q+-fTk(}g~Jrj|6ddy!T+-Xk)htM5Ja>j-9V*Uh-MyU>!JRsSgA8jDnz zEumT2%t3NQ%K7o@I6?f8#{M${9s@@j>)Hss+h$Kr=v&wyk=BQ<<{MuOk8MrzM#}ox zg6F&*ZFHYQ!}GS>^s4n<*BC3M+f8Z9x?MBR@|?^8hf|tcInD}6n*ShqZdKS<68y4t=y;|V7v`!F4|cEYfg-+%`@TCpqBJ!7BQF(Qkgz#DHsH5CH`a4Lo0`qiG+ErRL)7m3qc6^n zQqJdApv}Nf_$d!`5hJ-EZ#R|(J%;W-UQL(lU#@~@=1*b|Dq7q;taUu6&RUjX{;X^K z39Rr`wo0*ia*Y-&(B7#R1Qkc`B{gt4F$& zGDjR6PBVY8-D*brWBgX_^&7g3;ti}U5}%Ks2@qX!KirZZ9LZ( z!>vKAqK{&UQmiOM|d6<|O&33!YdMa7Gqr+j(7v($q z8Hc3LJY%kGqkG#PJ}vxGVi;6xmyz|x z(D`QQ+pc#14;EWGF5$B?A2&V{YnS4#G^fA7v~NdlSHlC3Hiex*K~U*fan`kPBFoJB z@_c?k(2&yrwpUHzSoTs6$>UJ)R*bsn4C3a<~Y%C`K`uy&wZD@ zmQE!4{>5kK zPBu^O?v`xbr+`zK7a6%aZfK;KRI;g+6U8W&DwprC5=n7S=rGHo( zm(IZUa-wR~eqX|ULB@e3!Bx&>s`aX+U8LnQPrIlqr|iA7)wbpfuHvwxAOE2JlAqcsG{7-vUmj%&wYaN@jET`@1oGo!&i}y z>bA^?DAY~M*DmPXl>fF_XfP*qS4E55JE^A%tSx1q!1hVmv)r)F=WgL}Swdc{)V9T) z8s%BT+3UXkSaGaAc0C>cY~AQl?mfrvgjT=9x_zn2%q?6PpWx~~`g(Pq3o9h}fU+Lf zsEY8lOgPpv!>>ew)B1_Y>_)ENq`awL%170nNIaj*`7eH$U#17D=FFeCE9RgNKdOwN>#S$ zZIH(YUmE@0_Y#jd(6KPk_jypCS{Ge8r-9oD?Fyb7?Wf6HZGqo5886Oj-0^Ug*~(%O zu}JN|NsWyWW7%Kl=2RG$u}Oo)#)M$cJL@e*wWBB`{X3r^wlmU?9K<)XWhXJ~WyIIb z#j1-Iv=0RXvklHpCs$wQ`e2ikEMUD6IVDI2Dk<93$9G4H;*?Aaid(-8wP(6CUXkf% z08ts43m-r38+=YJ;<_XlgsJ(q->Z%!{`vc~n!A-i@=eWylDri>qbF=HJY@@_s}Z15 zX4!M~Y7teq#K1}QGQNhI=Xo_fb$Xr9I4F2$e$1<`ZHFp%FFTVZyKU|s+!2~HbuQRC zzbh1H@x!yDgzcMW`$L9Bg+=bnU$-{)>y$8Cd|ZLPvDC|HtoorhkVu|)nSH(eVIa2A zB3N;3xd`|xCbd}i(FgJH7Tx<|QoEkCAByTXeCs9X^l?Cd4pT;I*Fed}{JSW&y*YzZ zU|MQGg9G?OC=XKt==Z<6zc5^v=0hwF{2{T>+aK-zJ&4YAM3PVCQxyx-H5L4`C@KC< z<3-JFn%(9YpL1HC@404JkDA`AEB{P2bIEs!gHiLa9El!sZq>bMXxDuMC*o5t+EpZB>wfi z{QCxFz7O7T9lx^g@a`^DN#y zmzh6);8lJ6c@?W!TVs8(-fsyc8GnlK-#+{D;Aw%b95u=uSL3aqltuQ*o- z55`~p^2uzQ6Z@uFz0U*g{?E3}^3nxhybsKzH8M%w^|;DA_li$9IA=6)F~1kDFEf3I z`TK0997QJ9hp|Z87`Mk1o!dRut~0ItWBJ_VyR^---|O0z4z^8JQB9QiaPuhoV^BP} zwQ}ZphQ3kj=_7O7*k<*X=O@`iLldslJ&UPZKiM}vF?7dNpBDGz1GSskF%lEWY2|GC z!iXpFM~q?MhhU}p>reFXi7r7G&whDw^5d(0iczg4ix)q#k$coMZwi5i?AK4pJ_|vl zlF}DjXZAT$r|ytmJ62zwWW+CTc{^RlF7-nAn!VjCCmqolJI~=w=RF!NOoDb6+OJ5+ zp6RpCnrkGn$7esOzi6yu{YqT^ptb=fvwZ)JD5`{Wf-?onvYnKNsgc-r=%^Y;3y)wbHprSgb}S>fdqdy6*gX03_g4=4 z8!t_WtZtm+V4by448mvxjiqUn7swqVobGzKwdQunxV#FE7V> zTVFWq&4goaySt%l>d`{AQr~W@@?jbw)r_{GlIzU09tH|Hy9VxAN$SHDq*)^vyQM|@ zQ26}A+t%!s#viUo!1>g3ZzZ52{h+-rbMM26cN4SEtu1by@>NrAsn2iFi<_CxD7-_8 za@@D%5xZC1eKFc~;6;DDZpo&U*t_$#yu+XAmK@pjZ{KS0Jj6({6~Yrlec$ORR^Jfe z9y|N+2qu9FC)BrRrg?|WqQK2MGj*N5M{Gm;`KDxP6HYDe8r`MhzO6UtnX|nEM~72h zkN5st{kF|FBS{lpQ`NKdmpv6ffB#YAEMvUH(~^qOe>N^at!S0`daP#rU?(OgDC0>< z4|SvU@y+C3~ZKR)Xm(#5QgB2CaA*-!E;w{OuMEvYZLC1$aGMw0Kb&yft( z+zspIpE#yh(-JM%MON%@)OSs>;0bLS6E8<3bvy45F~If5Z>|G7zP{Fdoptbb3x6A% zi~`4p%rE6PKldmXsm)Z2lTcMHUPf}0!!CSmek0A51@yS%I+rW=%E&=`B z^@ZnfR~PhB@B-9Hg7Fk*iz&BlZr$iTEhST(O;R1kzB7G^!{2&HZ62>*;>YSYZs2@yq;w47(wlA}GzTEx3)uyHv>rGGeg1fXhjp>np{~P1NLD zBsI&&Rv6#=vR8u7 z-bdM@gk#E7sty%h7iO}_PK~qNPWL44#=_!;fKT|&#Zv~Br6(J5b`gD8*xdJY^4EI~ zeEn>cc&aaCVPs{IwQ7MNrdLgp?YzpZ1)gj{Q!J&!;+lI$b1m-N-*A+k#cJ3+XPb%F z_gF>I$ANyLdvkzBAgliY{(ag5H;2I9d-iy%N@XHbz0k!?mf{3?0iWGlvtHYZ)omI0 z>UNx7ZpN46x_KuHmJMV7_~HIa`LW>(*LC(eQ29)2Je+RvR}2_RjD9atQO<|$qt8_w zWV=T7<9kRf2?bFlA`4eZim`iY=9eZee4HGk(f2Hwcbk&lS~v1JapBhLV|$H?_#kzR zM(9CmA5MBK-K0DZYw-49G4iO>RH+3^pC6wiSuu(DCLvL?lgw*apF1DIMN0}&Sh`5- z$XUT&?zvr#(7!yY6MxXHwP0c|we0NI{*{y8nR`r3e823m5~y;w-^cLYfaWrTVXaQe zl;^vrW5G`*->jF;q_s=kcxJ}1ZbL;m)47TZXTDOI&j%{*pLMP9aT^s!i@Vo^13J+f|8@~p&`XIB1 zi`9>$<4wC|kErrFTDI`)#a3)C_l(Y0==N`VZzz+|zkQpk)>&VW=Aau*Z1~~F z@l!{#RvP=AYC}z_$4>?>U+*kz0K1v|2+ecfzqY0woRoRDM6e#OPA~~-{K`?dG#|o` zC;N<;vNLt~>75aAmJoM!#l^9h9uU}OZF#KTxW*yUnQqihgN6LjrOqXsc+thU)-6%G zjJ9>}DIuEcl@OXN0bEz!XEkQT_x>*c$Ury0fyLKdKeYV(u{YrKpZp%0Jp!$-DObS% z?YR zTK{U>#wkxP2m<)6yWf6OvoU?#qFCgYL>e+61DMb;jLdX5GJB7RV2y>bW|4?)UMadb zl1cA09+3WY5O8QRM(VMTe+jPNhbabM+QzQ^Sl?X5LPr5b{oOPj9D3~yBaeIL|NCuB zz3N-HtN?rvFjoK?d>L@H#nt68%=!Zf;xrJ2bP~Rh`f>%3$auu-BP~dPPf|XTkk$s{ zRF|IDLBQX-`{pD6@5g`eqf@PgKWo_LqA>=>dZ-&lxiK)tMBMACyGAV-fK5dj@z;b9 zitr?zkZ8nz#z5SMnT6fh`11WD_gQ-9KaX;mO>J0izUc;_(HOdaxxRwl=O2VA%*bPj zWwl1gLIYM?eFlsMzsPFdmsCCj{-s*~xOc28-hiA$NRgga1M2u0iHuWydfpPi@lU?< z*6E3*6BDhu>y5D<%*OaXHsk{p$S&CXf`IW za_66q+-Lr^H=u`psGj6+&-AhHtv^0;{|le_C|dnO)W?>7!3P)}@mGM0j8OsWAm9s> zHHjp^uhu1(Is-1Lb|~I~sxCc#O@5pPr{|pu@H-!W$F;5I?D1wXc}&=wr?3GC!aVx_ zgy5$e-N*fq7c309WcW+uE5)UMmYBr;}!f$4dr0Dk||Kl<*O$-O5g8nefYvB8=L>unIEez5LVOgjvsM~wjBKGr{e$-^hWP8@CRd1uAD>fsjs3ua|)BrqW$RSNA9=uuAfO>K0-v8|Nb8w zxnKX8C((GO4S-;*-MRp<7OB-oq5uG@N_IKP2>dNsf3Am%OQ65;ki6hI`7 zye(q|Fg!h{1n|3m|IUSpV(#`t7`uthkTza~^THFOYy=2ngT^AOU3jM?E?G zM?pn!8aVt{lQ$-Iv|-6u6P}J0vx)v^?gsrtAF##1{Th1@qdQq*V%=MD`_e06=az5m3 zK0RkHz#rWE<2TK;c0W4Z+I5>X%^196*>6mg_5ie?=U^aUe5Y1Hz`9@{Cj+WP{F6s= za}^L$d>E$w$7Nz*5t!^Z(fR5ZhVJJGnEw8E003Nru<*7YA9<|ym+pmKCjkg6fUW$D zQUKb=&m;b6UIR{{PFs9UK+0mLz)$zDRRA@>kGqVr{;{Fj`X@~ur%L*U6TpAD_gzQ+ z_`aY0)A`8*e>TyWUrc>_mhgIr5B1S~O&B&%fZ9LC1LD3SSGl9P*r}>209*d)_yX5u zN`tkJePHCiv#|U-s5@DH%_s_9b=O)ICc`NNxf2=z+9%oNCiIlmPzdzMsBze(KPPnbw}0ttm1q zZ=!XF0S5t-Af8eKV)sor4XgL}DGmiH4MC_p2F!moLAY)g?!ws!fME*2Al*MGf&QoN z0+s!t`!+9o5z1+xc-fVp#>DXBv{%tN@j$fT;iR^z0A#`v7)S#m{tEn>Kn4LHHNd?z z>mLsn3f3-HiHFoVgc3sNifG2ElD;7^fd6*lM~}?3_8o5(v)B9KItReOkP&cFF88^^ za!^ksEYJBfGW_m`{tU<}q~On=!4I9uwKw2a&%>==0GWO)Gb|Ye1_mbq zjgCR@$uFXD=oJHZElmOJnZ^9~{Mg9jY<}Srzy*%}g==E%==O0ltx;BANBnE#Kh(K^ zAm0>3yjF?DXBoR|TY<13;1z-h`NBkAIl05s?@%;4ZYBk_jQrwm) z2_clEP7Qp-b3@uTRjIP@sywS4WXpWKF$)Y#UpE5yqX&NCy19vi@0lp(kNKP7K*qpO z=(WLW8xx23AXK0x_$ZYCvfge5a$w)vgWZGGSP#PZtU#y&h`?6hr(PHRGf#oq8wq&5 zvRCqXNHXeK0O`o#Z~xPGfo}dqzziM@m_GJq(8TOeb7j{~gPyoh0jLQt`rJygQ3Pd{ zo*_#q_*Gu)|3z84YZ`FGPwVrnJr3A~D7_B;QH=0|EMQz*C)Pf?1Qgb>m6xy~69zF?c-7!t0l$(u zZS=l&5-7_A{JL|A^>NaTMgz3qA8F%9EW4R@JVH2?Fwq zOX>le0yG>1I$ogcvmk3bzV4$fIA6SYN-3cb(z^8d08H2^ag0e|aC+7R@S%r){?_@{ z6}L|mvx@`=M=tf}gVhJrc)4sRwcUs>Z;gRVrU2`h0TAUm`PqU*GS2U(55qgW&Vq~! zghQasjTHP0T5a`R?CX3eHOBrcE?Hd-k%7j?!NhAgW`52Kz#uP9(T`~dO z@$kjL{N|Q*MC#Qp+zEhyTi3gVhT(h9nQHydDMCHHY${kHLJ~yDs`V7u3hb zJ)ezW$$a&T8U{30fj# zkmne{mL>vmydbNilGo=Q^3gQ=b=w*E3l~d7m{gsU2<2^KOCL!@$U?VkoCc=W1x#U>8`goA-oJCR>vJ8^rt;wUgK1ownPAb_t-BiOcuLupKo4%TfFAKi~6Ag+KAg#}EB2!198 z?;5bbo^A(3$D2qJ>0^F8BuEnM5=S9CttY*%CgQJ2LDqE~`oDcEimQ*Ic-eQLTsVTV zIaeRzvDWQn#}H@o)*kW4o#fK(~d@K>m*LKQN6XfR8=#?(1e7`;SjG_8w*6 zC*VX>0CDOri*xm(IKEG00XPA_pCANZP6Z7)7$}4gVg~Y{kEj7sBLJZ;RtNAMz}RR( zBH%;5;tm9I*mQ6YokjV@kD&KwABJ6M0J8;(t6zb}%f1z~|5Yfbc7t3mcFGD^qZ;Uc z;%`CMykpBZ$Nl4-=%4E*om|%!7{JG0wvbGi36xv?I>4q1APxgEK~{mU>P6i#P_h20 z?C0Z3LAiH{#aASvt$#{r@%Y-U7^i9)NC3-U`yUsk82b}4l|H!@Qe&}A92@AWs1v8}saaFK#zVq-I%(S0IcXIFO8)FSt|MdNtg{w+j z!;^cql0-d43z~qm0$7&dke8$;!+tsNYsp1j-x?C(o1lGm;pWWx3kj%w6ZZtF9Mby6 zsXo;bz^6|Bo0}F|FFrm|EG-f_I4V8F6!7DWzI0jp?^*>|GANlWh#-Etni$0EDzgKV zslOcMr$|5m^5TU*k~gF~7DWI4-IwndJ?S*-xn zwPtv0uxrxF_T_7>L|vNS($(3SDItK5o&4n^bB)W7FSK5KJy1aA?`xTlFqB7>MCGHz z&y+zHBZwAW#pn}vLO&NkieLy#Ch^#ia`Kne0hFr{)&od^o?CeZ^dwMNGh!eS|6mg9 z{PQFq%xnLXe}~qK|2uT-%+MR-?tVX9$0bA}_DusTJo~Vfe$F1|Nm)zd6bBj)VeQk}kKTi>` z6xR!IbwE&!5?JE{f>I{{z|a1ZQ3Uqe5lMnnTtJQ!EIiLFBreUoK+!dj*Mf0pNFx5# zb($ceixA5uf0gwanh(`pxa%>r9{y>x4o;!@vR9%wb{)#OE6{1~Lf`pwY*~lZyFOh7 zYo;hDD1a@y0Q@wNssll_FtF?MVG>dW_?k$n`2_fiSbVl>{Y7y=fm?4^B%*_Wkm~_e zf2?E-|G}wo&*^`4?PBv~$7dRcj~U~wek#ccVF1~KlC5|0;7%IwOGg3_%SfHk!fUs| zmk$9#M3RUBMpDOMmjFRs1k#}39`M+F2nzC^VnO6DCF=y(iHney9To9s@Jk0&`xAi8 zC)ct0#2V=DKMMNYkD#@C0(3BgFA2>dGQBk+Yp)OD>6`{}w;(8uzseTr1ZshS!Gr*AmXG;<*8 zLn8c{{|+$7_@Nao8#1O0ib%-V4|pWQUUv5unGXc)%$uoXEw2Ux($5(Ea;Ah2GmDMeNwG8 z{$;9LNczam0zNlpS;{e!JPUqDrt+9wu|DkY!shio}NCstl# z{0~lzo?C-K2bMe$Skr<*fdc-sQzbfZ)PsPXC+;wSg(z4(AOIeb9I`nBmcry9SbwVw zPY`lSJ&+IvOy&mUBLm@B!C~-cCXWJBTnE3LJo1~o7!+bX^xu>)(#|LyE(2~Pf;=x9 zEBWeh-(VsEyk1-V=qIr_F$MdQ*P%0W6djsEudKh>F$h>ww*q^DQ{lfF;L|AJXM_U2 zKEtg)=Vh~M52-dKM8MZj4yP5sm;wf+M(KJ0y57*k0H%OJ0gDDabSw;-Fos|aP`WOh z=|#Y)AR&2yFY5%5wf;YEH3C+S9f+SFRE5kPAhljSEeJjiDE_iSwZ4$3Xx2E)!+_vlS5=aC85|La${-#6 zdjbglEkQ24Er|2gZ6w<{@4%l7b0DYkL}7pO>-Ar9z8%4VzxI?X{R-d#PgY%l)v{g+3MJQ&bv9ML6_gyIbe0>*)@Sy|WiHDC|2U}R!qJXg$wy-eP zz!vs|EsAAR*kx;r<%fUo)*VL(jltkY=m}2UY==H|K(q-e2|yqOQ^2AbhVmv*pao+} zkSPJ`!l5TpUym?wOd+}fRzi8XMVI&f$*e=plPCmvF^7rjfZY@p0&#X<3IG8-pZHMl z`&Q}eq&vP?zsz!7GWhvcsQRjPUkv!wHQ?6PfuFaA^|5R`f8G!+ZcUph-B;J05C}|$cyzHO7_k=0T zvbA>E7`y!7Z~lvKdO~POpA9MY;gkq!iGm#Z)K{P}Fs6aRG+<2=Ad3Q%urvXO9>|n1 z=modI178Ay9MnS`>f>QNjvWv|1tDYJD}E1pKiSAH`}zp-PyiD}-8%s4BK~;?+!9zb z;A-*!R6c?g>N*RQBp=D6@(~a2^$aL=ulRr(n01n0TjvV+8C0Mc8-Y3&(qqZ_hkCf3 z3iWDP6M^n{HNe;HQ`apeDF5GSJApe6eJ372{OvH-q9`n^Eno@@Q&?DQ{IVwi|A16s zJu#R-2o|>)YjMrb|IUe`**HN&%huTC&;DOO_E-&uUeKu_7E3xWb9JQD2!5yzo0TC? z2%tXt(&+;spb2Z5Fs23LEE;AK4qX`Z;7lJ5T>*0H>aQq*?6ch|SaX05$_s*OR7C8=0|{3u9Q@gCzC#hi-)E%Y@!f(iWPOW%q!dtVM)c&l%0p@45- zm=H9;dD(vFb2lhqsHJCW6Uka8hrcz{D+SkW0qmevN3k~6Su$p1xE-C z0W)7xZS_gr-~i~v$|R)0Ozxx}F6#t1C3>z8LL2O>TX;ffm=>C7p`?Czt8f=3bz#vD zq`=P%5aNSI6ebYqNkEjk4hU_RAuxnau0T->em)0(p=#y7BE4woRUR2YN8|-|3arCF z+dZhhc#Z-f$>-qIz^oyKq-|BQNrA813RRy1vI2f${rMWpwv>45tbd1q5AJ;(9z1eA zFfolrp}}qf0Tc@K;aB+A&RJs;ix0yDU;+ssyhX_fDR{dS`kygJjWM^t;1&S*C;#i8 zJ!%Y=jWNrBS-$&UzwLQX2o334l@2aFKazoT95Af8NK^spCVT6}9mvT#Q5DXiBt;1D zgiu(o5SnHZ4m~(mqNFZL>cAQYXM7BShx_=w032A1F9bo{&JCA{e`s#@@J~Vzh}Q$j zwc$Jign?VlhmvB_B0%f&x{lEie^oc>d}@u8Jn4J})CBw)80F4Gor_*hfu9nK27a}E zeZA{QLD$8%<#WXxKK#;~u)6C?FY|4b`X&bX1oYN=D<9WI>&~DDFB=Vb6Gp0)8bG>E zmSK*1GBCHm0N?p*AM}JkScb7D?)+D8xew2?6z2gjNR|Fd0Gk8CXH`c}t^Tlj2IWB| z`skroZiqrCjE^4_riH>zp>$msB9ycV3WQLi>utS^5&)hY_&Wn?K9B->B5f}s;vcV> zKu`#9FjzD4P<2pJp8}E23%1kx5-??XvGgiG5d3xERA3asL|%V-3~4h|o4L2eE+PBq z{V8i>+&^BsGer2SL*I#iJbD9cVbNfj&sJY60B!Yy)Hg*0x0d+g1_-dN+76koqCaI1 zBx(TSf*R>>M*|t$0szazvKl zAs#*jd}8VA!CC<>lL}ukr2ujjA^D`fNFH1LY&S8a27pOheW|Yw{8D{dpU;PR7};?L zkxK-6UT-87sN>{h7fC|b1$=z=3Vis=??Jh6Fhutw@J(3uWDtgwZ3Mh!i>@rbv3}TD z6oAzLkAHn0aTY7P0ij(&05Xw{*F(ztoLK~LbNJ!9fA^Ow04)Pp2C#hRuYBVbLPKt) z?yL-04fa9cuLCnt27Z|Iq5uFO07*naRQ??Jy0c{4$YmWj-R96kw;w=b;0eLB(6Ccz z**WxG2hNq~Q5(*6VN4$|{)3`ElCHpC_YsewVCBoC@(Nt3a>()w8V#U$A>#GG;NiX$ ztW?P-#3a|S4wz8+!SzXAKRH?9)R>X5x z`FLG3u&Vsvi2oMyd3{0{IeFBr*XI?_p8yn-lUUrl2eh_YbBvs%(qlQ*E@>mhVNIeq zt50tuB5XRshi9(9JuiPd+{`ZT#lr!4{*-IzY;!?`#RXCTmZK4@B{%9L7w~h z3CLwXUK8h}NGU}EngG&OG-&(Roe<0y&ipP#@I!UVJmSxvrcyU6%$U&t7SLuSP|Zi+ zR!PF8;IF9z1~@KHoD24@lk1X9@nA`uKn`&sP+q!5?ao%b4c@==;uf@NU)PRj)xj4)>K< zH%QgbdAV+WhYODI!9CaDi&wuzhW7L%9vf!vvBkH>0utD>{+$OZ!VeDcDZq0XASeMK zl@Zwzh`cOcY0Q-q3(79Hd&|G zx@xTzIZ#qd3dEW`a?obP!1c=Ug>X_rk#0@O`h)z}fb!#$L#NyTbps(7pkXIa*a^_? zMYQ+q!^*4M_;T5SJ-dpTubjiu<83TGzJbZ}eQ;Ny*h<|u-Zvux0G}-8apx=FfwetXrgC4C zfR_FS$q=6bD}pSL7bo&W3tkECMF|hX);{&|dA%>|{~3487WX1{VCOJCc-uE*ObR(fb!|D3&mm9JbxTqJ_z zdZpySQxPJl#B?fvXTYrikOpq*LO|uy`uGH5o~rfnD+l?Uxc6uhlXl5QzC{C$6Wjf@ zlJKD`uE$r8z5z5bjYd(ZC-&-w^pFni*&O7lpYPGERR9Fg;@gK-8)M}Cvqp{Qg%wc+ z$g)4@_&U1`m;&Z!M*xdUf_(`8j>s-^Azdc?7d(3Hwj4;qWtE9NHkv@RxeIz0pJpSYuv;pRY|0+|+s|M4(9})ThDD z^#Qz3eSP%`FV@EuXz3n_ijS3%fL@l>im*byLxhh{T!y<|{SNe(4#|WbqyHK$`GYTC zWRZL!04q{m&)Q4<`YgB?thai7Z4C)%$YiS9P=ypKz#e4Lw?G1gF_>>yEHny~{Stj5 zIOn#J04g^*f+m&NNVkX1QWx=u?nnSY*(^Y@)`-W_WWyF)I`uH%^ROX2z`;O5cFeT> zQ3WV6$bOCp{r);OX-(oA&0-48Vj5;{0w-x5Uz=~>-j_Df=sWB=+sEZAB@Ul0abVS9 z(hPY87Y04xlYy1bs6qr;eL@(T2#~Dj`k?BAl#7HB2{^qTIpIW-$&L%Y-a>ck*gpRy zwD5sL-+{9)`)*ik(JZ{Qx60qogM0#dJ&P|HzqbPVWWtV~sFMf%z{7X`t0{@!A5!`j zYqee#irw8nUPB$%1QMuWf+50Ss<7DGY5+v&^-BT1qis|G{$_1`j=u(vQ{^;ih#V$D z-Ed!}o&@-@V@Dux8&e`8-9DGAi3n;J_UGdY9Z6XYz^ub3KcL-R12uugnMIsGe;!36 zbhbu;XAU)R_Aqdt0UABRo-kN5sB4x2AL(&x+It}k?S1Z5KLDEB?d87A+vkUM3N^$RBKk!b(yIl& z3i^S4b>lxd0RWbt6jg)(D4*U$l(Im8r-Q`cQx^Oq=#mP@14tmt4N3)oJyT6g!=Ud< zlqDs==lpi0M%n=&zyVUQ>5jvo<((oJ>+-Ra38X6^g|cXr95W90MM6mkBtK?ePh<_X z%~i`fO)&mgA&w|*6c>UBrZ58jXkem0Ns|Vr4^QIs;Yobn07chf>FEv*p6+4)=^pm1 zlxP_f+lL6;Bu_d&0IXR)R+AG!3eH@K4XzvE6YG%>xXsYVi7i*akH`8PHSyuA--7d3 zyv{$hXDw_a0^b&1!drecS5MnjLwsW4WnR8f(|9eQfXnxXRG^Ltg7F)DeSd+pLqHnH zfiSAj^MFq=UoE+npx{iyV((-FR62CaG6B5gx7P(wjF0q&rPex-1Bky9Da*CMQ~7}K z8o=@e*+RrW(uuO_VV#cnXW-9|=}1tWZHEwB;xdTxQ0pDyymMpN^^}_|eL`JmH(~J1 z<&!vb`6TWKq0x2Nb*hW~Py65Qb0r#l7Z}7sgf(F0inRr<%h%PBS!JrJFReQ~PeJk6 zmE%cWfLa3(aJnSiv3NDU^6Ga2lQU=*{8A1-ZPzAp-&Ev2jCTzrq7MsW2ng$}miY{H z6};nVyCl(mL=s38zlpg3Rv`gI$#3|24YHOHY(yjEVx(!zWTY^d^%Z$ z>$3(Z3X@oVzOC^)hLqlC1L+X`34>FYPvF$$Eu09~boWV52A7@cV*jb0w?hMgn-W(& z5eQSKdG3^|4yf~u_g!|5_c4@})zU6yLc%dRMEHcA!`;W;iT=Kq_?L79z{h#}*|Yf} zk0i*4WGp`h^$_!b%;IC~4M05`rag9c6;d!nH=Wc9z#1#pA6!?31ZwVgD4UZ&zR%)B z6N}-aE!}>ZfV^}ufF1MB3$krUG76x!7I`Ai?0St=xYv3o~ z7f_~P7d25A@#p%7kboRR1Kz3i;pZi*skith?FI7Ya$?t!LSBIkz?uC`oW87q`(HT) za=_lFJGks*5BpDbaoN+J5b7*F__`wZk=6pDRlN>7}9R^4^_7i=F z4<3F4PQLt~!c5NiXZ2-Vp8;QE`SWSJHv01t15m?vB*T1B$}_e^<|C2-5i5cC^~nUU zL?K>4vJbdi?hXtffePm96u$u`tij%iCR)~@*YDT+`W4^bZL5iWtliwsf0`z))O?hKPfkL$3z$9>zMFGEwfYcy@Aw0dWg_C=l zxbKxya75TA3ZaJsr+V>T@iobbRRk`0Lg_Jn-t9 z(BJ>k@RANImY;!dlX?3wu;;Bm;v>4++A|rb;k+2lM+Cj70EE}d`6WUK5fA`a*^Tsx zz{m?_W!s=zyJZr{eFI?kL<38$0{wo8PPg1L=(l|oK!e$X3D6OLw&a%@@vnd|CmYp? zV_8KA$$nwE67i4qx~%lz(w5uTIW61SHyk~OknCJcY zJXm#^nlhSjPIMfWU;JG-_sZ`@Q50wt#y`1N6fhRqlY2&FzWDbBo&YRF1;B()%L~}G ze2>;YBAx?v;l@zVO$kq!nQ(CwAz zmmb_iae|L?t`hNgj>HY%KDZsJ5iMtp)nxGJRzm+x@~Jh}3gO}-{=mRF{yF$FAg5M8 z9M`F`7ZdSEYW?|U`RB?Zy~@9eEWNmbb)skiOtj7lis7%Ga+i@Fmsb}2$$brc?Fat{ zOj?Vji2_p(J%HB352AedAFy%H-RPZq68&~Nv_&0Jkz6$&J3ik}1M;yJT?VnKr%S>| zW{=>Z>wXxR+l`if5ohw~t{T(NAInwKcl{^Kf(n3@x%p|34|4JI>+anw3yojZ-0&JX zKA%7bQePxX6SO)LNG%EEdS8=(8X2(0VE04=i%pAO-(j;~1$pV55B${(`K1bA8%F_v z*zr?l=g!YXYhLM9140nti6>6u;KAKB)?3x1iGmbRRsD6~hjUrIH7b}_=U#OD*>enxlOz+f}O7c-xqPid`|`u(C#~I z^d0E%)v%Xejfw9zSiH4?=_ejR^NSClyzkRkzvpAl3Mw)&9mz6Bu`K%^u;#eMiv zs++^-ss&hx9?JQ}x&0(0KqTlNNCHtEWyt`u4U7GgO%Mp1Js#KR$UZ6)eCqNX_}lgv zKqAKzKqKjZ3Gi#cFLeM|dEx+e+pF*okKTi)E<8*aq>FBF(~{41Dy_(9P9pGNzhyU}^-DU|K? zNGJ?!eIBx28wcQtKH)>VUWSw3{v$9`GiX{Hqx|~)d~)~8TfdDHfnuji!41lTezDWlfnNjfpS=1_c-<@Ch_~Vg008GMoW)n4dIa}8 z@KJp6@lRrY7h(TpyRd)%BD8xDJ6Q()aMG6=@z-_M62XXwzjKh|`|`>#1;5_k^JulT z9H3A}pl%|Vq>Lt^gwT-vOj4fvDRDiJ%P1Coy+>HHgiH48@A0tNmb;oDGB57xu-=2uSD3qWA#3}}< z$iY1?^Vv3$n79v(8kalyOB>gXfUondUvR_{U==V#sdnIV;5bU=d zMC0{0V*Xzin0ew6%zfzr;KZlV{?wi5o;r!%#+ne*Lmh_?AAKWMuYC*5)QtH0$?%?u z!+bK4N57y$1^0~20k493P~UW5OAZD;&)GNX%7s5Buz`)&%~u;g8>g=ggBFM&PgFnv zctQdyRFJ1e;pP5x0d#ua>X)Iv4JLpL_!{(jIoWr6CIZN7!NvL_1;2Dg1%3fn0{qyK zN2O+?h0FIJ#pU~t;>I_=9e`H}kDq)5pZUUFc;Jy`OwE zRbH9I+%K3|^RLjXMl_y#tN?MGi)?q~kKuub@5PCS?!u|{FW~UuCG6QVhi0=#)Jm=V zhhE1;8PsYSNY0}!;xBop&o=-)Tq?i9FGP#$*Rg`}MOfQtvW3Jqc$xSO#+vvtM91sS zZP4J4>LZ?Gby?kN-yR3_DJX>20?fC(5sg=VCw5H~cxO>yb7K={o>{|Fr_bYSPn|{A zQS!XLRd3^(MTcIBw_X230DzU%v-rYS?!!NR^*(&^!9U01t|qR! zYF}u3j3cD&g(5aeBL19r;wmU&$idG9$)v(v(8O_mg~SkyLU`e*ZWjOnI?2aL<$XI+ zJX-pM2$c1YWg&G7c_Sw{AQA+{ma1zc_f*yAh|nj(X20~Y4*<=@SzNVe0k6L1DB2qx zoIAgP(<>V|y|Rj4Ny#g@lDG1(|E>rj0^Nq|mXd((Mj`YHLU?#LSZ}!)WIYKmQh7f@ zWC{gzo-x435a)nNzywz=>&i?5)&RRF3M@?&DEkiUopOM$?`r+{TK~Rg9jR?X4%7{J!fpz7~rAUA+q{X>ic*C_f;0@Q_fLq>r8~*t3eh+8yS4o?ONBmP@G0+)A z{vuQr1G8?w?4e)wKxEOdlaS#(5TJuW0%S#yvN`~rkE^dYD-##@Nr4?|=eDWZHTaY5 zvSYp;RW(4;`h;u%l30r)pdTde8S=h(b7l%hmuB(u=0WtkU97HkaOOfAE9;x+^aE=s z0`N#H_;ZcmDY`n~$JW=V%V_}rCF#&mW5Q$A;Mscs!YAb9o(e&SV-v-B032kL11QE( z@<6P=)(_2}o#0|P$IS-e%LGSO#q2B{Gx+Mr5 zSQs>m2KMh=z>5y=g6lb~b$VFY=wM}|i~qm9FORb9D(n57TeqsZyQ;gZx;p93Kn9W! zAefFAG$@!xoWS?uywv4W`?;3$6cBhKEbT0wpyCkLdQasc(?WfyU=lz;7h(b=gcp${ zBoN48I^7w1uCDQp=X-ygv-kde-`?jARn?iiyVvcy=j?sH{Y}5$-e;e4&uMk?68PmJ zo|)>~g*~rhxluzggK##gyZr^l;$L1vhP*s|4@g$)dIWYpkN5W$!08CpF3W)3o9ozG zA48{;W2M>dyTadV%CATN975Oni#jBU!9B`;y`>J3XvN1qW| zgl!Q7*)(dWz^_Jk63qShcJ_GJJOO@7UZGE*D>*t{V5QZ8ESEEcu?#cQn{oEsG^CSb zx!uJ=vx9|33(ZbRpvb(Q;SAh6*S{HB8C{@M%^CnH7ZPj8BsZhHpY)d(8r+tX?-a<) z<-uaTXY2SF&YY;DRuil?+vRorYS2e3|B4850<{A@%AB*a0F=a_2d#}L_0)>L12kuW z20kUY`SA~rI;Xp2$mo$?2>tgUg2LIsks^X_-a)I~M7!HcfS$q6318ZmIiDf;%Z{W@ zr}D-hKZ57w>b@ZlJwl5IDqwY>5RnQ%Od#+?5c;p`!?(>v%OJUYdJvAWWDMlU(QN~( ztqzVY6?mGc)v$GZ3}XiSWN=;u?)K=#xXr!L#xxl%4#> z6aZN5bg?yjA3rPxaWJh*+!=v^}EUQcz3&QVl6=8_9?Kd zX}8yLX<4`dEEn<%{dX zPG1M8kci=OU!iBG+eWM1K)c&Qx7$r7u0CJ33C=(a`?9P&jq=mw^5bvyEfb9OftKM1 zQ{Q*s6u{`{t6MJeRh{3bZ}D{lQq|pj5$Mf=U;%!8Lz}-{eFKVmEO^y- zq+a5o0<5pUXYO3BU&(-NyOyxJ`BvQY@1MjIhfe~XX;OvzSSQ9ZOxA1I zJy}P6^Eg(zIhMP{?aj?j7buqefElO!?lwhIUJ(P(Qm)r2 zlEpD}vKfqYY>yTH2>h;1iZq}>AAz6q$mI^=_POL(=?a#cg5yi8$WX&XJ;Ubu7|xm) z$N1(+EO!N~-5iUp9F2CT$RYO&L6LGHu(&c%SebbrxO0XKz@}OaXHM2JSx3)eqtB&wl_%k1nE)?RehVFU7-;K7hINi{pxgyat+`Rdl)? zeeRzaX@uA{MGUFq7*#&)RRWVg9H?oAz@SqgHejh7eSLeDAAGIq!3_fu0HMDylPSLs zx#ACRhqfj3hYJ0j-(2n_NKFw*h((SZD^0<2qYcO^1cFWVIyTkEuxql8iLDb@=?GTx z97~-XjaE0DB+$(Td=pR&8RjNxn5ox`QGd0GPB*N^Cs+D=f!<8xQ|I++nxC5X6WIN> zZ_9+gzT$5M_EGEkNUylAh@gI?7v*H{QeK$#AW*>;ohT4&-8zZs>2Zh%mgRO#o!LaI z-9e+Xigu?}0p2QL2g2)nT%M{6r0Id57dG*)6XE*^J_#U20Fd*UAUZ|?o^=;3-wRbh zBM=%}RsXq>p>)vTX7EO%xFrwv83_Em&Y078Ns0jRCtR$1w(Awbd#|{TS?(*YRzeUQ>VCpPc%a zF;JKL%Jl_L1nh$iD0QDiQg)<9_K;8{=^PkwEAN=^iDAn0Utaj~&8p(IHt zVb)(Z_A!vX9U$N)`tjR&XzSAtVfIOng$y$|&cj@=(rja?A&|ue0AR9S!=`!-vy*k~ z-ZF_sSI`77E`rTjacASHW~+EmM+InmUh;Eo2VL2BVD>o=KggtSP%G*^d2A>FIA<%c zgbWM73%AI^wI^b5TDo(SffN*kOL2rL0bgbxN?EQw6OXYo42KBLeUQFBt>;TLT4K&KqA>j43K>o4?UAm=`)P9k7ZawsR$G=7*a3vAUP8Omh(<&e@BtU`G^+To-ZzM>(6>u^QOEj3R*6^vF48FmCkPOlBtm@;@GC-z z^#=$P9&~iPA{K6=fzI_#A8T`HPq~At_Wi-{^vp@`0CZ7x(M-X`s|2gPwx9fEo)~cV zTjZnf(P*!}^J#$Sd@j%Zi~XP(FZp2uO5OFS!Cw8??V{i6*LFkr=dpc?i)AiocN=K5 zm(c306xZv4IMM9i$~J2w3$0XdvVihWZyf;I2E05Y&VbwyLy87=X~mD*ys&s8jue^raR5 zKnXFpT}2F1+n@E7+wBqPd(n1jq6vOVulTEeh)DR{+WVJt+K^pXyvzOTw!-KiZtT$` zJYuk0Qb^S41wE*CcE1{M%^HA+-sc0i&Qj+NoQItf{-G7(!xaTL_1bPY5cDaVCZ_R= z-}lE5$+6s6z{7{Xg0CL@Jnnz|vyl1{CMJQgx<{2taG&Tp57$o?G>TTr^2tlUM$n6~ zOuNest&1GPliCIAPD4&=OP^|32p4ZRr&&$hwj7OhC z?*ENp0RLhTFk`OcwcR2^oC5fgVQSNMTz39TaoPDV1@Kz5yG+Dll}_>)PVSyh}0;nbl1cafTv|DGN@Sb46~;2yIa2ohEi0%)JFvR%_Qb`p~I_A}OJOA_#&=cOwmopmd9* zAkwWMNS7!L5(<*i9fBeupp-O7rx_$?svMRd^c4o+RM*EpG1Y44Ac?ipSm(Yiax4YJ4d2PJn76zj)! zFZLyE&x;^+1fl1~{fxIwFEMJL1zQnfNbR6mnCfsn)ZJ1LsvO~oeRU~F!`t2XZ0;59 ztJJ7w&KC`#8n}~na|-8#UjC9YSGPN~<5ExN3|FM<9*!jzYvp@YqG)bT=xIy$JfCnq z+clYdNdHsUm1llQ^X26#%B?Rk8SQKFab?l=8nW za@+8d2BU%(wz>5HA&Q+I7r(*Q&v$lUh`%=2>;axa$CDhRbXXS34#c&uZNL;c1`zrZXz9kHVm z{_GV|2_1~?MOvh)n?k6-Ob5-H^XgFLY>+oz!tm)h~hO@o| zMG~94!6$zvD{`kZ*EG^iEjZMMuU%jc^z~h-Um|V9Q*GV6xBmDMnG=pNZdvde}NPN%b8&wK#DJ zTk1pLX5PlB1cN++)=qTH&2{~^n-96d zyBXe(ElNC$(PJz`Z%a8$Xj_onh!H+|aU*y4jKM9p{&!tLUi5z5bZHNcd?=PGs_#z! ztgNima>6^mFt*#?+Eyjh=P>GAXWzN*v&cGg0Vy}{$azoV@GFEEW39Dg;yq)y`)i}T zfdd1dR=b>xh~t1YydRCSwe;hN^AUf3Gk8LPb2WsV~s$1r;u35J*rp zlhord5h$Bn5a}*FZAwUcYqMSaeG1mc?RUyUhV}aICpqrcLXx1H>prH8S@y+51txNp zJcEfoe9U@n^Hp3CYNHm>cf1S0+Q{?{!;_#rWJ>FPBK}f~%)>|rt{B&u9)0nh;17*IcrdLK zEQA8@(K!=`bM~pWYVkZH=sGZ~9$&I;{l4ikz{MXx`2Lf$m;!F?TJ2Ui*5@%=IwBi? z>(`Zz3^|NSD99ppt~_i@Fd)5w)B(pL)r?vc_D4Doc(!ueuBAa zYqmVvCd*;e7=I*bx9`{k!8t-P)K0=^%or4Ej0m0lshyB=6$WqYWYeAt(*MB zWk>ZQ&rEK4JBt;`En>XBjArfN4ED%4sKxjqWzOyK?c>$w$|Ib4ygmVosyUV`4& zI|qH@2XmYaIK5Am2EH_6EqLaS^Km-Oht=1KSGIVhF+u(ST{)Sh(3)=C?uo%mABfI`K*C%s?*fl&=_ zt9ZBCdsK4JJ>oX5b~UWha5Oqn(qVHNKXVAuW{fQyRqy(FI`-bB!X7@Kca9he6`q&d zA2pWyU+=s;eEK227=Lb9mVbXrHp*)3o;Pn2TX;`==#G{09*(HGS0_pAWwzb3Boe zdXj?`eUH;ws!iWJ^Oz=rXeU^wC{QC;#K#tEn08N@Z|kj15l3ch75F;r;A^(ssuu=0M*< zEAP!1v&XoK8!608eC2WlMscGS<0Wj<`@Ah*NOg(ZmeoI|MqOc&jCvAcO)2?pu+47& zUIQ?2Erj*+{MFhNhJ5A1OU!rz99(z)CbVLo5xCqU?xGs-{ z`caYcbvf)f6tBMtAg{ zvBaH@9#gv$`)ZT7?k~|VJP~Z@gk_UNb9B1})_CZK@d0&CxL*4|R$HA%4zZ^;y^`Fv z32n+(0_@i6)l9RzxNKZFywXQnCN0;oCud}`P<36Dl7S-qEv@{*%UzP4FKE`N&BV|S zA4QOoy$|>jjBgrnU&Uh)6VKy9k8a;%at7-MzR%^h6CZyM z3xm!b2S5ZjnWP zRDB1{`+#z>;jlMQO3OD+oO(vTaTc{pmC;wT@#$GK#kkblsqar`d_^_t71+>E3Y_gz2~|y3t~W*e3RR z;P38UE5Oq)XnpXRpSn^1lJntfT7mIty!xFWy9=&7{*Rx2@;qBcxWgJ5lZEnQo8+&G z5>qP4$FxuOyn=cmz>Z~ftL@glAGOjo$%huIk+cSs32>wpq0`Fm%#;R?p(v58okns4|qFVqg2+=r(`xLR5Aru!S$=Ys0)$Y8Yl zDxIIz*U6A?@Eyu}bMr#@LD7Si&HJwBxM_FPhndL}LKG>h z<}%%eK6;cYXxHj9HBjO37H-gEz7T!yTmAI|ZxU}n^3gl;iBAfTcNqOYiyD0RSm1fd z3$Kj89^XDgh}5A4sr(nTV>kHw{lt}>xf;~yGrOA+?a`RYVNTy7t2`5aMpf=Uj{8G_ z=NCz|@axBkFmv!maHW)UF|DuyH69s^yct4b_mL=7-sTwD;YX>Kh08FnAG^B>Ds<5*LFeA>*-#cy zQ;}(tnx_eNwx8+*oMcpPtGrm}B(cVs3fg=>U67>Si>F}rkn5Mw!|_?E(>GmbCBqd) zcP%&FTM~x;?$#;(RMXw<(@SX`H|Nd@C3Q39RAsLU zlva*1l%QS7z4evI^Ul)y2n3Br?6t?0{O9i6Tjr5(%{-w3+w!n9qR$KMq$Z+PxDxFeIRA&_X`>9fNoQHA!LfH(FXWY+; zG+Glf5gWO4--y8U!f|0%;mguFc$zu0piY3Nj(@IZImQs@mq1pcau0{l(P^56BaF&d zr|Dm5VUN=mW{9?=9$4h7AEnfdYqa$$YwRZ!yM0<13BxHQFpk#34rKUoWulxTFZpzN zPidCi}$rGkl$?PinIh(-uY?rtu@57$WJMjv zMc2HAFe9k16H6XSi49O9JUHPteaFBsa^dDNhl6^!*A z$XjSLqOe|v;cdkgJv+1#w?6o0`?TTF%@5>?+dj`Z@)JDWEXZAr)ig7TX|LYbXKR} z%(9LBozaKI8oOJU zretA=_aetesGUW$ZX1-eZp{r3WP$6?E9WrtKI?q-Yk5NR^`Y)$lpbdzqY$-9?mM=w zg%3qJ4g><&Au8%Y4}S6U*FB$IO4uk|zJuJ`LDvw>Tx-rD^CGI{RsPl1;=Rv!URci0 zbJ#pfv1;v$6Xuvj%Dj`Nvc)3zX`Tm_r#wD*L=al0bLvpQK@iZ5Lw6NcC{Op{otz-^7fQ7^PDbz$;`qf7_ub zBx>X>RiN8N{oxR{CX*VG%SY?+Zgj1Y`z?vrUmV`#kHHMG#revvM2dEE$xWL6dq7A; z$Kv=G;(NWCi=WmrDcrc}t)gmZ9F=J7kJjgiuxl699P<}h9@$p;({t|L=5m`^w)jS% zGcT~3iXXmSYbYi}p}i^a&Pz1H^8F@!YAWcfa*Xru{&3>01ig9gk!hMIa zr2WftYKgxZ0_jeXK3mxGw|I2MMI~)J~^Uut|tG9LW>oqWM@WM`4x`Y{+}}=Ti;zeNdIaP4#x|*}5QZo@%{) ze)%U)p`+T}{I`9#H`H(#;^q0dQmDi*ycIt3e!R!@z?%$+<<4(<8Z6}Kzd1d>U9gc- zc6gf3!$FaonVx%GIKnCj?_H@LrI=8%Ur?*Lj-Nwtjq*<Wsq;zg@PRPC%4@MSIp2*S@jUs)LDNM?!OnYF zRCiR^FP)MkWHGxRey7=w#HzGj#I%b48HKt3McY&R_c3ldUngerEsl-)prWBF;QB&Z zXfylbML#;8r)-fK&B@}L2T{%G`pJ3%EvS#JicH2uqTTk}#$wqXOtt&r8B_zR^$+cp z_8+8vX`HmAz7+RTOUv5O@?pUxFwy>%;2qu+{s+_~41 z`1p0`kvKKR+1kY6`>FdxuGjC|3lf|y#WRV+%H0ULpVh!D!#wNWEi2u+W9*n;bTtj{ z(nvZJ@ssr^W-iW+kLNUPFoNoc=x;J=VzPK|;p8g2z8ZLjNjG`UAWws%Xil2z>*3}_ zg5+jqZGHoivJ6L6Bdi5GC;dU~Y+j!F1-hLksqU2@d=)*-M(H6oE0g*(ycPFpc7#3g zXO{=KR7rKOm0`8M)J>MWcIbDgvB&xCIz?_~MUFskC#>%GE{ln*amhQ88D?u~&<4D~ zJtSICc;3m_^n;>A?`BaG(;1xhF>m#s^BJTpCeBnA=S*@iS0gsD3#d#Q?AP@LwjFbK zcdUuV%Dj@(f)fi~3Q)ec&h)tQC3+;y^q$wPP{t3nT=p*$*-vx4o18eK!1sPgqMs>V z@ZGJS^@XNhigjIO!nS2MEw|4{QD3r)WOT2*w2+&ht;-}IF60wC<(0J5WK`A&wMml~ zKZ;1Y*C>}X?R&eWHX9|odfCwybg)^Rf1TMt!*vyvMxKq0ii^zOa23<`i;#Zpi2-_I za%D#*6Jo})Z^!F`L_CbBRxxoeTu8{H|H|`dieDpE^k%y|cMOd%SNba= zj2hP`VPY~8-Owa?{yI(G9_}qQ%V|fA4SCv~)fj!R9R5ZA`_A}Sgt$hS7_R8nwMBR7{?}}8{6qK0BK#5(!^<(6I5@L$_I93we#yhT*648< zqL>tPbWMk?vDmKfB%ji7RGGx7SYQWG8M4eURO&O!oVpb|wHdms)WO9@j8U(X>%N^W zMC4(4h#ww*k4zN6qL|j6n|xk4Dou_dd)u^ z^$efFrvs~narot4Ax}9fr&(@LJ5|q}&w~J+jO6U$sD*6h3}TxK`_%YH)7;WZfsYB? ziN%=VsL;Z@r|9ZtpZVMNB3MIY>TNx6yj5#3yPx_2rxmXMqC8-V$fH)XOf}BJofk&YpKYsFHXFN(ru?k; zXE_?}yy>D5W{-%eS(>$)-7RdwE>zE7P28)V< zP*>n_7sg$S^gm`et9v(>GKn!il3j2nx4J{;`(xz6!z=-E3p<7j>CJa{f~hQtmjcFr zq!^IdsZU?qor}kgAB@BAra$B}CvwxFOSCq6k}@-MeYzJ-VNZ^Uz_(L?4)@mdB@sP4 z4{RO#E+ttTw4lY^0CH;QwcQJv_pJN*y!}YK@qQXiXQ<=dDaq;Q`hHMf=rg*(=n~pr zJDE25I*A+AoL&~5?C3@MzPMCy*s9h-3_n6%ic~L@`8DI^#JU5s-Lau;ZxyG9%2gBv zQsg*6PrJzVZ3BJGG~aJT8HSjooZhRUu3NSw8DycF}a*rrIz_QgmW%bPSmFKFmg z&xd6Pq6fK_qS<=2)?u}7b2s>FDIEmM3}5q))p7!Pp2dO+~Qr zw*J^5|9q^9f#$_pg*40pm*KLksR~c`v{0ghW?rN2ICNphBA>C{adL)4cC_d9xEOb_ z`W&n1FWFpTTH7o2TUmWxaerw*C) z+@$=Rjqb($QqzxM-Tf*Gb;d|uO8k~Sro|1x-OoNQIvz*cKBNnc@$%K{b?z9WdtW#( zmP0qf7F&Hq=ZuCK=9*C^$=m3n5;TKdn=MNi=ffOpmS4%(tZGK#Hk=N6yWe@Sj_z<| z)UG;iexZnpcx`wK|Lk~Id8^*~!mrw_Ls8d#UDOKt+G&(?ufEq$TC{C^ll&A$e?2jQ zQ~ArZ+f1}ET>XmYTJ(2hRtAudac^QP9r1Nvkw`oGPKbB}?54#YMQ_U8T1xY%R>~MR z$GwJUI~0sN`tw(S{nv^(WB2;UF7EC8z0OlN;-+>)w@n?Xu5L)nd2$N4ULF)+##m81;&PG{wFib&>EYxI$3?OWOG*(u7EAI?#1X8PS@IX>uhgu7>VN0#U% zjm!$(N&E9wuYM9%|4g|3!-b4N4(@wy3S^3;(Aa(9Smo_d`Sd1SzKFS%*Vx8}RdYBC zyZ6!~OH$$V@T~|J)PCLOPQcg~4($9r(PaDhH{PkG9x}`e6bA{<31v)nx7#}#^Q)(q zQa7fYueUz1-Q`lI(G?pFF;_gtmow;t+@q@K5@#H#)NIn9Zd=#!22;#2@zTqpg!(I7(e^&0_F_c8 z@7P*CU3>YshEK$l0#iq?7)@r9LGxfsm310@C?Lqwb%}M=vaeq9)z%T=eJ0)>YCm2hT8fkG zY)89V`)kpHEjRo?WPHc7aJl4YRI!KBXww{3<;0(Te!u;_Lv4txmt2CrA+_r(#=-1) zo=G>OhwB?2-#_h5)^n0}&_BM8#%o@jg{#-wyH`7_e>iq$DZ+@ekltU1{W6OCbKvK( z-ir^54(o%1-;FJ7SA4NGazEp=@;ylAL7^=iSmrn;if$X?zB`;86Y=c5hI~Vhpe>1T z&qY)`S_&%CM#D;7(1JL`I=)%|pxgAtJECMBelGWUEex-t!2r9-UDCVlr>18lQn5w0 zctxIeT<>iY)Mca9?Pg33G(>T6J~Ce!2pSx``i#>1r~KEsr0hEwaoE>5HU{f#rUS~y zV!Lrwwp_8p(0_r<}JETVhu9EmwZOis8xhmOMg?1n*OAlg??B(wRIOeH~Krdymn}x5tiFr^<4> zipj=h2i_sAYQJxCR+aMTvr9kLhrC=-UI!!OhFsLUmQh!X+w9kGJq^w(MGg2S*BP0= z)^#Zh&UKemBij{s`X%T5%4p4vTA00>TvDiGed*%B(c${!7N&&FFKaR)Y%d`{xuMy~ zrI-ea%|^OU%T(y&y1L|IZx41iGDy9zuDTow2N8QN?C3eymiJ;s)KpyZM-y|W`gj4I zKfIi(p@KA$nK5j@X@7P1RcmD2_R}kbxTDPKxF414Wxk8N#0$i9voI0bp2MqN(V#pU zk0Cd3V2-#+ty^?AWQkB@hSlu*HPI#|v(@#w2@!|wpQUuY&ZXnlsxRzx=MzrTxwv7GUnA!*216IR(=VG=b~QPd z^fcS;)~i(?Md?- z^$hG%Z7ad?w)EEZ4`DoOQ5;_EdM-cwz51tQ$e~-!){HQGuCHEv$oo!9l*b)YTd6Zt zovzBnW-n>HlxXlGNw3C~1@mzl`VUudk8M}|U0*xt8>T^fz}ViF2Fm7& zX!(yVa$E8SJr z(U(W3mn@_4ON+gF(h~}%bEmo^Tf48x|qn8>jSlK6TqYq2O9E34QGuJG6RZ ztwme3r1_aM{h}GT6M|m9e2t#g`-?p@LQDEtTl<(~NNtQ$NN4FCP63qeoTu9B8C8`I zu~hv?aGC4MZ~*4!l#69_M&%~=tEl(k6%QZc>F<8`DBn+F(Q%%L-z{wKYL-63|6yaR z1OGdf7-r|VFQ(liq;%alm5sgNC7egDt1=x)b0{B&8lsP%`_$}j>MvE)JZo~{fU4jb z`mW^cP;n5->Q`T~a4zn|Lb6~NNh2Al8NQlWLoUH(85|sHL$AO@A*7nfTNN(pRB-bn zyK$jny0Bh`9%lmcLKTMUr|oBqcRp&B5sL=gelkFM1m?U?h=k8zj+@uYt z-QtPu&5s_T0@S>{l-TxjnNjy_T>396;`XD}mmPiPzY||1VZ{gu@_Ki=G6T)q{NzWcqyHxDUf zU1xc89KZ4Cl)ABY#?!V|O!;!+mi9deSH_uh3Rkj`NPm@nmGboa>)dy!lxG41qdTSC zjh4*9=OZ-FU=UAdlqR?f(_4`CWl$&$H}O2VNHgcExVY-TvC59VD-d)akoVPGL6?ge z9);{Zyv-O<`&b75aAy(hm3VIk=EoPBYP*hR8ctP|@ZL&QllK{0E*v+%Ac=pO-mct9 zb-9O8{8=!!4;u~t6O90siEd%vdpBL~nPsZdEIEdb@PpptS?O?r&ILw z&u_SPv5e8~8nd+zT-Oe-CTj%^YwM;(kQp*=wiPuSm^~0`+&d!O&L+`MK35@}iWQY; zepC3ahQ9b}3PCxC8Q}#?j`U#{n&sEYF>a%W9}RzIym^kBqEfLa89>IMvwdi;qnjUA zSEPx2Z?(A1}$$jv(UJ1N}F#365#X|su# ziLrB5?=9Z0C@|l{^H|s16=hBh`Z5;&^scB~Et(B>kw+rO!O?xu8XA)N>(z78!WL@| z>P_fkVUbOV!LK&6)Oamrn{V9+`jAB-Y(}Xj*zA1+b58EkeL-KLZp{>iJe5z6N#@rH zeb;Cw87t%L4urfP442g{A4*j(sX-oS2@G+iKxy=znW{;ar$qdUAie4YdoY|7alL{!m%XmV9-9 zi4>~qz$hx&J9AMoGT^>Q8m3YtW+0<$epkD7Zhn(tzmv;|l?;q93RTA!|%&NHzo%?i^6E7~o>^kaV z3jG&$EI#x+w4hCkS;OXxm&ZIqjcr7G(@}JwlGdNdns!GQTSl|J%sO+=?+O|p_L~RhxR}C2IT7WvY#8F^ z3;KSiY9nYPsB>*U>UF(u`(h(Qgv|MtvRB344%mkU?#dQQ z2FVrFBz9jE|ejg%z7~-u{b--ckc{gNFM+S2( zxw?8pp7Sz)j36>2P`+B`x~Yxj`J!wKpMIkNwSNxg%aS$}+?t|E(4K!% zoq1Asgrqj(aK_eo zgAS`q+*AscUX(hMmh9HGeN7Pey8jL2adjt*a49-2``*LR>)y^EQe{Br2ij6H>@Ft9 zB5eZ;BGWOwciDGWSBKd5K4jE5apseHn{}Yn1d}Fr_*gdKT;X~3uBaC=ix`uy1RXiMtDWEtiSLN9#rOwC3}AER5CO(qatAK5SIlP zwC%)$Ia`rsB=?&!H8hQT=(2Pe@B597fnv_%bDxmb^_t$jRs~|#e$gy9!^xjtR46g zBw}mVF!w{frf+m*|7B)x?Na#wjT`TM+2wo)}L3Wn&+KdK9}{C-n!;X z1_O?p65|T)me{BYN?#9InBYw!`*TH)oVtUnZuG8CuL%lt1H@hP2eKp~5~w6fGY z2?O8D$o4g47$5@;`HP(6#hdA5^?%%tXEFqIPbF~m{e&@-)2w)3L~4)`{U2xK-wcY^ zJx>>_O{E`s>s)e}3hCJMo2MaA8XumBVMCHB8$_y;jUmTX{`Oz~f8ES>mfU&TVH{c`^=DNasgiIA7h|KFpxvI+F1xuB!)C=u0Obrj|D3b!&7NO@D|0ba~H&> z`|m(Op~=ZXt;cUmXDET|U@*P_ z@p`lndzbozGvNqlGVHj2AJe7+Bt8&sks>}g4So{%;4Xx{X2XRM`2Q5c#fJ(U3|2C> zw5DSDH2MOol-k@tV;}&W4r*)Xjf8Cf*Xz|r%Rp7Q6qG-gfcJr-P;`vJix2WWd7vfJ z7QVH_zv3hM4#kOI{_=sBC@whjVS*7Gx*+m9k*_EE zua~PKrW~w+2DU6d+WE|Gw^kV+DZ>Rjd7!tH0$LLhK<55r&E=#4=aGE`Qyta48-ILT zA14PjQ8G{&E{R|ehxY*@Q1?<7zJG0k@zK$LjPE^F@XCk*Ug(}b;eECv-yd$pYEvUV zAV+*~1_I?zgD>I(Hz91W;KUF>ET={W-Z29Hx89;W87{WmiH`Z4aW<+1V3h`PsZ4{7U(VyhtASaXwP>CM3er^`J2arkZi_;>?4?T&ON25UfZ7Ph<$;*ayeIROMejki`Fo{o?O~ zL8veQ_;V2qneK2S^)c|KIDy*RKwu9uM=%(|6@MML?5PP%&T0q-MPRU&feZFGfyYZ1 z!Grh&@x^%~VW8IG1&Z68&{ysZUy2=|J;w~1Q}keMb@gP7biDtqNj`sR5~8b!CS}>5PKhlV0a3WCC{OLup9aY z`=RDr2V9GO0PLY=!1l}#Sp4q6B@YC{V^u)n8IEIu*H8aso8C$q@qyTJ9KdmG_gA?? zZ`ot`Qse;L$h9|r{yZ5UG9CVCQo-Lf>EXXMDc?&RUfZ%ks^w)QPawQ9pg+;msM{2X z56&WSK=cp$Wve1VvOE+-OM*bAEDR>b$4*AwKo@Yu+rhOcOF-fokaz|b-@9yrL#0P_Q-teu`4SGwRV4xudc6WA8#>X6IsC%IaPzce-m(bI4Z#0N-D`-}U>YlXU4IKfaB0NoN`kAx4t6UH2NU`h7k+{HJ;F#`4A2c4o=3_`Z9cY?FMDf zm7p=v2Jy`snCNama)dmRAB2${&5fK>fjl=p1P>QvAUsMz>W#l*IR??wV;}r|{#|;C z;eLA#s5iVs><|7EYk$SuU+dVGA~@Dnw&35IbnFYENbBGJIMG)`o2(ISLiBX}Q!tFS zJcZ!~-xCfkjP}6M(b0c7#=GjFB2483LxGP3)Wm2b91=tFHG;vB6Uo)ANGu~fI?fS@ zo{=0i}F?YNknVkgBid483V+HKt$C_kx;tP=&```NN zdFOXrBbr2SDGieyp)me22u2VLL-jt;{>Ba_dz#^YT_(RYA-Ta8s-tfsF`<0IoyG(c zq;`@=Fo;90D?d{AaUptyU_f%hOC*+$een1B_Z_H)V+@bK6oFCIOW4`oJ{e>4)8Jf@ z1ZsKD5M6bH8;GWIy|hO#Aet0rc9I{&;vIi$6B5_QF>#Wo5j~ymj)ck2p)hugq4~)P zhGR`?f9njjaSsqpb%6SK187Y*fuyT!}hUgI!Qftut!~THbdhi{n0{6aBFs*zEd%u32jP{W}(5ZU`>J`x-Q}7G~ko?A- z{OGqPA-c*LZ3PmE&cJ@GO~*0er3FkbNPa-H^+#_kf*}II5CUUu&)|EL|35UTyYL}& zZsUh>11`9&LiB zL$z=png7GX!~b%89PR<-q7aZrFi5`k0+EErAeV&jDiZMp;)AQl+UkvHE0Px$1``kr zFJPu88oo70LqbFhBn4VQb(|qo#oR?|Ic;c4z6&jBci}^t7JNuOwp|^X5|p7K9;uyT z<)Ai39zLWa`8(AH%0q8M(Gvxv?huD8q`vz*|6``xA$+10f<_wQLB*^8n?|htT!+^F zPVjr53>xK8pj7xAfm|PHC=R*_oZ}@9SL1n1qNgs4v zgV)4pAUVSlN&?mYVSmzm55)iIgy^YH5IXh&o_%iszrlZNl6^-WSbod~la@5lt51YG zrHP=G@D5}jW&(@!YhdQBMaD=^c)3Ki)12LiR&s zki4z)5Bt-WzCz;s7kKfb1EMBBLdaMP1b+XwCb{*#2bZ1_@a!vt+mDCfrp^TL$PdGH z>3-ml=mR#9ZeS7U0HzZREkMiB2-GJSDo^H{guWQTkPo7YAAy49H!c!}A_PMokWl)9 z8V@go>o7yS1yZxwBR#SU59E6CBXtFW<*5jQMf9YXM*8Vhq#w;k#y|SKtknTXTkM0x zxo(J?`3&*XpW($+JG`3u4ADqz2OwD72P(j#I}b$fO@ffdIPfcu0*~Afa2;b1?*&%j zF1Rf48JKw5fPw26LjzE;*1(P1pW*DqQXr-;0x^|OC&$PyAwIbH9*F4*fRO49;F2W( z4V4bqNHRd!Jw|w8#tJF6*C6xpjj?y$0{Mmh!nP#=q8xvZ*}rcWZ;V0d`WO_geut8k z?~u9B2k|o<@N}#ZJcg>k@#}lA`jQVO9XTLwI1M7&2nO|W;8Prd8^;)=z5%;fFK{aK z0=sNCFy3ebkE9X!{B;DXyT{?Ty`>I zL!d6`Hobn7;bpqN+xWlRuHBp-tJ;`=qLpDtL3nhmN5SLG;Pbr}T>8twuD1{@kOz7U zKV^cH`412`Krm=c!mWpsz^^(AH)IFlP2TY#`S0f(W*UidmixnABlG?WiQ{7}eL7MP z9)p$O*jEBp-S5EUb2jLi8)&S~(&o+q#0sn!tCc2Asc@ zg6)?Au;_RL1|MI8_J>$3>bcGG`qY!QPoc$PZ(XaBgzHF6t*sNC_BV-4Lu{-sHE zAgkE}7rB3H)W75VJ&+)AaEdwy@j>eUC;R`x;WJ$0<eyQh54v)}q&*9CTGK$Y zDG?sUuY%r_AAf4>WBWV4Eu8HCwa%%WhKQ{3lVc5?vxu&?03D)9@>+d=;`=;165m&9 z5MGrddV1`GTts7E|F_tGjKjP;m(IHDU7h*o9586h0PSWZ_agm)OY%Ax1^)O`V=LKx z|FeC^e&}R-c3}+?5dIw3-Hjb{e{5E``}HI?E^@a5{k5hOUY+PEV=0i(BR-(X`ERrT zFJB;>x7BG$S-stu04jBHU7oKuz$9oIc6N{&8yT*VLnrIf##xZjnS}0d%P0HaCs$9_ zji2Y?GJpGtCMoF*0E^(?^TcnBCBO79AJFFhx7&Y=1L=>6kpA-IKBsT)CYU1j@9iNq zHZn?TXHM2W(W9XA@CSH=e*bgs_KW`HyprALo($&#ViXbH@G7Qn!H4oYigVP$m} znm;arqt926P#*vRg+9=?LUNw`R|LZs1VaZ>Gact@L{Hi4kyx&Rb4;cGar;AScflfT z@*g#JNWv)CMXZ2j;1Za6FTgFt&fE7V;FjqWDCv!X?49o*t~LM`PE#PRJ_rIg`+!Tb z8{twrF!TK8(eIkC;y-SGWYaIOjF^HC?Thf@^*Fdije%R@GC00i1>3M?utc7RG4Y%S zy+^a4Z8ZZL#*?6^I|eeE!(i?>4T?G=Aa!d11QmPXx>Of};S=JA4~QN$05xm%f874q zwmpbw`~@LZI}lj14Sso>;F-ArE=gzXP$=+Yny14W4f|!8&mrOd?l7 zH)sVkk$OYTbpaF++vSkjN!ok{l&yY%fx{G-J{$ujoqmv1??Gas131Oo{^Rz)9z24Q z?}v~-Z~%E<_aUWy4?=2o!KY{&oO3q84$05vv1?!uvI5%vOQ7Mt0LqW%;ikEv7Sd(nAPqPJlGh51Boh z0u%dTxT)RqAGiNF7bJG=tE6`9jm0(pjsf3-ZE(r_33do?ETUGyAZQu1eUZ6Gc%$qv z2l7@3Z_Ivx1k(G6>Hgbxp<5FteUP9kQiB^zf`#MA|A_s^7+$sR5k=PSh8=5==Nn}1 zQ#QaV4w-vIe{`QLfhNKm)kkxnU^5G{$GkB@c%z5#=I$hj+?fF3zu7LJG6n)lV<2&B zyzcng|C7t(tW8mujGtY0uaLP%a+m>^!}tl#6EGw*xma?p$!<1S_}W{k-Qp-3(zH0k1!bkl{#B}z%?iLP`NF_k1l7o$Xs zlqaHOQsgdOB+50KVR9M2^II!V?b*XYalKzu)(7S(DD+ zyh7Lf>c4!vuR4cmirbo&KDU(So}7Evx&|Yc8|h;muDg!1t^HrMgIBflrpJUP@5eib zRnxyU*S=~B@wrJ&jWZ={uAb4v&vd-u>m`69wI;O=vMXP;cYbriRgU<-s^)mb_7=}W zY$~N))OjyH-+xi_Y~(_38oN%lc_)Plv@?Kgq!TW`ssknKYR*{dMKHoQZdw^8)hhm&y_ME_VT zei5AMZ3(tP__%%Oe+u79cyHsv=1Rt-o1>m&VzzIdjWfA1ctl^aG%Es$!XpnHE_o!u zkO-bCk^G48Mg(^u*tQt!Zse&t$T?8;ZQTsxnmE?tk=ZjHQQGvnq(Zm=8r}NqJ z$ z9pBSm%!_wyiTTgxCF4rbKKXfXh!yV(eU3p3*=wfBS}=YI&OH%h%Q<+-tM;V7Yg4cV zBQgSTxcn3v8t$RsN|~%JxB8)9h=aTq-)!`5z8>UD#P1;^!wYBj&PNWtUe)%?PYo#9 z8%ytJ92qN9&b=XhTW7L*BgifYul5Vcwyn47(sKU3^f!gaY&&L4mi=b(`Ni|aWtH4tOAErWjSB7C;W7GKk! zH{b5hxtJ;M3-O!G%3g_UC6TBWJ}{XG;bWKW^}xC805Uc!x$lG0-JaxymeSw$rblFo zj1{9f25pX^Z_D|I)1M10KZH4C9mIJHpY&|?C0WPi?}pAs59n@MjA<#s7$DfIKwJ8` z^u(1x^rPwJo{T_ENj%OS48fTn0@>Fb6sJ0HPUwGbHluGlk^ZMS{pV%$JQvbCH{=+G zw)8K21>sFCq>ocbHm9NSKJv~KAs{0TMzLPfTQ*Gch1K>TXs?-r0jnMHxu-Kc12)3m zF%7-?r%|U)#E3!G*yPUVF`178QO5Kxjiq;~TyG?qyPPOP6s5SKFljLkedp5B|3>;t z!sA;^PrxlV0ZxTmVSP9rZUsBw_2VuqIJzB%ajWsQix>J%j=`t8ne_5K53-!@OW}xuvM$v1e-$-^;aEsZI<2g2)mi~oL7gt}3ZFObX zR8x%gl?7OLC0}}TGdAZzkFm7yQu@=A6Pl&)6oudL(I>m{VUOKt)nS|TMMq31kp5Mh zE<5n~Cl*kc_o0XQmGG-Tzv_kOHd1(Q(r;7!{`NOk;{biC9kJ@a>+AqDQk>3=dJ$+2!0k-Otf;Jzo z#F&0s2%r9G)uwrx2`S#%wizpRG*tbn>Y_*WRfigD=rNUH4f|e6f60U1mNs5lL5YwWA?RG6seao#g5i#@_S5+RtQhJK>Pp4xh{RxXr$6%|d!cD&4Y?o9btKhoa zPd<1Jk=rWeez(B~rDkkkQ6k47Jgl~24(Ofy$4BwM+rMf&{*`%pD=+7i3V%d+JJ$3@ zoYM>78eT)s;~o~hsKtJ5-5o@3t&+#P4#=SnUx;z^afEg){LOaMOy3uC&|&Lq_pchq zw%#ymTeieFq?A?iZJnfY(Dm> zmQw21lO4;co0iCHA-wXA^bf=wsM`7?b%e4>5wP_}i!MD&aT)fCn=oNtlO0b(pE}{- zv1QZ@%V9_DaIj$+$54!(1CL1`sXgc5kMyhh4mC|hz|OnyU0aU1t1Ibq-G|%SdvKwi zEPRP+)RIl?sWOJ3 zPq6Jcby4Lna8GQ6!L2LA%(>|5gpW7b0 z{wwxU*Ni&c1V7G!$F`r~LQULe)m>ODrv~fRtYHcrO>hwU-S) zt~P9y&N$fh&o;~aAx*|ut|6A)mElvmG_E#o2fwlESTU+KlH7)iyeDOlLF?DP7K|VM zT{R{V*Nfbe4Gz5+OMc1tdJ^_WS|Z-54}y%^Ld5eTw~AVVh@%y%@4Z};H`QZ|qF}t9 z&fJ3r#%v8;>~-XME29nB{s?Sw)54Y02T@a9Ez7>}Y19nbW9Kph$>oO`x5Wzmcf~c8 z8yQ^%V$*OTsRB=cf~9udU6DpmUfVi%wX8OIUfbJ4|RjLb=p{5#|SapChISbhdxk$Eu`#NgQ96>y}&bWEHWXbxF^VF2_tjGZn%&^cCRP77? zarX8Q@-4UFyd@Z4driS-%&idYuW_8W%ykhQc6z!exmai9#<;hu?ku7jbi{hH+XB03_8?ww=GSY%=owU_g<`9U!GWYpNHa9PZOr*at zcKS^s$H&}}1p{$#jRj+%xzq^sIA5B~0r<3~{l&$*;JzagPoF-8Q(_>@Bi!+cn=!s* zp3CIqK*`dIzV_t?kU`x-hBg+J$JdgZ4abGtVDjWXjP(}dRHhI4(uKUvquIxRmiGP6 zXJcu~I(g3be|~`S`m6Y2*;MF}^|ne1!oYCGcK+5#NDD`FL;|d)M`7748|;r@zD$e- z^5aD;$CxU59Ex`?VqEKq-1SZ^?XRgk2KVeun3=p9Cf^6aAaNPA!)9ab+933sIUk>! zN2B}D47oNG?BF|Go9m1_g#LYP5b7`pX^fZl1`C$4na#|OFl78@-O_%1T`9sV@)3Og zAd)IiU{ggQjF+CmP~!@+5Er=KC`31Ma36F{fu%<&+H~9oJ>%mtCi<{*0LBj-h7C?% zP#YPD^c6aSrBsqRRH&|R4q3N?##&_Exr}YsOEJGJ14Cw?z<6iIKZa)!yrmlPd-z^+ zDV920+peh?HK|y}fr1%)=i@kh+1*STW!qa%)#l4}(w*}ru{TaOh#13@@r^SXqLH4( zxOKB0Ha=HSRZ}nbO&6Y&bvwp)29rxAb19fO!D7AK-mCT_uNJ5)E@nrNQGyNQ2g|5a zXl#6drRy)r`f}A>WF5UO_dAl28b9rfWR3)H^g;LZ-)&#jr_+fYdQ*y^N3$t!pEsU(swQw+f z$+|3Dew8tCvE)TMk`;N~c2)mp5pevLD*s}P!$nvuxB)Xq#uB#Y$rYc)EDy4bU*zSd!$vL_l~;;yYQPZ6xZx%nREdDDOUri{G8o39n= zdn@;qKf|umPmoy9gs2}L!FgQ+rgCj2)`enCx**^t*Tpp$V}AOL=jY_D=O)Q#o;F0$ zkM^M#EqrL3sCYuH}Wl zD!f&Cd#d$hDgDTZ*PZuQ{omZ^20i+SRi^Y1=^vKrTVIQy?dvBChmHIjKm5UsIpA%=#;aR} zO;itH-ui2uW5Jryx*02mB(JyY-WX%kiR+vXnYTQIxu)$9IHr~F%R^z#Vvink#?I|z zrJc<@%bfRT4EeGo#=vWpv;Qp4{}ow6tOGF#?k>dAapztQiV(Aw%%+-cb) z)ctf1ZS97l^GrQhZ&aWo*DT(lmiXyfA&&2G!xrYLCAklx*5@R16TYMN9%9_K$WfV4 z)H}>hO2+#y6hX_w0`^J4=*oOQ-xw!UQqTSM*<(DsSBtHSbWuWGdf#dj=DYR6+Nm9i zmX+=)8kg#i#>c;4MxsB)t)7D^(VqC!Z#q_|EWm?P3Y4W;GVj94KO-H2nUe#noRz`T)h zIGQk(TCP9Wa<1nSZkGM%R+5hS1>3Qb{I$uvV=$RP-KUolL&qG)yPfu--Dib(uYEWy zhW0_K#}~}?))Bb^jd^i4!3B@%b{^w=5@?lLJoPx$#^Ud`x%$2p5XBl;MBQ?7&IymGh8lG zmpROL3iXfoQN9c#|d8B(L4=t0R2xWc}0cuRiiCY@!*{hTcJR`p*bR zXh29}1IIvyeaw&4lk)!Q_j9j3e|(V7;2nicn91jtiRVq|&!i3zavK4WH(+31;{U2~ zzkF;})}yHT-`&Sl#`A(J89|<8m}zx`u4#3$h12yBFaIy{)zqHTr_X!&;6Lv3@#IUd NK9A-8OQnhX{{?OVn?wKr literal 0 HcmV?d00001 diff --git a/PFHook/res/PFHook.rc2 b/PFHook/res/PFHook.rc2 new file mode 100644 index 0000000000000000000000000000000000000000..64fd5a771b638431a7c243c36351a131f41b51d1 GIT binary patch literal 668 zcmezWPoF`bftP^`Na`~vFa$8TF?cZK17S9U9zzg=GlLOOR2PVS88R7?8H#{1#Xy|K zPy!SS1Bw+hlrkhT+*- z>T_nYZa#LmL55^zc4wY>=ggdU|NJhgq_31vkz&-ttaPFVgqc3- z8N4AgzC}+L$tbIz=u#urm}p8Jpr=NOmvbXjpE})59jQg{HDiiOS^KKEy=0NQu-cHC zMBqeST5a}U#7p0m;l9cxS0VS*huDX1y&C)`ajR3O?*@!YnZ9M3t>neSXjS&!*0DRq z4#bEjc5>Uab-HJl3)ywFHqjmH4Qq6$kK8GA?^waJ^P_FLQ+5N*cAEBNbA?JyOb!Zl zw@_^{FK^9WkHKco>wdlyJLU!_w=h|6legO6g?+36s{3$5B9ZV{DcNn$eo?4w^5`KuFh6VEK`*+YS!^Fk?>70-@X;}LqI$$_xa4~@UPgbt F{};Wz?x_F( literal 0 HcmV?d00001 diff --git a/PFHook/stdafx.cpp b/PFHook/stdafx.cpp new file mode 100644 index 0000000..4e69168 --- /dev/null +++ b/PFHook/stdafx.cpp @@ -0,0 +1,8 @@ + +// stdafx.cpp : ֻ׼ļԴļ +// PFHook.pch ΪԤͷ +// stdafx.obj ԤϢ + +#include "stdafx.h" + + diff --git a/PFHook/stdafx.h b/PFHook/stdafx.h new file mode 100644 index 0000000..7d8a96a --- /dev/null +++ b/PFHook/stdafx.h @@ -0,0 +1,54 @@ + +// stdafx.h : ׼ϵͳļİļ +// Ǿʹõĵ +// ضĿİļ + +#pragma once + +#ifndef VC_EXTRALEAN +#define VC_EXTRALEAN // Windows ͷųʹõ +#endif + +#include "targetver.h" + +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // ijЩ CString 캯ʽ + +// ر MFC ijЩɷĺԵľϢ +#define _AFX_ALL_WARNINGS + +#include // MFC ͱ׼ +#include // MFC չ + + +#include // MFC Զ + + + +#ifndef _AFX_NO_OLE_SUPPORT +#include // MFC Internet Explorer 4 ؼ֧ +#endif +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC Windows ؼ֧ +#endif // _AFX_NO_AFXCMN_SUPPORT + +#include // Ϳؼ MFC ֧ + + + + + + + + + +#ifdef _UNICODE +#if defined _M_IX86 +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +#elif defined _M_X64 +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +#else +#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif +#endif + + diff --git a/PFHook/targetver.h b/PFHook/targetver.h new file mode 100644 index 0000000..416cebf --- /dev/null +++ b/PFHook/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// SDKDDKVer.h õ߰汾 Windows ƽ̨ + +// ҪΪǰ Windows ƽ̨Ӧó WinSDKVer.h +// _WIN32_WINNT ΪҪֵ֧ƽ̨Ȼٰ SDKDDKVer.h + +#include diff --git a/PFHook_sys/Debug/PFHook_sys.inf b/PFHook_sys/Debug/PFHook_sys.inf new file mode 100644 index 0000000..92b0d95 --- /dev/null +++ b/PFHook_sys/Debug/PFHook_sys.inf @@ -0,0 +1,93 @@ +; +; PFHook_sys.inf +; + +[Version] +Signature="$WINDOWS NT$" +Class=Sample ; TODO: edit Class +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} ; TODO: edit ClassGuid +Provider=%ManufacturerName% +CatalogFile=PFHook_sys.cat +DriverVer=10/16/2016,12.30.32.690 + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 + +[SourceDisksNames] +1 = %DiskName%,,,"" + +[SourceDisksFiles] +PFHook_sys.sys = 1,, + +;***************************************** +; Install Section +;***************************************** + +[Manufacturer] +%ManufacturerName%=Standard,NTx86 + +[Standard.NTx86] +%PFHook_sys.DeviceDesc%=PFHook_sys_Device, Root\PFHook_sys ; TODO: edit hw-id + +[PFHook_sys_Device.NT] +CopyFiles=Drivers_Dir + +[Drivers_Dir] +PFHook_sys.sys + +;-------------- Service installation +[PFHook_sys_Device.NT.Services] +AddService = PFHook_sys,%SPSVCINST_ASSOCSERVICE%, PFHook_sys_Service_Inst + +; -------------- PFHook_sys driver install sections +[PFHook_sys_Service_Inst] +DisplayName = %PFHook_sys.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\PFHook_sys.sys + +; +;--- PFHook_sys_Device Coinstaller installation ------ +; + +[DestinationDirs] +PFHook_sys_Device_CoInstaller_CopyFiles = 11 + +[PFHook_sys_Device.NT.CoInstallers] +AddReg=PFHook_sys_Device_CoInstaller_AddReg +CopyFiles=PFHook_sys_Device_CoInstaller_CopyFiles + +[PFHook_sys_Device_CoInstaller_AddReg] +; + + +[PFHook_sys_Device_CoInstaller_CopyFiles] +; + + +[SourceDisksFiles] +; + + +[PFHook_sys_Device.NT.Wdf] +KmdfService = PFHook_sys, PFHook_sys_wdfsect +[PFHook_sys_wdfsect] +KmdfLibraryVersion = 1.15 + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +ManufacturerName="" ;TODO: Replace with your manufacturer name +ClassName="Samples" ; TODO: edit ClassName +DiskName = "PFHook_sys Installation Disk" +PFHook_sys.DeviceDesc = "PFHook_sys Device" +PFHook_sys.SVCDESC = "PFHook_sys Service" diff --git a/PFHook_sys/Debug/PFHook_sys.log b/PFHook_sys/Debug/PFHook_sys.log new file mode 100644 index 0000000..ac30311 --- /dev/null +++ b/PFHook_sys/Debug/PFHook_sys.log @@ -0,0 +1,47 @@ +C:\Users\Xiaobao\OneDrive\Code\VS2015\PFHook\PFHook_sys\PFHook_sys.vcxproj(113,5): warning MSB4011: 无法再次导入“C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\BuildCustomizations\masm.props”。可能已在“C:\Program Files (x86)\Windows Kits\10\build\WindowsDriver.Shared.props (463,3)”处导入过它。这很可能是生成创作错误。将忽略此后续导入。 +C:\Users\Xiaobao\OneDrive\Code\VS2015\PFHook\PFHook_sys\PFHook_sys.vcxproj(197,5): warning MSB4011: 无法再次导入“C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\BuildCustomizations\masm.targets”。可能已在“C:\Program Files (x86)\Windows Kits\10\build\WindowsDriver.common.targets (1832,3)”处导入过它。这很可能是生成创作错误。将忽略此后续导入。 + Building 'PFHook_sys' with toolset 'WindowsKernelModeDriver10.0' and the 'Universal' target platform. + Stamping Debug\PFHook_sys.inf [Version] section with DriverVer=10/16/2016,12.30.32.690 + Assembling base\vtasm.asm... +base\vtasm.asm(5): error A2013: .MODEL must precede this directive +base\vtasm.asm(6): error A2034: must be in segment block +base\vtasm.asm(7): error A2034: must be in segment block +base\vtasm.asm(9): error A2034: must be in segment block +base\vtasm.asm(10): error A2034: must be in segment block +base\vtasm.asm(11): error A2034: must be in segment block +base\vtasm.asm(12): error A2034: must be in segment block +base\vtasm.asm(13): error A2034: must be in segment block +base\vtasm.asm(14): error A2034: must be in segment block +base\vtasm.asm(15): error A2034: must be in segment block +base\vtasm.asm(16): error A2034: must be in segment block +base\vtasm.asm(17): error A2034: must be in segment block +base\vtasm.asm(18): error A2034: must be in segment block +base\vtasm.asm(19): error A2034: must be in segment block +base\vtasm.asm(20): error A2034: must be in segment block +base\vtasm.asm(21): error A2034: must be in segment block +base\vtasm.asm(22): error A2034: must be in segment block +base\vtasm.asm(23): error A2034: must be in segment block +base\vtasm.asm(24): error A2034: must be in segment block +base\vtasm.asm(25): error A2034: must be in segment block +base\vtasm.asm(27): error A2013: .MODEL must precede this directive +base\vtasm.asm(29): error A2034: must be in segment block : Asm_CPUID +base\vtasm.asm(30): error A2034: must be in segment block +base\vtasm.asm(31): error A2034: must be in segment block +base\vtasm.asm(32): error A2034: must be in segment block +base\vtasm.asm(33): error A2034: must be in segment block +base\vtasm.asm(35): error A2034: must be in segment block +base\vtasm.asm(36): error A2034: must be in segment block +base\vtasm.asm(37): error A2034: must be in segment block +base\vtasm.asm(38): error A2034: must be in segment block +base\vtasm.asm(39): error A2034: must be in segment block +base\vtasm.asm(40): error A2034: must be in segment block +base\vtasm.asm(41): error A2034: must be in segment block +base\vtasm.asm(42): error A2034: must be in segment block +base\vtasm.asm(43): error A2034: must be in segment block +base\vtasm.asm(45): error A2034: must be in segment block +base\vtasm.asm(46): error A2034: must be in segment block +base\vtasm.asm(47): error A2034: must be in segment block +base\vtasm.asm(48): error A2034: must be in segment block +base\vtasm.asm(49): error A2034: must be in segment block +base\vtasm.asm(50): fatal error A1010: unmatched block nesting : Asm_CPUID +C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\BuildCustomizations\masm.targets(50,5): error MSB3721: 命令“ml.exe /c /nologo /Zi /Fo"Debug\vtasm.obj" /W3 /errorReport:prompt /Tabase\vtasm.asm”已退出,返回代码为 1。 diff --git a/PFHook_sys/Debug/PFHook_sys.tlog/PFHook_sys.lastbuildstate b/PFHook_sys/Debug/PFHook_sys.tlog/PFHook_sys.lastbuildstate new file mode 100644 index 0000000..6e9547e --- /dev/null +++ b/PFHook_sys/Debug/PFHook_sys.tlog/PFHook_sys.lastbuildstate @@ -0,0 +1,2 @@ +#TargetFrameworkVersion=v4.5:PlatformToolSet=WindowsKernelModeDriver10.0:EnableManagedIncrementalBuild=false:VCToolArchitecture=Native32Bit:WindowsTargetPlatformVersion=10.0.14393.0 +Debug|Win32|C:\Users\Xiaobao\OneDrive\Code\VS2015\PFHook\| diff --git a/PFHook_sys/Debug/PFHook_sys.tlog/PFHook_sys.write.1u.tlog b/PFHook_sys/Debug/PFHook_sys.tlog/PFHook_sys.write.1u.tlog new file mode 100644 index 0000000000000000000000000000000000000000..191608783857b864adf3b011147419bbda2d5943 GIT binary patch literal 182 zcmZ9G%?g505QV>W(5r|BZQ5Rmb_r3mkRma{LN90uzI=M_&?4N-%$f5uGkz}=+~~NI z)AP2uCi?Xy3mhA9N7J;bIcF;I-ke@|Pa}Uyt9@@tPHbt@i32NQ64p{?Y}twPyk_XC VvYxd9GZKP%Kg=kj`1CM2#vAb*T}32J!-iUn!OsDgqR~2 QmCc?5`Q-0j{Tf&mf7EhxL)v^0|<9~m)Bsn=O-LL;Y E0q_$%#Q*>R literal 0 HcmV?d00001 diff --git a/PFHook_sys/Debug/PFHook_sys.tlog/stampinf.write.1.tlog b/PFHook_sys/Debug/PFHook_sys.tlog/stampinf.write.1.tlog new file mode 100644 index 0000000000000000000000000000000000000000..7ba76722cf160267bb515b5c4de93462dbbd7d3c GIT binary patch literal 304 zcmezWFOI>P!HOYLS6WBddXl>H$sm1M1~v;G(;GK>lz7 M`rjRgi;>+10PET=IRF3v literal 0 HcmV?d00001 diff --git a/PFHook_sys/Debug/PFHook_sys.tlog/unsuccessfulbuild b/PFHook_sys/Debug/PFHook_sys.tlog/unsuccessfulbuild new file mode 100644 index 0000000..e69de29 diff --git a/PFHook_sys/Driver.cpp b/PFHook_sys/Driver.cpp new file mode 100644 index 0000000..e1f47cb --- /dev/null +++ b/PFHook_sys/Driver.cpp @@ -0,0 +1,20 @@ +#include "header.h" +#include "vtsystem.h" +extern "C" NTSTATUS DriverEntry(_In_ struct _DRIVER_OBJECT *DriverObject, _In_ PUNICODE_STRING RegisterPath); +void DriverUnload(_DRIVER_OBJECT * DriverObject); + +extern "C" NTSTATUS DriverEntry(_In_ struct _DRIVER_OBJECT *DriverObject,_In_ PUNICODE_STRING RegisterPath) +{ + NTSTATUS status = STATUS_SUCCESS; + + DriverObject->DriverUnload = DriverUnload; + KdPrint(("Exit DriverEntry.\n")); + StartVirtualTechnology(); + return status; +} + +void DriverUnload(_DRIVER_OBJECT * DriverObject) +{ + KdPrint(("Exit DriverUnload.\n")); + StopVirtualTechnology(); +} \ No newline at end of file diff --git a/PFHook_sys/PFHook_sys.inf b/PFHook_sys/PFHook_sys.inf new file mode 100644 index 0000000..e144fd5 --- /dev/null +++ b/PFHook_sys/PFHook_sys.inf @@ -0,0 +1,90 @@ +; +; PFHook_sys.inf +; + +[Version] +Signature="$WINDOWS NT$" +Class=Sample ; TODO: edit Class +ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171} ; TODO: edit ClassGuid +Provider=%ManufacturerName% +CatalogFile=PFHook_sys.cat +DriverVer=3.6 + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[ClassInstall32] +Addreg=SampleClassReg + +[SampleClassReg] +HKR,,,0,%ClassName% +HKR,,Icon,,-5 + +[SourceDisksNames] +1 = %DiskName%,,,"" + +[SourceDisksFiles] +PFHook_sys.sys = 1,, + +;***************************************** +; Install Section +;***************************************** + +[Manufacturer] +%ManufacturerName%=Standard,NT$ARCH$ + +[Standard.NT$ARCH$] +%PFHook_sys.DeviceDesc%=PFHook_sys_Device, Root\PFHook_sys ; TODO: edit hw-id + +[PFHook_sys_Device.NT] +CopyFiles=Drivers_Dir + +[Drivers_Dir] +PFHook_sys.sys + +;-------------- Service installation +[PFHook_sys_Device.NT.Services] +AddService = PFHook_sys,%SPSVCINST_ASSOCSERVICE%, PFHook_sys_Service_Inst + +; -------------- PFHook_sys driver install sections +[PFHook_sys_Service_Inst] +DisplayName = %PFHook_sys.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\PFHook_sys.sys + +; +;--- PFHook_sys_Device Coinstaller installation ------ +; + +[DestinationDirs] +PFHook_sys_Device_CoInstaller_CopyFiles = 11 + +[PFHook_sys_Device.NT.CoInstallers] +AddReg=PFHook_sys_Device_CoInstaller_AddReg +CopyFiles=PFHook_sys_Device_CoInstaller_CopyFiles + +[PFHook_sys_Device_CoInstaller_AddReg] +HKR,,CoInstallers32,0x00010000, "WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCoInstaller" + +[PFHook_sys_Device_CoInstaller_CopyFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll + +[SourceDisksFiles] +WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll=1 ; make sure the number matches with SourceDisksNames + +[PFHook_sys_Device.NT.Wdf] +KmdfService = PFHook_sys, PFHook_sys_wdfsect +[PFHook_sys_wdfsect] +KmdfLibraryVersion = $KMDFVERSION$ + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +ManufacturerName="" ;TODO: Replace with your manufacturer name +ClassName="Samples" ; TODO: edit ClassName +DiskName = "PFHook_sys Installation Disk" +PFHook_sys.DeviceDesc = "PFHook_sys Device" +PFHook_sys.SVCDESC = "PFHook_sys Service" diff --git a/PFHook_sys/PFHook_sys.vcxproj b/PFHook_sys/PFHook_sys.vcxproj new file mode 100644 index 0000000..7704385 --- /dev/null +++ b/PFHook_sys/PFHook_sys.vcxproj @@ -0,0 +1,201 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + Debug + ARM + + + Release + ARM + + + Debug + ARM64 + + + Release + ARM64 + + + + {00AFD5A9-6927-417C-94DC-6C2E612352C9} + {1bc93793-694f-48fe-9372-81e2b05556fd} + v4.5 + 12.0 + Debug + Win32 + PFHook_sys + + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + + + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Desktop + + + Windows7 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Desktop + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + + + + + + + + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + true + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + + false + Level2 + base;$(IntDir);%(AdditionalIncludeDirectories) + + + $(OutDir)$(TargetName)$(TargetExt) + + + + + Level2 + + + + + false + Disabled + base;$(IntDir);%(AdditionalIncludeDirectories) + + + false + $(OutDir)$(TargetName)$(TargetExt) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PFHook_sys/PFHook_sys.vcxproj.filters b/PFHook_sys/PFHook_sys.vcxproj.filters new file mode 100644 index 0000000..0873a91 --- /dev/null +++ b/PFHook_sys/PFHook_sys.vcxproj.filters @@ -0,0 +1,69 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {8E41214B-6785-4CFE-B992-037D68949A14} + inf;inv;inx;mof;mc; + + + + + Driver Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + + \ No newline at end of file diff --git a/PFHook_sys/PFHook_sys.vcxproj.user b/PFHook_sys/PFHook_sys.vcxproj.user new file mode 100644 index 0000000..d9cd657 --- /dev/null +++ b/PFHook_sys/PFHook_sys.vcxproj.user @@ -0,0 +1,6 @@ + + + + WindowsLocalDebugger + + \ No newline at end of file diff --git a/PFHook_sys/base/common.cpp b/PFHook_sys/base/common.cpp new file mode 100644 index 0000000..a53ea83 --- /dev/null +++ b/PFHook_sys/base/common.cpp @@ -0,0 +1,117 @@ +#include "header.h" +#include "common.h" +#include "vtasm.h" +#include "vtsystem.h" + +NTSTATUS InitializeSegmentSelector( PSEGMENT_SELECTOR SegmentSelector, USHORT Selector, ULONG64 GdtBase ) +{ + PSEGMENT_DESCRIPTOR2 SegDesc; + + if( !SegmentSelector ) + { + return STATUS_INVALID_PARAMETER; + } + + // + // ѡӵT1 = 1ʾLDTе, ûʵ + // + if( Selector & 0x4 ) + { + + return STATUS_INVALID_PARAMETER; + } + + // + // GDTȡԭʼĶ + // + SegDesc = ( PSEGMENT_DESCRIPTOR2 )( ( PUCHAR ) GdtBase + ( Selector & ~0x7 ) ); + + // + // ѡ + // + SegmentSelector->sel = Selector; + + // + // λַ15-39λ 55-63λ + // + SegmentSelector->base = SegDesc->base0 | SegDesc->base1 << 16 | SegDesc->base2 << 24; + + // + // ޳0-15λ 47-51λ, ȡ + // + SegmentSelector->limit = SegDesc->limit0 | ( SegDesc->limit1attr1 & 0xf ) << 16; + + // + // 39-47 51-55 ע۲ȡ + // + SegmentSelector->attributes.UCHARs = SegDesc->attr0 | ( SegDesc->limit1attr1 & 0xf0 ) << 4; + + // + // жԵDTλ, жǷϵͳǴݶ + // + if( !( SegDesc->attr0 & LA_STANDARD ) ) + { + ULONG64 tmp; + + // + // ʾϵͳ, оΪ64λ׼İ, + // 32λλַֻ32λ. ѵ64λʲô? + // + tmp = ( *( PULONG64 )( ( PUCHAR ) SegDesc + 8 ) ); + + SegmentSelector->base = ( SegmentSelector->base & 0xffffffff ) | ( tmp << 32 ); + } + + // + // Ƕν޵λ, 1Ϊ4K. 0Ϊ1BYTE + // + if( SegmentSelector->attributes.fields.g ) + { + // + // λΪ1, ôͳ4K. ƶ12λ + // + SegmentSelector->limit = ( SegmentSelector->limit << 12 ) + 0xfff; + } + + return STATUS_SUCCESS; +} + +ULONG NTAPI VmxAdjustControls( + ULONG64 Ctl, + ULONG64 Msr + ) +{ + LARGE_INTEGER MsrValue; + + MsrValue.QuadPart = Asm_ReadMsr(Msr); + Ctl &= MsrValue.HighPart; /* bit == 0 in high word ==> must be zero */ + Ctl |= MsrValue.LowPart; /* bit == 1 in low word ==> must be one */ + return Ctl; +} + + +NTSTATUS FillGuestSelectorData(ULONG64 GdtBase, ULONG Segreg, USHORT + Selector) +{ + SEGMENT_SELECTOR SegmentSelector = { 0 }; + ULONG uAccessRights; + + InitializeSegmentSelector(&SegmentSelector, Selector, GdtBase); + uAccessRights = ((PUCHAR)& SegmentSelector.attributes)[0] + (((PUCHAR)& + SegmentSelector.attributes)[1] << 12); + + if (!Selector) + uAccessRights |= 0x10000; + + Vmx_VmWrite(GUEST_ES_SELECTOR + Segreg * 2, Selector & 0xFFF8); + Vmx_VmWrite(GUEST_ES_BASE + Segreg * 2, SegmentSelector.base); + Vmx_VmWrite(GUEST_ES_LIMIT + Segreg * 2, SegmentSelector.limit); + Vmx_VmWrite(GUEST_ES_AR_BYTES + Segreg * 2, uAccessRights); + + +// if ((Segreg == LDTR) || (Segreg == TR)) +// // don't setup for FS/GS - their bases are stored in MSR values +// Vmx_VmWrite(GUEST_ES_BASE + Segreg * 2, SegmentSelector.base); + + return STATUS_SUCCESS; +} \ No newline at end of file diff --git a/PFHook_sys/base/common.h b/PFHook_sys/base/common.h new file mode 100644 index 0000000..a86827e --- /dev/null +++ b/PFHook_sys/base/common.h @@ -0,0 +1,265 @@ +#pragma once + + +#define LA_ACCESSED 0x01 +#define LA_READABLE 0x02 // for code segments +#define LA_WRITABLE 0x02 // for data segments +#define LA_CONFORMING 0x04 // for code segments +#define LA_EXPANDDOWN 0x04 // for data segments +#define LA_CODE 0x08 +#define LA_STANDARD 0x10 +#define LA_DPL_0 0x00 +#define LA_DPL_1 0x20 +#define LA_DPL_2 0x40 +#define LA_DPL_3 0x60 +#define LA_PRESENT 0x80 + +#define LA_LDT64 0x02 +#define LA_ATSS64 0x09 +#define LA_BTSS64 0x0b +#define LA_CALLGATE64 0x0c +#define LA_INTGATE64 0x0e +#define LA_TRAPGATE64 0x0f + +#define HA_AVAILABLE 0x01 +#define HA_LONG 0x02 +#define HA_DB 0x04 +#define HA_GRANULARITY 0x08 + +#define P_PRESENT 0x01 +#define P_WRITABLE 0x02 +#define P_USERMODE 0x04 +#define P_WRITETHROUGH 0x08 +#define P_CACHE_DISABLED 0x10 +#define P_ACCESSED 0x20 +#define P_DIRTY 0x40 +#define P_LARGE 0x80 +#define P_GLOBAL 0x100 + +#define PML4_BASE 0xFFFFF6FB7DBED000 //windowsں˵ĸӦ +#define PDP_BASE 0xFFFFF6FB7DA00000 //#define PXE_BASE 0xFFFFF6FB7DBED000UI64 +#define PD_BASE 0xFFFFF6FB40000000 //#define PPE_BASE 0xFFFFF6FB7DA00000UI64 +#define PT_BASE 0xFFFFF68000000000 //#define PDE_BASE 0xFFFFF6FB40000000UI64 +//#define PTE_BASE 0xFFFFF68000000000UI64 + +#define ITL_TAG 'LTI' + +#define BP_GDT_LIMIT 0x6f +#define BP_IDT_LIMIT 0xfff +#define BP_TSS_LIMIT 0x68 // 0x67 min + +#define BP_GDT_LIMIT 0x6f +#define BP_IDT_LIMIT 0xfff +#define BP_TSS_LIMIT 0x68 // 0x67 min + + +#define TRAP_MTF 0 +#define TRAP_DEBUG 1 +#define TRAP_INT3 3 +#define TRAP_INTO 4 +#define TRAP_GP 13 +#define TRAP_PAGE_FAULT 14 +#define TRAP_INVALID_OP 6 + +/* +* Attribute for segment selector. This is a copy of bit 40:47 & 52:55 of the +* segment descriptor. +*/ +typedef union +{ + USHORT UCHARs; + struct + { + USHORT type:4; /* 0; Bit 40-43 */ + USHORT s:1; /* 4; Bit 44 */ + USHORT dpl:2; /* 5; Bit 45-46 */ + USHORT p:1; /* 7; Bit 47 */ + // gap! + USHORT avl:1; /* 8; Bit 52 */ + USHORT l:1; /* 9; Bit 53 */ + USHORT db:1; /* 10; Bit 54 */ + USHORT g:1; /* 11; Bit 55 */ + USHORT Gap:4; + } fields; +} SEGMENT_ATTRIBUTES; + +typedef struct _SEGMENT_SELECTOR +{ + USHORT sel; + SEGMENT_ATTRIBUTES attributes; + ULONG limit; + ULONG64 base; +} SEGMENT_SELECTOR, *PSEGMENT_SELECTOR; + +typedef struct _SEGMENT_DESCRIPTOR +{ + USHORT limit0; + USHORT base0; + UCHAR base1; + UCHAR attr0; + UCHAR limit1attr1; + UCHAR base2; +} SEGMENT_DESCRIPTOR, *PSEGMENT_DESCRIPTOR; + +typedef struct _TSS64 +{ + ULONG Reserved0; + PVOID RSP0; + PVOID RSP1; + PVOID RSP2; + ULONG64 Reserved1; + PVOID IST1; + PVOID IST2; + PVOID IST3; + PVOID IST4; + PVOID IST5; + PVOID IST6; + PVOID IST7; + ULONG64 Reserved2; + USHORT Reserved3; + USHORT IOMapBaseAddress; +} TSS64, +*PTSS64; + +typedef struct _SEG_DESCRIPTOR +{ + unsigned LimitLo :16; + unsigned BaseLo :16; + unsigned BaseMid :8; + unsigned Type :4; + unsigned System :1; + unsigned DPL :2; + unsigned Present :1; + unsigned LimitHi :4; + unsigned AVL :1; + unsigned L :1; + unsigned DB :1; + unsigned Gran :1; // Granularity + unsigned BaseHi :8; + +} SEG_DESCRIPTOR; + +enum SEGREGS +{ + ES = 0, + CS, + SS, + DS, + FS, + GS, + LDTR, + TR +}; + +typedef struct _DEBUG_DR6_ +{ + unsigned B0 : 1;//Dr0ϵ + unsigned B1 : 1;//Dr1ϵ + unsigned B2 : 1;//Dr2ϵ + unsigned B3 : 1;//Dr3ϵ + unsigned Reverted : 9; + unsigned BD : 1;//DEBUGĴ#DB쳣 + unsigned BS : 1;//е#DB쳣 + unsigned BT : 1;//TASK switch л#DB쳣 + unsigned Reverted2 : 16; +}DEBUG_DR6, *PDEBUG_DR6; + +typedef struct _DEBUG_DR7_ +{ + + unsigned L0 : 1; //0 DR0ϵ#DB + unsigned G0 : 1; //1 + unsigned L1 : 1; //2 DR1ϵ#DB + unsigned G1 : 1; //3 + unsigned L2 : 1; //4 DR2ϵ#DB + unsigned G2 : 1; //5 + unsigned L3 : 1; //6 DR3ϵ#DB + unsigned G3 : 1; //7 + unsigned LE : 1; //8 + unsigned GE : 1; //9 + unsigned reserved : 3; //001 //10-11-12 + unsigned GD : 1; //13...DEBUGĴʲ#DB쳣 + unsigned reserved2 : 2; //00 + unsigned RW0 : 2;//DR0 00Bִжϵ 01Bдϵ 10B IO/дϵ11B /дϵ + unsigned LEN0 : 2;//DR0ֽڳ 00Bһֽ 01B WORD 10B QWORD 11B DWORD + unsigned RW1 : 2;//DR1 + unsigned LEN1 : 2;//DR1ֽڳ + unsigned RW2 : 2;//DR2 + unsigned LEN2 : 2;//DR2ֽڳ + unsigned RW3 : 2;//DR3 + unsigned LEN3 : 2;//DR3ֽڳ + +}DEBUG_DR7, *PDEBUG_DR7; + +typedef struct _INTERRUPT_INJECT_INFO_FIELD{ + unsigned Vector : 8; + unsigned InterruptionType : 3; + unsigned DeliverErrorCode : 1; + unsigned Reserved : 19; + unsigned Valid : 1; +} INTERRUPT_INJECT_INFO_FIELD, *PINTERRUPT_INJECT_INFO_FIELD; + +typedef struct _INTERRUPT_IBILITY_INFO { + unsigned STI : 1; + unsigned MOV_SS : 1; + unsigned SMI : 1; + unsigned NMI : 1; + unsigned Reserved : 27; +} INTERRUPT_IBILITY_INFO, *PINTERRUPT_IBILITY_INFO; + + +#define DIVIDE_ERROR_EXCEPTION 0 +#define DEBUG_EXCEPTION 1 +#define NMI_INTERRUPT 2 +#define BREAKPOINT_EXCEPTION 3 +#define OVERFLOW_EXCEPTION 4 +#define BOUND_EXCEPTION 5 +#define INVALID_OPCODE_EXCEPTION 6 +#define DEVICE_NOT_AVAILABLE_EXCEPTION 7 +#define DOUBLE_FAULT_EXCEPTION 8 +#define COPROCESSOR_SEGMENT_OVERRUN 9 +#define INVALID_TSS_EXCEPTION 10 +#define SEGMENT_NOT_PRESENT 11 +#define STACK_FAULT_EXCEPTION 12 +#define GENERAL_PROTECTION_EXCEPTION 13 +#define PAGE_FAULT_EXCEPTION 14 +#define X87_FLOATING_POINT_ERROR 16 +#define ALIGNMENT_CHECK_EXCEPTION 17 +//#define MACHINE_CHECK_EXCEPTION 18 +#define SIMD_FLOATING_POINT_EXCEPTION 19 + +#define EXTERNAL_INTERRUPT 0 +#define HARDWARE_EXCEPTION 3 +#define SOFTWARE_INTERRUPT 4 +#define PRIVILEGED_SOFTWARE_EXCEPTION 5 +#define SOFTWARE_EXCEPTION 6 +#define OTHER_EVENT 7 + +typedef struct _INTERRUPT_INFO_FIELD { + unsigned Vector : 8; + unsigned InterruptionType : 3; + unsigned ErrorCodeValid : 1; + unsigned NMIUnblocking : 1; + unsigned Reserved : 18; + unsigned Valid : 1; +} INTERRUPT_INFO_FIELD, *PINTERRUPT_INFO_FIELD; + +typedef struct +{ + USHORT limit0; + USHORT base0; + UCHAR base1; + UCHAR attr0; + UCHAR limit1attr1; + UCHAR base2; +} SEGMENT_DESCRIPTOR2, *PSEGMENT_DESCRIPTOR2; + + +ULONG NTAPI VmxAdjustControls( + ULONG64 Ctl, + ULONG64 Msr + ); + +NTSTATUS FillGuestSelectorData(ULONG64 GdtBase, ULONG Segreg, USHORT Selector); + +NTSTATUS InitializeSegmentSelector( PSEGMENT_SELECTOR SegmentSelector, USHORT Selector, ULONG64 GdtBase ); \ No newline at end of file diff --git a/PFHook_sys/base/ept.cpp b/PFHook_sys/base/ept.cpp new file mode 100644 index 0000000..db49df0 --- /dev/null +++ b/PFHook_sys/base/ept.cpp @@ -0,0 +1,324 @@ +#include "header.h" +#include "vtsystem.h" +#include "ept.h" +#include "common.h" +#include "vtasm.h" + +#define NUM_PAGES 24 // 24GB内存 注意这里,必须在系统物理内存以下 +#define NUM_MAX_HOOK 256 + +extern VMX_CPU g_VMXCPU[128]; +extern GUEST_REGS g_GuestRegs[128]; +EptPml4Entry* g_pPml4T; +EptPdpteEntry* g_pPdpteTable; +EptPdeEntry* g_pPdeTable[NUM_PAGES]; +EptPteEntry* g_pPteTable[NUM_PAGES][512]; + +EPTHOOKITEM g_EptHookItems[NUM_MAX_HOOK]; +PEPTREWATCHINFO g_EptRewatch[128]; +int g_nEptHookCount = 0; + +EptPml4Entry* EptInitialization() +{ + EptPml4Entry* ept_PML4T; + PHYSICAL_ADDRESS FirstPtePA, FirstPdePA, FirstPdptePA; + ept_PML4T = (EptPml4Entry*)(ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'aa')); + RtlZeroMemory(ept_PML4T, PAGE_SIZE); + + EptPdpteEntry* ept_PDPTE = (EptPdpteEntry*)(ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'aa')); + RtlZeroMemory(ept_PDPTE, PAGE_SIZE); + FirstPdptePA = MmGetPhysicalAddress(ept_PDPTE); + + ept_PML4T->Read = 1; + ept_PML4T->Write = 1; + ept_PML4T->Execute = 1; + ept_PML4T->PhysAddr = FirstPdptePA.QuadPart >> 12; + g_pPml4T = ept_PML4T; + g_pPdpteTable = ept_PDPTE; + for (ULONG64 a = 0;a < NUM_PAGES;a++) + { + EptPdeEntry* ept_PDE = (EptPdeEntry*)(ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'aa')); // 普通模式 + RtlZeroMemory(ept_PDE, PAGE_SIZE); + FirstPdePA = MmGetPhysicalAddress(ept_PDE); + + ept_PDPTE->Read = 1; + ept_PDPTE->Write = 1; + ept_PDPTE->Execute = 1; + ept_PDPTE->PhysAddr = FirstPdePA.QuadPart >> 12; + ept_PDPTE++; + g_pPdeTable[a] = ept_PDE; + + for (int b = 0;b < 512;b++) + { + EptPteEntry* ept_PTE = (EptPteEntry*)(ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'aa')); // 普通模式 + g_pPteTable[a][b] = ept_PTE; + RtlZeroMemory(ept_PTE, PAGE_SIZE); + FirstPtePA = MmGetPhysicalAddress(ept_PTE); + + ept_PDE->PhysAddr = FirstPtePA.QuadPart >> 12; + ept_PDE->Read = 1; + ept_PDE->Write = 1; + ept_PDE->Execute = 1; + + ept_PDE++; + + for (int c = 0;c < 512;c++) + { + ept_PTE->PhysAddr = (a*(1 << 30) + b*(1 << 21) + c*(1 << 12)) >> 12; + ept_PTE->Read = 1; + ept_PTE->Write = 1; + ept_PTE->Execute = 1; + ept_PTE->MemoryType = EPT_MEMORY_TYPE_WB; + ept_PTE++; + } + } + } + return ept_PML4T; +} + +BOOLEAN EptReleasePages() +{ + ExFreePoolWithTag(g_pPml4T,'aa'); + ExFreePoolWithTag(g_pPdpteTable,'aa'); + + for (int i = 0;i> 32; + lowPA.QuadPart = lowPA.QuadPart & 0xFFFFF000; + + Page_ID = (lowPA.QuadPart >> 21) * 8 + (highPA.QuadPart * 4 * 0x1000);// 得到页表偏移 + Page_Offset = (lowPA.QuadPart >> 9) & 0xFFF; + + PageFirstPte = (EptPteEntry*)(*(PULONG64)((ULONG64)(g_pPteTable)+Page_ID)); + PagePte = (EptPteEntry*)((ULONG64)PageFirstPte + Page_Offset); + return PagePte; + +} + +void HandleEptViolation() +{ + PHYSICAL_ADDRESS Guest_PA, Guest_LA; + ULONG uGuest_PA_Q, uGuest_PA_Q_H, uGuest_LA_Q; + ULONG64 Exit_Qualification; + PEPT_ATTRIBUTE_PAGE pEpt_Attribute; + EptPteEntry* pPte; + ULONG uCPUID; + ULONG uGuestRIP; + + uCPUID = KeGetCurrentProcessorNumber(); + uGuest_PA_Q = Vmx_VmRead(GUEST_PHYSICAL_ADDRESS); + uGuest_PA_Q_H = Vmx_VmRead(GUEST_PHYSICAL_ADDRESS_HIGH); + uGuest_LA_Q = Vmx_VmRead(GUEST_LINEAR_ADDRESS); + Exit_Qualification = Vmx_VmRead(EXIT_QUALIFICATION); + pEpt_Attribute = (PEPT_ATTRIBUTE_PAGE)&Exit_Qualification; + + Guest_PA.LowPart = uGuest_PA_Q; + Guest_PA.HighPart = uGuest_PA_Q_H; + Guest_LA.QuadPart = uGuest_LA_Q; + + uGuestRIP = Vmx_VmRead(GUEST_RIP); + pPte = EptGetPteAddressByPA(Guest_PA); + if (pEpt_Attribute->Read) + { + // Read Access + } + + if (pEpt_Attribute->Write) + { + // Write Access + } + + if (pEpt_Attribute->Execute) + { + // Execute Access + for (int i = 0;i < g_nEptHookCount;i++) + { + if ((ULONG64)g_EptHookItems[i].pHookedVA == uGuestRIP) + { + if (!g_EptHookItems[i].bDisabled) + { + KdPrint(("From 0x%p to 0x%p.\n", uGuestRIP, g_EptHookItems[i].pJmpVA)); + Vmx_VmWrite(GUEST_RIP, (ULONG64)g_EptHookItems[i].pJmpVA); + } + g_EptHookItems[i].nHitCount++; + } + if (g_EptHookItems[i].pPte == pPte) + { + g_EptRewatch[uCPUID] = SetRewathInfo(pPte, g_EptHookItems[i].pOldPte, (PVOID64)Guest_PA.QuadPart); + } + + } + } + + + pPte->Read = 1; + pPte->Write = 1; + pPte->Execute = 1; + + EptInvept(); + EnableMTF(); +} + +void HandleEptMisconfig() +{ + PHYSICAL_ADDRESS Guest_PA, Guest_LA; + ULONG uGuest_PA_Q, uGuest_LA_Q; + ULONG64 Exit_Qualification; + PEPT_ATTRIBUTE_PAGE pEpt_Attribute; + ULONG uCPUID; + // 如果程序运行到了这里,则说明ept表填写错误 + + uCPUID = KeGetCurrentProcessorNumber(); + uGuest_PA_Q = Vmx_VmRead(GUEST_PHYSICAL_ADDRESS); + uGuest_LA_Q = Vmx_VmRead(GUEST_LINEAR_ADDRESS); + Exit_Qualification = Vmx_VmRead(EXIT_QUALIFICATION); + + pEpt_Attribute = (PEPT_ATTRIBUTE_PAGE)&Exit_Qualification; + Guest_PA.QuadPart = uGuest_PA_Q; + Guest_LA.QuadPart = uGuest_LA_Q; + + KeBugCheck(0x12345678); +} + +void HandleMTF() +{ + ULONG64 GuestRip = Vmx_VmRead(GUEST_RIP); + ULONG64 GuestRsp = Vmx_VmRead(GUEST_RSP); // [rsp+8]储存有上一个地址 + + EPTREWATCHINFO RewatchInfo; + ULONG uCPUID = KeGetCurrentProcessorNumber(); + + if (g_EptRewatch[uCPUID] == NULL) + return; + + RewatchInfo = *(g_EptRewatch[uCPUID]); + *(RewatchInfo.pPte) = RewatchInfo.pOldPte; + EptInvept(); + + ExFreePoolWithTag(g_EptRewatch[uCPUID], 'info'); + g_EptRewatch[uCPUID] = NULL; + + DisableMTF(); +} + +void EptInvept() +{ + ULONG uCPUID; + uCPUID = KeGetCurrentProcessorNumber(); + EptTablePointer EPTP = { 0 }; + PHYSICAL_ADDRESS phys = MmGetPhysicalAddress((void *)g_VMXCPU[uCPUID].ept_PML4T); + + // Set up the EPTP + EPTP.Bits.PhysAddr = phys.QuadPart >> 12; + EPTP.Bits.PageWalkLength = 3; + + Vmx_Invept((PVOID)&EPTP, 2); +} + +PVOID EptAllocateNewPage() +{ + PVOID pPage; + pPage = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 0); + if (!pPage) + return NULL; + RtlZeroMemory(pPage, PAGE_SIZE); + return pPage; +} + +void EnableMTF() +{ + ULONG64 uCPUBase; + uCPUBase = Vmx_VmRead(CPU_BASED_VM_EXEC_CONTROL); + uCPUBase |= CPU_BASED_MTF_TRAP_EXITING; + Vmx_VmWrite(CPU_BASED_VM_EXEC_CONTROL, uCPUBase); +} + +void DisableMTF() +{ + ULONG64 uCPUBase; + uCPUBase = Vmx_VmRead(CPU_BASED_VM_EXEC_CONTROL); + uCPUBase &= ~CPU_BASED_MTF_TRAP_EXITING; + Vmx_VmWrite(CPU_BASED_VM_EXEC_CONTROL, uCPUBase); +} + +VOID EnableTF()//开启TF单步调试功能 +{ + ULONG64 uTemp64; + _EFLAGS* Rflags; + uTemp64 = Vmx_VmRead(GUEST_RFLAGS); + Rflags = (_EFLAGS*)&uTemp64; + + Rflags->TF = 1; + Vmx_VmWrite(GUEST_RFLAGS, uTemp64); + +} + + +VOID DisableTF()//关闭TF单步调试功能 +{ + ULONG64 uTemp64; + _EFLAGS* Rflags; + uTemp64 = Vmx_VmRead(GUEST_RFLAGS); + Rflags = (_EFLAGS*)&uTemp64; + + Rflags->TF = 0; + Vmx_VmWrite(GUEST_RFLAGS, uTemp64); +} + +int AddEPTHookItem(IN PEPTHOOKITEM pItem) +{ + if (g_nEptHookCount > NUM_MAX_HOOK) + return -1; + + pItem->pPte->Execute = 0; + + pItem->pOldPte = *pItem->pPte; + pItem->bDisabled = FALSE; + + g_EptHookItems[g_nEptHookCount] = *pItem; + + g_nEptHookCount++; + return g_nEptHookCount - 1; +} + +BOOLEAN RemoveEPTHookItem(IN int uItemID) +{ + EptPteEntry* pPte; + if (g_nEptHookCount == 0) + return FALSE; + + pPte = g_EptHookItems[uItemID].pPte; + g_EptHookItems[uItemID].bDisabled = TRUE; + return TRUE; +} + +PEPTREWATCHINFO SetRewathInfo(IN EptPteEntry* pPte,IN EptPteEntry pOldPte,IN PVOID64 pContext) +{ + PEPTREWATCHINFO pInfo = (PEPTREWATCHINFO)ExAllocatePoolWithTag(NonPagedPool, sizeof(EPTREWATCHINFO),'info'); + if (pInfo == NULL) + return NULL; + RtlZeroMemory(pInfo, sizeof(EPTREWATCHINFO)); + + pInfo->pPte = pPte; + pInfo->pOldPte = pOldPte; + pInfo->pContext = pContext; + + return pInfo; +} \ No newline at end of file diff --git a/PFHook_sys/base/ept.h b/PFHook_sys/base/ept.h new file mode 100644 index 0000000..2d128b5 --- /dev/null +++ b/PFHook_sys/base/ept.h @@ -0,0 +1,210 @@ +/** +@file +Header which defines structures for handling EPT translations + +@date 1/17/2012 +***************************************************************/ + +#ifndef _MORE_EPT_H_ +#define _MORE_EPT_H_ + + +#define PML4_BASE 0xFFFFF6FB7DBED000 +#define PDP_BASE 0xFFFFF6FB7DA00000 +#define PD_BASE 0xFFFFF6FB40000000 +#define PT_BASE 0xFFFFF68000000000 + +#pragma pack(push, ept, 1) +struct EptTablePointer_s +{ + + ULONG64 MemoryType : 3;//2 + ULONG64 PageWalkLength : 3;//5 + ULONG64 D : 1;//6 + ULONG64 reserved : 5;//11 + ULONG64 PhysAddr : 28;//39 + ULONG64 reserved2 : 24;//63 +}; + +union EptTablePointer_u +{ + ULONG64 unsignedVal; + struct EptTablePointer_s Bits; +}; + +typedef union EptTablePointer_u EptTablePointer; + +struct EptPml4Entry_s +{ + ULONG64 Read : 1; // If the 512 GB region is Read (read access) + ULONG64 Write : 1; // If the 512 GB region is writable + ULONG64 Execute : 1; // If the 512 GB region is executable + ULONG64 reserved1 : 9; // Reserved + ULONG64 PhysAddr : 28; // Physical address + ULONG64 reserved2 : 24; // Reserved +}; + +typedef struct EptPml4Entry_s EptPml4Entry; + +struct EptPdpteEntry_s +{ + ULONG64 Read : 1; // If the 1 GB region is Read (read access) + ULONG64 Write : 1; // If the 1 GB region is writable + ULONG64 Execute : 1; // If the 1 GB region is executable + ULONG64 reserved1 : 9; // Reserved + ULONG64 PhysAddr : 28; // Physical address + ULONG64 reserved2 : 24; // Reserved +}; + +typedef struct EptPdpteEntry_s EptPdpteEntry; + +struct EptPdeEntry_s +{ + ULONG64 Read : 1; // If the 2 MB region is Read (read access) + ULONG64 Write : 1; // If the 2 MB region is writable + ULONG64 Execute : 1; // If the 2 MB region is executable + ULONG64 reserved1 : 9; // Reserved + ULONG64 PhysAddr : 28; // Physical address + ULONG64 reserved2 : 24; // Reserved +}; + +typedef struct EptPdeEntry_s EptPdeEntry; + +struct EptPteEntry_s +{ + ULONG64 Read : 1; // If the 1 GB region is Read (read access) + ULONG64 Write : 1; // If the 1 GB region is writable + ULONG64 Execute : 1; // If the 1 GB region is executable + ULONG64 MemoryType : 3; // EPT Memory type + ULONG64 IgnorePat : 1; // Flag for whether to ignore PAT + ULONGLONG LargePage : 1;//7ҳߴλ + ULONGLONG Accessed : 1;//// 8״̬ ҳδ/д=0 ҳѱ/д=1 + + ULONGLONG Dirty : 1;//// 9ҳ״̬ ҳδĶ=0 ҳѱĶ=1 + + ULONGLONG reserved1 : 2;//11 + ULONG64 PhysAddr : 28; // Physical address + ULONG64 reserved2 : 24; // Reserved +}; + +typedef struct EptPteEntry_s EptPteEntry; + +typedef struct _EPT_ATTRIBUTE_PAE { + ULONGLONG Read : 1; //0 + ULONGLONG Write : 1; //1д + ULONGLONG Execute : 1; //2ִ + ULONGLONG ReadAble : 1; //3Ϊ1ʱʾGPAɶ + ULONGLONG WriteAble : 1; //4Ϊ1ʱʾGPAд + ULONGLONG ExecuteAble : 1;//5Ϊ1ʱʾGPAִ + ULONGLONG reserved : 1;//// 6 + ULONGLONG Valid : 1;//Ϊ1ʱ 7һԵַ + ULONGLONG TranSlation : 1;////8Ϊ1ʱEPT VIOLATIONGPAתHPA Ϊ0ڶguest paging-stuctureַʻ + ULONGLONG reserved2 : 1;//9 Ϊ0 + ULONGLONG NMIunblocking : 1;//10Ϊ1ִIRETָNMIѾ + ULONGLONG reserved3 : 1;//11 + ULONGLONG reserved4 : 13;//23:11 + ULONGLONG GET_PTE : 1;//24 + ULONGLONG GET_PAGE_FRAME : 1;//25 + ULONGLONG FIX_ACCESS : 1;//26Ϊ1ʱ access ringht޸ + ULONGLONG FIX_MISCONF : 1;//27Ϊ1ʱ misconfiguration޸ + ULONGLONG FIX_FIXING : 1;//28Ϊ1ʱ ޸ Ϊ0ӳ + ULONGLONG EPT_FORCE : 1;//29Ϊ1ʱ ǿƽӳ + ULONGLONG reserved5 : 1; +} EPT_ATTRIBUTE_PAGE, *PEPT_ATTRIBUTE_PAGE; +#pragma pack(pop, ept) + +#pragma pack(push,1) +typedef struct _EPTREWATCHINFO +{ + EptPteEntry* pPte; + EptPteEntry pOldPte; + PVOID pHookedAddress; + PVOID pJmpAddress; + PVOID64 pContext; +}EPTREWATCHINFO,*PEPTREWATCHINFO; +#pragma pack(pop) +typedef struct _EPTHOOKITEM +{ + EptPteEntry* pPte; + EptPteEntry pOldPte; + + PVOID pHookedVA; + PVOID pJmpVA; + PHYSICAL_ADDRESS pPA; + + BOOLEAN bDisabled; + ULONG64 nHitCount; + char szName[100]; +}EPTHOOKITEM,*PEPTHOOKITEM; + +/** Intel-defined EPT memory types */ +enum EPT_MEMORY_TYPE_E +{ + EPT_MEMORY_TYPE_UC = 0, + EPT_MEMORY_TYPE_WC = 1, + EPT_MEMORY_TYPE_WT = 4, + EPT_MEMORY_TYPE_WP = 5, + EPT_MEMORY_TYPE_WB = 6, +}; + +typedef enum EPT_MEMORY_TYPE_E EPT_MEMORY_TYPE; + +/** Boolean for whether or not to split the TLB */ +#define SPLIT_TLB 1 +/** Maximum addressing width for the processor */ +#define PHYSICAL_ADDRESS_WIDTH 36 +/** Guest VPID value (must be non-zero) */ +#define VM_VPID 1 + +/** Number of pages to pre-allocate for later use */ +#define NUM_PAGES_ALLOC 1024 + + +// Defines for parsing the EPT violation exit qualification +/** Bitmask for data read violation */ +#define EPT_MASK_DATA_READ 0x1 +/** Bitmask for data write violation */ +#define EPT_MASK_DATA_WRITE (1 << 1) +/** Bitmask for data execute violation */ +#define EPT_MASK_DATA_EXEC (1 << 2) +/** Bitmask for if the guest linear address is valid */ +#define EPT_MASK_GUEST_LINEAR_VALID (1 << 7) + +#endif + +// This function can load our ept page system. +EptPml4Entry* EptInitialization(); + +// Release All EPT Pages +BOOLEAN EptReleasePages(); + +// We can get a physcial address with a PA +// If it is a usermode address,please change the cr3 and then MmGetPhysicalAddress to get its PA +EptPteEntry* EptGetPteAddressByPA(IN PHYSICAL_ADDRESS PA); + +PVOID EptAllocateNewPage(); + +// To refresh the ept page +void EptInvept(); + +// Handle EPT Violation Event +void HandleEptViolation(); +// Handle EPT Misconfig Event(remark:If it hit this function,it means that the PML4T is error.) +void HandleEptMisconfig(); +// Handle MTF to rewat +void HandleMTF(); + +void EnableMTF(); +void DisableMTF(); + +VOID EnableTF(); +VOID DisableTF(); + +// Add a EPTHook Item +int AddEPTHookItem(IN PEPTHOOKITEM pItem); + +// Remove a EPTHook Item +BOOLEAN RemoveEPTHookItem(IN int uItemID); + +// Set Rewatch Information +PEPTREWATCHINFO SetRewathInfo(IN EptPteEntry* pPte, IN EptPteEntry pOldPte, IN PVOID64 pContext); diff --git a/PFHook_sys/base/exithandler.h b/PFHook_sys/base/exithandler.h new file mode 100644 index 0000000..ebe6f8d --- /dev/null +++ b/PFHook_sys/base/exithandler.h @@ -0,0 +1,713 @@ +#pragma once +#include "common.h" +#include "ept.h" +#include "global.h" +GUEST_REGS g_GuestRegs[128]; +extern VMX_CPU g_VMXCPU[128]; + +void HandleCPUID() +{ + ULONG64 uCPUID; + uCPUID = KeGetCurrentProcessorNumber(); + if (g_GuestRegs[uCPUID].rax == 'Mini') + { + g_GuestRegs[uCPUID].rbx = 0x88888888; + g_GuestRegs[uCPUID].rcx = 0x11111111; + g_GuestRegs[uCPUID].rdx = 0x12345678; + } + if (g_GuestRegs[uCPUID].rax == PFHOOK_CODE_HOOK) + { + HandleEvent_Hook(&g_GuestRegs[uCPUID]); + } + else if (g_GuestRegs[uCPUID].rax == PFHOOK_CODE_UNHOOK) + { + HandleEvent_Unhook(&g_GuestRegs[uCPUID]); + } + else if (g_GuestRegs[uCPUID].rax == PFHOOK_CODE_CHECKVT) + { + HandleEvent_CheckVT(&g_GuestRegs[uCPUID]); + } + else Asm_CPUID(g_GuestRegs[uCPUID].rax,&g_GuestRegs[uCPUID].rax,&g_GuestRegs[uCPUID].rbx,&g_GuestRegs[uCPUID].rcx,&g_GuestRegs[uCPUID].rdx); +} + +void HandleInvd() +{ + Asm_Invd(); +} + +void HandleVmCall() +{ + ULONG64 JmpEIP; + ULONG64 uCPUID; + uCPUID = KeGetCurrentProcessorNumber(); + if (g_GuestRegs[uCPUID].rax == 'SVT') + { + JmpEIP = g_GuestRegs[uCPUID].rip + Vmx_VmRead(VM_EXIT_INSTRUCTION_LEN); + Vmx_VmxOff(); + Asm_AfterVMXOff(g_GuestRegs[uCPUID].rsp,JmpEIP); + } +} + +void HandleMsrRead() +{ + ULONG64 uCPUID; + uCPUID = KeGetCurrentProcessorNumber(); + switch(g_GuestRegs[uCPUID].rcx) + { + case MSR_IA32_SYSENTER_CS: + { + g_GuestRegs[uCPUID].rax = Vmx_VmRead(GUEST_SYSENTER_CS) & 0xFFFFFFFF; + g_GuestRegs[uCPUID].rdx = Vmx_VmRead(GUEST_SYSENTER_CS) >> 32; + break; + } + case MSR_IA32_SYSENTER_ESP: + { + g_GuestRegs[uCPUID].rax = Vmx_VmRead(GUEST_SYSENTER_ESP) & 0xFFFFFFFF; + g_GuestRegs[uCPUID].rdx = Vmx_VmRead(GUEST_SYSENTER_ESP) >> 32; + break; + } + case MSR_IA32_SYSENTER_EIP: // KiFastCallEntry + { + g_GuestRegs[uCPUID].rax = Vmx_VmRead(GUEST_SYSENTER_EIP) & 0xFFFFFFFF; + g_GuestRegs[uCPUID].rdx = Vmx_VmRead(GUEST_SYSENTER_EIP) >> 32; + break; + } + case MSR_FS_BASE: + { + g_GuestRegs[uCPUID].rax = Vmx_VmRead(GUEST_FS_BASE) & 0xFFFFFFFF; + g_GuestRegs[uCPUID].rdx = Vmx_VmRead(GUEST_FS_BASE) >> 32; + break; + } + case MSR_GS_BASE: + { + g_GuestRegs[uCPUID].rax = Vmx_VmRead(GUEST_GS_BASE) & 0xFFFFFFFF; + g_GuestRegs[uCPUID].rdx = Vmx_VmRead(GUEST_GS_BASE) >> 32; + break; + } + case MSR_EFER: + { + g_GuestRegs[uCPUID].rax = Asm_ReadMsr(MSR_EFER) & 0xFFFFFFFF; + g_GuestRegs[uCPUID].rdx = Asm_ReadMsr(MSR_EFER) >> 32; + break; + } + case MSR_SHADOW_GS_BASE: + { + g_GuestRegs[uCPUID].rax = Asm_ReadMsr(MSR_SHADOW_GS_BASE) & 0xFFFFFFFF; + g_GuestRegs[uCPUID].rdx = Asm_ReadMsr(MSR_SHADOW_GS_BASE) >> 32; + break; + } + default: + g_GuestRegs[uCPUID].rax = Asm_ReadMsr(g_GuestRegs[uCPUID].rcx) & 0xFFFFFFFF; + g_GuestRegs[uCPUID].rdx = Asm_ReadMsr(g_GuestRegs[uCPUID].rcx) >> 32; + } + +} + +void HandleMsrWrite() +{ + ULONG64 uCPUID; + ULONG64 uMsr; + uCPUID = KeGetCurrentProcessorNumber(); + uMsr = (g_GuestRegs[uCPUID].rax & 0xFFFFFFFF) | (g_GuestRegs[uCPUID].rdx << 32); + switch(g_GuestRegs[uCPUID].rcx) + { + case MSR_IA32_SYSENTER_CS: + { + Vmx_VmWrite(GUEST_SYSENTER_CS,uMsr); + break; + } + case MSR_IA32_SYSENTER_ESP: + { + Vmx_VmWrite(GUEST_SYSENTER_ESP, uMsr); + break; + } + case MSR_IA32_SYSENTER_EIP: // KiFastCallEntry + { + Vmx_VmWrite(GUEST_SYSENTER_EIP, uMsr); + break; + } + case MSR_FS_BASE: + { + Vmx_VmWrite(GUEST_FS_BASE, uMsr); + break; + } + case MSR_GS_BASE: + { + Vmx_VmWrite(GUEST_GS_BASE, uMsr); + break; + } + case MSR_EFER: + { + Asm_WriteMsr(MSR_EFER, uMsr | EFER_LME); + break; + } + case MSR_SHADOW_GS_BASE: + { + Asm_WriteMsr(g_GuestRegs[uCPUID].rcx, uMsr); + break; + } + default: + Asm_WriteMsr(g_GuestRegs[uCPUID].rcx, uMsr); + } +} + +void HandleCrAccess() +{ + ULONG64 movcrControlRegister; + ULONG64 movcrAccessType; + ULONG64 movcrOperandType; + ULONG64 movcrGeneralPurposeRegister; + ULONG64 ExitQualification; + ULONG64 uCPUID; + uCPUID = KeGetCurrentProcessorNumber(); + + ExitQualification = Vmx_VmRead(EXIT_QUALIFICATION) ; + movcrControlRegister = ( ExitQualification & 0x0000000F ); + movcrAccessType = ( ( ExitQualification & 0x00000030 ) >> 4 ); + movcrOperandType = ( ( ExitQualification & 0x00000040 ) >> 6 ); + movcrGeneralPurposeRegister = ( ( ExitQualification & 0x00000F00 ) >> 8 ); + if (movcrOperandType == 0) + { + if (movcrControlRegister == 3) + { + if (movcrAccessType == 0) // дCR3 + { + switch (movcrGeneralPurposeRegister) + { + case 0: + { + Vmx_VmWrite(GUEST_CR3, g_GuestRegs[uCPUID].rax); + break; + } + case 1: + { + Vmx_VmWrite(GUEST_CR3, g_GuestRegs[uCPUID].rcx); + break; + } + case 2: + { + Vmx_VmWrite(GUEST_CR3, g_GuestRegs[uCPUID].rdx); + break; + } + case 3: + { + Vmx_VmWrite(GUEST_CR3, g_GuestRegs[uCPUID].rbx); + break; + } + case 4: + { + Vmx_VmWrite(GUEST_CR3, g_GuestRegs[uCPUID].rsp); + break; + } + case 5: + { + Vmx_VmWrite(GUEST_CR3, g_GuestRegs[uCPUID].rbp); + break; + } + case 6: + { + Vmx_VmWrite(GUEST_CR3, g_GuestRegs[uCPUID].rsi); + break; + } + case 7: + { + Vmx_VmWrite(GUEST_CR3, g_GuestRegs[uCPUID].rdi); + break; + } + default: + break; + } + } + } + else if (movcrAccessType == 1) + { + switch (movcrGeneralPurposeRegister) + { + case 0: + { + g_GuestRegs[uCPUID].rax = g_GuestRegs[uCPUID].cr3; + break; + } + case 1: + { + g_GuestRegs[uCPUID].rcx = g_GuestRegs[uCPUID].cr3; + break; + } + case 2: + { + g_GuestRegs[uCPUID].rdx = g_GuestRegs[uCPUID].cr3; + break; + } + case 3: + { + g_GuestRegs[uCPUID].rbx = g_GuestRegs[uCPUID].cr3; + break; + } + case 4: + { + g_GuestRegs[uCPUID].rsp = g_GuestRegs[uCPUID].cr3; + break; + } + case 5: + { + g_GuestRegs[uCPUID].rbp = g_GuestRegs[uCPUID].cr3; + break; + } + case 6: + { + g_GuestRegs[uCPUID].rsi = g_GuestRegs[uCPUID].cr3; + break; + } + case 7: + { + g_GuestRegs[uCPUID].rdi = g_GuestRegs[uCPUID].cr3; + break; + } + default: + break; + } + } + } + +} + +void HandleRDTSC() +{ + ULONG uCPUID; + uCPUID = KeGetCurrentProcessorNumber(); + Asm_Rdtsc(&g_GuestRegs[uCPUID].rax, &g_GuestRegs[uCPUID].rdx); +} + +// Work for Windows 10 +void HandleRDTSCP() +{ + ULONG uCPUID; + uCPUID = KeGetCurrentProcessorNumber(); + g_GuestRegs[uCPUID].rax = (Asm_GetTSC() & 0xFFFFFFFF); + g_GuestRegs[uCPUID].rdx = (Asm_GetTSC() >> 32); +} + +VOID EventInject(ULONG32 trap) +{ + ULONG32 InjectEvent = (ULONG32)Vmx_VmRead(VM_EXIT_INTR_INFO); + PINTERRUPT_INJECT_INFO_FIELD pInjectEvent = (PINTERRUPT_INJECT_INFO_FIELD)&InjectEvent; + ULONG64 error_code = Vmx_VmRead(VM_EXIT_INTR_ERROR_CODE); + ULONG32 IInfo = (ULONG32)Vmx_VmRead(GUEST_INTERRUPTIBILITY_INFO); + PINTERRUPT_IBILITY_INFO pINTERRUPTIBILITY = (PINTERRUPT_IBILITY_INFO)&IInfo; + //ULONG32 IInfo = (ULONG32)Vmx_VmRead ( GUEST_INTERRUPTIBILITY_INFO ); + //PINTERRUPT_IBILITY_INFO pINTERRUPTIBILITY=(PINTERRUPT_IBILITY_INFO)&IInfo; + + if (trap == TRAP_INT3) + { + InjectEvent = 0; + pInjectEvent->Vector = BREAKPOINT_EXCEPTION; //7-0 + pInjectEvent->InterruptionType = SOFTWARE_EXCEPTION;//10-8 + pInjectEvent->DeliverErrorCode = 0;//11 + pInjectEvent->Reserved = 0;//32-12 + pInjectEvent->Valid = 1;//31 + + } + else if (trap == TRAP_INTO) + { + InjectEvent = 0; + pInjectEvent->Vector = OVERFLOW_EXCEPTION; //7-0 + pInjectEvent->InterruptionType = SOFTWARE_EXCEPTION;//10-8 + pInjectEvent->DeliverErrorCode = 0;//11 + pInjectEvent->Reserved = 0;//32-12 + pInjectEvent->Valid = 1;//31 + + } + else if (trap == TRAP_DEBUG) + { + InjectEvent = 0; + pInjectEvent->Vector = DEBUG_EXCEPTION; //7-0 + pInjectEvent->InterruptionType = HARDWARE_EXCEPTION;//10-8 + pInjectEvent->DeliverErrorCode = 0;//11 + pInjectEvent->Reserved = 0;//32-12 + pInjectEvent->Valid = 1;//31 + + + } + else if (trap == TRAP_PAGE_FAULT) + { + // + // ע#PF + // + + Asm_SetCr2(Vmx_VmRead(EXIT_QUALIFICATION)); + //VmxWrite( VM_ENTRY_EXCEPTION_ERROR_CODE, error_code ); + InjectEvent = 0; + pInjectEvent->Vector = PAGE_FAULT_EXCEPTION; + pInjectEvent->InterruptionType = HARDWARE_EXCEPTION; + pInjectEvent->DeliverErrorCode = 1; + pInjectEvent->Reserved = 0;//32-12 + pInjectEvent->Valid = 1;//31 + + } + else if (trap == TRAP_GP) + { + // + // ע#PF + // + + Asm_SetCr2(Vmx_VmRead(EXIT_QUALIFICATION)); + InjectEvent = 0; + pInjectEvent->Vector = GENERAL_PROTECTION_EXCEPTION; + pInjectEvent->InterruptionType = HARDWARE_EXCEPTION; + pInjectEvent->DeliverErrorCode = 1; + pInjectEvent->Reserved = 0;//32-12 + pInjectEvent->Valid = 1;//31 + + } + else if (trap == TRAP_MTF) + { + pInjectEvent->Vector = DIVIDE_ERROR_EXCEPTION; //7-0 + pInjectEvent->InterruptionType = OTHER_EVENT;//10-8 + pInjectEvent->DeliverErrorCode = 0; + pInjectEvent->Reserved = 0;//32-12 + pInjectEvent->Valid = 1;//31 + pINTERRUPTIBILITY->MOV_SS = 1; + Vmx_VmWrite(GUEST_INTERRUPTIBILITY_INFO, IInfo); + + } + else if (trap == 0x80) + { + pInjectEvent->Vector = 0x80; //7-0 + pInjectEvent->InterruptionType = OTHER_EVENT;//10-8 + pInjectEvent->DeliverErrorCode = 0; + pInjectEvent->Reserved = 0;//32-12 + pInjectEvent->Valid = 1;//31 + + } + + Vmx_VmWrite(VM_ENTRY_INSTRUCTION_LEN, Vmx_VmRead(VM_EXIT_INSTRUCTION_LEN));//Լĵ¼ȻͻῪʼһ + Vmx_VmWrite(VM_ENTRY_INTR_INFO_FIELD, InjectEvent); + + if (error_code != -1) + Vmx_VmWrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code); + + +} + +BOOLEAN DrnReadWriht(ULONG64 Drn, PGUEST_REGS pGuestRegs) +{ + if (pGuestRegs->rax == Drn) + { + return TRUE; + } + if (pGuestRegs->rbx == Drn) + { + return TRUE; + } + if (pGuestRegs->rcx == Drn) + { + return TRUE; + } + if (pGuestRegs->rdx == Drn) + { + return TRUE; + } + if (pGuestRegs->rsp == Drn) + { + return TRUE; + } + if (pGuestRegs->rbp == Drn) + { + return TRUE; + } + if (pGuestRegs->rsi == Drn) + { + return TRUE; + } + if (pGuestRegs->rdi == Drn) + { + return TRUE; + } + if (pGuestRegs->r8 == Drn) + { + return TRUE; + } + if (pGuestRegs->r9 == Drn) + { + return TRUE; + } + if (pGuestRegs->r10 == Drn) + { + return TRUE; + } + if (pGuestRegs->r11 == Drn) + { + return TRUE; + } + if (pGuestRegs->r12 == Drn) + { + return TRUE; + } + if (pGuestRegs->r13 == Drn) + { + return TRUE; + } + if (pGuestRegs->r14 == Drn) + { + return TRUE; + } + if (pGuestRegs->r15 == Drn) + { + return TRUE; + } + return FALSE; +} + +VOID DebugInt1DrnAccess(PGUEST_REGS pGuestRegs) +{ + ULONG64 RegDr0, RegDr1, RegDr2, RegDr3, RegDr6, RegDr7; + ULONG32 DebugDr6, GuestDr7; + PDEBUG_DR6 pRegDr6; + PDEBUG_DR7 pRegDr7; + ULONG64 GuestRip = Vmx_VmRead(GUEST_RIP); + DebugDr6 = (ULONG32)Asm_GetDr6(); + GuestDr7 = (ULONG32)Vmx_VmRead(GUEST_DR7); + pRegDr6 = (PDEBUG_DR6)&DebugDr6; + pRegDr7 = (PDEBUG_DR7)&GuestDr7; + RegDr0 = Asm_GetDr0(); + RegDr1 = Asm_GetDr1(); + RegDr2 = Asm_GetDr2(); + RegDr3 = Asm_GetDr3(); + RegDr6 = Asm_GetDr6(); + RegDr7 = Asm_GetDr7(); + if (GuestRip == RegDr0) + { + if (pRegDr7->L0 || pRegDr7->G0) + { + pRegDr6->B0 = 1; + } + } + else if (GuestRip == RegDr1) + { + if (pRegDr7->L1 || pRegDr7->G1) + { + pRegDr6->B1 = 1; + } + } + else if (GuestRip == RegDr2) + { + if (pRegDr7->L2 || pRegDr7->G2) + { + pRegDr6->B2 = 1; + } + } + else if (GuestRip == RegDr3) + { + if (pRegDr7->L2 || pRegDr7->G2) + { + pRegDr6->B2 = 1; + } + } + else if (DrnReadWriht(RegDr0, pGuestRegs)) + { + if (pRegDr7->L0 || pRegDr7->G0) + { + pRegDr6->B0 = 1; + } + } + else if (DrnReadWriht(RegDr1, pGuestRegs)) + { + if (pRegDr7->L1 || pRegDr7->G1) + { + pRegDr6->B1 = 1; + } + } + else if (DrnReadWriht(RegDr2, pGuestRegs)) + { + if (pRegDr7->L2 || pRegDr7->G2) + { + pRegDr6->B2 = 1; + } + } + else if (DrnReadWriht(RegDr3, pGuestRegs)) + { + if (pRegDr7->L3 || pRegDr7->G3) + { + pRegDr6->B3 = 1; + } + } + else + { + return; + } + pRegDr7->GD = 0; + Asm_SetDr7(GuestDr7); + Asm_SetDr6(DebugDr6); +} + +VOID HandleException() +{ + ULONG32 Event, InjectEvent, DebugDr6, GuestDr7; + ULONG64 ErrorCode, ExitQualification, GuestRip; + PINTERRUPT_INFO_FIELD pEvent; + PINTERRUPT_INJECT_INFO_FIELD pInjectEvent; + PDEBUG_DR6 pRegDr6; + PDEBUG_DR7 pRegDr7; + ULONG64 uCurCPU; + GuestDr7 = (ULONG32)Vmx_VmRead(GUEST_DR7); + pRegDr7 = (PDEBUG_DR7)&GuestDr7; + Event = (ULONG32)Vmx_VmRead(VM_EXIT_INTR_INFO); + pEvent = (PINTERRUPT_INFO_FIELD)&Event; + uCurCPU = KeGetCurrentProcessorNumber(); + + InjectEvent = 0; + pInjectEvent = (PINTERRUPT_INJECT_INFO_FIELD)&InjectEvent; + + GuestRip = Vmx_VmRead(GUEST_RIP); + switch (pEvent->InterruptionType) + { + case NMI_INTERRUPT: + { + InjectEvent = 0; + pInjectEvent->Vector = NMI_INTERRUPT; + pInjectEvent->InterruptionType = NMI_INTERRUPT; + pInjectEvent->DeliverErrorCode = 0; + pInjectEvent->Valid = 1; + Vmx_VmWrite(VM_ENTRY_INTR_INFO_FIELD, InjectEvent); + break; + } + case EXTERNAL_INTERRUPT: + break; + + case HARDWARE_EXCEPTION: + { + switch (pEvent->Vector) + { + case DEBUG_EXCEPTION: + { + DebugInt1DrnAccess(&g_GuestRegs[uCurCPU]); + DebugDr6 = (ULONG32)Asm_GetDr6(); + pRegDr6 = (PDEBUG_DR6)&DebugDr6; + EventInject(TRAP_DEBUG); + break; + } + + + case PAGE_FAULT_EXCEPTION: + { + EventInject(TRAP_PAGE_FAULT); + break; + } + + + default: + break; + } + break; + } + + case SOFTWARE_EXCEPTION: + { + switch (pEvent->Vector) + { + case BREAKPOINT_EXCEPTION: + EventInject(TRAP_INT3); // DzIDTHookҲԼint3ϵ + break; + + case OVERFLOW_EXCEPTION: + break; + default: + break; + } + break; + } + + + default: + break; + } +} + +extern "C" ULONG64 GetGuestRegsAddress() +{ + ULONG64 uCPUID; + uCPUID = KeGetCurrentProcessorNumber(); + return (ULONG64)&g_GuestRegs[uCPUID]; +} + +extern "C" void VMMEntryPoint() +{ + ULONG64 ExitReason; + ULONG64 ExitInstructionLength; + ULONG64 GuestResumeEIP; + ULONG64 uCPUID; + uCPUID = KeGetCurrentProcessorNumber(); + ExitReason = Vmx_VmRead(VM_EXIT_REASON); + ExitInstructionLength = Vmx_VmRead(VM_EXIT_INSTRUCTION_LEN); + + g_GuestRegs[uCPUID].rsp = Vmx_VmRead(GUEST_RSP); + g_GuestRegs[uCPUID].rip = Vmx_VmRead(GUEST_RIP); + g_GuestRegs[uCPUID].cr3 = Vmx_VmRead(GUEST_CR3); + switch (ExitReason) + { + case EXIT_REASON_EXCEPTION_NMI: + { + HandleException(); + return; + } + case EXIT_REASON_TRIPLE_FAULT: + { + break; + } + case EXIT_REASON_CPUID: + { + HandleCPUID(); + break; + } + case EXIT_REASON_INVD: + { + HandleInvd(); + break; + } + case EXIT_REASON_VMCALL: + { + HandleVmCall(); + break; + } + case EXIT_REASON_MSR_READ: + { + HandleMsrRead(); + break; + } + case EXIT_REASON_MSR_WRITE: + { + HandleMsrWrite(); + break; + } + case EXIT_REASON_RDTSC: // 16 + { + HandleRDTSC(); + break; + } + case EXIT_REASON_RDTSCP: // 51 + { + HandleRDTSCP(); + break; + } + case EXIT_REASON_EPT_VIOLATION: + { + HandleEptViolation(); + return; + } + case EXIT_REASON_EPT_MISCONFIG: + { + HandleEptMisconfig(); + return; + } + case EXIT_REASON_MTF_TRAP_FLAG: + { + HandleMTF(); + return; + } + default: + break; + } + GuestResumeEIP = g_GuestRegs[uCPUID].rip+ExitInstructionLength; + Vmx_VmWrite(GUEST_RIP,GuestResumeEIP); + Vmx_VmWrite(GUEST_RSP,g_GuestRegs[uCPUID].rsp); +} \ No newline at end of file diff --git a/PFHook_sys/base/global.cpp b/PFHook_sys/base/global.cpp new file mode 100644 index 0000000..2dae0d5 --- /dev/null +++ b/PFHook_sys/base/global.cpp @@ -0,0 +1,124 @@ +#include "header.h" +#include "global.h" +#include "vtsystem.h" +#include "vtasm.h" +#include "ept.h" + +BOOLEAN HandleEvent_Hook(IN OUT PGUEST_REGS pRegs) +{ + NTSTATUS status = STATUS_SUCCESS; + PEPROCESS proc; + EPTHOOKITEM item; + KAPC_STATE state; + PHYSICAL_ADDRESS pa; + ULONG uCPUID; + ULONG64 uPID, uHookedAddr, uJmpAddr; + int uItemID; + + uCPUID = KeGetCurrentProcessorNumber(); + uPID = pRegs->rbx; + uHookedAddr = pRegs->rcx; + uJmpAddr = pRegs->rdx; + + if (uHookedAddr == 0 || uJmpAddr == 0) + goto Error; + status = PsLookupProcessByProcessId((HANDLE)uPID, &proc); + if (NT_SUCCESS(status)) + { + KeStackAttachProcess(proc, &state); // л̣ܻȡַ + + pa = MmGetPhysicalAddress((PVOID)uHookedAddr); + if (!pa.QuadPart) + goto Error; + + item.pPte = EptGetPteAddressByPA(pa); + if (!item.pPte) + goto Error; + + item.pHookedVA = (PVOID)uHookedAddr; + item.pJmpVA = (PVOID)uJmpAddr; + + uItemID = AddEPTHookItem(&item); + if (uItemID == -1) + goto Error; + + KeUnstackDetachProcess(&state); + + KdPrint(("EPTHook %s[%d] 0x%llX --> 0x%llX\n", PsGetProcessImageFileName(proc), uPID, uHookedAddr, uJmpAddr)); + + pRegs->rsi = uItemID; + return TRUE; + } + +Error: + pRegs->rsi = -1; + return FALSE; +} + +BOOLEAN HandleEvent_Unhook(IN OUT PGUEST_REGS pRegs) +{ + PEPROCESS proc; + NTSTATUS status = STATUS_SUCCESS; + EPTHOOKITEM item; + KAPC_STATE state; + PHYSICAL_ADDRESS pa; + ULONG uCPUID; + ULONG64 uPID, uHookedAddr, uJmpAddr; + int uItemID; + + uCPUID = KeGetCurrentProcessorNumber(); + uItemID = pRegs->rcx; + + if (RemoveEPTHookItem(uItemID)) + { + pRegs->rsi = 1; + return TRUE; + } + +Error: + pRegs->rsi = 0; + return FALSE; +} + +void HandleEvent_CheckVT(IN OUT PGUEST_REGS pRegs) +{ + pRegs->rsi = 1; +} + +// ⲿִǰŪ޺R0 Hookʱд +ULONG64 GetKeServiceDescriptorTable64() +{ + ULONG64 pKiSystemCall64 = Asm_ReadMsr(0xc0000082); + ULONG uOffset; + UCHAR a1, a2, a3; + + for (int i = 0;i <= 0x500;i++) + { + if (MmIsAddressValid((PVOID)(i + pKiSystemCall64)) && MmIsAddressValid((PVOID)(i + pKiSystemCall64 + 1)) && MmIsAddressValid((PVOID)(i + pKiSystemCall64 + 2))) + { + a1 = *((UCHAR*)(i + pKiSystemCall64)); + a2 = *((UCHAR*)(i + 1 + pKiSystemCall64)); + a3 = *((UCHAR*)(i + 2 + pKiSystemCall64)); + if (a1 == 0x4C && a2 == 0x8D && a3 == 0x15) + { + uOffset = *((PULONG)(i + pKiSystemCall64 + 3)); + return i + pKiSystemCall64 + uOffset + 7; + } + } + } + return 0; +} + +ULONG64 GetSSDTFunctionAddr(IN ULONG uID) +{ + PSYSTEM_SERVICE_TABLE pKeServiceDescriptorTable64; + ULONG64 qwTemp; + ULONG dwTemp; + pKeServiceDescriptorTable64 = (PSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTable64(); + if (!pKeServiceDescriptorTable64) + return 0; + qwTemp = (ULONG64)pKeServiceDescriptorTable64->ServiceTableBase + 4 * uID; + dwTemp = *((PULONG)(qwTemp)); + dwTemp = dwTemp >> 4; + return (ULONG64)pKeServiceDescriptorTable64->ServiceTableBase + dwTemp; +} \ No newline at end of file diff --git a/PFHook_sys/base/global.h b/PFHook_sys/base/global.h new file mode 100644 index 0000000..a7d8f6d --- /dev/null +++ b/PFHook_sys/base/global.h @@ -0,0 +1,29 @@ +#include "vtsystem.h" +// ͨŴ +#define PFHOOK_CODE_CHECKVT 0x800 +#define PFHOOK_CODE_HOOK 0x801 +#define PFHOOK_CODE_UNHOOK 0x802 + +typedef struct _SYSTEM_SERVICE_TABLE { + PVOID ServiceTableBase; + PVOID ServiceCounterTableBase; + ULONGLONG NumberOfServices; + PVOID ParamTableBase; +} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE; + +BOOLEAN HandleEvent_Hook(IN OUT PGUEST_REGS pRegs); +BOOLEAN HandleEvent_Unhook(IN OUT PGUEST_REGS pRegs); +void HandleEvent_CheckVT(IN OUT PGUEST_REGS pRegs); + +extern "C" +{ + NTSYSAPI + UCHAR * + NTAPI + PsGetProcessImageFileName( + IN PEPROCESS proc + ); +} + +ULONG64 GetKeServiceDescriptorTable64(); +ULONG64 GetSSDTFunctionAddr(IN ULONG uID); diff --git a/PFHook_sys/base/vtasm.asm b/PFHook_sys/base/vtasm.asm new file mode 100644 index 0000000..4acc290 --- /dev/null +++ b/PFHook_sys/base/vtasm.asm @@ -0,0 +1,563 @@ +GetGuestRegsAddress Proto +VMMEntryPoint Proto +SetupVMCS Proto + +.data +GuestRSP qword ? +GuestReturn qword ? + +EntryRAX qword ? +EntryRCX qword ? +EntryRDX qword ? +EntryRBX qword ? +EntryRSP qword ? +EntryEBP qword ? +EntryESI qword ? +EntryRDI qword ? +EntryR8 qword ? +EntryR9 qword ? +EntryR10 qword ? +EntryR11 qword ? +EntryR12 qword ? +EntryR13 qword ? +EntryR14 qword ? +EntryR15 qword ? +EntryRflags qword ? + +.code + +Asm_CPUID Proc + push rbp + mov rbp, rsp + push rbx + push rsi + + mov [rbp+18h], rdx + mov eax, ecx + cpuid + mov rsi, [rbp+18h] + mov [rsi], eax + mov [r8], ebx + mov [r9], ecx + mov rsi, [rbp+30h] + mov [rsi], edx + + pop rsi + pop rbx + mov rsp, rbp + pop rbp + ret +Asm_CPUID Endp + +Asm_ReadMsr Proc + xor rax,rax + xor rdx,rdx + + rdmsr + shl rdx,32 + or rax,rdx + ret +Asm_ReadMsr Endp + +Asm_WriteMsr Proc + xor rax,rax + + mov eax,edx + shr rdx,32 + wrmsr + ret +Asm_WriteMsr Endp + +Asm_Invd Proc + invd + ret +Asm_Invd Endp + +Asm_GetCs PROC + mov rax, cs + ret +Asm_GetCs ENDP + +Asm_GetDs PROC + mov rax, ds + ret +Asm_GetDs ENDP + +Asm_GetEs PROC + mov rax, es + ret +Asm_GetEs ENDP + +Asm_GetSs PROC + mov rax, ss + ret +Asm_GetSs ENDP + +Asm_GetFs PROC + mov rax, fs + ret +Asm_GetFs ENDP + +Asm_GetGs PROC + mov rax, gs + ret +Asm_GetGs ENDP + + +Asm_GetCr0 Proc + mov rax, cr0 + ret +Asm_GetCr0 Endp + +Asm_GetCr3 Proc + + mov rax, cr3 + ret +Asm_GetCr3 Endp + +Asm_GetCr4 Proc + mov rax, cr4 + ret +Asm_GetCr4 Endp + +Asm_SetCr0 Proc + mov cr0, rcx + ret +Asm_SetCr0 Endp + +Asm_SetCr2 Proc + mov cr2, rcx + ret +Asm_SetCr2 Endp + +Asm_SetCr3 Proc + mov cr3, rcx + ret +Asm_SetCr3 Endp + +Asm_SetCr4 Proc + mov cr4, rcx + ret +Asm_SetCr4 Endp + +Asm_GetDr0 PROC + mov rax, dr0 + ret +Asm_GetDr0 ENDP + +Asm_GetDr1 PROC + mov rax, dr1 + ret +Asm_GetDr1 ENDP + +Asm_GetDr2 PROC + mov rax, dr2 + ret +Asm_GetDr2 ENDP + +Asm_GetDr3 PROC + mov rax, dr3 + ret +Asm_GetDr3 ENDP + +Asm_GetDr6 PROC + mov rax, dr6 + ret +Asm_GetDr6 ENDP + +Asm_GetDr7 PROC + mov rax, dr7 + ret +Asm_GetDr7 ENDP + +Asm_SetDr0 PROC + mov dr0, rcx + ret +Asm_SetDr0 ENDP + +Asm_SetDr1 PROC + mov dr1, rcx + ret +Asm_SetDr1 ENDP + +Asm_SetDr2 PROC + mov dr2, rcx + ret +Asm_SetDr2 ENDP + +Asm_SetDr3 PROC + mov dr3, rcx + ret +Asm_SetDr3 ENDP + +Asm_SetDr6 PROC + mov dr6, rcx + ret +Asm_SetDr6 ENDP + +Asm_SetDr7 PROC + mov dr7, rcx + ret +Asm_SetDr7 ENDP + +Asm_GetRflags PROC + pushfq + pop rax + ret +Asm_GetRflags ENDP + +Asm_GetIdtBase PROC + LOCAL idtr[10]:BYTE + + sidt idtr + mov rax, qword PTR idtr[2] + ret +Asm_GetIdtBase ENDP + +Asm_GetIdtLimit PROC + LOCAL idtr[10]:BYTE + + xor rax,rax + sidt idtr + mov ax, WORD PTR idtr[0] + ret +Asm_GetIdtLimit ENDP + +Asm_GetGdtBase PROC + LOCAL gdtr[10]:BYTE + + sgdt gdtr + mov rax, qword PTR gdtr[2] + ret +Asm_GetGdtBase ENDP + +Asm_GetGdtLimit PROC + LOCAL gdtr[10]:BYTE + + xor rax,rax + sgdt gdtr + mov ax, WORD PTR gdtr[0] + ret +Asm_GetGdtLimit ENDP + +Asm_GetLdtr PROC + sldt rax + ret +Asm_GetLdtr ENDP + +Asm_GetTr PROC + str rax + ret +Asm_GetTr ENDP + +Asm_SetGdtr Proc + push rcx + shl rdx, 16 + push rdx + + lgdt fword ptr [rsp+2] + pop rax + pop rax + ret +Asm_SetGdtr Endp + +Asm_SetIdtr Proc + push rcx + shl rdx, 16 + push rdx + lidt fword ptr [rsp+2] + pop rax + pop rax + ret +Asm_SetIdtr Endp + +Asm_Rdtsc Proc + mov rbx,rcx + mov rsi,rdx + + xor rax,rax + xor rdx,rdx + + rdtsc + mov [rbx],rax + mov [rsi],rdx + ret +Asm_Rdtsc Endp + +Asm_GetTSC PROC +; rdtscp + rdtsc + shl rdx, 32 + or rax, rdx + ret +Asm_GetTSC ENDP + + +Vmx_VmxOn Proc + push rcx + Vmxon qword ptr [rsp] + add rsp,8 + ret +Vmx_VmxOn Endp + +Vmx_VmxOff Proc + Vmxoff + ret +Vmx_VmxOff Endp + +Vmx_VmPtrld Proc + push rcx + vmptrld qword ptr [rsp] + add rsp,8 + ret +Vmx_VmPtrld endp + +Vmx_VmClear Proc + push rcx + vmclear qword ptr [rsp] + add rsp,8 + ret +Vmx_VmClear endp + +Vmx_VmRead Proc + mov rax,rcx + vmread rcx,rax + mov rax,rcx + ret +Vmx_VmRead endp + +Vmx_VmWrite Proc + mov rax,rcx + mov rcx,rdx + vmwrite rax,rcx + ret +Vmx_VmWrite endp + +; CmInvept (PVOID Ep4ta(rcx), ULONG inval (rdx) ); +Vmx_Invept proc + push rbp + mov rbp, rsp + push rsi + mov rsi, rcx + mov rax, rdx + invept rax, xmmword ptr [rsi] + pop rsi + mov rsp, rbp + pop rbp + ret +Vmx_Invept endp + +Vmx_VmCall Proc + push rax + push rcx + push rdx + push rbx + push rsp ;HOST_RSP + push rbp + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 ;pushaq + + + pushfq + mov rax,rcx + vmcall + + popfq + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rbp + pop rsp + pop rbx + pop rdx + pop rcx + pop rax ;popaq + + ret +Vmx_VmCall endp + +Vmx_VmLaunch Proc + vmlaunch + ret +Vmx_VmLaunch endp + +Vmx_VmResume Proc + vmresume + ret +Vmx_VmResume endp + +Asm_GetGuestRSP Proc + mov rax,GuestRSP + ret +Asm_GetGuestRSP Endp + +Asm_GetGuestReturn Proc + mov rax,GuestReturn + ret +Asm_GetGuestReturn Endp + +Asm_AfterVMXOff Proc + mov rsp,rcx + jmp rdx + ret +Asm_AfterVMXOff Endp + +Asm_RunToVMCS Proc + mov rax,[rsp] + mov GuestReturn,rax ;ȡصַvmlaunchͻִصĴ + + call SetupVMCS + ret +Asm_RunToVMCS Endp + +Asm_SetupVMCS Proc + cli + mov GuestRSP,rsp + + mov EntryRAX,rax + mov EntryRCX,rcx + mov EntryRDX,rdx + mov EntryRBX,rbx + mov EntryRSP,rsp + mov EntryEBP,rbp + mov EntryESI,rsi + mov EntryRDI,rdi + mov EntryR8,r8 + mov EntryR9,r9 + mov EntryR10,r10 + mov EntryR11,r11 + mov EntryR12,r12 + mov EntryR13,r13 + mov EntryR14,r14 + mov EntryR15,r15 + + pushfq + pop EntryRflags + + call Asm_RunToVMCS + + push EntryRflags + popfq + mov rax,EntryRAX + mov rcx,EntryRCX + mov rdx,EntryRDX + mov rbx,EntryRBX + mov rsp,EntryRSP + mov rbp,EntryEBP + mov rsi,EntryESI + mov rdi,EntryRDI + mov r8,EntryR8 + mov r9,EntryR9 + mov r10,EntryR10 + mov r11,EntryR11 + mov r12,EntryR12 + mov r13,EntryR13 + mov r14,EntryR14 + mov r15,EntryR15 + + mov rsp,GuestRSP + sti + ret +Asm_SetupVMCS Endp + +Asm_VMMEntryPoint Proc + cli + push rax + push rcx + push rdx + push rbx + push rsp ;HOST_RSP + push rbp + push rsi + push rdi + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + mov [rsp-1280h],rax + mov [rsp-1288h],rbx + mov [rsp-1290h],rcx + call GetGuestRegsAddress + mov rcx,[rsp-1290h] + mov [rax+8h],rcx + mov [rax+10h],rdx + mov [rax+18h],rbx + mov [rax+20h],rsp + mov [rax+28h],rbp + mov [rax+30h],rsi + mov [rax+38h],rdi + mov [rax+40h],r8 + mov [rax+48h],r9 + mov [rax+50h],r10 + mov [rax+58h],r11 + mov [rax+60h],r12 + mov [rax+68h],r13 + mov [rax+70h],r14 + mov [rax+78h],r15 + mov rbx,[rsp-1280h] + mov [rax],rbx + mov rax,[rsp-1280h] + mov rbx,[rsp-1288h] + + call VMMEntryPoint + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rdi + pop rsi + pop rbp + pop rsp + pop rbx + pop rdx + pop rcx + pop rax + + call GetGuestRegsAddress + mov rcx,[rax+8h] + mov rdx,[rax+10h] + mov rbx,[rax+18h] + mov rsp,[rax+20h] + mov rbp,[rax+28h] + mov rsi,[rax+30h] + mov rdi,[rax+38h] + mov r8,[rax+40h] + mov r9,[rax+48h] + mov r10,[rax+50h] + mov r11,[rax+58h] + mov r12,[rax+60h] + mov r13,[rax+68h] + mov r14,[rax+70h] + mov r15,[rax+78h] + mov rax,[rax] + sti + vmresume +Asm_VMMEntryPoint Endp + +END \ No newline at end of file diff --git a/PFHook_sys/base/vtasm.h b/PFHook_sys/base/vtasm.h new file mode 100644 index 0000000..29caf4e --- /dev/null +++ b/PFHook_sys/base/vtasm.h @@ -0,0 +1,191 @@ +/* +Author:Xiaobao +QQ:1121402724 +*/ +#pragma once + +typedef struct +{ + unsigned PE : 1; + unsigned MP : 1; + unsigned EM : 1; + unsigned TS : 1; + unsigned ET : 1; + unsigned NE : 1; + unsigned Reserved_1 : 10; + unsigned WP : 1; + unsigned Reserved_2 : 1; + unsigned AM : 1; + unsigned Reserved_3 : 10; + unsigned NW : 1; + unsigned CD : 1; + unsigned PG : 1; + unsigned Reserved_64 : 32; +}_CR0; + +typedef struct +{ + unsigned VME:1; + unsigned PVI:1; + unsigned TSD:1; + unsigned DE:1; + unsigned PSE:1; + unsigned PAE:1; + unsigned MCE:1; + unsigned PGE:1; + unsigned PCE:1; + unsigned OSFXSR:1; + unsigned PSXMMEXCPT:1; + unsigned UNKONOWN_1:1; //These are zero + unsigned UNKONOWN_2:1; //These are zero + unsigned VMXE:1; //It's zero in normal + unsigned Reserved:18; //These are zero + unsigned Reserved_64 : 32; +}_CR4; + +typedef struct +{ + unsigned CF:1; + unsigned Unknown_1:1; //Always 1 + unsigned PF:1; + unsigned Unknown_2:1; //Always 0 + unsigned AF:1; + unsigned Unknown_3:1; //Always 0 + unsigned ZF:1; + unsigned SF:1; + unsigned TF:1; + unsigned IF:1; + unsigned DF:1; + unsigned OF:1; + unsigned TOPL:2; + unsigned NT:1; + unsigned Unknown_4:1; + unsigned RF:1; + unsigned VM:1; + unsigned AC:1; + unsigned VIF:1; + unsigned VIP:1; + unsigned ID:1; + unsigned Reserved:10; //Always 0 + unsigned Reserved_64:32; //Always 0 +}_EFLAGS; + +typedef struct +{ + unsigned SSE3:1; + unsigned PCLMULQDQ:1; + unsigned DTES64:1; + unsigned MONITOR:1; + unsigned DS_CPL:1; + unsigned VMX:1; + unsigned SMX:1; + unsigned EIST:1; + unsigned TM2:1; + unsigned SSSE3:1; + unsigned Reserved:22; + unsigned Reserved_64:32; +}_CPUID_ECX; + +typedef struct _IA32_FEATURE_CONTROL_MSR +{ + unsigned Lock :1; // Bit 0 is the lock bit - cannot be modified once lock is set + unsigned Reserved1 :1; // Undefined + unsigned EnableVmxon :1; // Bit 2. If this bit is clear, VMXON causes a general protection exception + unsigned Reserved2 :29; // Undefined + unsigned Reserved3 :32; // Undefined + +} IA32_FEATURE_CONTROL_MSR; + +typedef struct _VMX_BASIC_MSR +{ + unsigned RevId: 32;//汾Ϣ + unsigned szVmxOnRegion: 12; + unsigned ClearBit: 1; + unsigned Reserved: 3; + unsigned PhysicalWidth: 1; + unsigned DualMonitor: 1; + unsigned MemoryType: 4; + unsigned VmExitInformation: 1; + unsigned Reserved2: 9; +} VMX_BASIC_MSR, *PVMX_BASIC_MSR; + +extern "C" ULONG64 Asm_GetRflags(); +extern "C" ULONG64 Asm_GetCs(); +extern "C" ULONG64 Asm_GetDs(); +extern "C" ULONG64 Asm_GetEs(); +extern "C" ULONG64 Asm_GetFs(); +extern "C" ULONG64 Asm_GetGs(); +extern "C" ULONG64 Asm_GetSs(); +extern "C" ULONG64 Asm_GetLdtr(); +extern "C" ULONG64 Asm_GetTr(); + +extern "C" void Asm_SetGdtr(ULONG64 uBase,ULONG64 uLimit); +extern "C" void Asm_SetIdtr(ULONG64 uBase,ULONG64 uLimit); + +extern "C" ULONG64 Asm_GetGdtBase(); +extern "C" ULONG64 Asm_GetIdtBase(); +extern "C" ULONG64 Asm_GetGdtLimit(); +extern "C" ULONG64 Asm_GetIdtLimit(); + +extern "C" ULONG64 Asm_GetCr0(); +extern "C" ULONG64 Asm_GetCr2(); +extern "C" ULONG64 Asm_GetCr3(); +extern "C" ULONG64 Asm_GetCr4(); +extern "C" void Asm_SetCr0(ULONG64 uNewCr0); +extern "C" void Asm_SetCr2(ULONG64 uNewCr2); +extern "C" void Asm_SetCr3(ULONG64 uNewCr3); +extern "C" void Asm_SetCr4(ULONG64 uNewCr4); + +extern "C" ULONG64 Asm_GetDr0(); +extern "C" ULONG64 Asm_GetDr1(); +extern "C" ULONG64 Asm_GetDr2(); +extern "C" ULONG64 Asm_GetDr3(); +extern "C" ULONG64 Asm_GetDr6(); +extern "C" ULONG64 Asm_GetDr7(); +extern "C" void Asm_SetDr0(ULONG64 uNewDr0); +extern "C" void Asm_SetDr1(ULONG64 uNewDr1); +extern "C" void Asm_SetDr2(ULONG64 uNewDr2); +extern "C" void Asm_SetDr3(ULONG64 uNewDr3); +extern "C" void Asm_SetDr6(ULONG64 uNewDr6); +extern "C" void Asm_SetDr7(ULONG64 uNewDr7); + +extern "C" ULONG64 Asm_ReadMsr(ULONG64 uIndex); +extern "C" void Asm_WriteMsr(ULONG64 uIndex,ULONG64 QuadPart); + +extern "C" void Asm_CPUID(ULONG64 uFn,PULONG64 uRet_EAX,PULONG64 uRet_EBX,PULONG64 uRet_ECX,PULONG64 uRet_EDX); +extern "C" void Asm_Invd(); +extern "C" void Asm_Rdtsc(PULONG64 uRet_EAX, PULONG64 uRet_EDX); +extern "C" ULONG64 Asm_GetTSC(); + +extern "C" void Vmx_VmxOn(ULONG64 QuadPart); +extern "C" void Vmx_VmxOff(); +extern "C" void Vmx_VmClear(ULONG64 QuadPart); +extern "C" void Vmx_VmPtrld(ULONG64 QuadPart); +extern "C" ULONG64 Vmx_VmRead(ULONG64 uField); +extern "C" void Vmx_VmWrite(ULONG64 uField,ULONG64 uValue); +extern "C" void Vmx_VmLaunch(); +extern "C" void Vmx_VmResume(); +extern "C" void Vmx_VmCall(ULONG64 uCallNumber); +extern "C" void Vmx_Invept(PVOID Ep4ta, ULONG64 inval); + +extern "C" void Asm_VMMEntryPoint(); + +extern "C" void Asm_SetupVMCS(); + +extern "C" ULONG64 Asm_GetGuestReturn(); +extern "C" ULONG64 Asm_GetGuestRSP(); + +extern "C" void Asm_AfterVMXOff(ULONG64 JmpESP,ULONG64 JmpEIP); + +extern "C" void Asm_MyNtOpenProcess(); +extern "C" void Asm_MyNtOpenThread(); +extern "C" void Asm_MyNtCreateUserProcess(); +extern "C" void Asm_MyNtCreateThreadEx(); +extern "C" void Asm_MyNtCreateFile_Win7(); +extern "C" void Asm_MyNtCreateFile_Win10(); +extern "C" void Asm_MyNtLoadDriver(); +extern "C" void Asm_MyNtCreateKey(); +extern "C" void Asm_MyNtQueryKey_Win7(); +extern "C" void Asm_MyNtQueryKey_Win10(); +extern "C" void Asm_MyNtDeleteFile(); +extern "C" void Asm_MyNtReadVirtualMemory(); \ No newline at end of file diff --git a/PFHook_sys/base/vtsystem.cpp b/PFHook_sys/base/vtsystem.cpp new file mode 100644 index 0000000..4fbac3e --- /dev/null +++ b/PFHook_sys/base/vtsystem.cpp @@ -0,0 +1,388 @@ +#include "header.h" +#include "vtsystem.h" +#include "vtasm.h" +#include "exithandler.h" +#include "common.h" +#include "ept.h" + +VMX_CPU g_VMXCPU[128]; + +KMUTEX g_GlobalMutex; +EptPml4Entry* g_Pml4; +NTSTATUS AllocateVMXRegion() +{ + PVOID pVMXONRegion; + PVOID pVMCSRegion; + PVOID pHostEsp; + ULONG64 uCPUID; + + uCPUID = KeGetCurrentProcessorNumber(); + pVMXONRegion = ExAllocatePoolWithTag(NonPagedPool,0x1000,'vmon'); //4KB + if (!pVMXONRegion) + { + return STATUS_MEMORY_NOT_ALLOCATED; + } + RtlZeroMemory(pVMXONRegion,0x1000); + + pVMCSRegion = ExAllocatePoolWithTag(NonPagedPool,0x1000,'vmcs'); + if (!pVMCSRegion) + { + ExFreePoolWithTag(pVMXONRegion,0x1000); + return STATUS_MEMORY_NOT_ALLOCATED; + } + RtlZeroMemory(pVMCSRegion,0x1000); + + pHostEsp = ExAllocatePoolWithTag(NonPagedPool,0x2000,'mini'); + if (!pHostEsp) + { + ExFreePoolWithTag(pVMXONRegion,0x1000); + ExFreePoolWithTag(pVMCSRegion,0x1000); + return STATUS_MEMORY_NOT_ALLOCATED; + } + RtlZeroMemory(pHostEsp,0x2000); + + g_VMXCPU[uCPUID].pVMXONRegion = pVMXONRegion; + g_VMXCPU[uCPUID].pVMXONRegion_PA = MmGetPhysicalAddress(pVMXONRegion); + g_VMXCPU[uCPUID].pVMCSRegion = pVMCSRegion; + g_VMXCPU[uCPUID].pVMCSRegion_PA = MmGetPhysicalAddress(pVMCSRegion); + g_VMXCPU[uCPUID].pHostEsp = pHostEsp; + g_VMXCPU[uCPUID].ept_PML4T = g_Pml4; + return STATUS_SUCCESS; +} + +void SetupVMXRegion() +{ + VMX_BASIC_MSR Msr; + ULONG uRevId; + _CR4 uCr4; + _EFLAGS uEflags; + ULONG64 uCPUID; + + uCPUID = KeGetCurrentProcessorNumber(); + + RtlZeroMemory(&Msr,sizeof(Msr)); + + *((PULONG64)&Msr) = Asm_ReadMsr(MSR_IA32_VMX_BASIC); + uRevId = Msr.RevId; + + *((PULONG)g_VMXCPU[uCPUID].pVMXONRegion) = uRevId; + *((PULONG)g_VMXCPU[uCPUID].pVMCSRegion) = uRevId; + + *((PULONG64)&uCr4) = Asm_GetCr4(); + uCr4.VMXE = 1; + Asm_SetCr4(*((PULONG64)&uCr4)); + + Vmx_VmxOn(g_VMXCPU[uCPUID].pVMXONRegion_PA.QuadPart); + *((PULONG64)&uEflags) = Asm_GetRflags(); + if (uEflags.CF != 0) + { + return; + } +} + +extern "C" void SetupVMCS() +{ + _EFLAGS uEflags; + ULONG64 GdtBase,IdtBase; + SEGMENT_SELECTOR SegmentSelector; + ULONG64 uCPUBase; + ULONG64 uCPUID; + ULONG64 uTemp64; + + uCPUID = KeGetCurrentProcessorNumber(); + + Vmx_VmClear(g_VMXCPU[uCPUID].pVMCSRegion_PA.QuadPart); + *((PULONG64)&uEflags) = Asm_GetRflags(); + if (uEflags.CF != 0 || uEflags.ZF != 0) + { + return; + } + Vmx_VmPtrld(g_VMXCPU[uCPUID].pVMCSRegion_PA.QuadPart); + + GdtBase = Asm_GetGdtBase(); + IdtBase = Asm_GetIdtBase(); + + // + // 1.Guest State Area + // + Vmx_VmWrite(GUEST_CR0,Asm_GetCr0()); + Vmx_VmWrite(GUEST_CR3,Asm_GetCr3()); + Vmx_VmWrite(GUEST_CR4,Asm_GetCr4()); + + Vmx_VmWrite(GUEST_DR7,0x400); + Vmx_VmWrite(GUEST_RFLAGS,Asm_GetRflags()); + + FillGuestSelectorData(GdtBase,ES,Asm_GetEs()); + FillGuestSelectorData(GdtBase,FS,Asm_GetFs()); + FillGuestSelectorData(GdtBase,DS,Asm_GetDs()); + FillGuestSelectorData(GdtBase,CS,Asm_GetCs()); + FillGuestSelectorData(GdtBase,SS,Asm_GetSs()); + FillGuestSelectorData(GdtBase,GS,Asm_GetGs()); + FillGuestSelectorData(GdtBase,TR,Asm_GetTr()); + FillGuestSelectorData(GdtBase,LDTR,Asm_GetLdtr()); + + Vmx_VmWrite(GUEST_CS_BASE,0); + Vmx_VmWrite(GUEST_DS_BASE,0); + Vmx_VmWrite(GUEST_ES_BASE,0); + Vmx_VmWrite(GUEST_SS_BASE,0); + Vmx_VmWrite(GUEST_FS_BASE,Asm_ReadMsr(MSR_FS_BASE)); + Vmx_VmWrite(GUEST_GS_BASE,Asm_ReadMsr(MSR_GS_BASE)); + Vmx_VmWrite(GUEST_GDTR_BASE,GdtBase); + Vmx_VmWrite(GUEST_GDTR_LIMIT,Asm_GetGdtLimit()); + Vmx_VmWrite(GUEST_IDTR_BASE,IdtBase); + Vmx_VmWrite(GUEST_IDTR_LIMIT,Asm_GetIdtLimit()); + + Vmx_VmWrite(GUEST_IA32_DEBUGCTL,Asm_ReadMsr(MSR_IA32_DEBUGCTL)); + Vmx_VmWrite(GUEST_IA32_DEBUGCTL_HIGH,Asm_ReadMsr(MSR_IA32_DEBUGCTL)>>32); + Vmx_VmWrite(GUEST_IA32_EFER,Asm_ReadMsr(MSR_EFER)); + + Vmx_VmWrite(GUEST_SYSENTER_CS,Asm_ReadMsr(MSR_IA32_SYSENTER_CS)); + Vmx_VmWrite(GUEST_SYSENTER_ESP,Asm_ReadMsr(MSR_IA32_SYSENTER_ESP)); + Vmx_VmWrite(GUEST_SYSENTER_EIP,Asm_ReadMsr(MSR_IA32_SYSENTER_EIP)); // KiFastCallEntry + + Vmx_VmWrite(GUEST_RSP,Asm_GetGuestRSP()); + Vmx_VmWrite(GUEST_RIP,Asm_GetGuestReturn());// ָvmlaunchͻڵ ÿͻִмĴ + + Vmx_VmWrite(GUEST_INTERRUPTIBILITY_INFO, 0); + Vmx_VmWrite(GUEST_ACTIVITY_STATE, 0); + Vmx_VmWrite(VMCS_LINK_POINTER, 0xffffffff); + Vmx_VmWrite(VMCS_LINK_POINTER_HIGH, 0xffffffff); + + // + // 2.Host State Area + // + Vmx_VmWrite(HOST_CR0,Asm_GetCr0()); + Vmx_VmWrite(HOST_CR3,Asm_GetCr3()); + Vmx_VmWrite(HOST_CR4,Asm_GetCr4()); + + Vmx_VmWrite(HOST_ES_SELECTOR,KGDT64_R0_DATA); + Vmx_VmWrite(HOST_CS_SELECTOR,KGDT64_R0_CODE); + Vmx_VmWrite(HOST_SS_SELECTOR,KGDT64_R0_DATA); + Vmx_VmWrite(HOST_DS_SELECTOR,KGDT64_R0_DATA); + Vmx_VmWrite(HOST_FS_SELECTOR,(Asm_GetFs()&0xF8)); + Vmx_VmWrite(HOST_GS_SELECTOR,(Asm_GetGs()&0xF8)); + Vmx_VmWrite(HOST_TR_SELECTOR,(Asm_GetTr()&0xF8)); + + Vmx_VmWrite(HOST_FS_BASE,Asm_ReadMsr(MSR_FS_BASE)); + Vmx_VmWrite(HOST_GS_BASE,Asm_ReadMsr(MSR_GS_BASE)); + InitializeSegmentSelector(&SegmentSelector,Asm_GetTr(),GdtBase); + Vmx_VmWrite(HOST_TR_BASE,SegmentSelector.base); + + Vmx_VmWrite(HOST_GDTR_BASE,GdtBase); + Vmx_VmWrite(HOST_IDTR_BASE,IdtBase); + + Vmx_VmWrite(HOST_IA32_EFER,Asm_ReadMsr(MSR_EFER)); + Vmx_VmWrite(HOST_IA32_SYSENTER_CS,Asm_ReadMsr(MSR_IA32_SYSENTER_CS)); + Vmx_VmWrite(HOST_IA32_SYSENTER_ESP,Asm_ReadMsr(MSR_IA32_SYSENTER_ESP)); + Vmx_VmWrite(HOST_IA32_SYSENTER_EIP,Asm_ReadMsr(MSR_IA32_SYSENTER_EIP)); // KiFastCallEntry + + Vmx_VmWrite(HOST_RSP,((ULONG64)g_VMXCPU[uCPUID].pHostEsp) + 0x1FFF);//8KB 0x2000 + Vmx_VmWrite(HOST_RIP,(ULONG64)&Asm_VMMEntryPoint);//ﶨǵVMM + + // + // 3.п + // + Vmx_VmWrite(PIN_BASED_VM_EXEC_CONTROL,VmxAdjustControls(0,MSR_IA32_VMX_PINBASED_CTLS)); + + Vmx_VmWrite(PAGE_FAULT_ERROR_CODE_MASK,0); + Vmx_VmWrite(PAGE_FAULT_ERROR_CODE_MATCH,0); + Vmx_VmWrite(TSC_OFFSET,0); + Vmx_VmWrite(TSC_OFFSET_HIGH,0); + + uCPUBase = VmxAdjustControls(0,MSR_IA32_VMX_PROCBASED_CTLS); + +/* uCPUBase &= ~CPU_BASED_ACTIVATE_MSR_BITMAP;*/ + uCPUBase &= ~CPU_BASED_CR3_LOAD_EXITING; + uCPUBase &= ~CPU_BASED_CR3_STORE_EXITING; + uCPUBase |= CPU_BASED_ACTIVATE_SECONDARY_CONTROLS; +// uCPUBase &= ~CPU_BASED_CR8_LOAD_EXITING; +// uCPUBase &= ~CPU_BASED_CR8_STORE_EXITING; // УȻزTF + uCPUBase |= CPU_BASED_RDTSC_EXITING; + //uCPUBase |= CPU_BASED_MOV_DR_EXITING; // صԼĴ + //uCPUBase |= CPU_BASED_USE_IO_BITMAPS; // ؼϢ + //uCPUBase |= CPU_BASED_ACTIVATE_MSR_BITMAP; // MSR + + Vmx_VmWrite(CPU_BASED_VM_EXEC_CONTROL,uCPUBase); + + uTemp64 = 0; + //ָEPT ڴҳ ڴõ ʱҪ + uTemp64 |= SECONDARY_EXEC_RDTSCP; + uTemp64 |= SECONDARY_EXEC_ENABLE_EPT; + uTemp64 |= SECONDARY_EXEC_ENABLE_VPID; + Vmx_VmWrite(SECONDARY_VM_EXEC_CONTROL, uTemp64); + + Vmx_VmWrite(IO_BITMAP_A,0); + Vmx_VmWrite(IO_BITMAP_A_HIGH,0); + Vmx_VmWrite(IO_BITMAP_B,0); + Vmx_VmWrite(IO_BITMAP_B_HIGH,0); + Vmx_VmWrite(MSR_BITMAP, 0); + Vmx_VmWrite(MSR_BITMAP_HIGH, 0); + + uTemp64 = 0; + uTemp64 |= 1 << DEBUG_EXCEPTION; + uTemp64 |= 1 << PAGE_FAULT_EXCEPTION; + + Vmx_VmWrite(EXCEPTION_BITMAP, uTemp64); + + + Vmx_VmWrite(CR3_TARGET_COUNT,0); + Vmx_VmWrite(CR3_TARGET_VALUE0,0); + Vmx_VmWrite(CR3_TARGET_VALUE1,0); + Vmx_VmWrite(CR3_TARGET_VALUE2,0); + Vmx_VmWrite(CR3_TARGET_VALUE3,0); + + // + // 4.VMEntryп + // + Vmx_VmWrite(VM_ENTRY_CONTROLS,VmxAdjustControls(VM_ENTRY_IA32E_MODE|VM_ENTRY_LOAD_IA32_EFER,MSR_IA32_VMX_ENTRY_CTLS)); + Vmx_VmWrite(VM_ENTRY_MSR_LOAD_COUNT,0); + Vmx_VmWrite(VM_ENTRY_INTR_INFO_FIELD,0); + + + // + // 5.VMExitп + // + Vmx_VmWrite(VM_EXIT_CONTROLS,VmxAdjustControls(VM_EXIT_IA32E_MODE|VM_EXIT_ACK_INTR_ON_EXIT,MSR_IA32_VMX_EXIT_CTLS)); + Vmx_VmWrite(VM_EXIT_MSR_LOAD_COUNT,0); + Vmx_VmWrite(VM_EXIT_MSR_STORE_COUNT,0); + + EptTablePointer EPTP = { 0 }; + PHYSICAL_ADDRESS phys = MmGetPhysicalAddress((void *)g_VMXCPU[uCPUID].ept_PML4T); + + // Set up the EPTP + EPTP.Bits.PhysAddr = phys.QuadPart >> 12; + EPTP.Bits.MemoryType = EPT_MEMORY_TYPE_WB; + EPTP.Bits.PageWalkLength = 3; + EPTP.Bits.D = 1; + Vmx_VmWrite(EPT_POINTER, EPTP.unsignedVal & 0xFFFFFFFF); + Vmx_VmWrite(EPT_POINTER_HIGH, EPTP.unsignedVal >> 32); + Vmx_VmWrite(VIRTUAL_PROCESSOR_ID, uCPUID + 1); + + Vmx_VmLaunch(); + + g_VMXCPU[uCPUID].bVTStartSuccess = FALSE; +} + +void SetupVT() +{ + ULONG64 uCPUID; + + uCPUID = KeGetCurrentProcessorNumber(); + NTSTATUS status = STATUS_SUCCESS; + if (!IsVTEnabled()) + return; + status = AllocateVMXRegion(); + if (!NT_SUCCESS(status)) + { + return; + } + SetupVMXRegion(); + g_VMXCPU[uCPUID].bVTStartSuccess = TRUE; + + Asm_SetupVMCS(); + + if (g_VMXCPU[uCPUID].bVTStartSuccess) + { + + } + else KdPrint(("VT Engine Error(%d)!\n",KeGetCurrentProcessorNumber())); +} + +void UnsetupVT() +{ + _CR4 uCr4; + ULONG64 uCPUID; + uCPUID = KeGetCurrentProcessorNumber(); + if(g_VMXCPU[uCPUID].bVTStartSuccess) + { + Vmx_VmCall('SVT'); + + *((PULONG64)&uCr4) = Asm_GetCr4(); + uCr4.VMXE = 0; + Asm_SetCr4(*((PULONG64)&uCr4)); + + ExFreePoolWithTag(g_VMXCPU[uCPUID].pVMXONRegion,'vmon'); + ExFreePoolWithTag(g_VMXCPU[uCPUID].pVMCSRegion,'vmcs'); + ExFreePoolWithTag(g_VMXCPU[uCPUID].pHostEsp,'mini'); + + } +} + +NTSTATUS StartVirtualTechnology() +{ + KeInitializeMutex(&g_GlobalMutex,0); + KeWaitForMutexObject(&g_GlobalMutex,Executive,KernelMode,FALSE,0); + g_Pml4 = EptInitialization(); + + for (int i = 0;i +#include +} +#endif \ No newline at end of file