From 86dc487f773a397dadab575b045fb7759d45cae1 Mon Sep 17 00:00:00 2001 From: tandasat Date: Mon, 1 Jun 2015 23:07:18 -0700 Subject: [PATCH] Add x64 support --- README.md | 31 +- .../Release/RemoteWriteMonitor.sys | Bin 11776 -> 0 bytes RemoteWriteMonitor/Release/injector.exe | Bin 23040 -> 0 bytes RemoteWriteMonitor/RemoteWriteMonitor.sln | 99 +- .../RemoteWriteMonitor/Arch/AMD64/amd64.asm | 68 + .../RemoteWriteMonitor/Arch/x86/asm.cpp | 51 + .../RemoteWriteMonitor/RemoteWriteMonitor.cpp | 1127 ++++++--------- .../RemoteWriteMonitor.vcxproj | 393 +++-- .../RemoteWriteMonitor.vcxproj.filters | 126 +- .../RemoteWriteMonitor.vcxproj.user | 34 +- RemoteWriteMonitor/RemoteWriteMonitor/asm.h | 50 + .../RemoteWriteMonitor/check.cpp | 416 ++++++ RemoteWriteMonitor/RemoteWriteMonitor/check.h | 45 + .../RemoteWriteMonitor/inline.cpp | 158 ++ .../RemoteWriteMonitor/inline.h | 60 + RemoteWriteMonitor/RemoteWriteMonitor/log.cpp | 1280 ++++++++--------- RemoteWriteMonitor/RemoteWriteMonitor/log.h | 212 +-- .../RemoteWriteMonitor/satoshitanda.pfx | Bin 0 -> 2614 bytes .../RemoteWriteMonitor/ssdt.cpp | 146 ++ RemoteWriteMonitor/RemoteWriteMonitor/ssdt.h | 46 + .../RemoteWriteMonitor/stdafx.h | 205 +-- .../RemoteWriteMonitor/util.cpp | 143 ++ RemoteWriteMonitor/RemoteWriteMonitor/util.h | 51 + RemoteWriteMonitor/clean.bat | 12 +- RemoteWriteMonitor/clean_for_release.bat | 9 + RemoteWriteMonitor/injector/injector.vcxproj | 253 ++-- .../injector/injector.vcxproj.user | 28 +- 27 files changed, 3193 insertions(+), 1850 deletions(-) delete mode 100644 RemoteWriteMonitor/Release/RemoteWriteMonitor.sys delete mode 100644 RemoteWriteMonitor/Release/injector.exe create mode 100644 RemoteWriteMonitor/RemoteWriteMonitor/Arch/AMD64/amd64.asm create mode 100644 RemoteWriteMonitor/RemoteWriteMonitor/Arch/x86/asm.cpp create mode 100644 RemoteWriteMonitor/RemoteWriteMonitor/asm.h create mode 100644 RemoteWriteMonitor/RemoteWriteMonitor/check.cpp create mode 100644 RemoteWriteMonitor/RemoteWriteMonitor/check.h create mode 100644 RemoteWriteMonitor/RemoteWriteMonitor/inline.cpp create mode 100644 RemoteWriteMonitor/RemoteWriteMonitor/inline.h create mode 100644 RemoteWriteMonitor/RemoteWriteMonitor/satoshitanda.pfx create mode 100644 RemoteWriteMonitor/RemoteWriteMonitor/ssdt.cpp create mode 100644 RemoteWriteMonitor/RemoteWriteMonitor/ssdt.h create mode 100644 RemoteWriteMonitor/RemoteWriteMonitor/util.cpp create mode 100644 RemoteWriteMonitor/RemoteWriteMonitor/util.h create mode 100644 RemoteWriteMonitor/clean_for_release.bat diff --git a/README.md b/README.md index 3dcf413..c0d2e61 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,18 @@ thread context manipulation. A supporting tool 'injector' is a sample program doing that type of code injection. -A related blog entory can be found [here](http://standa-note.blogspot.ca/2015/03/section-based-code-injection-and-its.html). +A related blog entory can be found here: + + http://standa-note.blogspot.ca/2015/03/section-based-code-injection-and-its.html). Installation and Uninstallation ----------------- -Use the 'sc' command, for example, for installation: +Get an archive file for compiled files form this link: + + https://github.com/tandasat/RemoteWriteMonitor/releases/latest + +Then use the 'sc' command. For installation: >sc create rwmon type= kernel binPath= C:\Users\user\Desktop\RemoteWriteMonitor.sys >sc start rwmon @@ -23,6 +29,16 @@ For uninstallation: >sc stop rwmon >sc delete rwmon + +On the x64 bit platform, you have to enable test signing to install the driver. +To do that, open the command prompt with the administrator privilege and type +the following command: + + >bcdedit /set {current} testsigning on + +Then, reboot the system to activate the change. You also have to disable the +Kernel Patch Protection (PatchGuard), and Google helps you do that work. + Usage ------- @@ -36,8 +52,8 @@ what was written or mapped into the remote process. Output can be seen with DebugView and are all saved under the C:\Windows\RemoteWriteMonitor\ directory. Written and mapped data is stored as \.bin apart from a log file. -'injector' could be used to test the driver's function. Injecting and executing code into -notepad.exe could be done by the following commands: +'injector' could be used to test the driver's function. Injecting and executing +code into notepad.exe can be done by the following commands: >notepad && tasklist | findstr notepad notepad.exe 3368 Console 1 4,564 K @@ -54,6 +70,9 @@ notepad.exe could be done by the following commands: Output on DebugView would look like this: ![DebugView](/img/injector.png) +Note that the injector only works against 32 bit processes. + + Caveats ------- - It reports all those API calls regardless of its memory protection, contents @@ -62,7 +81,7 @@ output related to the sample you are analyzing as it reports a lot of legit activities too. - It was designed so because it is far more difficult to track all written -regions and reports only when it is executed (I wrote [it](https://sites.google.com/site/tandasat/home/egg) long time ago, and it was hell). +regions and reports only when it is executed. - It does not monitor any of processes existed when the driver was installed. Thus, the second injection will not be reported if the sample injects code @@ -79,7 +98,7 @@ may be happening. Supported Platform(s) ----------------- -- Windows 7 SP1 x86 +- Windows 7 SP1 and 8.1 (x86/x64) License diff --git a/RemoteWriteMonitor/Release/RemoteWriteMonitor.sys b/RemoteWriteMonitor/Release/RemoteWriteMonitor.sys deleted file mode 100644 index 65a9b682344a858b6bb820cd26422553acdf26c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11776 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 Bgy8@H diff --git a/RemoteWriteMonitor/Release/injector.exe b/RemoteWriteMonitor/Release/injector.exe deleted file mode 100644 index a5a530ff9451eea52ed715c193879634fc5f6904..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23040 zcmeHv4|EgPmG`qP8vz1~)Fvj7MI@%sLWphswI$&(SjIFq$Qav@1Y~2&$d0ibX#}Q$ z1S)7z2eqVSyXh`p3ro6RHp}kPZoUo4W*ZTx4TKPrP(oY2)ZKN{A<)(|1WZDi{oOYs zBlD+eyL)=hp3~vndH?Ud@4ox)z3+`o^}{<@24hSIP!z@vAf-p(@8A8B(L8?Y%j4M# zqu-czAg$_+X-flbVN+KqxH{Cd#?;)@*%=f~tNf-=q|?;aX)0e_Yg!X*@y{7IZcM(0 zy6pNlreuD5XmJwsZ_HS%Ls|bu-r|WoowPWOr_*_A_{C$38Sj5}aU;_1_wHRhinrg- z$92E=X`U`=YYq_3ctXX$BU(+rFipk}68JK2HM2oR`@7v<9# z%VcR(F`Tj;D3H+bWH4bf??`=V08fV#s$~1B8Ec_T7i0Y@Dk|N0HZisX_;!~uMl9Uv znOSku+6at^x5K9)m?QeTMdTlP5HI4Z0HE=W9DhuVHO>jOG>J`&ZOB8F0JsNW0i-+v zTIQ(T*r`l3Gy(|MB0$O`FqW7T3Wu6e$LESUlMLAsK*}Q!zL38oh>kROwh5307+xXd z@d%8~LQ{FqH~S{`z#rmLEEfCm>KcdZS!G$ye+^H0o|WBx(2((92)H)AW##3E;78~b}a*V4?1nCF7iqtA{%jW(&; z(Caxh3Y~gAeKc0e*=$a<#ypAE;`E5leDwTO=~#Sy`uVK*-%xX_lomah-numr`Eb^o z(#HOiS8JBUs}u66X^h1^2@ptk_GCxDiLTBw*MwiH0X~mhi?y0!z(ALY@Ti(PlbE3B*hci||`-Zi~vZ7Dr z=))7G1x7)UWpCC>JK}^t_Len655+LeHaH^g@qUt%NQXL^v5@)n0 zkE9<3Myy51phOZ%?o!UZPV!uUD1~#)*A<0C(hG#0{@+ z;w%zEXslu%9(|iRMtk#|*CRiy9quD}^iuVume@FA)wl8y>E|mBN!0^^u6%(>X^K}T zjXNM9+b916$>fNVyAQ%W9<`)i+4BI>|ii^p*EXMP557PtABOI;0b=9D9w*-OJtcuouRF zQuMGc$>aVssVcu^OUnW~ppEJxOC@ z8#1?JgX>yDpns0Qk{aiQ%;aYfqG%fT9Ek)?x(-I5JinaO3+<2UQ4o-a z(Jap|;N2>DH&og{dz%PU(?P4O7>Lnq$eH+VIQmFo@<(a@%DO#2)mMN zM5|ZHH6YoV5buk7hG=tX#ZN^yUSncbbmI^cM@c;^$>^Y!RLh*;C2k+!w*razpmI^k z%^_+l^wLn^6lxk`VVz{nLtSqMr*}Fqz^mu$mD7PiHETe2NX;6N)tA$_`8mkuso6Z` zv@bS)igak@BP$MZdo&2EQ!hmf4edgJf-?SUXZ4U84?~sQDWF+TYHmWEiD)Cur&Rti zY^by$KMxvrm$Xr@`kdYc6hE=^9q|Pwj1p+u)JR9Yc?UAw*Uk7RQG`M9S)<~b6tI|3~6>gQsfN? z>VEl|(W-s*^uujN2Dc`#de}D#{F0+u7Ij{wZh$kD3_-|{q}Z#0Aw(A6y@jRC7mn*0 z)0Fro?d>K7g`#ffii*vuoQ43Up?dh2*>GG2D8oc^LfW2x3OI(N3nC0BXhbg~wz(rF z=I6*)(W7@u{#hjSa4}%EI=#{2g>Amx3J}h1ah~OF1k|`4k|? zZa`?uErPc-`*eAJqmo+yB-F**pyXPRhn=`zjmKe69jI_3wh-?g*(LIy=F8`{VBw|m z{3?vND2*f2ODd{lOl62mrYN~C)POxb*dxorU3-C?rLxzf&MF;j&ZY?AULXq`Nyk<| z>;raNM&VSBWtOw1(R2)`ZX^~3ASHMBOUy>XIwPt%V1!MJzmi`7L(WaxOaFpK=tstD zAAE=K$F}6lXp3**UAzsko%yFwMx>@8+fB$K`g#g0+S9l?<#v<_ti1)Ckle^bW0%vI zN{j(BdERy%k{sHB5L6Y%Bt8szV12WpcwMEvH3N}zYiVSTYfj@ z$lsYSYa&ZS^kG5t(Ex5Ow8~mNLw&whpRXwmsXD3!ChB47T^Qlg^bXrlQ$23E-T(OR zKfWve4Fv23H_;(Q{HnSdTk`c;TnBYXl4}y0s;@7toOJcs^+$>~L90rdMaPZMfl{w1E_79~xy)-XR>5F+T!J1U|cz)Z~sgF)Am|_h4JSremDS4s1I3vIc#Fx1q-CRo+4n z3?EA~KcD558Vu-veI!4_wt%YZrHfJ)lzpbMR_B#}1%*{?!>6DTCbzeOZ`hSdOQIs( zv|i7|v9N_@x}GvUY~;vhI?sUbT99cNflXSblVH~9oMwp6*S!S73=UW?J*guX4gVZH zqHqU2Y1lzWk5Y{uE}C36&R`yWEDLKMTd6~+qDrJ*dR&L#>oX0}HrW@LlEvE)(YUc;yJh>01erWW1);G$(6P zlmS_+4arNECK@vU6(nqE~z4K46t zvEdqi)L>Sy5c!+0)0EatjO*4O?9V{#>xB)HhJY6VVV0767_)+(qx0-oMA8lN80k zM8>x=HeZ)i4t2h8etow%zvzXTwbEN$C`R&;%9SHGyX(cNaQ3J#u8h*0&<_ zfwY_>ohwyekkZgEG`~9j}m=bIoE{+Q?uv)o%5_tOVIU} z8%^LIKFOF*>3I`c;I?#}=Zs@Y?k12wpv$KVmW=~w)MihNHb5@PE~qB0P;$4^ydr&& zjvjkIcN-P6Xu+xq91v|LNuMrxaG7+c;O)l#Xdi>IAOb`?fhhZ5kJJ#-X9y`$zCb-I zVOh!ErN`1>nqUsoVB%m&4yIUK-Z)RktEtCL=2E-=si}wDaK5A1bY$QwKZ0dd&xWrV zu=DtSqupD)-k3day)n9;-{XvyR^&K8mOjXycLfVSS*kO}pXGAJePpXMJ#sHDC8MvU zFN*xyoPfu;-}!Mk&$kjL7hA>1vR|Z5lM~VY>%#ed7?qE+{}P8_-lpyIWCHrwPyM&- zry87wu#co4wdZK+t?m3l+WD8o`*4v8A>q70Dh%JkldM5lx>$uFw#axs-F#Vk;tz5L zSWFy%eX)#W+)Gz)uulQDi4HRqO+%o$`B*dxhvd$?*$CT3b5iFs_&es4ciZ{Qr1{k0 zKJ%6-ogJQ1tUl*|Y*q$!I!2`q=LXE>ZzrmA0srLEtMvh)iJZo9Om<5`)W<7~u+=)c zNEnVCXue6=JZ?}PNAyjy8KIKcDWjyccG@ev{sw;i?}cA^+#r=1mB({5Pm1xEJ^D28 z*qf#EAch-*Kr?x*iyf4VYlt^`(7#stvGaBD3EX*Z)W;t;B0PBHc2~Gh>h$3}DNDO~ zt{9{UE_vH+kSYdaqk1dIK*9>d78s=pU2FkecUS1>5=f0rT4NQ1bQjv27hg2!d_A&L zg8#rJ2v!wu&W|}Ul0#`UH-1u;K50}MRYCFIJiby*5w}C*H9ItZ<()>Q%E$G4$mc)~ zI;E`Gfu$qMz=xwHgM(5DF>A2`M_Nre9LgSaiWqof4+Vz^jJJ7r9pba=)8)6<_wrr za$kY6&AM8=5r%7{(fL87Dz3{et}u>h=d>z}p*(3~4NPv2E}h9UVcv-h?2&;Z$1i%O@_ByXG}&oN65|{O-4BI0jl1hKkOyLN2;6ekH{RtXi7sco6$G3 zrHiqM996Eb)k#C=XGkk^DDZp!$}gnXPo6vZzVq1HPaVM54<+uJH0z-A%-YOXd1-N) zW}Wy~D{Nj*^s2IM0@jYgF0h2_2H5)t)wl~35rPXK=%uhheh>)J;XwN=^0N5If&gG6 zx?GS}7!as0+0?azj5 z$$!&QkC;J4j+zo+ed-_JM zI~HIkJc=~9()<%*8&!hIo1$gpOy@w-}Lbw&!qMkDSncF4R)zUo-g%KT4ZK> zu0~Tjp=n#75xkJXeJ&N7w_YxRQV-MD_N%i}7a1oZnl(hS_NURp6eU!nrS4~3!o-ZR zN}XDl$`ZAw#p{M}R6#tkMDfDw8xR>fKc|a~2gdY>9$(ma*nv+bJWU<0(Z^-L=AS7a zD9Yxe^h1MKWll0g)adi}tGpej?v!>YB6NU0Xr>&~)#DjCGrsZg<#+g03w#&?)64Du z)peht(MkIcB`_cLwZ`$nv+wT=V3Ksg_?lozij*+A!=ZxKH>< z^$CmM6X=U4`2;<-aMCB_&>W$0aov3Qg$8`QPUDDE#dE12$6zuRTtHv=gys(MH3~q|c z_##?IIEAg3BiY<#5dP7G9KQ>>oznpRT6~aG{Y54B7o=TDe_@MWrDD=w#C7xFFs7-M zybnz6tTWPylOMxv^o?*3+S-m_d4!KRsrra^F6=o_Xxu?-K0>eVPi?DfUV`@eBid%? z;SdB}>aDGjSK(90?@;=Ydz&M>p|J;8#fW$m@)+qq_Wz;&0}BsQ)ZetkhMqHe!@= zI_4RmYZkgGP;v(d=Xrm-UVa^1E|2=JX}x(r#Y*TY+mDZP_<7C%Zu;?UoX9Juz4Bg+ z!NMa1-H1D!0p&F3iEfEbbH?C>u`v`G8<9!9@(<7*moaBSt(mCN$70}?I&57tsB68!` zjxOFTBZJ$A_O!i$Rdi^$Bw&W^xZfeBrEz&>n$S3_d$ywwZePYtLG_+ZydZxAq~(2J zFzRNF0>~KxaxtOOqtkNvMKnj)mWy$FP#uN9X9rC1EyYG{stt9bqn5m zDH{;!)kUukyp-HQuoOL1d=S$^Fmph6`Jr}0pnz6zQ96F=@{`z#Yb|@|cZ(A4 zus{BOf)?|bIWd_1-SIHLW1X)>2F;f%r0eH1kQ7@FqdhXFeX9H)7`^;_N%6hBW_FCX z1a^Vlz;?Xk9L!|-a-g_dTc`sHh@5*Dub8zFJZZQl2e#1|k;c3Som#%UHU(R~!UUqkn(o2I}bWATd! zklRBjlM3mC`W%ucWMH;WLqj2MFNyo73`^@Dz%L{W5){>#{m;D!T{8w_h+nZY7RNRk zyA88GKoOUCbvfsMWljiSCy6x&!{vUwa3zM(gati412{m9RU$bJFwvmPXn%GQxh@p= zj;l6ym*5I!REsB*R!S}pa2R@quX7%GNxyR5butLu@^AEf+ zzpO-tB>s~W+QTu=5I#G%?4@z=*n`>;Yq<|T0by&6x|efs4XK)zK!pn`L-^!3(=pA% zdHlB_u%!}H=MtG7?KWmarbPB^cI#>|T*W;)*v$!30MnQ}Rz;%mPi*n#CzCAWtJ;6*flLtH};*RX6L zH>?MB3Xr*yHz3Eoh`1DdJwSC{wPgvU%74}*mPkTAevx;jFWOJB6^T1l!rHG1ajM48 z-Zne1X%pEy4@w6~`L*u&sF$)N>HsbdO&%|)HdRmd6xUNE7;|ECF*l}$msMk0lZUSa z>JNA>6cBPBPWX6Tm)N^T2vsvgAeoXpi1%+=S-N1Sg)M!mEcxX&B6p1@J} z;J6737(3W+9D5`-j+E!h$=EoeqyDs}zTx{_3kCKnz0Qy2-y!kEE!v}VUI|^qmpQa| zQ#-wG+YZTR(8Aydf4N?+7{Rx6@!Pm@QZW_e(=jwR$Vl;x7& zd?7yrSI>9LzXcUo&dm1oQ3ox6?R_imxLbrZea8}M%3p8Fh6#_i(C zz1=*?6nQeeizg3sAldcR%-u*N58Jzwr@Fn*AdP$Ud!ME9*0YErdo%l236N46+e3|e zccIjqxdU%vxHoerZ^g|`=58(5u3Pq4g7t2C7Ugt?IqTZCY1yI z5SM%$x(@4cNSSUyQJL=GZx??rgBX?RjXeLTTEpKhc&oZiZ~UD1T;ON)v);rx<3&X| zunB8_?ijX^+K1YQ2G#aKp1yFdoA=*$j{OJv4^jWW9`27{;$jzOZ^3T@u>s`kFw5u8 zp#LHK3hG=o2H=-T=Vs7A*XJ;>X?UQ<5`J%UE)V?=bW<~Z_17Ab?eS$-6y;ndVT!vb z`+h%Vt0?*iyVsjZfo*D;f?i&RME>_ZTNX3+HNbAbKLHYeQvez8 z5y0SO>^?vdpc2pm5CK~NI{|+S_*cL&z*~U#0G9!#Wf%+C)quZ!0rUXQ0)7M74xr~k z4P$ebFxCn93g9^4SAaRS@D70I0fT@d{5?nra13xy9b-o@Zabg@Pz5Lg(DOC4eIIZd z@Gd|GTn6Z1lg0z406q(F0Tu!30V@Fkz>|Ql0d@oS0bT|i2J`_20M`Ncfo}_-9Plte z1bh+rHvmF_0ALxQlJc1Q5`YU30??DA@k#l|Yy$osM_@*P36OlgXT?`pi@@H1Cr>`f zzgbF0#Y#X|1)xN2O}5c~%mMVFtq*MjI5q}=XU8gm^#jT}O_9z(Q)f$uzs2P5ZuWPH zZNW}csHrXNZ<)hb6YIdgAZrGW)F;gRs1sQm+B(q?LU|4HB2s#qc|Dc7QHMGE*91ks zsjQ_X1b zF#k(5bJ40ck^VxBI?W**w;`wD1QWq#;C{dq3Hwd;Z6Ptz)KNy2o^F+D*x$^hsP=ex zU3F7eeVc#X;?`QVf$-nVxQ0BW%1J{$ACeLMs_axTsRWvXo%HuxTCZBas5Z`5t8uNs z9)sjVSQ$T7&&-NwNtlXF?aIp?!LYxQuRW{n@cX+^$>#w);a~?}$OV3}R&3Eap}is0 zCi=DZGL$>9x-DEsp!@4o3fe4DzIo!*r>=9&P2wP4FqXJMS{n&>`8!*@>RPC6OZ?%; z8h^5?p{Y%5>s)PW4Tel2t);3!SZGbETyq&)3uy;KuBCHLVlZgx2zIW9-B}>8{WApC z1xNrk0roFIaEUZoXF`4mb>G82A>jRH3T(B%(;sSUW`;`a9h8lq!I%dG{E?i1KUf1U zfJtjhj!kWA3x-83=o*?+GH+6=O#VcnOnSvCbDqAA#lE#0}=&u)wfkqEnjVaPJri7~u9EU57Mu;>S;v*G4x zuDi{U6QBU{NY0h!QAw{q@}WRg0RFKUzofRjc1`QNU&t%IQ~v7Kx9_R?nv?%IMMcS@ zwZTXT+wxI$8*l}z4tBO-X>VWE;cp82A8qSw=gU8*t7VnOAO5_8-F*jMP2gn+wlVpn zA)|*57yC0$K@|zq-eD>djhsIOi)aMB59)(V(K9^9p19iKQ63_Ic z(mRf}4#4i4#!+7{>ImoUy#>+m%A+$S3sOq0#vpjn3a` zV@bX`g!x1-A8G27oM!@kNuEcvvDAm!=%M)%&|Im`0UQF- z*V1J9X~gC9B+I8#+F!Q|?5BV`wNrgJzIf1+9G{B+MYQ$b)Q)>z&Ik2BS|YHs0OKN# zg1@rpEFs|Rfiqh2`84V}%5XMH=~sfY$OiN~n^LzQb;nV6yXVj6!9V?Za0%lvu@-z! zO7nELiOCpvQ^sOM$;s0AjD_Kb;5wTnS-}!%h}`E#LLo$i$;!NR)Kv#tA{|=P-@urM z>ZpTuGGpw0-N+*i&fS?j=+e+s`AxJ+0u}oji2}gB<~X zN0+&%wzH`#90(##J)~35BIcrswoq7Phlk6H_!%J$UkZB!vw$_o1a-4 z>1=L_tPY62;VX_L3Ev`gQNVv!1ed-^38<&CRgu}_S!?`j)&$r3q2pv6I)jXTd(^sS zeuifYb-u1h$lnZ;!k)lLhq%7W?`!L94GKcdJdbZt*}VD!@WmKWT{28Hh`xm>ee23R zv`yL03?Htk9%H&pU!;@g=A`=wd$8Hp91__-rTKg?b-_+QTn#JH`Lx_)X})F6U1CLB zhv*NnyVHG5Yr?Csm(Ypn(|x{mtNr4drnXKPm@w{gfd%Hk*VNVJBPMXGnx*!QVvaQE zK~Pm|1muF9t!=Q4bPEVZnsD)SNyUKswa}U-T=9f(H8L&(6K@m2z#5;Q8E%x=XySm{ z{GDrY^PKK$rT)qSrB?NHrg*&t@<1`~Ag5Ee=~@(<-ysR^<0L zHThdDW@}SRbBk@RAgEp&lvC;dp>$g9=2fl!LVrtPQ*m>1QLDYkQczq_RNPYRFKqD_ zuDTPQKY-5>{3XZz9&|^HOOYP;YdomAY3{y-nzL3TOBi#}zZ&s*Qc$UK@-0v=uH!abvIX~hGM873SkI$N439C6QB z?+?!?ojY!ft0^4zuUXZx-UI}lVfTzksIw&89PqDc3O}%>tvM772V2Dl;P^|L!fWQN zEu3M3OKfZPhsF9-YCvT&xx^4Y04_v4rD4186R?@@{tiMcT@m={ux*Mr%_?*gwPc z;4mK7gLkIldT<0!t_O!FhvBXVlX64hrnR_j(TKZItXo>ago0@WwgRE>g~IEFuA;`G zr;5H@^v$Ag6}?pS{h~ufeMLVk`gPGX^Gx#tX195jx!e3@^A7Va^WT~?EYmEXwUk;G zT2@&evuv_FV~Jb8ZvAWPx2^wdebM@=^=<2qto_!XTR*h^)|zP>XS>Tb$@UkvIksZk z9^0$79@`IXAJ|6Ov+a}Y_uC8XPJ4yD#{Q`Nuk2}#T*o~QljDnyZ#(umzUO$!anSL) zRg?!O|Boh&bdBx z-RGX`u6K91zvSNT{<`}a_jB(3?jE;oZpYkBbH6&bwG>h@;qDd5j)D&hJ}y{Q_*&t~ z!rY>Yq7RFfn4d5|Z~n-vnD50Li!EQUe8ut&%QvjASbu7rZ)>$}w|&p{S#a&Mf7!mn ze%$^;`y9vr1D_WhA37!!Pb=P7+*5qK_`Tu_#m|=fOG!^jv2&r*@7(F^bKdK^-}O`1 z&t03{Uvb|vcU)<#^mOUj(#xgSh@S#%K&;7;f_Dlo7jzYVq3~OUZxkAeyhZgz8RpUE zJ?4Yv6Xp_2wPm@b#j@7&g5_n)>z31&pIG`WS1egpgLSIaVlB1SS%cQ~)|ah4*5lT9 zt+Mr^^J^Kaw zW&1UIrelm_rsJ<1mmD8CSaDYIgyK2H>x-W(K2iK`@j!7!$wMV8N}eeBYRO-g2+l{G ztDS4HdfT1<<~#(+{Lne*{LpE0xm@#Hm9Ay3m97?7yDRMayesbdf$MeGN!L5B|L^)w z*ZZ!Et|6D=%5sl$=eVc1^WC%E4tJTm!d->+Zgh9KH@SDXzlrt!C-)2Pm)!}h`f>ML z?u+heb040&e(v|?o}Bx$xfkcAl};$Vuk_QUMK~AFE3F{;3-}!eR{-mcku{ zI}4vFJX3hKu)lDi@Qmdwa2Rm-DsU+HCgkmGp)0&1y}=z z)nyf|<@Isb_Clk*+rG{IjQu(LKKm>7g#EDnti9iU$*yzg9eIu^j@ga^hs9Cl@H&<{ z0*(&H7RNTnF2`=i0mmzjgyXQ|j6;S-Uc*Z0iuJ|D;+e&cVps9D;vL2Niw_i^Dn5%f zxm3(bOeHf*3Q8O$@MSm7Fb+O9o5yPNUP}EO&aH%dlFzoXDriverUnload = RWMonpDriverUnload; - DBG_BREAK(); - - // Create a directory for a log file and dumped files before initializing - // the Log system - status = RWMonpCreateDirectory(RWMONP_OUT_DIRECTORY_PATH); - if (!NT_SUCCESS(status)) { - return status; - } - - // Initialize the Log system - status = LogInitialization( - RWMONP_LOG_LEVEL | LOG_OPT_DISABLE_TIME | LOG_OPT_DISABLE_FUNCTION_NAME, - RWMONP_LOG_FILE_PATH, nullptr); - if (!NT_SUCCESS(status)) { - return status; - } - auto scopedLogTermination = - stdexp::make_scope_exit([] { LogTermination(nullptr); }); - - // Check the OS version and initialize right indexes for SSDT hook. - RTL_OSVERSIONINFOW osVersion = {sizeof(osVersion)}; - status = RtlGetVersion(&osVersion); - if (!NT_SUCCESS(status)) { - LOG_ERROR("RtlGetVersion failed (%08x)", status); - return status; - } - if (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion == 1) { - g_RWMonpNtMapViewOfSection_Index = 0xA8; - g_RWMonpNtWriteVirtualMemory_Index = 0x18F; - } else if (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion == 3) { - g_RWMonpNtMapViewOfSection_Index = 0xF6; - g_RWMonpNtWriteVirtualMemory_Index = 0x3; - } else { - LOG_ERROR("Unsupported OS version"); - return STATUS_DEVICE_CONFIGURATION_ERROR; - } - - // Save existing processes' IDs in a white list - auto index = 0; - status = RWMonpForEachProcess(RWMonpSaveExistingPID, &index); - if (!NT_SUCCESS(status)) { - LOG_ERROR("ForEachProcess failed (%08x)", status); - return status; - } - - // Initialize the crypt APIs. - status = BCryptOpenAlgorithmProvider(&g_RWMonpSha1AlgorithmHandle, - BCRYPT_SHA1_ALGORITHM, nullptr, 0); - if (!NT_SUCCESS(status)) { - LOG_ERROR("BCryptOpenAlgorithmProvider failed (%08x)", status); - return status; - } - - // Install SSDT hooks - RWMonpSetMonitorHooks(true); - scopedLogTermination.release(); - LOG_INFO("RemoteWriteMonitor installed"); - return status; -} - -// Create a directory -ALLOC_TEXT(INIT, RWMonpCreateDirectory) -EXTERN_C static NTSTATUS RWMonpCreateDirectory(_In_ const wchar_t *PathW) { - PAGED_CODE(); - - UNICODE_STRING path = {}; - RtlInitUnicodeString(&path, PathW); - OBJECT_ATTRIBUTES objAttr = RTL_INIT_OBJECT_ATTRIBUTES( - &path, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE); - - IO_STATUS_BLOCK ioStatus = {}; - HANDLE directory = nullptr; - NTSTATUS status = ZwCreateFile( - &directory, GENERIC_WRITE, &objAttr, &ioStatus, nullptr, - FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, - FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE, nullptr, 0); - if (NT_SUCCESS(status)) { - ZwClose(directory); - } - - return status; -} - -// Apply Callback for each process. Enumeration can be discontinued by returning -// false from Callback. -ALLOC_TEXT(INIT, RWMonpForEachProcess) -EXTERN_C static NTSTATUS RWMonpForEachProcess( - _In_ bool (*Callback)(_In_ const SYSTEM_PROCESS_INFORMATION *ProcessInfo, - _In_opt_ void *Context), - _In_opt_ void *Context) { - PAGED_CODE(); - - auto processInfo = reinterpret_cast( - ExAllocatePoolWithTag(PagedPool, 0x10000, RWMON_POOL_TAG_NAME)); - if (!processInfo) { - return STATUS_MEMORY_NOT_ALLOCATED; - } - - ULONG returnLength = 0; - auto status = ZwQuerySystemInformation(SystemProcessInformation, processInfo, - 0x10000, &returnLength); - if (!NT_SUCCESS(status) && returnLength) { - ExFreePoolWithTag(processInfo, RWMON_POOL_TAG_NAME); - processInfo = - reinterpret_cast(ExAllocatePoolWithTag( - PagedPool, returnLength + PAGE_SIZE, RWMON_POOL_TAG_NAME)); - if (!processInfo) { - return STATUS_MEMORY_NOT_ALLOCATED; - } - - status = - ZwQuerySystemInformation(SystemProcessInformation, processInfo, - (returnLength + PAGE_SIZE), &returnLength); - } - const auto scopedExFreePoolWithTag = stdexp::make_scope_exit( - [processInfo] { ExFreePoolWithTag(processInfo, RWMON_POOL_TAG_NAME); }); - if (!NT_SUCCESS(status)) { - return status; - } - - for (auto current = processInfo; current; /**/) { - if (!Callback(current, Context)) { - break; - } - - if (!current->NextEntryOffset) { - break; - } - current = reinterpret_cast( - reinterpret_cast(current) + current->NextEntryOffset); - } - - return status; -} - -// A callback routine saving existing processes' IDs into a white list. -ALLOC_TEXT(INIT, RWMonpSaveExistingPID) -EXTERN_C static bool RWMonpSaveExistingPID( - _In_ const SYSTEM_PROCESS_INFORMATION *ProcessInfo, _In_ void *Context) { - PAGED_CODE(); - - auto &index = *static_cast(Context); - if (index >= - RWMONP_WHITELIST_ARRAY_SIZE - 1) { // -1 to have 0 at the end at least - return false; - } - if (ProcessInfo->UniqueProcessId) { - g_RWMonpWhiteListedProcessIDs[index++] = ProcessInfo->UniqueProcessId; - } - return true; -} - -// -// Unloading Functions -// - -// Unloading the driver. Close and restore everything. -ALLOC_TEXT(PAGED, RWMonpDriverUnload) -EXTERN_C static void RWMonpDriverUnload(_In_ PDRIVER_OBJECT DriverObject) { - PAGED_CODE(); - UNREFERENCED_PARAMETER(DriverObject); - - LOG_DEBUG("Being terminated."); - // DBG_BREAK(); - - RWMonpSetMonitorHooks(false); - RWMonpSleep(1000); - BCryptCloseAlgorithmProvider(g_RWMonpSha1AlgorithmHandle, 0); - LogTermination(nullptr); -} - -// Sleep. -ALLOC_TEXT(PAGED, RWMonpSleep) -EXTERN_C static NTSTATUS RWMonpSleep(_In_ LONG Millisecond) { - PAGED_CODE(); - - LARGE_INTEGER interval = {}; - interval.QuadPart = -(10000 * Millisecond); // msec - return KeDelayExecutionThread(KernelMode, FALSE, &interval); -} - -// -// Common -// - -// Install or Uninstall necessary SSDT hooks. -EXTERN_C static NTSTATUS RWMonpSetMonitorHooks(_In_ bool Enable) { - // Need to rise IRQL not to allow the system to change an execution processor - // during the operation because this code changes a state of processor (CR0). - KIRQL oldIrql = 0; - KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); - const auto scopedIrql = - stdexp::make_scope_exit([oldIrql]() { KeLowerIrql(oldIrql); }); - - RWMonpDisableWriteProtect(); - const auto scopedWriteProtection = - stdexp::make_scope_exit([] { RWMonpEnableWriteProtect(); }); - - if (Enable) { - // Install - RWMonpHookSSDT(g_RWMonpNtMapViewOfSection_Index, - RWMonpNtMapViewOfSection_Hook, - reinterpret_cast(&g_RWMonpNtMapViewOfSection_Orig)); - RWMonpHookSSDT( - g_RWMonpNtWriteVirtualMemory_Index, RWMonpNtWriteVirtualMemory_Hook, - reinterpret_cast(&g_RWMonpNtWriteVirtualMemory_Orig)); - } else { - // Uninstall - RWMonpHookSSDT(g_RWMonpNtMapViewOfSection_Index, - g_RWMonpNtMapViewOfSection_Orig, nullptr); - RWMonpHookSSDT(g_RWMonpNtWriteVirtualMemory_Index, - g_RWMonpNtWriteVirtualMemory_Orig, nullptr); - } - return STATUS_SUCCESS; -} - -// Disable the write protection -EXTERN_C static void RWMonpDisableWriteProtect() { - CR0_REGISTER cr0 = {__readcr0()}; - cr0.Fields.WP = false; - __writecr0(cr0.Value); -} - -// Enable the write protection -EXTERN_C static void RWMonpEnableWriteProtect() { - CR0_REGISTER cr0 = {__readcr0()}; - cr0.Fields.WP = true; - __writecr0(cr0.Value); -} - -// Get an original value of the SSDT and replace it with a new value. -EXTERN_C static void RWMonpHookSSDT(_In_ ULONG Index, _In_ void *HookRoutine, - _Out_opt_ void **OriginalRoutine) { - if (OriginalRoutine) { - *OriginalRoutine = KeServiceDescriptorTable->ServiceTable[Index]; - } - KeServiceDescriptorTable->ServiceTable[Index] = HookRoutine; -} - -// -// Hook Handlers -// - -// A hook handler for NtWriteVirtualMemory -ALLOC_TEXT(PAGED, RWMonpNtWriteVirtualMemory_Hook) -EXTERN_C static NTSTATUS NTAPI -RWMonpNtWriteVirtualMemory_Hook(_In_ HANDLE ProcessHandle, - _In_ PVOID BaseAddress, _In_ PVOID Buffer, - _In_ ULONG BytesToWrite, - _Out_opt_ PULONG BytesWritten) { - PAGED_CODE(); - - const auto result = g_RWMonpNtWriteVirtualMemory_Orig( - ProcessHandle, BaseAddress, Buffer, BytesToWrite, BytesWritten); - if (NT_SUCCESS(result)) { - RWMonpCheckData(ProcessHandle, BaseAddress, Buffer, BytesToWrite); - } - return result; -} - -// A hook handler for NtMapViewOfSection -ALLOC_TEXT(PAGED, RWMonpNtMapViewOfSection_Hook) -EXTERN_C static NTSTATUS NTAPI RWMonpNtMapViewOfSection_Hook( - _In_ HANDLE SectionHandle, _In_ HANDLE ProcessHandle, - _Inout_ PVOID *BaseAddress, _In_ ULONG_PTR ZeroBits, _In_ SIZE_T CommitSize, - _Inout_opt_ PLARGE_INTEGER SectionOffset, _Inout_ PSIZE_T ViewSize, - _In_ SECTION_INHERIT InheritDisposition, _In_ ULONG AllocationType, - _In_ ULONG Win32Protect) { - PAGED_CODE(); - - const auto result = g_RWMonpNtMapViewOfSection_Orig( - SectionHandle, ProcessHandle, BaseAddress, ZeroBits, CommitSize, - SectionOffset, ViewSize, InheritDisposition, AllocationType, - Win32Protect); - if (NT_SUCCESS(result)) { - RWMonpCheckData(ProcessHandle, *BaseAddress, nullptr, *ViewSize); - } - return result; -} - -// Check if the call is inter-process write, and log it if so. -ALLOC_TEXT(PAGED, RWMonpCheckData) -EXTERN_C static bool RWMonpCheckData(_In_ HANDLE ProcessHandle, - _In_ void *RemoteAddress, - _In_opt_ void *Contents, - _In_ ULONG DataSize) { - PAGED_CODE(); - - const auto isWriteVirtualMemory = (Contents != nullptr); - - // Check if it is a interprocess operation - PEPROCESS targetProcess = nullptr; - if (!RWMonpIsInterprocessWrite(ProcessHandle, &targetProcess)) { - return false; - } - const auto scopedDereference = stdexp::make_scope_exit( - [targetProcess] { ObDereferenceObject(targetProcess); }); - - // Allocate a memory to copy written data - auto data = stdexp::make_unique_resource( - ExAllocatePoolWithTag(PagedPool, DataSize, RWMON_POOL_TAG_NAME), - [](void *p) { ExFreePoolWithTag(p, RWMON_POOL_TAG_NAME); }); - if (!data) { - return false; - } - - // Copy the written data - auto status = STATUS_SUCCESS; - if (Contents) { - status = - RWMonpCopyDataFromUserSpace(data.get(), Contents, DataSize, nullptr); - } else { - status = RWMonpCopyDataFromUserSpace(data.get(), RemoteAddress, DataSize, - targetProcess); - } - if (!NT_SUCCESS(status)) { - LOG_ERROR_SAFE("CopyDataFromUserSpace failed (%08x)", status); - return false; - } - - // Calculate SHA1 of the written data - UCHAR sha1Hash[20] = {}; - if (!RWMonpGetSha1(sha1Hash, data.get(), DataSize)) { - return false; - } - wchar_t sha1HashW[41] = {}; - for (auto i = 0; i < RTL_NUMBER_OF(sha1Hash); ++i) { - const auto outW = sha1HashW + i * 2; - RtlStringCchPrintfW(outW, 3, L"%02x", sha1Hash[i]); - } - - // Save it to a file - wchar_t outPathW[260]; - status = RtlStringCchPrintfW(outPathW, RTL_NUMBER_OF(outPathW), L"%s\\%s.bin", - RWMONP_OUT_DIRECTORY_PATH, sha1HashW); - if (!NT_SUCCESS(status)) { - LOG_ERROR_SAFE("RtlStringCchPrintfW failed (%08x)", status); - return false; - } - status = RWMonpWriteFile(outPathW, data.get(), DataSize, GENERIC_WRITE, - FILE_CREATE); - if (!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_COLLISION) { - LOG_ERROR_SAFE("WriteFile failed (%08x)", status); - return false; - } - - // Log it - LOG_INFO_SAFE("Remote %s onto %5lu (%-15s) at %p (saved as %S, %lu bytes)", - (isWriteVirtualMemory) ? "write" : "map ", - PsGetProcessId(targetProcess), - PsGetProcessImageFileName(targetProcess), RemoteAddress, - sha1HashW, DataSize); - return true; -} - -// Check if the write operation is interprocess and from a not white listed -// process -ALLOC_TEXT(PAGED, RWMonpIsInterprocessWrite) -_Success_(return == true) EXTERN_C - static bool RWMonpIsInterprocessWrite(_In_ HANDLE ProcessHandle, - _Out_ PEPROCESS *TargetProcess) { - PAGED_CODE(); - - if (ProcessHandle == ZwCurrentProcess()) { - return false; - } - - const auto pid = PsGetCurrentProcessId(); - for (auto i = 0; g_RWMonpWhiteListedProcessIDs[i]; ++i) { - if (g_RWMonpWhiteListedProcessIDs[i] == pid) { - return false; - } - } - - auto status = ObReferenceObjectByHandle( - ProcessHandle, 0, *PsProcessType, UserMode, - reinterpret_cast(TargetProcess), nullptr); - if (!NT_SUCCESS(status)) { - LOG_ERROR_SAFE("ObReferenceObjectByHandle failed (%08x)", status); - return false; - } - - if (*TargetProcess == PsGetCurrentProcess()) { - ObDereferenceObject(*TargetProcess); - return false; - } - return true; -} - -// Copy data from user-space -ALLOC_TEXT(PAGED, RWMonpCopyDataFromUserSpace) -EXTERN_C static NTSTATUS RWMonpCopyDataFromUserSpace( - _Out_ void *Buffer, _In_ const void *BaseAddress, _In_ ULONG DataSize, - _In_opt_ PEPROCESS TargetProcess) { - PAGED_CODE(); - - if (TargetProcess) { - // Need to switch to another process memory space to access the data - KAPC_STATE apcState = {}; - KeStackAttachProcess(TargetProcess, &apcState); - const auto scopedKeUnstackDetachProcess = stdexp::make_scope_exit( - [&apcState] { KeUnstackDetachProcess(&apcState); }); - return RWMonpCopyMemoryWithSEH(Buffer, BaseAddress, DataSize); - } else { - // The current process contains the data - return RWMonpCopyMemoryWithSEH(Buffer, BaseAddress, DataSize); - } -} - -// RtlCopyMemory wrapped with SEH -ALLOC_TEXT(PAGED, RWMonpCopyMemoryWithSEH) -EXTERN_C static NTSTATUS RWMonpCopyMemoryWithSEH(_Out_ void *Destionation, - _In_ const void *Source, - _In_ SIZE_T Length) { - PAGED_CODE(); - - auto status = STATUS_SUCCESS; - __try { - RtlCopyMemory(Destionation, Source, Length); - } __except (status = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER) { - } - return status; -} - -// Calculate SHA1 -ALLOC_TEXT(PAGED, RWMonpGetSha1) -_Success_(return == true) EXTERN_C - static bool RWMonpGetSha1(_Out_ UCHAR(&Sha1Hash)[20], _In_ void *Data, - _In_ SIZE_T DataSize) { - PAGED_CODE(); - - BCRYPT_HASH_HANDLE hashHandle = nullptr; - auto status = BCryptCreateHash(g_RWMonpSha1AlgorithmHandle, &hashHandle, - nullptr, 0, nullptr, 0, 0); - if (!NT_SUCCESS(status)) { - LOG_ERROR_SAFE("BCryptCreateHash failed (%08x)", status); - return false; - } - const auto scopedBCryptDestroyHash = - stdexp::make_scope_exit([hashHandle] { BCryptDestroyHash(hashHandle); }); - - status = BCryptHashData(hashHandle, static_cast(Data), DataSize, 0); - if (!NT_SUCCESS(status)) { - LOG_ERROR_SAFE("BCryptHashData failed (%08x)", status); - return false; - } - - static_assert(sizeof(Sha1Hash) == 20, "Size check"); - status = BCryptFinishHash(hashHandle, Sha1Hash, sizeof(Sha1Hash), 0); - if (!NT_SUCCESS(status)) { - LOG_ERROR_SAFE("BCryptFinishHash failed (%08x)", status); - return false; - } - - return true; -} - -// Write data to a file -ALLOC_TEXT(PAGED, RWMonpWriteFile) -EXTERN_C static NTSTATUS RWMonpWriteFile(_In_ const wchar_t *OutPathW, - _In_ void *Buffer, - _In_ ULONG BufferSize, - _In_ ACCESS_MASK DesiredAccess, - _In_ ULONG CreateDisposition) { - PAGED_CODE(); - - UNICODE_STRING outPath = {}; - RtlInitUnicodeString(&outPath, OutPathW); - OBJECT_ATTRIBUTES objAttr = RTL_INIT_OBJECT_ATTRIBUTES( - &outPath, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE); - - IO_STATUS_BLOCK ioStatus = {}; - HANDLE file = nullptr; - auto status = ZwCreateFile( - &file, DesiredAccess, &objAttr, &ioStatus, nullptr, FILE_ATTRIBUTE_NORMAL, - FILE_SHARE_READ | FILE_SHARE_WRITE, CreateDisposition, - FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT | - FILE_NON_DIRECTORY_FILE, - nullptr, 0); - if (!NT_SUCCESS(status)) { - return status; - } - - status = ZwWriteFile(file, nullptr, nullptr, nullptr, &ioStatus, Buffer, - BufferSize, nullptr, nullptr); - ZwClose(file); - return status; -} +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// This module implements an entry point of the driver and initializes other +// components in this module. +// +#include "stdafx.h" +#include "log.h" +#include "asm.h" +#include "inline.h" +#include "check.h" +#include "ssdt.h" +#include "util.h" + +namespace stdexp = std::experimental; + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +//////////////////////////////////////////////////////////////////////////////// +// +// constants and macros +// + +static const wchar_t RWMONP_OUT_DIRECTORY_PATH[] = + L"\\SystemRoot\\RemoteWriteMonitor"; +static const wchar_t RWMONP_LOG_FILE_PATH[] = + L"\\SystemRoot\\RemoteWriteMonitor\\RemoteWriteMonitor.log"; + +#if DBG +static const auto RWMONP_LOG_LEVEL = LOG_PUT_LEVEL_DEBUG; +#else +static const auto RWMONP_LOG_LEVEL = LOG_PUT_LEVEL_INFO; +#endif + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// + +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +EXTERN_C +NTSTATUS NTAPI +NtMapViewOfSection(_In_ HANDLE SectionHandle, _In_ HANDLE ProcessHandle, + _Inout_ PVOID *BaseAddress, _In_ ULONG_PTR ZeroBits, + _In_ SIZE_T CommitSize, + _Inout_opt_ PLARGE_INTEGER SectionOffset, + _Inout_ PSIZE_T ViewSize, + _In_ SECTION_INHERIT InheritDisposition, + _In_ ULONG AllocationType, _In_ ULONG Win32Protect); +using NtMapViewOfSectionType = decltype(&NtMapViewOfSection); + +EXTERN_C +NTSTATUS NTAPI NtWriteVirtualMemory(_In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, _In_ PVOID Buffer, + _In_ ULONG BytesToWrite, + _Out_opt_ PULONG BytesWritten); + +using NtWriteVirtualMemoryType = decltype(&NtWriteVirtualMemory); + +EXTERN_C DRIVER_INITIALIZE DriverEntry; + +EXTERN_C static NTSTATUS RWMonpCreateDirectory(_In_ const wchar_t *PathW); + +EXTERN_C static NTSTATUS RWMonpInitVersionDependentValues(); + +EXTERN_C static DRIVER_UNLOAD RWMonpDriverUnload; + +EXTERN_C static NTSTATUS RWMonpSleep(_In_ LONG Millisecond); + +EXTERN_C static NTSTATUS RWMonpInstallHooks(); + +EXTERN_C static NTSTATUS RWMonpUninstallHooks(); + +EXTERN_C static NTSTATUS NTAPI +RWMonpNtWriteVirtualMemory_Hook(_In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, _In_ PVOID Buffer, + _In_ ULONG BytesToWrite, + _Out_opt_ PULONG BytesWritten); + +EXTERN_C static NTSTATUS NTAPI RWMonpNtMapViewOfSection_Hook( + _In_ HANDLE SectionHandle, _In_ HANDLE ProcessHandle, + _Inout_ PVOID *BaseAddress, _In_ ULONG_PTR ZeroBits, _In_ SIZE_T CommitSize, + _Inout_opt_ PLARGE_INTEGER SectionOffset, _Inout_ PSIZE_T ViewSize, + _In_ SECTION_INHERIT InheritDisposition, _In_ ULONG AllocationType, + _In_ ULONG Win32Protect); + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +static HookInfo g_RWMonpNtMapViewOfSectionInfo = {}; +static HookInfo g_RWMonpNtWriteVirtualMemoryInfo = {}; + +static NtMapViewOfSectionType g_RWMonpNtMapViewOfSectionOriginal = nullptr; +static NtWriteVirtualMemoryType g_RWMonpNtWriteVirtualMemoryOriginal = nullptr; + +static ULONG g_RWMonpNtMapViewOfSectionSSDTIndex = 0; +static ULONG g_RWMonpNtWriteVirtualMemorySSDTIndex = 0; + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// + +// +// INIT section begin +// +ALLOC_TEXT(INIT, DriverEntry) +EXTERN_C NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, + _In_ PUNICODE_STRING RegistryPath) { + PAGED_CODE(); + UNREFERENCED_PARAMETER(RegistryPath); + auto status = STATUS_UNSUCCESSFUL; + + DriverObject->DriverUnload = RWMonpDriverUnload; + DBG_BREAK(); + + // Create a directory for a log file and dumped files before initializing + // the Log system + status = RWMonpCreateDirectory(RWMONP_OUT_DIRECTORY_PATH); + if (!NT_SUCCESS(status)) { + return status; + } + + // Initialize the Log system + status = LogInitialization( + RWMONP_LOG_LEVEL | LOG_OPT_DISABLE_TIME | LOG_OPT_DISABLE_FUNCTION_NAME, + RWMONP_LOG_FILE_PATH, nullptr); + if (!NT_SUCCESS(status)) { + return status; + } + auto scopedLogTermination = + stdexp::make_scope_exit([] { LogTermination(nullptr); }); + + // Init SSDT + status = SSDTInitialization(); + if (!NT_SUCCESS(status)) { + return status; + } + auto scopedSSDTTermination = + stdexp::make_scope_exit([] { SSDTTermination(); }); + + // Init globals + status = RWMonpInitVersionDependentValues(); + if (!NT_SUCCESS(status)) { + return status; + } + + // Init the Check subsystem + status = CheckInitialization(RWMONP_OUT_DIRECTORY_PATH); + if (!NT_SUCCESS(status)) { + return status; + } + auto scopedCheckTermination = + stdexp::make_scope_exit([] { CheckTermination(); }); + + status = RWMonpInstallHooks(); + if (!NT_SUCCESS(status)) { + return status; + } + + scopedCheckTermination.release(); + scopedSSDTTermination.release(); + scopedLogTermination.release(); + LOG_INFO("RemoteWriteMonitor installed"); + return status; +} + +ALLOC_TEXT(INIT, RWMonpInitVersionDependentValues) +EXTERN_C static NTSTATUS RWMonpInitVersionDependentValues() { + PAGED_CODE(); + + // Check the OS version and initialize right indexes for SSDT hook. + RTL_OSVERSIONINFOW osVersion = {sizeof(osVersion)}; + auto status = RtlGetVersion(&osVersion); + if (!NT_SUCCESS(status)) { + LOG_ERROR("RtlGetVersion failed (%08x)", status); + return status; + } + + if (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion == 1) { + // Windows 7 + if (IsX64()) { + // x64 + g_RWMonpNtMapViewOfSectionSSDTIndex = 0x25; + g_RWMonpNtMapViewOfSectionOriginal = + reinterpret_cast( + AsmNtMapViewOfSection_Win81_7); + status = InlineInitHookInfo( + reinterpret_cast( + SSDTGetProcAdderss(g_RWMonpNtMapViewOfSectionSSDTIndex)), + reinterpret_cast(RWMonpNtMapViewOfSection_Hook), + reinterpret_cast(AsmNtMapViewOfSection_Win81_7), + reinterpret_cast(AsmNtMapViewOfSection_Win81_7End), + &g_RWMonpNtMapViewOfSectionInfo); + if (!NT_SUCCESS(status)) { + return status; + } + + g_RWMonpNtWriteVirtualMemorySSDTIndex = 0x37; + g_RWMonpNtWriteVirtualMemoryOriginal = + reinterpret_cast( + AsmNtWriteVirtualMemory_Win7); + status = InlineInitHookInfo( + reinterpret_cast( + SSDTGetProcAdderss(g_RWMonpNtWriteVirtualMemorySSDTIndex)), + reinterpret_cast(RWMonpNtWriteVirtualMemory_Hook), + reinterpret_cast(AsmNtWriteVirtualMemory_Win7), + reinterpret_cast(AsmNtWriteVirtualMemory_Win7End), + &g_RWMonpNtWriteVirtualMemoryInfo); + if (!NT_SUCCESS(status)) { + return status; + } + } else { + // x86 + g_RWMonpNtMapViewOfSectionSSDTIndex = 0xa8; + g_RWMonpNtMapViewOfSectionOriginal = + reinterpret_cast( + SSDTGetProcAdderss(g_RWMonpNtMapViewOfSectionSSDTIndex)); + + g_RWMonpNtWriteVirtualMemorySSDTIndex = 0x18f; + g_RWMonpNtWriteVirtualMemoryOriginal = + reinterpret_cast( + SSDTGetProcAdderss(g_RWMonpNtWriteVirtualMemorySSDTIndex)); + } + } else if (osVersion.dwMajorVersion == 6 && osVersion.dwMinorVersion == 3) { + // Windows 8.1 + if (IsX64()) { + // x64 + g_RWMonpNtMapViewOfSectionSSDTIndex = 0x27; + g_RWMonpNtMapViewOfSectionOriginal = + reinterpret_cast( + AsmNtMapViewOfSection_Win81_7); + status = InlineInitHookInfo( + reinterpret_cast( + SSDTGetProcAdderss(g_RWMonpNtMapViewOfSectionSSDTIndex)), + reinterpret_cast(RWMonpNtMapViewOfSection_Hook), + reinterpret_cast(AsmNtMapViewOfSection_Win81_7), + reinterpret_cast(AsmNtMapViewOfSection_Win81_7End), + &g_RWMonpNtMapViewOfSectionInfo); + if (!NT_SUCCESS(status)) { + return status; + } + + g_RWMonpNtWriteVirtualMemorySSDTIndex = 0x39; + g_RWMonpNtWriteVirtualMemoryOriginal = + reinterpret_cast( + AsmNtWriteVirtualMemory_Win81); + status = InlineInitHookInfo( + reinterpret_cast( + SSDTGetProcAdderss(g_RWMonpNtWriteVirtualMemorySSDTIndex)), + reinterpret_cast(RWMonpNtWriteVirtualMemory_Hook), + reinterpret_cast(AsmNtWriteVirtualMemory_Win81), + reinterpret_cast(AsmNtWriteVirtualMemory_Win81End), + &g_RWMonpNtWriteVirtualMemoryInfo); + if (!NT_SUCCESS(status)) { + return status; + } + + } else { + // x86 + g_RWMonpNtMapViewOfSectionSSDTIndex = 0xf6; + g_RWMonpNtMapViewOfSectionOriginal = + reinterpret_cast( + SSDTGetProcAdderss(g_RWMonpNtMapViewOfSectionSSDTIndex)); + + g_RWMonpNtWriteVirtualMemorySSDTIndex = 0x3; + g_RWMonpNtWriteVirtualMemoryOriginal = + reinterpret_cast( + SSDTGetProcAdderss(g_RWMonpNtWriteVirtualMemorySSDTIndex)); + } + } else { + LOG_ERROR("Unsupported OS version"); + return STATUS_DEVICE_CONFIGURATION_ERROR; + } + return status; +} + +// Create a directory +ALLOC_TEXT(INIT, RWMonpCreateDirectory) +EXTERN_C static NTSTATUS RWMonpCreateDirectory(_In_ const wchar_t *PathW) { + PAGED_CODE(); + + UNICODE_STRING path = {}; + RtlInitUnicodeString(&path, PathW); + OBJECT_ATTRIBUTES objAttr = RTL_INIT_OBJECT_ATTRIBUTES( + &path, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE); + + IO_STATUS_BLOCK ioStatus = {}; + HANDLE directory = nullptr; + NTSTATUS status = ZwCreateFile( + &directory, GENERIC_WRITE, &objAttr, &ioStatus, nullptr, + FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN_IF, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE, nullptr, 0); + if (NT_SUCCESS(status)) { + ZwClose(directory); + } + + return status; +} + +// Unloading the driver. Close and restore everything. +ALLOC_TEXT(PAGED, RWMonpDriverUnload) +EXTERN_C static void RWMonpDriverUnload(_In_ PDRIVER_OBJECT DriverObject) { + PAGED_CODE(); + UNREFERENCED_PARAMETER(DriverObject); + + LOG_DEBUG("Being terminated."); + // DBG_BREAK(); + + RWMonpUninstallHooks(); + RWMonpSleep(1000); + CheckTermination(); + SSDTTermination(); + LogTermination(nullptr); +} + +// Sleep. +ALLOC_TEXT(PAGED, RWMonpSleep) +EXTERN_C static NTSTATUS RWMonpSleep(_In_ LONG Millisecond) { + PAGED_CODE(); + + LARGE_INTEGER interval = {}; + interval.QuadPart = -(10000 * Millisecond); // msec + return KeDelayExecutionThread(KernelMode, FALSE, &interval); +} + +ALLOC_TEXT(INIT, RWMonpInstallHooks) +EXTERN_C static NTSTATUS RWMonpInstallHooks() { + PAGED_CODE(); + + auto status = STATUS_SUCCESS; + if (IsX64()) { + status = InlineInstallHook(g_RWMonpNtMapViewOfSectionInfo); + if (!NT_SUCCESS(status)) { + return status; + } + status = InlineInstallHook(g_RWMonpNtWriteVirtualMemoryInfo); + if (!NT_SUCCESS(status)) { + InlineUninstallHook(g_RWMonpNtMapViewOfSectionInfo); + return status; + } + } else { + SSDTSetProcAdderss( + g_RWMonpNtMapViewOfSectionSSDTIndex, + reinterpret_cast(RWMonpNtMapViewOfSection_Hook)); + SSDTSetProcAdderss( + g_RWMonpNtWriteVirtualMemorySSDTIndex, + reinterpret_cast(RWMonpNtWriteVirtualMemory_Hook)); + } + return status; +} + +ALLOC_TEXT(PAGED, RWMonpUninstallHooks) +EXTERN_C static NTSTATUS RWMonpUninstallHooks() { + PAGED_CODE(); + + auto status = STATUS_SUCCESS; + if (IsX64()) { + status = InlineUninstallHook(g_RWMonpNtWriteVirtualMemoryInfo); + status = InlineUninstallHook(g_RWMonpNtMapViewOfSectionInfo); + } else { + SSDTSetProcAdderss( + g_RWMonpNtMapViewOfSectionSSDTIndex, + reinterpret_cast(g_RWMonpNtMapViewOfSectionOriginal)); + SSDTSetProcAdderss( + g_RWMonpNtWriteVirtualMemorySSDTIndex, + reinterpret_cast(g_RWMonpNtWriteVirtualMemoryOriginal)); + } + return status; +} + +// +// Hook Handlers +// + +// A hook handler for NtMapViewOfSection +ALLOC_TEXT(PAGED, RWMonpNtMapViewOfSection_Hook) +EXTERN_C static NTSTATUS NTAPI RWMonpNtMapViewOfSection_Hook( + _In_ HANDLE SectionHandle, _In_ HANDLE ProcessHandle, + _Inout_ PVOID *BaseAddress, _In_ ULONG_PTR ZeroBits, _In_ SIZE_T CommitSize, + _Inout_opt_ PLARGE_INTEGER SectionOffset, _Inout_ PSIZE_T ViewSize, + _In_ SECTION_INHERIT InheritDisposition, _In_ ULONG AllocationType, + _In_ ULONG Win32Protect) { + PAGED_CODE(); + + const auto result = g_RWMonpNtMapViewOfSectionOriginal( + SectionHandle, ProcessHandle, BaseAddress, ZeroBits, CommitSize, + SectionOffset, ViewSize, InheritDisposition, AllocationType, + Win32Protect); + if (NT_SUCCESS(result)) { + CheckData(ProcessHandle, *BaseAddress, nullptr, + static_cast(*ViewSize)); + } + return result; +} + +// A hook handler for NtWriteVirtualMemory +ALLOC_TEXT(PAGED, RWMonpNtWriteVirtualMemory_Hook) +EXTERN_C static NTSTATUS NTAPI +RWMonpNtWriteVirtualMemory_Hook(_In_ HANDLE ProcessHandle, + _In_ PVOID BaseAddress, _In_ PVOID Buffer, + _In_ ULONG BytesToWrite, + _Out_opt_ PULONG BytesWritten) { + PAGED_CODE(); + + const auto result = g_RWMonpNtWriteVirtualMemoryOriginal( + ProcessHandle, BaseAddress, Buffer, BytesToWrite, BytesWritten); + if (NT_SUCCESS(result)) { + CheckData(ProcessHandle, BaseAddress, Buffer, BytesToWrite); + } + return result; +} \ No newline at end of file diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj b/RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj index 687d074..0e38377 100644 --- a/RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj +++ b/RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj @@ -1,115 +1,280 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {287B2687-2894-4AA5-A5A9-686AE6C5F34A} - Win32Proj - RemoteWriteMonitor - - - - Application - true - WindowsKernelModeDriver8.1 - NotSet - - - Application - false - WindowsKernelModeDriver8.1 - true - NotSet - - - - - - - - - - - - - false - .sys - false - - - false - .sys - false - - - - Use - Level4 - Disabled - WIN32;_DEBUG;_WINDOWS;_USRDLL;RemoteWriteMonitor_EXPORTS;%(PreprocessorDefinitions) - true - $(WindowsSdkDir)Include\km - false - false - - - Native - true - $(WindowsSdkDir)Lib\winv6.3\km\$(PlatformTarget);$(WindowsSdkDir)Lib\winv6.3\um\$(PlatformTarget);%(AdditionalLibraryDirectories) - ntoskrnl.lib;wdm.lib;wmilib.lib;hal.lib;Aux_klib.lib;wdmsec.lib;ksecdd.lib;BufferOverflowK.lib - true - Driver - GsDriverEntry - - - - - Level4 - Use - MaxSpeed - true - true - WIN32;NDEBUG;_WINDOWS;_USRDLL;RemoteWriteMonitor_EXPORTS;%(PreprocessorDefinitions) - true - $(WindowsSdkDir)Include\km - false - false - - - Native - true - true - true - $(WindowsSdkDir)Lib\winv6.3\km\$(PlatformTarget);$(WindowsSdkDir)Lib\winv6.3\um\$(PlatformTarget);%(AdditionalLibraryDirectories) - ntoskrnl.lib;wdm.lib;wmilib.lib;hal.lib;Aux_klib.lib;wdmsec.lib;ksecdd.lib;BufferOverflowK.lib - true - Driver - GsDriverEntry - - - - - - - - - - - - - Create - Create - - - - - + + + + + Win8.1 Debug + Win32 + + + Win8.1 Release + Win32 + + + Win7 Debug + Win32 + + + Win7 Release + Win32 + + + Win8.1 Debug + x64 + + + Win8.1 Release + x64 + + + Win7 Debug + x64 + + + Win7 Release + x64 + + + + {987A0E0D-CF4E-4DC8-A5FE-1CCCC3D75082} + {1bc93793-694f-48fe-9372-81e2b05556fd} + v4.5 + 11.0 + Win8.1 Debug + Win32 + RemoteWriteMonitor + + + + WindowsV6.3 + true + WindowsKernelModeDriver8.1 + Driver + KMDF + + + WindowsV6.3 + false + WindowsKernelModeDriver8.1 + Driver + KMDF + + + Windows7 + true + WindowsKernelModeDriver8.1 + Driver + KMDF + + + Windows7 + false + WindowsKernelModeDriver8.1 + Driver + KMDF + + + WindowsV6.3 + true + WindowsKernelModeDriver8.1 + Driver + KMDF + + + WindowsV6.3 + false + WindowsKernelModeDriver8.1 + Driver + KMDF + + + Windows7 + true + WindowsKernelModeDriver8.1 + Driver + KMDF + + + Windows7 + false + WindowsKernelModeDriver8.1 + Driver + KMDF + + + + + + + + + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + + false + trace.h + true + $(VCInstallDir)\include;%(AdditionalIncludeDirectories) + Use + + + Ksecdd.lib;%(AdditionalDependencies) + + + + + false + trace.h + true + $(VCInstallDir)\include;%(AdditionalIncludeDirectories) + Use + + + Ksecdd.lib;%(AdditionalDependencies) + + + + + false + trace.h + true + $(VCInstallDir)\include;%(AdditionalIncludeDirectories) + Use + + + Ksecdd.lib;%(AdditionalDependencies) + + + + + false + trace.h + true + $(VCInstallDir)\include;%(AdditionalIncludeDirectories) + Use + + + Ksecdd.lib;%(AdditionalDependencies) + + + + + false + trace.h + true + $(VCInstallDir)\include;%(AdditionalIncludeDirectories) + Use + + + Ksecdd.lib;%(AdditionalDependencies) + + + + + false + trace.h + true + $(VCInstallDir)\include;%(AdditionalIncludeDirectories) + Use + + + Ksecdd.lib;%(AdditionalDependencies) + + + + + false + trace.h + true + $(VCInstallDir)\include;%(AdditionalIncludeDirectories) + Use + + + Ksecdd.lib;%(AdditionalDependencies) + + + + + false + trace.h + true + $(VCInstallDir)\include;%(AdditionalIncludeDirectories) + Use + + + Ksecdd.lib;%(AdditionalDependencies) + + + + + + + + + true + true + true + true + + + + + + + + Create + Create + Create + Create + Create + Create + Create + Create + + + + + + + + + + + + + + + Document + true + true + true + true + + + + + \ No newline at end of file diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj.filters b/RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj.filters index 776c765..171019c 100644 --- a/RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj.filters +++ b/RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj.filters @@ -1,48 +1,80 @@ - - - - - {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 - - - {e24d7c43-af6b-4430-9f6f-7e9907caa1b0} - - - {44082a1d-9f05-42e9-b7cd-db78af9eb8ed} - - - - - Header Files - - - Header Files\../Common\ScopedResource - - - Header Files\../Common\ScopedResource - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - + + + + + {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 + + + {92ec6841-3b89-43a6-8ce1-3dd70898e636} + + + {50b9ecfc-b33b-4adb-8bcf-6cf305042be6} + + + {e6866ce5-f1d2-47bd-b2b5-763276207d0c} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\Arch\x86 + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + \ No newline at end of file diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj.user b/RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj.user index a839d78..8bd5638 100644 --- a/RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj.user +++ b/RemoteWriteMonitor/RemoteWriteMonitor/RemoteWriteMonitor.vcxproj.user @@ -1,5 +1,31 @@ - - - - + + + + $(ProjectDir)satoshitanda.pfx + + + TestSign + $(ProjectDir)satoshitanda.pfx + + + $(ProjectDir)satoshitanda.pfx + + + TestSign + $(ProjectDir)satoshitanda.pfx + + + $(ProjectDir)satoshitanda.pfx + + + TestSign + $(ProjectDir)satoshitanda.pfx + + + $(ProjectDir)satoshitanda.pfx + + + TestSign + $(ProjectDir)satoshitanda.pfx + \ No newline at end of file diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/asm.h b/RemoteWriteMonitor/RemoteWriteMonitor/asm.h new file mode 100644 index 0000000..0cf8575 --- /dev/null +++ b/RemoteWriteMonitor/RemoteWriteMonitor/asm.h @@ -0,0 +1,50 @@ +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// This module declares interfaces to functions written in assembler. +// +#pragma once + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +//////////////////////////////////////////////////////////////////////////////// +// +// constants and macros +// + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// + +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +EXTERN_C void AsmNtMapViewOfSection_Win81_7(); + +EXTERN_C void AsmNtMapViewOfSection_Win81_7End(); + +EXTERN_C void AsmNtWriteVirtualMemory_Win81(); + +EXTERN_C void AsmNtWriteVirtualMemory_Win81End(); + +EXTERN_C void AsmNtWriteVirtualMemory_Win7(); + +EXTERN_C void AsmNtWriteVirtualMemory_Win7End(); + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/check.cpp b/RemoteWriteMonitor/RemoteWriteMonitor/check.cpp new file mode 100644 index 0000000..ec123bd --- /dev/null +++ b/RemoteWriteMonitor/RemoteWriteMonitor/check.cpp @@ -0,0 +1,416 @@ +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// This module implements an entry point of the driver and initializes other +// components in this module. +// +#include "stdafx.h" +#include "check.h" +#include "log.h" + +namespace stdexp = std::experimental; + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +//////////////////////////////////////////////////////////////////////////////// +// +// constants and macros +// + +static const auto CHECKP_WHITELIST_ARRAY_SIZE = 1000; + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// +struct SYSTEM_PROCESS_INFORMATION { + ULONG NextEntryOffset; + ULONG NumberOfThreads; + BYTE Reserved1[48]; + PVOID Reserved2[3]; + HANDLE UniqueProcessId; + PVOID Reserved3; + ULONG HandleCount; + BYTE Reserved4[4]; + PVOID Reserved5[11]; + SIZE_T PeakPagefileUsage; + SIZE_T PrivatePageCount; + LARGE_INTEGER Reserved6[6]; +}; + +enum SYSTEM_INFORMATION_CLASS { + SystemProcessInformation = 5, +}; +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +EXTERN_C NTKERNELAPI UCHAR *NTAPI +PsGetProcessImageFileName(_In_ PEPROCESS Process); + +EXTERN_C NTSTATUS NTAPI +ZwQuerySystemInformation(_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, + _Inout_ PVOID SystemInformation, + _In_ ULONG SystemInformationLength, + _Out_opt_ PULONG ReturnLength); + +EXTERN_C static NTSTATUS CheckpForEachProcess( + _In_ bool (*Callback)(_In_ const SYSTEM_PROCESS_INFORMATION *ProcessInfo, + _In_opt_ void *Context), + _In_opt_ void *Context); + +EXTERN_C static bool CheckpSaveExistingPID( + _In_ const SYSTEM_PROCESS_INFORMATION *ProcessInfo, _In_ void *Context); + +_Success_(return == true) EXTERN_C + static bool CheckpIsInterprocessWrite(_In_ HANDLE ProcessHandle, + _Out_ PEPROCESS *TargetProcess); + +EXTERN_C static NTSTATUS CheckpCopyDataFromUserSpace( + _Out_ void *Buffer, _In_ const void *BaseAddress, _In_ ULONG DataSize, + _In_opt_ PEPROCESS TargetProcess); + +EXTERN_C static NTSTATUS CheckpTryCopyMemory(_Out_ void *Destionation, + _In_ const void *Source, + _In_ SIZE_T Length); + +_Success_(return == true) EXTERN_C + static bool CheckpGetSha1(_Out_ UCHAR(&Sha1Hash)[20], _In_ void *Data, + _In_ ULONG DataSize); + +EXTERN_C static NTSTATUS CheckpWriteFile(_In_ const wchar_t *OutPathW, + _In_ void *Buffer, + _In_ ULONG BufferSize, + _In_ ACCESS_MASK DesiredAccess, + _In_ ULONG CreateDisposition); + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +static wchar_t g_CheckpLogDirecotry[MAX_PATH]; +static HANDLE g_CheckpWhiteListedProcessIDs[CHECKP_WHITELIST_ARRAY_SIZE] = {}; +static BCRYPT_ALG_HANDLE g_CheckpSha1AlgorithmHandle = nullptr; + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// + +ALLOC_TEXT(INIT, CheckInitialization) +EXTERN_C NTSTATUS CheckInitialization(_In_ const wchar_t *LogDirectry) { + PAGED_CODE(); + + auto status = RtlStringCchCopyW( + g_CheckpLogDirecotry, RTL_NUMBER_OF(g_CheckpLogDirecotry), LogDirectry); + if (!NT_SUCCESS(status)) { + LOG_ERROR("RtlStringCchCopyW failed (%08x)", status); + return status; + } + + // Save existing processes' IDs in a white list + auto index = 0; + status = CheckpForEachProcess(CheckpSaveExistingPID, &index); + if (!NT_SUCCESS(status)) { + LOG_ERROR("ForEachProcess failed (%08x)", status); + return status; + } + + // Initialize the crypt APIs. + status = BCryptOpenAlgorithmProvider(&g_CheckpSha1AlgorithmHandle, + BCRYPT_SHA1_ALGORITHM, nullptr, 0); + if (!NT_SUCCESS(status)) { + LOG_ERROR("BCryptOpenAlgorithmProvider failed (%08x)", status); + return status; + } + return status; +} + +ALLOC_TEXT(PAGED, CheckTermination) +EXTERN_C void CheckTermination() { + PAGED_CODE(); + BCryptCloseAlgorithmProvider(g_CheckpSha1AlgorithmHandle, 0); +} + +// Apply Callback for each process. Enumeration can be discontinued by returning +// false from Callback. +ALLOC_TEXT(INIT, CheckpForEachProcess) +EXTERN_C static NTSTATUS CheckpForEachProcess( + _In_ bool (*Callback)(_In_ const SYSTEM_PROCESS_INFORMATION *ProcessInfo, + _In_opt_ void *Context), + _In_opt_ void *Context) { + PAGED_CODE(); + + auto processInfo = reinterpret_cast( + ExAllocatePoolWithTag(PagedPool, 0x10000, RWMON_POOL_TAG_NAME)); + if (!processInfo) { + return STATUS_MEMORY_NOT_ALLOCATED; + } + + ULONG returnLength = 0; + auto status = ZwQuerySystemInformation(SystemProcessInformation, processInfo, + 0x10000, &returnLength); + if (!NT_SUCCESS(status) && returnLength) { + ExFreePoolWithTag(processInfo, RWMON_POOL_TAG_NAME); + processInfo = + reinterpret_cast(ExAllocatePoolWithTag( + PagedPool, returnLength + PAGE_SIZE, RWMON_POOL_TAG_NAME)); + if (!processInfo) { + return STATUS_MEMORY_NOT_ALLOCATED; + } + + status = + ZwQuerySystemInformation(SystemProcessInformation, processInfo, + (returnLength + PAGE_SIZE), &returnLength); + } + const auto scopedExFreePoolWithTag = stdexp::make_scope_exit( + [processInfo] { ExFreePoolWithTag(processInfo, RWMON_POOL_TAG_NAME); }); + if (!NT_SUCCESS(status)) { + return status; + } + + for (auto current = processInfo; current; /**/) { + if (!Callback(current, Context)) { + break; + } + + if (!current->NextEntryOffset) { + break; + } + current = reinterpret_cast( + reinterpret_cast(current) + current->NextEntryOffset); + } + + return status; +} + +// A callback routine saving existing processes' IDs into a white list. +ALLOC_TEXT(INIT, CheckpSaveExistingPID) +EXTERN_C static bool CheckpSaveExistingPID( + _In_ const SYSTEM_PROCESS_INFORMATION *ProcessInfo, _In_ void *Context) { + PAGED_CODE(); + + auto &index = *static_cast(Context); + if (index >= + CHECKP_WHITELIST_ARRAY_SIZE - 1) { // -1 to have 0 at the end at least + return false; + } + if (ProcessInfo->UniqueProcessId) { + g_CheckpWhiteListedProcessIDs[index++] = ProcessInfo->UniqueProcessId; + } + return true; +} + +// Check if the call is inter-process write, and log it if so. +ALLOC_TEXT(PAGED, CheckData) +EXTERN_C bool CheckData(_In_ HANDLE ProcessHandle, _In_ void *RemoteAddress, + _In_opt_ void *Contents, _In_ ULONG DataSize) { + PAGED_CODE(); + + const auto isWriteVirtualMemory = (Contents != nullptr); + + // Check if it is a interprocess operation + PEPROCESS targetProcess = nullptr; + if (!CheckpIsInterprocessWrite(ProcessHandle, &targetProcess)) { + return false; + } + const auto scopedDereference = stdexp::make_scope_exit( + [targetProcess] { ObDereferenceObject(targetProcess); }); + + // Allocate a memory to copy written data + auto data = stdexp::make_unique_resource( + ExAllocatePoolWithTag(PagedPool, DataSize, RWMON_POOL_TAG_NAME), + [](void *p) { ExFreePoolWithTag(p, RWMON_POOL_TAG_NAME); }); + if (!data) { + return false; + } + + // Copy the written data + auto status = STATUS_SUCCESS; + if (Contents) { + status = + CheckpCopyDataFromUserSpace(data.get(), Contents, DataSize, nullptr); + } else { + status = CheckpCopyDataFromUserSpace(data.get(), RemoteAddress, DataSize, + targetProcess); + } + if (!NT_SUCCESS(status)) { + LOG_ERROR_SAFE("CopyDataFromUserSpace failed (%08x)", status); + return false; + } + + // Calculate SHA1 of the written data + UCHAR sha1Hash[20] = {}; + if (!CheckpGetSha1(sha1Hash, data.get(), DataSize)) { + return false; + } + wchar_t sha1HashW[41] = {}; + for (auto i = 0; i < RTL_NUMBER_OF(sha1Hash); ++i) { + const auto outW = sha1HashW + i * 2; + RtlStringCchPrintfW(outW, 3, L"%02x", sha1Hash[i]); + } + + // Save it to a file + wchar_t outPathW[260]; + status = RtlStringCchPrintfW(outPathW, RTL_NUMBER_OF(outPathW), L"%s\\%s.bin", + g_CheckpLogDirecotry, sha1HashW); + if (!NT_SUCCESS(status)) { + LOG_ERROR_SAFE("RtlStringCchPrintfW failed (%08x)", status); + return false; + } + status = CheckpWriteFile(outPathW, data.get(), DataSize, GENERIC_WRITE, + FILE_CREATE); + if (!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_COLLISION) { + LOG_ERROR_SAFE("WriteFile failed (%08x)", status); + return false; + } + + // Log it + LOG_INFO_SAFE("Remote %s onto %5lu (%-15s) at %p (saved as %S, %lu bytes)", + (isWriteVirtualMemory) ? "write" : "map ", + PsGetProcessId(targetProcess), + PsGetProcessImageFileName(targetProcess), RemoteAddress, + sha1HashW, DataSize); + return true; +} + +// Check if the write operation is interprocess and from a not white listed +// process +ALLOC_TEXT(PAGED, CheckpIsInterprocessWrite) +_Success_(return == true) EXTERN_C + static bool CheckpIsInterprocessWrite(_In_ HANDLE ProcessHandle, + _Out_ PEPROCESS *TargetProcess) { + PAGED_CODE(); + + if (ProcessHandle == ZwCurrentProcess()) { + return false; + } + + const auto pid = PsGetCurrentProcessId(); + for (auto i = 0; g_CheckpWhiteListedProcessIDs[i]; ++i) { + if (g_CheckpWhiteListedProcessIDs[i] == pid) { + return false; + } + } + + auto status = ObReferenceObjectByHandle( + ProcessHandle, 0, *PsProcessType, UserMode, + reinterpret_cast(TargetProcess), nullptr); + if (!NT_SUCCESS(status)) { + LOG_ERROR_SAFE("ObReferenceObjectByHandle failed (%08x)", status); + return false; + } + + if (*TargetProcess == PsGetCurrentProcess()) { + ObDereferenceObject(*TargetProcess); + return false; + } + return true; +} + +// Copy data from user-space +ALLOC_TEXT(PAGED, CheckpCopyDataFromUserSpace) +EXTERN_C static NTSTATUS CheckpCopyDataFromUserSpace( + _Out_ void *Buffer, _In_ const void *BaseAddress, _In_ ULONG DataSize, + _In_opt_ PEPROCESS TargetProcess) { + PAGED_CODE(); + + if (TargetProcess) { + // Need to switch to another process memory space to access the data + KAPC_STATE apcState = {}; + KeStackAttachProcess(TargetProcess, &apcState); + const auto scopedKeUnstackDetachProcess = stdexp::make_scope_exit( + [&apcState] { KeUnstackDetachProcess(&apcState); }); + return CheckpTryCopyMemory(Buffer, BaseAddress, DataSize); + } else { + // The current process contains the data + return CheckpTryCopyMemory(Buffer, BaseAddress, DataSize); + } +} + +// RtlCopyMemory wrapped with SEH +ALLOC_TEXT(PAGED, CheckpTryCopyMemory) +EXTERN_C static NTSTATUS CheckpTryCopyMemory(_Out_ void *Destionation, + _In_ const void *Source, + _In_ SIZE_T Length) { + PAGED_CODE(); + + auto status = STATUS_SUCCESS; + __try { + RtlCopyMemory(Destionation, Source, Length); + } __except (status = GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER) { + } + return status; +} + +// Calculate SHA1 +ALLOC_TEXT(PAGED, CheckpGetSha1) +_Success_(return == true) EXTERN_C + static bool CheckpGetSha1(_Out_ UCHAR(&Sha1Hash)[20], _In_ void *Data, + _In_ ULONG DataSize) { + PAGED_CODE(); + + BCRYPT_HASH_HANDLE hashHandle = nullptr; + auto status = BCryptCreateHash(g_CheckpSha1AlgorithmHandle, &hashHandle, + nullptr, 0, nullptr, 0, 0); + if (!NT_SUCCESS(status)) { + LOG_ERROR_SAFE("BCryptCreateHash failed (%08x)", status); + return false; + } + const auto scopedBCryptDestroyHash = + stdexp::make_scope_exit([hashHandle] { BCryptDestroyHash(hashHandle); }); + + status = BCryptHashData(hashHandle, static_cast(Data), DataSize, 0); + if (!NT_SUCCESS(status)) { + LOG_ERROR_SAFE("BCryptHashData failed (%08x)", status); + return false; + } + + static_assert(sizeof(Sha1Hash) == 20, "Size check"); + status = BCryptFinishHash(hashHandle, Sha1Hash, sizeof(Sha1Hash), 0); + if (!NT_SUCCESS(status)) { + LOG_ERROR_SAFE("BCryptFinishHash failed (%08x)", status); + return false; + } + + return true; +} + +// Write data to a file +ALLOC_TEXT(PAGED, CheckpWriteFile) +EXTERN_C static NTSTATUS CheckpWriteFile(_In_ const wchar_t *OutPathW, + _In_ void *Buffer, + _In_ ULONG BufferSize, + _In_ ACCESS_MASK DesiredAccess, + _In_ ULONG CreateDisposition) { + PAGED_CODE(); + + UNICODE_STRING outPath = {}; + RtlInitUnicodeString(&outPath, OutPathW); + OBJECT_ATTRIBUTES objAttr = RTL_INIT_OBJECT_ATTRIBUTES( + &outPath, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE); + + IO_STATUS_BLOCK ioStatus = {}; + HANDLE file = nullptr; + auto status = ZwCreateFile( + &file, DesiredAccess, &objAttr, &ioStatus, nullptr, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ | FILE_SHARE_WRITE, CreateDisposition, + FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT | + FILE_NON_DIRECTORY_FILE, + nullptr, 0); + if (!NT_SUCCESS(status)) { + return status; + } + + status = ZwWriteFile(file, nullptr, nullptr, nullptr, &ioStatus, Buffer, + BufferSize, nullptr, nullptr); + ZwClose(file); + return status; +} diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/check.h b/RemoteWriteMonitor/RemoteWriteMonitor/check.h new file mode 100644 index 0000000..7353b6e --- /dev/null +++ b/RemoteWriteMonitor/RemoteWriteMonitor/check.h @@ -0,0 +1,45 @@ +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// +// +#pragma once + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +//////////////////////////////////////////////////////////////////////////////// +// +// constants and macros +// + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// + +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +EXTERN_C NTSTATUS CheckInitialization(_In_ const wchar_t *LogDirectry); + +EXTERN_C void CheckTermination(); + +EXTERN_C bool CheckData(_In_ HANDLE ProcessHandle, _In_ void *RemoteAddress, + _In_opt_ void *Contents, _In_ ULONG DataSize); + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/inline.cpp b/RemoteWriteMonitor/RemoteWriteMonitor/inline.cpp new file mode 100644 index 0000000..b4f2fae --- /dev/null +++ b/RemoteWriteMonitor/RemoteWriteMonitor/inline.cpp @@ -0,0 +1,158 @@ +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// This module implements an entry point of the driver and initializes other +// components in this module. +// +#include "stdafx.h" +#include "inline.h" +#include "log.h" +#include "util.h" + +namespace stdexp = std::experimental; + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +//////////////////////////////////////////////////////////////////////////////// +// +// constants and macros +// + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// + +// A structure reflects inline hook code. +#include +struct TrampolineCode { + UCHAR jmp[6]; + FARPROC FunctionAddress; +}; +static const auto DISPGP_MININUM_EPILOGUE_LENGTH = sizeof(TrampolineCode); +static_assert(sizeof(TrampolineCode) == DISPGP_MININUM_EPILOGUE_LENGTH, + "Size check"); +#include + +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +EXTERN_C +NTSTATUS static InlinepFixupAsmCode(_In_ UCHAR *OriginalRoutine, + _In_ FARPROC AsmHandler, + _In_ FARPROC AsmHandlerEnd); + +EXTERN_C static TrampolineCode InlinepMakeTrampolineCode( + _In_ UCHAR *HookAddress, _In_ FARPROC HookHandler); + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// + +// Fill out HookInfo in order to hook the begging of the function. This is not +// designed to execute original code like what DispgpSetEpilogueHookInfo() does. +ALLOC_TEXT(INIT, InlineInitHookInfo) +EXTERN_C NTSTATUS +InlineInitHookInfo(_In_ UCHAR *HookAddress, _In_ FARPROC HookHandler, + _In_ FARPROC AsmHandler, _In_ FARPROC AsmHandlerEnd, + _Out_ HookInfo *Info) { + PAGED_CODE(); + + NT_ASSERT(HookHandler); + NT_ASSERT(AsmHandler); + NT_ASSERT(AsmHandlerEnd); + NT_ASSERT(Info); + + if (!HookAddress) { + return STATUS_INVALID_PARAMETER; + } + + Info->HookHandler = HookHandler; + Info->HookAddress = HookAddress; + Info->OriginalCodeSize = DISPGP_MININUM_EPILOGUE_LENGTH; + memcpy(Info->OriginalCode, Info->HookAddress, Info->OriginalCodeSize); + + auto status = InlinepFixupAsmCode(HookAddress, AsmHandler, AsmHandlerEnd); + if (!NT_SUCCESS(status)) { + return status; + } + + LOG_DEBUG("HookHandler= %p, HookAddress= %p, OriginalCodeSize= %d", + Info->HookHandler, Info->HookAddress, Info->OriginalCodeSize); + + return status; +} + +// Build and return trampoline code. +ALLOC_TEXT(PAGED, InlinepMakeTrampolineCode) +EXTERN_C static TrampolineCode InlinepMakeTrampolineCode( + _In_ UCHAR *HookAddress, _In_ FARPROC HookHandler) { + PAGED_CODE(); + // jmp qword ptr [nextline] + // nextline: + // dq HookHandler + UNREFERENCED_PARAMETER(HookAddress); + return { + { + 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, + }, + HookHandler, + }; +} + +// Replaces placeholder (0xffffffffffffffff) in AsmHandler with a given +// ReturnAddress. AsmHandler does not has to be writable. Race condition between +// multiple processors should be taken care of by a programmer it exists; this +// function does not care about it. +ALLOC_TEXT(PAGED, InlinepFixupAsmCode) +EXTERN_C +NTSTATUS static InlinepFixupAsmCode(_In_ UCHAR *OriginalRoutine, + _In_ FARPROC AsmHandler, + _In_ FARPROC AsmHandlerEnd) { + PAGED_CODE(); + ASSERT(AsmHandlerEnd > AsmHandler); + SIZE_T asmHandlerSize = reinterpret_cast(AsmHandlerEnd) - + reinterpret_cast(AsmHandler); + + ULONG64 pattern = 0xffffffffffffffff; + auto addressOfMarker = UtilMemMem(reinterpret_cast(AsmHandler), + asmHandlerSize, &pattern, sizeof(pattern)); + ASSERT(addressOfMarker); + auto destinationAddress = + reinterpret_cast(OriginalRoutine + asmHandlerSize - 15); + return UtilForceMemCpy(addressOfMarker, &destinationAddress, + sizeof(destinationAddress)); +} + +// Install a inline hook (modify code) using HookInfo. +ALLOC_TEXT(PAGED, InlineInstallHook) +EXTERN_C NTSTATUS InlineInstallHook(_In_ const HookInfo &Info) { + PAGED_CODE(); + LOG_DEBUG("%p => %p", Info.HookAddress, Info.HookHandler); + auto newCode = InlinepMakeTrampolineCode(Info.HookAddress, Info.HookHandler); + auto status = UtilForceMemCpy(Info.HookAddress, newCode.jmp, sizeof(newCode)); + UtilInvalidateInstructionCache(Info.HookAddress, sizeof(newCode)); + return status; +} + +ALLOC_TEXT(PAGED, InlineUninstallHook) +EXTERN_C NTSTATUS InlineUninstallHook(_In_ const HookInfo &Info) { + PAGED_CODE(); + auto status = UtilForceMemCpy(Info.HookAddress, Info.OriginalCode, + Info.OriginalCodeSize); + UtilInvalidateInstructionCache(Info.HookAddress, Info.OriginalCodeSize); + return status; +} diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/inline.h b/RemoteWriteMonitor/RemoteWriteMonitor/inline.h new file mode 100644 index 0000000..75a69e6 --- /dev/null +++ b/RemoteWriteMonitor/RemoteWriteMonitor/inline.h @@ -0,0 +1,60 @@ +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// +// +#pragma once + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +//////////////////////////////////////////////////////////////////////////////// +// +// constants and macros +// + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// + +// Acceptable the minimum function epilogue size in bytes for inline hooking. +// It limits the length to 32 bytes due to a size of a backup area allocated by +// a macro NOP_32. +static const ULONG DISPGP_MAXIMUM_EPILOGUE_LENGTH = 32; + +// Holds a necessary context for installing and uninstalling inline hook. +struct HookInfo { + UCHAR *HookAddress; // An address to install inline hook + FARPROC HookHandler; // A hook handler to be called instead + SIZE_T OriginalCodeSize; // A size of saved original code + UCHAR OriginalCode[DISPGP_MAXIMUM_EPILOGUE_LENGTH]; // A saved original code +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +EXTERN_C NTSTATUS +InlineInitHookInfo(_In_ UCHAR *HookAddress, _In_ FARPROC HookHandler, + _In_ FARPROC AsmHandler, _In_ FARPROC AsmHandlerEnd, + _Out_ HookInfo *Info); + +EXTERN_C NTSTATUS InlineInstallHook(_In_ const HookInfo &Info); + +EXTERN_C NTSTATUS InlineUninstallHook(_In_ const HookInfo &Info); + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/log.cpp b/RemoteWriteMonitor/RemoteWriteMonitor/log.cpp index 9a67599..e80aa65 100644 --- a/RemoteWriteMonitor/RemoteWriteMonitor/log.cpp +++ b/RemoteWriteMonitor/RemoteWriteMonitor/log.cpp @@ -1,640 +1,640 @@ -// Copyright (c) 2015, tandasat. All rights reserved. -// Use of this source code is governed by a MIT-style license that can be -// found in the LICENSE file. - -// -// This module implements logging functions. -// -#include "stdafx.h" -#include "log.h" - -//////////////////////////////////////////////////////////////////////////////// -// -// macro utilities -// - -//////////////////////////////////////////////////////////////////////////////// -// -// constant and macro -// - -// A size for log buffer in NonPagedPool. Two buffers are allocated with this -// size. Exceeded logs are ignored silently. Make it bigger if a buffered log -// size often reach this size. -static const auto LOGP_BUFFER_SIZE_IN_PAGES = 5ul; - -// An actual log buffer size in bytes. -static const auto LOGP_BUFFER_SIZE = PAGE_SIZE * LOGP_BUFFER_SIZE_IN_PAGES; - -// A size that is usable for logging. Minus one because the last byte is kept -// for \0. -static const auto LOGP_BUFFER_USABLE_SIZE = LOGP_BUFFER_SIZE - 1; - -// An interval to flush buffered log entries into a log file. -static const auto LOGP_AUTO_FLUSH_INTERVAL_MSEC = 50; - -static const ULONG LOGP_POOL_TAG_NAME = ' gol'; - -//////////////////////////////////////////////////////////////////////////////// -// -// types -// - -struct LogBufferInfo { - volatile char *LogBufferHead; // A pointer to a buffer currently used. - // It is either LogBuffer1 or LogBuffer2. - volatile char *LogBufferTail; // A pointer to where the next log should - // be written. - char *LogBuffer1; - char *LogBuffer2; - SIZE_T LogMaximumUsage; // Holds the biggest buffer usage to - // determine a necessary buffer size. - HANDLE LogFileHandle; - KSPIN_LOCK SpinLock; - ERESOURCE Resource; - volatile bool BufferFlushThreadShouldBeAlive; - HANDLE BufferFlushThreadHandle; -}; - -//////////////////////////////////////////////////////////////////////////////// -// -// prototypes -// - -EXTERN_C NTKERNELAPI UCHAR *NTAPI -PsGetProcessImageFileName(_In_ PEPROCESS Process); - -EXTERN_C static NTSTATUS LogpInitializeBufferInfo( - _In_ const wchar_t *LogFilePath, _In_opt_ PDEVICE_OBJECT DeviceObject, - _Inout_ LogBufferInfo *Info); - -EXTERN_C static void LogpFinalizeBufferInfo(_In_opt_ PDEVICE_OBJECT - DeviceObject, - _In_ LogBufferInfo *Info); - -#ifdef _X86_ -_Requires_lock_not_held_(*SpinLock) _Acquires_lock_(*SpinLock) - _IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_saves_ - _IRQL_raises_(DISPATCH_LEVEL) inline KIRQL - KeAcquireSpinLockRaiseToDpc(_Inout_ PKSPIN_LOCK SpinLock); -#endif - -EXTERN_C static NTSTATUS LogpMakePrefix(_In_ ULONG Level, - _In_ const char *FunctionName, - _In_ const char *LogMessage, - _Out_ char *LogBuffer, - _In_ size_t LogBufferLength); - -EXTERN_C static const char *LogpFindBaseFunctionName( - _In_ const char *FunctionName); - -EXTERN_C static NTSTATUS LogpPut(_In_ const char *Message, - _In_ ULONG Attribute); - -EXTERN_C static NTSTATUS LogpWriteLogBufferToFile(_In_opt_ LogBufferInfo *Info); - -EXTERN_C static NTSTATUS LogpWriteMessageToFile(_In_ const char *Message, - _In_ const LogBufferInfo &Info); - -EXTERN_C static NTSTATUS LogpBufferMessage(_In_ const char *Message, - _In_opt_ LogBufferInfo *Info); - -EXTERN_C static bool LogpIsLogFileEnabled(_In_ const LogBufferInfo &Info); - -EXTERN_C static bool LogpIsLogNeeded(_In_ ULONG Level); - -EXTERN_C static KSTART_ROUTINE LogpBufferFlushThreadRoutine; - -EXTERN_C static NTSTATUS LogpSleep(_In_ LONG Millisecond); - -//////////////////////////////////////////////////////////////////////////////// -// -// variables -// - -static auto g_LogpDebugFlag = LOG_PUT_LEVEL_DISABLE; -static LogBufferInfo g_LogpLogBufferInfo = {}; - -//////////////////////////////////////////////////////////////////////////////// -// -// implementations -// - -ALLOC_TEXT(INIT, LogInitialization) -EXTERN_C NTSTATUS LogInitialization(_In_ ULONG Flag, - _In_opt_ const wchar_t *LogFilePath, - _In_opt_ PDEVICE_OBJECT DeviceObject) { - PAGED_CODE(); - - auto status = STATUS_SUCCESS; - - g_LogpDebugFlag = Flag; - - if (DeviceObject && !LogFilePath) { - return STATUS_INVALID_PARAMETER; - } - - // Initialize a log file if a log file path is specified. - if (LogFilePath) { - status = LogpInitializeBufferInfo(LogFilePath, DeviceObject, - &g_LogpLogBufferInfo); - if (!NT_SUCCESS(status)) { - return status; - } - } - - // Test the log. - status = LOG_INFO( - "Log system was initialized (Flag= %08x, Buffer= %p %p, File= %S).", Flag, - g_LogpLogBufferInfo.LogBuffer1, g_LogpLogBufferInfo.LogBuffer2, - LogFilePath); - if (!NT_SUCCESS(status)) { - goto Fail; - } - return status; - -Fail: - if (LogFilePath) { - LogpFinalizeBufferInfo(DeviceObject, &g_LogpLogBufferInfo); - } - return status; -} - -// Initialize a log file related code such as a flushing thread. -ALLOC_TEXT(INIT, LogpInitializeBufferInfo) -EXTERN_C static NTSTATUS LogpInitializeBufferInfo( - _In_ const wchar_t *LogFilePath, _In_opt_ PDEVICE_OBJECT DeviceObject, - _Inout_ LogBufferInfo *Info) { - NT_ASSERT(LogFilePath); - NT_ASSERT(Info); - - KeInitializeSpinLock(&Info->SpinLock); - - auto status = ExInitializeResourceLite(&Info->Resource); - if (!NT_SUCCESS(status)) { - return status; - } - - if (DeviceObject) { - // We can handle IRP_MJ_SHUTDOWN in order to flush buffered log entries. - status = IoRegisterShutdownNotification(DeviceObject); - if (!NT_SUCCESS(status)) { - LogpFinalizeBufferInfo(DeviceObject, Info); - return status; - } - } - - // Allocate two log buffers on NonPagedPool. - Info->LogBuffer1 = reinterpret_cast(ExAllocatePoolWithTag( - NonPagedPool, LOGP_BUFFER_SIZE, LOGP_POOL_TAG_NAME)); - if (!Info->LogBuffer1) { - LogpFinalizeBufferInfo(DeviceObject, Info); - return STATUS_INSUFFICIENT_RESOURCES; - } - - Info->LogBuffer2 = reinterpret_cast(ExAllocatePoolWithTag( - NonPagedPool, LOGP_BUFFER_SIZE, LOGP_POOL_TAG_NAME)); - if (!Info->LogBuffer2) { - LogpFinalizeBufferInfo(DeviceObject, Info); - return STATUS_INSUFFICIENT_RESOURCES; - } - - // Initialize these buffers - RtlFillMemory(Info->LogBuffer1, LOGP_BUFFER_SIZE, 0xff); // for debug - Info->LogBuffer1[0] = '\0'; - Info->LogBuffer1[LOGP_BUFFER_SIZE - 1] = '\0'; // at the end - - RtlFillMemory(Info->LogBuffer2, LOGP_BUFFER_SIZE, 0xff); // for debug - Info->LogBuffer2[0] = '\0'; - Info->LogBuffer2[LOGP_BUFFER_SIZE - 1] = '\0'; // at the end - - // Buffer should be used is LogBuffer1, and location should be written logs - // is the head of the buffer. - Info->LogBufferHead = Info->LogBuffer1; - Info->LogBufferTail = Info->LogBuffer1; - - // Initialize a log file - UNICODE_STRING logFilePathU = {}; - RtlInitUnicodeString(&logFilePathU, LogFilePath); - - OBJECT_ATTRIBUTES oa = {}; - InitializeObjectAttributes(&oa, &logFilePathU, - OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, nullptr, - nullptr); - - IO_STATUS_BLOCK ioStatus = {}; - status = ZwCreateFile( - &Info->LogFileHandle, FILE_APPEND_DATA | SYNCHRONIZE, &oa, &ioStatus, - nullptr, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN_IF, - FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, nullptr, 0); - if (!NT_SUCCESS(status)) { - LogpFinalizeBufferInfo(DeviceObject, Info); - return status; - } - - // Initialize a log buffer flush thread. - Info->BufferFlushThreadShouldBeAlive = true; - status = PsCreateSystemThread(&Info->BufferFlushThreadHandle, GENERIC_ALL, - nullptr, nullptr, nullptr, - LogpBufferFlushThreadRoutine, Info); - if (!NT_SUCCESS(status)) { - LogpFinalizeBufferInfo(DeviceObject, Info); - return status; - } - - return status; -} - -// Terminates the log functions without releasing resources. -ALLOC_TEXT(PAGED, LogIrpShutdownHandler) -EXTERN_C void LogIrpShutdownHandler() { - PAGED_CODE(); - - LOG_DEBUG("Flushing... (Max log usage = %08x bytes)", - g_LogpLogBufferInfo.LogMaximumUsage); - LOG_INFO("Bye!"); - g_LogpDebugFlag = LOG_PUT_LEVEL_DISABLE; - - // Wait until the log buffer is emptied. - auto &info = g_LogpLogBufferInfo; - while (info.LogBufferHead[0]) { - LogpSleep(LOGP_AUTO_FLUSH_INTERVAL_MSEC); - } -} - -// Terminates the log functions. -ALLOC_TEXT(PAGED, LogTermination) -EXTERN_C void LogTermination(_In_opt_ PDEVICE_OBJECT DeviceObject) { - PAGED_CODE(); - - LOG_DEBUG("Finalizing... (Max log usage = %08x bytes)", - g_LogpLogBufferInfo.LogMaximumUsage); - LOG_INFO("Bye!"); - g_LogpDebugFlag = LOG_PUT_LEVEL_DISABLE; - LogpFinalizeBufferInfo(DeviceObject, &g_LogpLogBufferInfo); -} - -// Terminates a log file related code. -ALLOC_TEXT(PAGED, LogpFinalizeBufferInfo) -EXTERN_C static void LogpFinalizeBufferInfo(_In_opt_ PDEVICE_OBJECT - DeviceObject, - _In_ LogBufferInfo *Info) { - PAGED_CODE(); - NT_ASSERT(Info); - - // Closing the log buffer flush thread. - if (Info->BufferFlushThreadHandle) { - Info->BufferFlushThreadShouldBeAlive = false; - auto status = - ZwWaitForSingleObject(Info->BufferFlushThreadHandle, FALSE, nullptr); - if (!NT_SUCCESS(status)) { - DBG_BREAK(); - } - ZwClose(Info->BufferFlushThreadHandle); - Info->BufferFlushThreadHandle = nullptr; - } - - // Cleaning up other things. - if (Info->LogFileHandle) { - ZwClose(Info->LogFileHandle); - Info->LogFileHandle = nullptr; - } - if (Info->LogBuffer2) { - ExFreePoolWithTag(Info->LogBuffer2, LOGP_POOL_TAG_NAME); - Info->LogBuffer2 = nullptr; - } - if (Info->LogBuffer1) { - ExFreePoolWithTag(Info->LogBuffer1, LOGP_POOL_TAG_NAME); - Info->LogBuffer1 = nullptr; - } - - if (DeviceObject) { - IoUnregisterShutdownNotification(DeviceObject); - } - ExDeleteResourceLite(&Info->Resource); -} - -#ifdef _X86_ -_Requires_lock_not_held_(*SpinLock) _Acquires_lock_(*SpinLock) - _IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_saves_ - _IRQL_raises_(DISPATCH_LEVEL) inline KIRQL - KeAcquireSpinLockRaiseToDpc(_Inout_ PKSPIN_LOCK SpinLock) { - KIRQL irql = {}; - KeAcquireSpinLock(SpinLock, &irql); - return irql; -} -#endif - -// Actual implementation of logging API. -EXTERN_C NTSTATUS LogPrint(_In_ ULONG Level, _In_ const char *FunctionName, - _In_ const char *Format, ...) { - auto status = STATUS_SUCCESS; - - if (!LogpIsLogNeeded(Level)) { - return status; - } - - va_list args; - va_start(args, Format); - char logMessage[300]; - status = - RtlStringCchVPrintfA(logMessage, RTL_NUMBER_OF(logMessage), Format, args); - va_end(args); - if (!NT_SUCCESS(status)) { - return status; - } - if (logMessage[0] == '\0') { - return STATUS_INVALID_PARAMETER; - } - - const auto pureLevel = Level & 0xf0; - const auto attribute = Level & 0x0f; - - // A single entry of log should not exceed 512 bytes. See - // Reading and Filtering Debugging Messages in MSDN for details. - char message[100 + RTL_NUMBER_OF(logMessage)]; - static_assert(RTL_NUMBER_OF(message) <= 512, - "One log message should not exceed 512 bytes."); - status = LogpMakePrefix(pureLevel, FunctionName, logMessage, message, - RTL_NUMBER_OF(message)); - if (!NT_SUCCESS(status)) { - return status; - } - - return LogpPut(message, attribute); -} - -// Concatenates meta information such as the current time and a process ID to -// user given log message. -EXTERN_C static NTSTATUS LogpMakePrefix(_In_ ULONG Level, - _In_ const char *FunctionName, - _In_ const char *LogMessage, - _Out_ char *LogBuffer, - _In_ size_t LogBufferLength) { - char const *levelString = nullptr; - switch (Level) { - case LOGP_LEVEL_DEBUG: - levelString = "DBG"; - break; - case LOGP_LEVEL_INFO: - levelString = "INF"; - break; - case LOGP_LEVEL_WARN: - levelString = "WRN"; - break; - case LOGP_LEVEL_ERROR: - levelString = "ERR"; - break; - default: - return STATUS_INVALID_PARAMETER; - } - - auto status = STATUS_SUCCESS; - - char timeBuffer[20] = {}; - if ((g_LogpDebugFlag & LOG_OPT_DISABLE_TIME) == 0) { - // Want the current time. - TIME_FIELDS timeFields; - LARGE_INTEGER systemTime, localTime; - KeQuerySystemTime(&systemTime); - ExSystemTimeToLocalTime(&systemTime, &localTime); - RtlTimeToTimeFields(&localTime, &timeFields); - - status = RtlStringCchPrintfA(timeBuffer, RTL_NUMBER_OF(timeBuffer), - "%02u:%02u:%02u.%03u\t", timeFields.Hour, - timeFields.Minute, timeFields.Second, - timeFields.Milliseconds); - if (!NT_SUCCESS(status)) { - return status; - } - } - - char functionNameBuffer[50] = {}; - if ((g_LogpDebugFlag & LOG_OPT_DISABLE_FUNCTION_NAME) == 0) { - // Want the function name - const auto baseFunctionName = LogpFindBaseFunctionName(FunctionName); - status = RtlStringCchPrintfA(functionNameBuffer, - RTL_NUMBER_OF(functionNameBuffer), "%-40s\t", - baseFunctionName); - if (!NT_SUCCESS(status)) { - return status; - } - } - - // - // It uses PsGetProcessId(PsGetCurrentProcess()) instead of - // PsGetCurrentThreadProcessId() because the later sometimes returns - // unwanted value, for example: - // PID == 4 but its image name != ntoskrnl.exe - // The author is guessing that it is related to attaching processes but - // not quite sure. The former way works as expected. - // - status = RtlStringCchPrintfA( - LogBuffer, LogBufferLength, "%s%s\t%5lu\t%5lu\t%-15s\t%s%s\r\n", - timeBuffer, levelString, - reinterpret_cast(PsGetProcessId(PsGetCurrentProcess())), - reinterpret_cast(PsGetCurrentThreadId()), - PsGetProcessImageFileName(PsGetCurrentProcess()), functionNameBuffer, - LogMessage); - return status; -} - -// Returns the function's base name, for example, -// NamespaceName::ClassName::MethodName will be returned as MethodName. -EXTERN_C static const char *LogpFindBaseFunctionName( - _In_ const char *FunctionName) { - if (!FunctionName) { - return nullptr; - } - - auto ptr = FunctionName; - auto name = FunctionName; - while (*(ptr++)) { - if (*ptr == ':') { - name = ptr + 1; - } - } - return name; -} - -// Logs the entry according to Attribute and the thread condition. -EXTERN_C static NTSTATUS LogpPut(_In_ const char *Message, - _In_ ULONG Attribute) { - auto status = STATUS_SUCCESS; - - // Log the entry to a file or buffer. - auto &info = g_LogpLogBufferInfo; - if (LogpIsLogFileEnabled(info)) { - // Can it log it to a file now? - if (((Attribute & LOGP_LEVEL_OPT_SAFE) == 0) && - KeGetCurrentIrql() == PASSIVE_LEVEL && !KeAreAllApcsDisabled()) { - // Yes, it can. Do it. - LogpWriteLogBufferToFile(&info); - status = LogpWriteMessageToFile(Message, info); - } else { - // No, it cannot. Buffer it. - status = LogpBufferMessage(Message, &info); - } - } - - // Can it safely be printed? - if (KeGetCurrentIrql() >= CLOCK_LEVEL) { - return STATUS_UNSUCCESSFUL; - } - - DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_ERROR_LEVEL, "%s", Message); - return status; -} - -// Switch the current log buffer and save the contents of old buffer to the log -// file. This function does not flush the log file, so code should call -// LogpWriteMessageToFile() or ZwFlushBuffersFile() later. -EXTERN_C static NTSTATUS LogpWriteLogBufferToFile( - _In_opt_ LogBufferInfo *Info) { - NT_ASSERT(Info); - auto status = STATUS_SUCCESS; - - // Enter a critical section and acquire a reader lock for Info in order to - // write a log file safely. - ExEnterCriticalRegionAndAcquireResourceExclusive(&Info->Resource); - - // Acquire a spin lock for Info.LogBuffer(s) in order to switch its head - // safely. - const auto irql = KeAcquireSpinLockRaiseToDpc(&Info->SpinLock); - auto oldLogBuffer = const_cast(Info->LogBufferHead); - if (oldLogBuffer[0]) { - Info->LogBufferHead = (oldLogBuffer == Info->LogBuffer1) ? Info->LogBuffer2 - : Info->LogBuffer1; - Info->LogBufferHead[0] = '\0'; - Info->LogBufferTail = Info->LogBufferHead; - } - KeReleaseSpinLock(&Info->SpinLock, irql); - - // Write all log entries in old log buffer. - IO_STATUS_BLOCK ioStatus = {}; - for (auto currentLogEntry = oldLogBuffer; currentLogEntry[0]; /**/) { - const auto currentLogEntryLength = strlen(currentLogEntry); - status = - ZwWriteFile(Info->LogFileHandle, nullptr, nullptr, nullptr, &ioStatus, - currentLogEntry, static_cast(currentLogEntryLength), - nullptr, nullptr); - if (!NT_SUCCESS(status)) { - // It could happen when you did not register IRP_SHUTDOWN and call - // LogIrpShutdownHandler() and the system tried to log to a file after - // a filesystem was unmounted. - DBG_BREAK(); - } - - currentLogEntry += currentLogEntryLength + 1; - } - oldLogBuffer[0] = '\0'; - - ExReleaseResourceAndLeaveCriticalRegion(&Info->Resource); - return status; -} - -// Logs the current log entry to and flush the log file. -EXTERN_C static NTSTATUS LogpWriteMessageToFile( - _In_ const char *Message, _In_ const LogBufferInfo &Info) { - IO_STATUS_BLOCK ioStatus = {}; - auto status = - ZwWriteFile(Info.LogFileHandle, nullptr, nullptr, nullptr, &ioStatus, - const_cast(Message), - static_cast(strlen(Message)), nullptr, nullptr); - if (!NT_SUCCESS(status)) { - // It could happen when you did not register IRP_SHUTDOWN and call - // LogIrpShutdownHandler() and the system tried to log to a file after - // a filesystem was unmounted. - DBG_BREAK(); - } - status = ZwFlushBuffersFile(Info.LogFileHandle, &ioStatus); - return status; -} - -// Buffer the log entry to the log buffer. -EXTERN_C static NTSTATUS LogpBufferMessage(_In_ const char *Message, - _In_opt_ LogBufferInfo *Info) { - NT_ASSERT(Info); - - // Acquire a spin lock to add the log safely. - const auto irql = KeAcquireSpinLockRaiseToDpc(&Info->SpinLock); - - // Copy the current log to the buffer. - size_t usedBufferSize = Info->LogBufferTail - Info->LogBufferHead; - auto status = - RtlStringCchCopyA(const_cast(Info->LogBufferTail), - LOGP_BUFFER_USABLE_SIZE - usedBufferSize, Message); - - // Update Info.LogMaximumUsage if necessary. - if (NT_SUCCESS(status)) { - const auto messageLength = strlen(Message) + 1; - Info->LogBufferTail += messageLength; - usedBufferSize += messageLength; - if (usedBufferSize > Info->LogMaximumUsage) { - Info->LogMaximumUsage = usedBufferSize; // Update - } - } else { - Info->LogMaximumUsage = LOGP_BUFFER_SIZE; // Indicates overflow - } - *Info->LogBufferTail = '\0'; - - KeReleaseSpinLock(&Info->SpinLock, irql); - return status; -} - -// Returns true when a log file is enabled. -EXTERN_C static bool LogpIsLogFileEnabled(_In_ const LogBufferInfo &Info) { - if (Info.LogFileHandle) { - NT_ASSERT(Info.LogBuffer1); - NT_ASSERT(Info.LogBuffer2); - NT_ASSERT(Info.LogBufferHead); - NT_ASSERT(Info.LogBufferTail); - return true; - } - NT_ASSERT(!Info.LogBuffer1); - NT_ASSERT(!Info.LogBuffer2); - NT_ASSERT(!Info.LogBufferHead); - NT_ASSERT(!Info.LogBufferTail); - return false; -} - -// Returns true when logging is necessary according to the log's severity and -// a set log level. -EXTERN_C static bool LogpIsLogNeeded(_In_ ULONG Level) { - return !!(g_LogpDebugFlag & Level); -} - -// A thread runs as long as info.BufferFlushThreadShouldBeAlive is true and -// flushes a log buffer to a log file every LOGP_AUTO_FLUSH_INTERVAL_MSEC msec. -ALLOC_TEXT(PAGED, LogpBufferFlushThreadRoutine) -EXTERN_C static VOID LogpBufferFlushThreadRoutine(_In_ void *StartContext) { - PAGED_CODE(); - auto status = STATUS_SUCCESS; - auto info = reinterpret_cast(StartContext); - LOG_DEBUG("Log thread started."); - NT_ASSERT(LogpIsLogFileEnabled(*info)); - - while (info->BufferFlushThreadShouldBeAlive) { - if (info->LogBufferHead[0]) { - NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); - NT_ASSERT(!KeAreAllApcsDisabled()); - status = LogpWriteLogBufferToFile(info); - // Do not flush the file for overall performance. Even a case of - // bug check, we should be able to recover logs by looking at both - // log buffers. - } - LogpSleep(LOGP_AUTO_FLUSH_INTERVAL_MSEC); - } - LOG_DEBUG("Log thread is ending."); - PsTerminateSystemThread(status); -} - -// Sleep the current thread's execution for Millisecond milli-seconds. -ALLOC_TEXT(PAGED, LogpSleep) -EXTERN_C static NTSTATUS LogpSleep(_In_ LONG Millisecond) { - PAGED_CODE(); - - LARGE_INTEGER interval = {}; - interval.QuadPart = -(10000 * Millisecond); // msec - return KeDelayExecutionThread(KernelMode, FALSE, &interval); -} +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// This module implements logging functions. +// +#include "stdafx.h" +#include "log.h" + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +//////////////////////////////////////////////////////////////////////////////// +// +// constant and macro +// + +// A size for log buffer in NonPagedPool. Two buffers are allocated with this +// size. Exceeded logs are ignored silently. Make it bigger if a buffered log +// size often reach this size. +static const auto LOGP_BUFFER_SIZE_IN_PAGES = 5ul; + +// An actual log buffer size in bytes. +static const auto LOGP_BUFFER_SIZE = PAGE_SIZE * LOGP_BUFFER_SIZE_IN_PAGES; + +// A size that is usable for logging. Minus one because the last byte is kept +// for \0. +static const auto LOGP_BUFFER_USABLE_SIZE = LOGP_BUFFER_SIZE - 1; + +// An interval to flush buffered log entries into a log file. +static const auto LOGP_AUTO_FLUSH_INTERVAL_MSEC = 50; + +static const ULONG LOGP_POOL_TAG_NAME = ' gol'; + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// + +struct LogBufferInfo { + volatile char *LogBufferHead; // A pointer to a buffer currently used. + // It is either LogBuffer1 or LogBuffer2. + volatile char *LogBufferTail; // A pointer to where the next log should + // be written. + char *LogBuffer1; + char *LogBuffer2; + SIZE_T LogMaximumUsage; // Holds the biggest buffer usage to + // determine a necessary buffer size. + HANDLE LogFileHandle; + KSPIN_LOCK SpinLock; + ERESOURCE Resource; + volatile bool BufferFlushThreadShouldBeAlive; + HANDLE BufferFlushThreadHandle; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +EXTERN_C NTKERNELAPI UCHAR *NTAPI +PsGetProcessImageFileName(_In_ PEPROCESS Process); + +EXTERN_C static NTSTATUS LogpInitializeBufferInfo( + _In_ const wchar_t *LogFilePath, _In_opt_ PDEVICE_OBJECT DeviceObject, + _Inout_ LogBufferInfo *Info); + +EXTERN_C static void LogpFinalizeBufferInfo(_In_opt_ PDEVICE_OBJECT + DeviceObject, + _In_ LogBufferInfo *Info); + +#ifdef _X86_ +_Requires_lock_not_held_(*SpinLock) _Acquires_lock_(*SpinLock) + _IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_saves_ + _IRQL_raises_(DISPATCH_LEVEL) inline KIRQL + KeAcquireSpinLockRaiseToDpc(_Inout_ PKSPIN_LOCK SpinLock); +#endif + +EXTERN_C static NTSTATUS LogpMakePrefix(_In_ ULONG Level, + _In_ const char *FunctionName, + _In_ const char *LogMessage, + _Out_ char *LogBuffer, + _In_ size_t LogBufferLength); + +EXTERN_C static const char *LogpFindBaseFunctionName( + _In_ const char *FunctionName); + +EXTERN_C static NTSTATUS LogpPut(_In_ const char *Message, + _In_ ULONG Attribute); + +EXTERN_C static NTSTATUS LogpWriteLogBufferToFile(_In_opt_ LogBufferInfo *Info); + +EXTERN_C static NTSTATUS LogpWriteMessageToFile(_In_ const char *Message, + _In_ const LogBufferInfo &Info); + +EXTERN_C static NTSTATUS LogpBufferMessage(_In_ const char *Message, + _In_opt_ LogBufferInfo *Info); + +EXTERN_C static bool LogpIsLogFileEnabled(_In_ const LogBufferInfo &Info); + +EXTERN_C static bool LogpIsLogNeeded(_In_ ULONG Level); + +EXTERN_C static KSTART_ROUTINE LogpBufferFlushThreadRoutine; + +EXTERN_C static NTSTATUS LogpSleep(_In_ LONG Millisecond); + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +static auto g_LogpDebugFlag = LOG_PUT_LEVEL_DISABLE; +static LogBufferInfo g_LogpLogBufferInfo = {}; + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// + +ALLOC_TEXT(INIT, LogInitialization) +EXTERN_C NTSTATUS LogInitialization(_In_ ULONG Flag, + _In_opt_ const wchar_t *LogFilePath, + _In_opt_ PDEVICE_OBJECT DeviceObject) { + PAGED_CODE(); + + auto status = STATUS_SUCCESS; + + g_LogpDebugFlag = Flag; + + if (DeviceObject && !LogFilePath) { + return STATUS_INVALID_PARAMETER; + } + + // Initialize a log file if a log file path is specified. + if (LogFilePath) { + status = LogpInitializeBufferInfo(LogFilePath, DeviceObject, + &g_LogpLogBufferInfo); + if (!NT_SUCCESS(status)) { + return status; + } + } + + // Test the log. + status = LOG_INFO( + "Log system was initialized (Flag= %08x, Buffer= %p %p, File= %S).", Flag, + g_LogpLogBufferInfo.LogBuffer1, g_LogpLogBufferInfo.LogBuffer2, + LogFilePath); + if (!NT_SUCCESS(status)) { + goto Fail; + } + return status; + +Fail: + if (LogFilePath) { + LogpFinalizeBufferInfo(DeviceObject, &g_LogpLogBufferInfo); + } + return status; +} + +// Initialize a log file related code such as a flushing thread. +ALLOC_TEXT(INIT, LogpInitializeBufferInfo) +EXTERN_C static NTSTATUS LogpInitializeBufferInfo( + _In_ const wchar_t *LogFilePath, _In_opt_ PDEVICE_OBJECT DeviceObject, + _Inout_ LogBufferInfo *Info) { + NT_ASSERT(LogFilePath); + NT_ASSERT(Info); + + KeInitializeSpinLock(&Info->SpinLock); + + auto status = ExInitializeResourceLite(&Info->Resource); + if (!NT_SUCCESS(status)) { + return status; + } + + if (DeviceObject) { + // We can handle IRP_MJ_SHUTDOWN in order to flush buffered log entries. + status = IoRegisterShutdownNotification(DeviceObject); + if (!NT_SUCCESS(status)) { + LogpFinalizeBufferInfo(DeviceObject, Info); + return status; + } + } + + // Allocate two log buffers on NonPagedPool. + Info->LogBuffer1 = reinterpret_cast(ExAllocatePoolWithTag( + NonPagedPool, LOGP_BUFFER_SIZE, LOGP_POOL_TAG_NAME)); + if (!Info->LogBuffer1) { + LogpFinalizeBufferInfo(DeviceObject, Info); + return STATUS_INSUFFICIENT_RESOURCES; + } + + Info->LogBuffer2 = reinterpret_cast(ExAllocatePoolWithTag( + NonPagedPool, LOGP_BUFFER_SIZE, LOGP_POOL_TAG_NAME)); + if (!Info->LogBuffer2) { + LogpFinalizeBufferInfo(DeviceObject, Info); + return STATUS_INSUFFICIENT_RESOURCES; + } + + // Initialize these buffers + RtlFillMemory(Info->LogBuffer1, LOGP_BUFFER_SIZE, 0xff); // for debug + Info->LogBuffer1[0] = '\0'; + Info->LogBuffer1[LOGP_BUFFER_SIZE - 1] = '\0'; // at the end + + RtlFillMemory(Info->LogBuffer2, LOGP_BUFFER_SIZE, 0xff); // for debug + Info->LogBuffer2[0] = '\0'; + Info->LogBuffer2[LOGP_BUFFER_SIZE - 1] = '\0'; // at the end + + // Buffer should be used is LogBuffer1, and location should be written logs + // is the head of the buffer. + Info->LogBufferHead = Info->LogBuffer1; + Info->LogBufferTail = Info->LogBuffer1; + + // Initialize a log file + UNICODE_STRING logFilePathU = {}; + RtlInitUnicodeString(&logFilePathU, LogFilePath); + + OBJECT_ATTRIBUTES oa = {}; + InitializeObjectAttributes(&oa, &logFilePathU, + OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, nullptr, + nullptr); + + IO_STATUS_BLOCK ioStatus = {}; + status = ZwCreateFile( + &Info->LogFileHandle, FILE_APPEND_DATA | SYNCHRONIZE, &oa, &ioStatus, + nullptr, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN_IF, + FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, nullptr, 0); + if (!NT_SUCCESS(status)) { + LogpFinalizeBufferInfo(DeviceObject, Info); + return status; + } + + // Initialize a log buffer flush thread. + Info->BufferFlushThreadShouldBeAlive = true; + status = PsCreateSystemThread(&Info->BufferFlushThreadHandle, GENERIC_ALL, + nullptr, nullptr, nullptr, + LogpBufferFlushThreadRoutine, Info); + if (!NT_SUCCESS(status)) { + LogpFinalizeBufferInfo(DeviceObject, Info); + return status; + } + + return status; +} + +// Terminates the log functions without releasing resources. +ALLOC_TEXT(PAGED, LogIrpShutdownHandler) +EXTERN_C void LogIrpShutdownHandler() { + PAGED_CODE(); + + LOG_DEBUG("Flushing... (Max log usage = %08x bytes)", + g_LogpLogBufferInfo.LogMaximumUsage); + LOG_INFO("Bye!"); + g_LogpDebugFlag = LOG_PUT_LEVEL_DISABLE; + + // Wait until the log buffer is emptied. + auto &info = g_LogpLogBufferInfo; + while (info.LogBufferHead[0]) { + LogpSleep(LOGP_AUTO_FLUSH_INTERVAL_MSEC); + } +} + +// Terminates the log functions. +ALLOC_TEXT(PAGED, LogTermination) +EXTERN_C void LogTermination(_In_opt_ PDEVICE_OBJECT DeviceObject) { + PAGED_CODE(); + + LOG_DEBUG("Finalizing... (Max log usage = %08x bytes)", + g_LogpLogBufferInfo.LogMaximumUsage); + LOG_INFO("Bye!"); + g_LogpDebugFlag = LOG_PUT_LEVEL_DISABLE; + LogpFinalizeBufferInfo(DeviceObject, &g_LogpLogBufferInfo); +} + +// Terminates a log file related code. +ALLOC_TEXT(PAGED, LogpFinalizeBufferInfo) +EXTERN_C static void LogpFinalizeBufferInfo(_In_opt_ PDEVICE_OBJECT + DeviceObject, + _In_ LogBufferInfo *Info) { + PAGED_CODE(); + NT_ASSERT(Info); + + // Closing the log buffer flush thread. + if (Info->BufferFlushThreadHandle) { + Info->BufferFlushThreadShouldBeAlive = false; + auto status = + ZwWaitForSingleObject(Info->BufferFlushThreadHandle, FALSE, nullptr); + if (!NT_SUCCESS(status)) { + DBG_BREAK(); + } + ZwClose(Info->BufferFlushThreadHandle); + Info->BufferFlushThreadHandle = nullptr; + } + + // Cleaning up other things. + if (Info->LogFileHandle) { + ZwClose(Info->LogFileHandle); + Info->LogFileHandle = nullptr; + } + if (Info->LogBuffer2) { + ExFreePoolWithTag(Info->LogBuffer2, LOGP_POOL_TAG_NAME); + Info->LogBuffer2 = nullptr; + } + if (Info->LogBuffer1) { + ExFreePoolWithTag(Info->LogBuffer1, LOGP_POOL_TAG_NAME); + Info->LogBuffer1 = nullptr; + } + + if (DeviceObject) { + IoUnregisterShutdownNotification(DeviceObject); + } + ExDeleteResourceLite(&Info->Resource); +} + +#ifdef _X86_ +_Requires_lock_not_held_(*SpinLock) _Acquires_lock_(*SpinLock) + _IRQL_requires_max_(DISPATCH_LEVEL) _IRQL_saves_ + _IRQL_raises_(DISPATCH_LEVEL) inline KIRQL + KeAcquireSpinLockRaiseToDpc(_Inout_ PKSPIN_LOCK SpinLock) { + KIRQL irql = {}; + KeAcquireSpinLock(SpinLock, &irql); + return irql; +} +#endif + +// Actual implementation of logging API. +EXTERN_C NTSTATUS LogpPrint(_In_ ULONG Level, _In_ const char *FunctionName, + _In_ const char *Format, ...) { + auto status = STATUS_SUCCESS; + + if (!LogpIsLogNeeded(Level)) { + return status; + } + + va_list args; + va_start(args, Format); + char logMessage[300]; + status = + RtlStringCchVPrintfA(logMessage, RTL_NUMBER_OF(logMessage), Format, args); + va_end(args); + if (!NT_SUCCESS(status)) { + return status; + } + if (logMessage[0] == '\0') { + return STATUS_INVALID_PARAMETER; + } + + const auto pureLevel = Level & 0xf0; + const auto attribute = Level & 0x0f; + + // A single entry of log should not exceed 512 bytes. See + // Reading and Filtering Debugging Messages in MSDN for details. + char message[100 + RTL_NUMBER_OF(logMessage)]; + static_assert(RTL_NUMBER_OF(message) <= 512, + "One log message should not exceed 512 bytes."); + status = LogpMakePrefix(pureLevel, FunctionName, logMessage, message, + RTL_NUMBER_OF(message)); + if (!NT_SUCCESS(status)) { + return status; + } + + return LogpPut(message, attribute); +} + +// Concatenates meta information such as the current time and a process ID to +// user given log message. +EXTERN_C static NTSTATUS LogpMakePrefix(_In_ ULONG Level, + _In_ const char *FunctionName, + _In_ const char *LogMessage, + _Out_ char *LogBuffer, + _In_ size_t LogBufferLength) { + char const *levelString = nullptr; + switch (Level) { + case LOGP_LEVEL_DEBUG: + levelString = "DBG"; + break; + case LOGP_LEVEL_INFO: + levelString = "INF"; + break; + case LOGP_LEVEL_WARN: + levelString = "WRN"; + break; + case LOGP_LEVEL_ERROR: + levelString = "ERR"; + break; + default: + return STATUS_INVALID_PARAMETER; + } + + auto status = STATUS_SUCCESS; + + char timeBuffer[20] = {}; + if ((g_LogpDebugFlag & LOG_OPT_DISABLE_TIME) == 0) { + // Want the current time. + TIME_FIELDS timeFields; + LARGE_INTEGER systemTime, localTime; + KeQuerySystemTime(&systemTime); + ExSystemTimeToLocalTime(&systemTime, &localTime); + RtlTimeToTimeFields(&localTime, &timeFields); + + status = RtlStringCchPrintfA(timeBuffer, RTL_NUMBER_OF(timeBuffer), + "%02u:%02u:%02u.%03u\t", timeFields.Hour, + timeFields.Minute, timeFields.Second, + timeFields.Milliseconds); + if (!NT_SUCCESS(status)) { + return status; + } + } + + char functionNameBuffer[50] = {}; + if ((g_LogpDebugFlag & LOG_OPT_DISABLE_FUNCTION_NAME) == 0) { + // Want the function name + const auto baseFunctionName = LogpFindBaseFunctionName(FunctionName); + status = RtlStringCchPrintfA(functionNameBuffer, + RTL_NUMBER_OF(functionNameBuffer), "%-40s\t", + baseFunctionName); + if (!NT_SUCCESS(status)) { + return status; + } + } + + // + // It uses PsGetProcessId(PsGetCurrentProcess()) instead of + // PsGetCurrentThreadProcessId() because the later sometimes returns + // unwanted value, for example: + // PID == 4 but its image name != ntoskrnl.exe + // The author is guessing that it is related to attaching processes but + // not quite sure. The former way works as expected. + // + status = RtlStringCchPrintfA( + LogBuffer, LogBufferLength, "%s%s\t%5lu\t%5lu\t%-15s\t%s%s\r\n", + timeBuffer, levelString, + reinterpret_cast(PsGetProcessId(PsGetCurrentProcess())), + reinterpret_cast(PsGetCurrentThreadId()), + PsGetProcessImageFileName(PsGetCurrentProcess()), functionNameBuffer, + LogMessage); + return status; +} + +// Returns the function's base name, for example, +// NamespaceName::ClassName::MethodName will be returned as MethodName. +EXTERN_C static const char *LogpFindBaseFunctionName( + _In_ const char *FunctionName) { + if (!FunctionName) { + return nullptr; + } + + auto ptr = FunctionName; + auto name = FunctionName; + while (*(ptr++)) { + if (*ptr == ':') { + name = ptr + 1; + } + } + return name; +} + +// Logs the entry according to Attribute and the thread condition. +EXTERN_C static NTSTATUS LogpPut(_In_ const char *Message, + _In_ ULONG Attribute) { + auto status = STATUS_SUCCESS; + + // Log the entry to a file or buffer. + auto &info = g_LogpLogBufferInfo; + if (LogpIsLogFileEnabled(info)) { + // Can it log it to a file now? + if (((Attribute & LOGP_LEVEL_OPT_SAFE) == 0) && + KeGetCurrentIrql() == PASSIVE_LEVEL && !KeAreAllApcsDisabled()) { + // Yes, it can. Do it. + LogpWriteLogBufferToFile(&info); + status = LogpWriteMessageToFile(Message, info); + } else { + // No, it cannot. Buffer it. + status = LogpBufferMessage(Message, &info); + } + } + + // Can it safely be printed? + if (KeGetCurrentIrql() >= CLOCK_LEVEL) { + return STATUS_UNSUCCESSFUL; + } + + DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_ERROR_LEVEL, "%s", Message); + return status; +} + +// Switch the current log buffer and save the contents of old buffer to the log +// file. This function does not flush the log file, so code should call +// LogpWriteMessageToFile() or ZwFlushBuffersFile() later. +EXTERN_C static NTSTATUS LogpWriteLogBufferToFile( + _In_opt_ LogBufferInfo *Info) { + NT_ASSERT(Info); + auto status = STATUS_SUCCESS; + + // Enter a critical section and acquire a reader lock for Info in order to + // write a log file safely. + ExEnterCriticalRegionAndAcquireResourceExclusive(&Info->Resource); + + // Acquire a spin lock for Info.LogBuffer(s) in order to switch its head + // safely. + const auto irql = KeAcquireSpinLockRaiseToDpc(&Info->SpinLock); + auto oldLogBuffer = const_cast(Info->LogBufferHead); + if (oldLogBuffer[0]) { + Info->LogBufferHead = (oldLogBuffer == Info->LogBuffer1) ? Info->LogBuffer2 + : Info->LogBuffer1; + Info->LogBufferHead[0] = '\0'; + Info->LogBufferTail = Info->LogBufferHead; + } + KeReleaseSpinLock(&Info->SpinLock, irql); + + // Write all log entries in old log buffer. + IO_STATUS_BLOCK ioStatus = {}; + for (auto currentLogEntry = oldLogBuffer; currentLogEntry[0]; /**/) { + const auto currentLogEntryLength = strlen(currentLogEntry); + status = + ZwWriteFile(Info->LogFileHandle, nullptr, nullptr, nullptr, &ioStatus, + currentLogEntry, static_cast(currentLogEntryLength), + nullptr, nullptr); + if (!NT_SUCCESS(status)) { + // It could happen when you did not register IRP_SHUTDOWN and call + // LogIrpShutdownHandler() and the system tried to log to a file after + // a filesystem was unmounted. + DBG_BREAK(); + } + + currentLogEntry += currentLogEntryLength + 1; + } + oldLogBuffer[0] = '\0'; + + ExReleaseResourceAndLeaveCriticalRegion(&Info->Resource); + return status; +} + +// Logs the current log entry to and flush the log file. +EXTERN_C static NTSTATUS LogpWriteMessageToFile( + _In_ const char *Message, _In_ const LogBufferInfo &Info) { + IO_STATUS_BLOCK ioStatus = {}; + auto status = + ZwWriteFile(Info.LogFileHandle, nullptr, nullptr, nullptr, &ioStatus, + const_cast(Message), + static_cast(strlen(Message)), nullptr, nullptr); + if (!NT_SUCCESS(status)) { + // It could happen when you did not register IRP_SHUTDOWN and call + // LogIrpShutdownHandler() and the system tried to log to a file after + // a filesystem was unmounted. + DBG_BREAK(); + } + status = ZwFlushBuffersFile(Info.LogFileHandle, &ioStatus); + return status; +} + +// Buffer the log entry to the log buffer. +EXTERN_C static NTSTATUS LogpBufferMessage(_In_ const char *Message, + _In_opt_ LogBufferInfo *Info) { + NT_ASSERT(Info); + + // Acquire a spin lock to add the log safely. + const auto irql = KeAcquireSpinLockRaiseToDpc(&Info->SpinLock); + + // Copy the current log to the buffer. + size_t usedBufferSize = Info->LogBufferTail - Info->LogBufferHead; + auto status = + RtlStringCchCopyA(const_cast(Info->LogBufferTail), + LOGP_BUFFER_USABLE_SIZE - usedBufferSize, Message); + + // Update Info.LogMaximumUsage if necessary. + if (NT_SUCCESS(status)) { + const auto messageLength = strlen(Message) + 1; + Info->LogBufferTail += messageLength; + usedBufferSize += messageLength; + if (usedBufferSize > Info->LogMaximumUsage) { + Info->LogMaximumUsage = usedBufferSize; // Update + } + } else { + Info->LogMaximumUsage = LOGP_BUFFER_SIZE; // Indicates overflow + } + *Info->LogBufferTail = '\0'; + + KeReleaseSpinLock(&Info->SpinLock, irql); + return status; +} + +// Returns true when a log file is enabled. +EXTERN_C static bool LogpIsLogFileEnabled(_In_ const LogBufferInfo &Info) { + if (Info.LogFileHandle) { + NT_ASSERT(Info.LogBuffer1); + NT_ASSERT(Info.LogBuffer2); + NT_ASSERT(Info.LogBufferHead); + NT_ASSERT(Info.LogBufferTail); + return true; + } + NT_ASSERT(!Info.LogBuffer1); + NT_ASSERT(!Info.LogBuffer2); + NT_ASSERT(!Info.LogBufferHead); + NT_ASSERT(!Info.LogBufferTail); + return false; +} + +// Returns true when logging is necessary according to the log's severity and +// a set log level. +EXTERN_C static bool LogpIsLogNeeded(_In_ ULONG Level) { + return !!(g_LogpDebugFlag & Level); +} + +// A thread runs as long as info.BufferFlushThreadShouldBeAlive is true and +// flushes a log buffer to a log file every LOGP_AUTO_FLUSH_INTERVAL_MSEC msec. +ALLOC_TEXT(PAGED, LogpBufferFlushThreadRoutine) +EXTERN_C static VOID LogpBufferFlushThreadRoutine(_In_ void *StartContext) { + PAGED_CODE(); + auto status = STATUS_SUCCESS; + auto info = reinterpret_cast(StartContext); + LOG_DEBUG("Log thread started."); + NT_ASSERT(LogpIsLogFileEnabled(*info)); + + while (info->BufferFlushThreadShouldBeAlive) { + if (info->LogBufferHead[0]) { + NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); + NT_ASSERT(!KeAreAllApcsDisabled()); + status = LogpWriteLogBufferToFile(info); + // Do not flush the file for overall performance. Even a case of + // bug check, we should be able to recover logs by looking at both + // log buffers. + } + LogpSleep(LOGP_AUTO_FLUSH_INTERVAL_MSEC); + } + LOG_DEBUG("Log thread is ending."); + PsTerminateSystemThread(status); +} + +// Sleep the current thread's execution for Millisecond milli-seconds. +ALLOC_TEXT(PAGED, LogpSleep) +EXTERN_C static NTSTATUS LogpSleep(_In_ LONG Millisecond) { + PAGED_CODE(); + + LARGE_INTEGER interval = {}; + interval.QuadPart = -(10000 * Millisecond); // msec + return KeDelayExecutionThread(KernelMode, FALSE, &interval); +} diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/log.h b/RemoteWriteMonitor/RemoteWriteMonitor/log.h index a694ecb..076c266 100644 --- a/RemoteWriteMonitor/RemoteWriteMonitor/log.h +++ b/RemoteWriteMonitor/RemoteWriteMonitor/log.h @@ -1,106 +1,106 @@ -// Copyright (c) 2015, tandasat. All rights reserved. -// Use of this source code is governed by a MIT-style license that can be -// found in the LICENSE file. - -// -// This module declares interfaces to logging functions. -// -#pragma once - -//////////////////////////////////////////////////////////////////////////////// -// -// macro utilities -// - -// -// Does log with respective severities. Here are some ideas to decide which -// level is appropriate: -// DEBUG: For developers. -// INFO: For all. -// WARN: For all. It may require some attention but does not prevent the -// program working properly. -// ERROR: For all. It stops the program working properly. -// -#define LOG_DEBUG(format, ...) \ - LogPrint(LOGP_LEVEL_DEBUG, __FUNCTION__, (format), __VA_ARGS__) -#define LOG_INFO(format, ...) \ - LogPrint(LOGP_LEVEL_INFO, __FUNCTION__, (format), __VA_ARGS__) -#define LOG_WARN(format, ...) \ - LogPrint(LOGP_LEVEL_WARN, __FUNCTION__, (format), __VA_ARGS__) -#define LOG_ERROR(format, ...) \ - LogPrint(LOGP_LEVEL_ERROR, __FUNCTION__, (format), __VA_ARGS__) - -// Buffers the log to buffer. It is recommended to use it when a status of -// callee is no predictable in order to avoid bug checks. -#define LOG_DEBUG_SAFE(format, ...) \ - LogPrint(LOGP_LEVEL_DEBUG | LOGP_LEVEL_OPT_SAFE, __FUNCTION__, (format), \ - __VA_ARGS__) -#define LOG_INFO_SAFE(format, ...) \ - LogPrint(LOGP_LEVEL_INFO | LOGP_LEVEL_OPT_SAFE, __FUNCTION__, (format), \ - __VA_ARGS__) -#define LOG_WARN_SAFE(format, ...) \ - LogPrint(LOGP_LEVEL_WARN | LOGP_LEVEL_OPT_SAFE, __FUNCTION__, (format), \ - __VA_ARGS__) -#define LOG_ERROR_SAFE(format, ...) \ - LogPrint(LOGP_LEVEL_ERROR | LOGP_LEVEL_OPT_SAFE, __FUNCTION__, (format), \ - __VA_ARGS__) - -//////////////////////////////////////////////////////////////////////////////// -// -// constants and macros -// - -// (internal) Save this log to buffer and not try to write to a log file. -static const auto LOGP_LEVEL_OPT_SAFE = 0x1ul; - -// (internal) Log levels. -static const auto LOGP_LEVEL_DEBUG = 0x10ul; -static const auto LOGP_LEVEL_INFO = 0x20ul; -static const auto LOGP_LEVEL_WARN = 0x40ul; -static const auto LOGP_LEVEL_ERROR = 0x80ul; - -// For LogInitialization(). Specifies what level of verbosity is needed. -static const auto LOG_PUT_LEVEL_DEBUG = - LOGP_LEVEL_ERROR | LOGP_LEVEL_WARN | LOGP_LEVEL_INFO | LOGP_LEVEL_DEBUG; -static const auto LOG_PUT_LEVEL_INFO = - LOGP_LEVEL_ERROR | LOGP_LEVEL_WARN | LOGP_LEVEL_INFO; -static const auto LOG_PUT_LEVEL_WARN = LOGP_LEVEL_ERROR | LOGP_LEVEL_WARN; -static const auto LOG_PUT_LEVEL_ERROR = LOGP_LEVEL_ERROR; -static const auto LOG_PUT_LEVEL_DISABLE = 0x00ul; - -// For LogInitialization(). Does not log a current time. -static const auto LOG_OPT_DISABLE_TIME = 0x100ul; - -// For LogInitialization(). Does not log a current function name. -static const auto LOG_OPT_DISABLE_FUNCTION_NAME = 0x200ul; - -//////////////////////////////////////////////////////////////////////////////// -// -// types -// - -//////////////////////////////////////////////////////////////////////////////// -// -// prototypes -// - -EXTERN_C NTSTATUS LogInitialization(_In_ ULONG Flag, - _In_opt_ const wchar_t *FilePath, - _In_opt_ PDEVICE_OBJECT DeviceObject); - -EXTERN_C void LogIrpShutdownHandler(); - -EXTERN_C void LogTermination(_In_opt_ PDEVICE_OBJECT DeviceObject); - -EXTERN_C NTSTATUS LogPrint(_In_ ULONG Level, _In_ const char *FunctionName, - _In_ const char *Format, ...); - -//////////////////////////////////////////////////////////////////////////////// -// -// variables -// - -//////////////////////////////////////////////////////////////////////////////// -// -// implementations -// +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// This module declares interfaces to logging functions. +// +#pragma once + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +// +// Does log with respective severities. Here are some ideas to decide which +// level is appropriate: +// DEBUG: For developers. +// INFO: For all. +// WARN: For all. It may require some attention but does not prevent the +// program working properly. +// ERROR: For all. It stops the program working properly. +// +#define LOG_DEBUG(format, ...) \ + LogpPrint(LOGP_LEVEL_DEBUG, __FUNCTION__, (format), __VA_ARGS__) +#define LOG_INFO(format, ...) \ + LogpPrint(LOGP_LEVEL_INFO, __FUNCTION__, (format), __VA_ARGS__) +#define LOG_WARN(format, ...) \ + LogpPrint(LOGP_LEVEL_WARN, __FUNCTION__, (format), __VA_ARGS__) +#define LOG_ERROR(format, ...) \ + LogpPrint(LOGP_LEVEL_ERROR, __FUNCTION__, (format), __VA_ARGS__) + +// Buffers the log to buffer. It is recommended to use it when a status of +// callee is no predictable in order to avoid bug checks. +#define LOG_DEBUG_SAFE(format, ...) \ + LogpPrint(LOGP_LEVEL_DEBUG | LOGP_LEVEL_OPT_SAFE, __FUNCTION__, (format), \ + __VA_ARGS__) +#define LOG_INFO_SAFE(format, ...) \ + LogpPrint(LOGP_LEVEL_INFO | LOGP_LEVEL_OPT_SAFE, __FUNCTION__, (format), \ + __VA_ARGS__) +#define LOG_WARN_SAFE(format, ...) \ + LogpPrint(LOGP_LEVEL_WARN | LOGP_LEVEL_OPT_SAFE, __FUNCTION__, (format), \ + __VA_ARGS__) +#define LOG_ERROR_SAFE(format, ...) \ + LogpPrint(LOGP_LEVEL_ERROR | LOGP_LEVEL_OPT_SAFE, __FUNCTION__, (format), \ + __VA_ARGS__) + +//////////////////////////////////////////////////////////////////////////////// +// +// constants and macros +// + +// (internal) Save this log to buffer and not try to write to a log file. +static const auto LOGP_LEVEL_OPT_SAFE = 0x1ul; + +// (internal) Log levels. +static const auto LOGP_LEVEL_DEBUG = 0x10ul; +static const auto LOGP_LEVEL_INFO = 0x20ul; +static const auto LOGP_LEVEL_WARN = 0x40ul; +static const auto LOGP_LEVEL_ERROR = 0x80ul; + +// For LogInitialization(). Specifies what level of verbosity is needed. +static const auto LOG_PUT_LEVEL_DEBUG = + LOGP_LEVEL_ERROR | LOGP_LEVEL_WARN | LOGP_LEVEL_INFO | LOGP_LEVEL_DEBUG; +static const auto LOG_PUT_LEVEL_INFO = + LOGP_LEVEL_ERROR | LOGP_LEVEL_WARN | LOGP_LEVEL_INFO; +static const auto LOG_PUT_LEVEL_WARN = LOGP_LEVEL_ERROR | LOGP_LEVEL_WARN; +static const auto LOG_PUT_LEVEL_ERROR = LOGP_LEVEL_ERROR; +static const auto LOG_PUT_LEVEL_DISABLE = 0x00ul; + +// For LogInitialization(). Does not log a current time. +static const auto LOG_OPT_DISABLE_TIME = 0x100ul; + +// For LogInitialization(). Does not log a current function name. +static const auto LOG_OPT_DISABLE_FUNCTION_NAME = 0x200ul; + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// + +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +EXTERN_C NTSTATUS LogInitialization(_In_ ULONG Flag, + _In_opt_ const wchar_t *FilePath, + _In_opt_ PDEVICE_OBJECT DeviceObject); + +EXTERN_C void LogIrpShutdownHandler(); + +EXTERN_C void LogTermination(_In_opt_ PDEVICE_OBJECT DeviceObject); + +EXTERN_C NTSTATUS LogpPrint(_In_ ULONG Level, _In_ const char *FunctionName, + _In_ const char *Format, ...); + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/satoshitanda.pfx b/RemoteWriteMonitor/RemoteWriteMonitor/satoshitanda.pfx new file mode 100644 index 0000000000000000000000000000000000000000..d211d7e2840eaa2d73b19aaf9a7258f29e0d0e53 GIT binary patch literal 2614 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 V+f>*mc1~V&fn&4F9DhFG{{oH1uC)LF literal 0 HcmV?d00001 diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/ssdt.cpp b/RemoteWriteMonitor/RemoteWriteMonitor/ssdt.cpp new file mode 100644 index 0000000..64027f9 --- /dev/null +++ b/RemoteWriteMonitor/RemoteWriteMonitor/ssdt.cpp @@ -0,0 +1,146 @@ +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// This module implements an entry point of the driver and initializes other +// components in this module. +// +#include "stdafx.h" +#include "ssdt.h" +#include "log.h" +#include "util.h" + +namespace stdexp = std::experimental; + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +//////////////////////////////////////////////////////////////////////////////// +// +// constants and macros +// + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// + +struct SERVICE_DESCRIPTOR_TABLE { + PULONG ServiceTable; + PULONG CounterTable; + ULONG_PTR TableSize; + PUCHAR ArgumentTable; +}; +static_assert(sizeof(SERVICE_DESCRIPTOR_TABLE) == sizeof(void *) * 4, + "Size check"); + +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +EXTERN_C PVOID NTAPI +RtlPcToFileHeader(_In_ PVOID PcValue, _Out_ PVOID *BaseOfImage); + +EXTERN_C static SERVICE_DESCRIPTOR_TABLE *SSDTpFindTable(); + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +static SERVICE_DESCRIPTOR_TABLE *g_SSDTpTable = nullptr; + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// + +ALLOC_TEXT(INIT, SSDTInitialization) +EXTERN_C NTSTATUS SSDTInitialization() { + PAGED_CODE(); + + g_SSDTpTable = SSDTpFindTable(); + if (!g_SSDTpTable) { + return STATUS_UNSUCCESSFUL; + } + return STATUS_SUCCESS; +} + +ALLOC_TEXT(PAGED, SSDTTermination) +EXTERN_C void SSDTTermination() { PAGED_CODE(); } + +ALLOC_TEXT(PAGED, SSDTGetProcAdderss) +EXTERN_C FARPROC SSDTGetProcAdderss(_In_ ULONG Index) { + PAGED_CODE(); + + if (IsX64()) { + return reinterpret_cast( + (g_SSDTpTable->ServiceTable[Index] >> 4) + + reinterpret_cast(g_SSDTpTable->ServiceTable)); + } else { + return reinterpret_cast(g_SSDTpTable->ServiceTable[Index]); + } +} + +// Get an original value of the SSDT and replace it with a new value. +EXTERN_C void SSDTSetProcAdderss(_In_ ULONG Index, _In_ FARPROC HookRoutine) { + // Need to rise IRQL not to allow the system to change an execution processor + // during the operation because this code changes a state of processor (CR0). + KIRQL oldIrql = 0; + KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); + const auto scopedIrql = + stdexp::make_scope_exit([oldIrql]() { KeLowerIrql(oldIrql); }); + + UtilDisableWriteProtect(); + const auto scopedWriteProtection = + stdexp::make_scope_exit([] { UtilEnableWriteProtect(); }); + g_SSDTpTable->ServiceTable[Index] = reinterpret_cast(HookRoutine); +} + +ALLOC_TEXT(INIT, SSDTpFindTable) +EXTERN_C static SERVICE_DESCRIPTOR_TABLE *SSDTpFindTable() { + PAGED_CODE(); + + if (!IsX64()) { + UNICODE_STRING name = RTL_CONSTANT_STRING(L"KeServiceDescriptorTable"); + return reinterpret_cast( + MmGetSystemRoutineAddress(&name)); + } + + UNICODE_STRING name = RTL_CONSTANT_STRING(L"KeAddSystemServiceTable"); + auto pKeAddSystemServiceTable = + reinterpret_cast(MmGetSystemRoutineAddress(&name)); + if (!pKeAddSystemServiceTable) { + return nullptr; + } + + UNICODE_STRING name2 = RTL_CONSTANT_STRING(L"RtlPcToFileHeader"); + auto pRtlPcToFileHeader = reinterpret_cast( + MmGetSystemRoutineAddress(&name2)); + if (!pRtlPcToFileHeader) { + return nullptr; + } + + ULONG offset = 0; + for (auto i = 0; i < 0x40; ++i) { + auto dwordBytes = *reinterpret_cast(pKeAddSystemServiceTable + i); + if ((dwordBytes & 0x00fffff0) == 0x00bc8340) { + offset = *reinterpret_cast(pKeAddSystemServiceTable + i + 4); + break; + } + } + if (!offset) { + return nullptr; + } + + UCHAR *base = nullptr; + if (!pRtlPcToFileHeader(pKeAddSystemServiceTable, + reinterpret_cast(&base))) { + return nullptr; + } + return reinterpret_cast(base + offset); +} diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/ssdt.h b/RemoteWriteMonitor/RemoteWriteMonitor/ssdt.h new file mode 100644 index 0000000..b8c465e --- /dev/null +++ b/RemoteWriteMonitor/RemoteWriteMonitor/ssdt.h @@ -0,0 +1,46 @@ +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// +// +#pragma once + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +//////////////////////////////////////////////////////////////////////////////// +// +// constants and macros +// + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// + +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +EXTERN_C NTSTATUS SSDTInitialization(); + +EXTERN_C void SSDTTermination(); + +EXTERN_C FARPROC SSDTGetProcAdderss(_In_ ULONG Index); + +EXTERN_C void SSDTSetProcAdderss(_In_ ULONG Index, _In_ FARPROC HookRoutine); + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/stdafx.h b/RemoteWriteMonitor/RemoteWriteMonitor/stdafx.h index 0129f32..9a2e6ea 100644 --- a/RemoteWriteMonitor/RemoteWriteMonitor/stdafx.h +++ b/RemoteWriteMonitor/RemoteWriteMonitor/stdafx.h @@ -1,94 +1,111 @@ -// Copyright (c) 2015, tandasat. All rights reserved. -// Use of this source code is governed by a MIT-style license that can be -// found in the LICENSE file. - -// -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#pragma once - -extern "C" { -#pragma warning(push, 0) -#include -#include -#include -#include -#include -#define NTSTRSAFE_NO_CB_FUNCTIONS -#include -#include -#include -#include -#include -#include -#include -#pragma warning(pop) -} - -#ifndef _HAS_EXCEPTIONS -#define _HAS_EXCEPTIONS 0 -#endif -#include "../Common/ScopedResource/unique_resource.h" -#include "../Common/ScopedResource/scope_exit.h" - -//////////////////////////////////////////////////////////////////////////////// -// -// macro utilities -// - -// Specifies where the code should be located -#ifdef ALLOC_PRAGMA -#define ALLOC_TEXT(Section, Name) __pragma(alloc_text(Section, Name)) -#else -#define ALLOC_TEXT(Section, Name) -#endif - -// Break point that works only when a debugger is enabled -#ifndef DBG_BREAK -#ifdef _ARM_ -// Nullify it since an ARM device never allow us to attach a debugger. -#define DBG_BREAK() -#else // _ARM_ -#define DBG_BREAK() \ - if (KD_DEBUGGER_ENABLED) { \ - __debugbreak(); \ - } else { \ - } \ - reinterpret_cast(0) -#endif // _ARM_ -#endif // DBG_BREAK - -// To explicitly suppress warnings -#ifndef UNREFERENCED_LOCAL_VARIABLE -#define UNREFERENCED_LOCAL_VARIABLE(x) (reinterpret_cast(!(x))) -#endif - -//////////////////////////////////////////////////////////////////////////////// -// -// constants and macros -// - -static const ULONG RWMON_POOL_TAG_NAME = 'nmwr'; - -//////////////////////////////////////////////////////////////////////////////// -// -// types -// - -//////////////////////////////////////////////////////////////////////////////// -// -// prototypes -// - -//////////////////////////////////////////////////////////////////////////////// -// -// variables -// - -//////////////////////////////////////////////////////////////////////////////// -// -// implementations -// +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +// +// NOTE: +// +// About building the driver: +// Visual Studio will pop up a dialog during a build process and require a +// password for code signing. Type "password" for it. +// + +#pragma once + +extern "C" { +#pragma warning(push, 0) +#include +#include +#include +#include +#include +#define NTSTRSAFE_NO_CB_FUNCTIONS +#include +#include +#include +#include +#include +#include +#include +#pragma warning(pop) +} + +#ifndef _HAS_EXCEPTIONS +#define _HAS_EXCEPTIONS 0 +#endif +#include "../Common/ScopedResource/unique_resource.h" +#include "../Common/ScopedResource/scope_exit.h" + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +// Specifies where the code should be located +#ifdef ALLOC_PRAGMA +#define ALLOC_TEXT(Section, Name) __pragma(alloc_text(Section, Name)) +#else +#define ALLOC_TEXT(Section, Name) +#endif + +// Break point that works only when a debugger is enabled +#ifndef DBG_BREAK +#ifdef _ARM_ +// Nullify it since an ARM device never allow us to attach a debugger. +#define DBG_BREAK() +#else // _ARM_ +#define DBG_BREAK() \ + if (KD_DEBUGGER_ENABLED) { \ + __debugbreak(); \ + } else { \ + } \ + reinterpret_cast(0) +#endif // _ARM_ +#endif // DBG_BREAK + +// To explicitly suppress warnings +#ifndef UNREFERENCED_LOCAL_VARIABLE +#define UNREFERENCED_LOCAL_VARIABLE(x) (reinterpret_cast(!(x))) +#endif + +//////////////////////////////////////////////////////////////////////////////// +// +// constants and macros +// + +static const ULONG RWMON_POOL_TAG_NAME = 'nmwr'; + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// + +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// + +// Returns true when it is running on the x64 system. +inline bool IsX64() { +#ifdef _AMD64_ + return true; +#else + return false; +#endif +} \ No newline at end of file diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/util.cpp b/RemoteWriteMonitor/RemoteWriteMonitor/util.cpp new file mode 100644 index 0000000..2d5ad85 --- /dev/null +++ b/RemoteWriteMonitor/RemoteWriteMonitor/util.cpp @@ -0,0 +1,143 @@ +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// +// +#include "stdafx.h" +#include "util.h" + +namespace stdexp = std::experimental; + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +//////////////////////////////////////////////////////////////////////////////// +// +// constants and macros +// + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// + +union CR0_REGISTER { + ULONG_PTR Value; + struct { + unsigned PE : 1; // [0] Protected Mode Enabled + unsigned MP : 1; // [1] Monitor Coprocessor FLAG + unsigned EM : 1; // [2] Emulate FLAG + unsigned TS : 1; // [3] Task Switched FLAG + unsigned ET : 1; // [4] Extension Type FLAG + unsigned NE : 1; // [5] Numeric Error + unsigned Reserved1 : 10; // [6-15] + unsigned WP : 1; // [16] Write Protect + unsigned Reserved2 : 1; // [17] + unsigned AM : 1; // [18] Alignment Mask + unsigned Reserved3 : 10; // [19-28] + unsigned NW : 1; // [29] Not Write-Through + unsigned CD : 1; // [30] Cache Disable + unsigned PG : 1; // [31] Paging Enabled + } Fields; +}; +static_assert(sizeof(CR0_REGISTER) == sizeof(void *), "Size check"); + +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// + +// Disable the write protection +EXTERN_C void UtilDisableWriteProtect() { + CR0_REGISTER cr0 = {__readcr0()}; + cr0.Fields.WP = false; + __writecr0(cr0.Value); +} + +// Enable the write protection +EXTERN_C void UtilEnableWriteProtect() { + CR0_REGISTER cr0 = {__readcr0()}; + cr0.Fields.WP = true; + __writecr0(cr0.Value); +} + +// memmem(). +EXTERN_C void *UtilMemMem(_In_ const void *SearchBase, _In_ SIZE_T SearchSize, + _In_ const void *Pattern, _In_ SIZE_T PatternSize) { + if (PatternSize > SearchSize) { + return nullptr; + } + auto searchBase = static_cast(SearchBase); + for (size_t i = 0; i <= SearchSize - PatternSize; i++) { + if (!memcmp(Pattern, &searchBase[i], PatternSize)) { + return const_cast(&searchBase[i]); + } + } + return nullptr; +} + +// Does memcpy safely even if Destination is a read only region. +EXTERN_C NTSTATUS UtilForceMemCpy(_In_ void *Destination, + _In_ const void *Source, _In_ SIZE_T Length) { + auto mdl = std::experimental::make_unique_resource( + IoAllocateMdl(Destination, static_cast(Length), FALSE, FALSE, + nullptr), + &IoFreeMdl); + if (!mdl) { + return STATUS_INSUFFICIENT_RESOURCES; + } + MmBuildMdlForNonPagedPool(mdl.get()); + + // + // Following MmMapLockedPagesSpecifyCache() call causes bug check in case + // you are using Driver Verifier. The reason is explained as follows: + // + // A driver must not try to create more than one system-address-space + // mapping for an MDL. Additionally, because an MDL that is built by the + // MmBuildMdlForNonPagedPool routine is already mapped to the system + // address space, a driver must not try to map this MDL into the system + // address space again by using the MmMapLockedPagesSpecifyCache routine. + // -- MSDN + // + // This flag modification hacks Driver Verifier's check and prevent leading + // bug check. + // + mdl.get()->MdlFlags &= ~MDL_SOURCE_IS_NONPAGED_POOL; + mdl.get()->MdlFlags |= MDL_PAGES_LOCKED; + + auto writableDest = std::experimental::make_unique_resource( + MmMapLockedPagesSpecifyCache(mdl.get(), KernelMode, MmCached, nullptr, + FALSE, NormalPagePriority), + [&mdl](void *p) { MmUnmapLockedPages(p, mdl.get()); }); + if (!writableDest) { + return STATUS_INSUFFICIENT_RESOURCES; + } + memcpy(writableDest.get(), Source, Length); + return STATUS_SUCCESS; +} + +// Invalidates an instruction cache for the specified region. +EXTERN_C void UtilInvalidateInstructionCache(_In_ void *BaseAddress, + _In_ SIZE_T Length) { + UNREFERENCED_PARAMETER(BaseAddress); + UNREFERENCED_PARAMETER(Length); +#if _AMD64_ + __faststorefence(); +#else + _mm_sfence(); +#endif +} \ No newline at end of file diff --git a/RemoteWriteMonitor/RemoteWriteMonitor/util.h b/RemoteWriteMonitor/RemoteWriteMonitor/util.h new file mode 100644 index 0000000..e066aab --- /dev/null +++ b/RemoteWriteMonitor/RemoteWriteMonitor/util.h @@ -0,0 +1,51 @@ +// Copyright (c) 2015, tandasat. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +// +// +// +#pragma once + +//////////////////////////////////////////////////////////////////////////////// +// +// macro utilities +// + +//////////////////////////////////////////////////////////////////////////////// +// +// constants and macros +// + +//////////////////////////////////////////////////////////////////////////////// +// +// types +// + +//////////////////////////////////////////////////////////////////////////////// +// +// prototypes +// + +EXTERN_C void UtilDisableWriteProtect(); + +EXTERN_C void UtilEnableWriteProtect(); + +EXTERN_C void *UtilMemMem(_In_ const void *SearchBase, _In_ SIZE_T SearchSize, + _In_ const void *Pattern, _In_ SIZE_T PatternSize); + +EXTERN_C NTSTATUS UtilForceMemCpy(_In_ void *Destination, + _In_ const void *Source, _In_ SIZE_T Length); + +EXTERN_C void UtilInvalidateInstructionCache(_In_ void *BaseAddress, + _In_ SIZE_T Length); + +//////////////////////////////////////////////////////////////////////////////// +// +// variables +// + +//////////////////////////////////////////////////////////////////////////////// +// +// implementations +// diff --git a/RemoteWriteMonitor/clean.bat b/RemoteWriteMonitor/clean.bat index f8ff1ba..63ed94e 100644 --- a/RemoteWriteMonitor/clean.bat +++ b/RemoteWriteMonitor/clean.bat @@ -3,9 +3,17 @@ del /a:h *.suo rmdir /s /q ipch rmdir /s /q Debug rmdir /s /q Release -rmdir /s /q RemoteWriteMonitor\Debug -rmdir /s /q RemoteWriteMonitor\Release +rmdir /s /q Win7Debug +rmdir /s /q Win7Release +rmdir /s /q Win8.1Debug +rmdir /s /q Win8.1Release +rmdir /s /q x64 rmdir /s /q injector\Debug rmdir /s /q injector\Release +rmdir /s /q RemoteWriteMonitor\Win7Debug +rmdir /s /q RemoteWriteMonitor\Win7Release +rmdir /s /q RemoteWriteMonitor\Win8.1Debug +rmdir /s /q RemoteWriteMonitor\Win8.1Release +rmdir /s /q RemoteWriteMonitor\x64 del /s *.aps pause diff --git a/RemoteWriteMonitor/clean_for_release.bat b/RemoteWriteMonitor/clean_for_release.bat new file mode 100644 index 0000000..57c8049 --- /dev/null +++ b/RemoteWriteMonitor/clean_for_release.bat @@ -0,0 +1,9 @@ +del /s *.pdb *.cer +mkdir x86 +move Win7Release x86 +move Win8.1Release x86 +mkdir bin +move x86 bin +move x64 bin +move Release bin +pause diff --git a/RemoteWriteMonitor/injector/injector.vcxproj b/RemoteWriteMonitor/injector/injector.vcxproj index e51ff85..7017af4 100644 --- a/RemoteWriteMonitor/injector/injector.vcxproj +++ b/RemoteWriteMonitor/injector/injector.vcxproj @@ -1,96 +1,159 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {FEE34C62-A273-4557-BF93-360BDA2855E5} - Win32Proj - injector - - - - Application - true - v120_xp - Unicode - - - Application - false - v120_xp - true - Unicode - - - - - - - - - - - - - true - - - false - - - - Use - Level4 - Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - true - false - - - Console - true - - - - - Level4 - Use - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - true - false - - - Console - true - true - true - - - - - - - - - - - - Create - Create - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {FEE34C62-A273-4557-BF93-360BDA2855E5} + Win32Proj + injector + + + + Application + true + v120_xp + Unicode + + + Application + true + v120_xp + Unicode + + + Application + false + v120_xp + true + Unicode + + + Application + false + v120_xp + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + Use + Level4 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + false + + + Console + true + + + + + Use + Level4 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + false + + + Console + true + + + + + Level4 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + false + + + Console + true + true + true + + + + + Level4 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + false + + + Console + true + true + true + + + + + + + + + + + + Create + Create + Create + Create + + + + + \ No newline at end of file diff --git a/RemoteWriteMonitor/injector/injector.vcxproj.user b/RemoteWriteMonitor/injector/injector.vcxproj.user index 63c37cc..153324e 100644 --- a/RemoteWriteMonitor/injector/injector.vcxproj.user +++ b/RemoteWriteMonitor/injector/injector.vcxproj.user @@ -1,11 +1,19 @@ - - - - 4468 section context - WindowsLocalDebugger - - - 4468 section context - WindowsLocalDebugger - + + + + 4468 section context + WindowsLocalDebugger + + + 4468 section context + WindowsLocalDebugger + + + 4468 section context + WindowsLocalDebugger + + + 4468 section context + WindowsLocalDebugger + \ No newline at end of file