前段时间微软的符号服务器特别不稳定,Windbg下载符号文件老是失败,所以就专门写了个pdb的下载工具(pdbdownloader)放到Github上。
P.S. 用WPF写界面确实是很有趣
从Windows8开始,Windows设计了一个新的中断,INT 29H,用来快速的抛出失败。在sdk中,他被声明为 __fastfail:
|
在中断代码执行后,操作系统会根据执行代码的环境来做出不同的处理。
如果__fastfail发生在Ring0中,操作系统会抛出一个KERNEL_SECURITY_CHECK_FAILURE (0x139)的蓝屏。如果__fastfail发生在Ring3,系统会抛出一个第二次机会的不可继续执行的异常,异常代码为0xC0000409,然后走进我们熟悉的Windows Error Reporting(WER)流程。另外,无论__fastfail发生在R0或者R3,如果有调试器正在调试系统或进程,都将得到一次中断到调试器的机会,这让我们能够看清楚具体发生了什么事情。但是正如我上面所说,这个是一个不可继续执行的异常,所以我们不能在调试器里处理了异常后让程序继续向前跑,当然也不能用try和except去捕获异常。
我觉得__fastfail是个非常不错的设计,它让程序可以快速的进入内核异常处理流程,不需要执行额外的用户层的代码,也不需要额外的内存空间,提高了不可恢复的异常处理的性能,更重要的是,简单快速不依赖内存的执行方式也保证了系统的安全。所以在系统的安全检查失败处理中,大量使用了这个方式,减少被攻击的可能性。
最后,如果INT 29H发生在Windows8以下的系统上,内核里会抛出一个常规的UNEXPECTED_KERNEL_MODE_TRAP的蓝屏,而用户层程序会抛出一个ACCESS VIOLATION的异常。
2012年的时候,我在blog上写到过开发了一个windbg的lua扩展dbglua,当时觉得windbg的原生脚本语法太奇怪了,而且太不容易使用。现在来看,依旧如此,只不过我已经很熟悉这个原生脚本了。而这个lua扩展反倒是没什么用,因为用起来也不太方便,比如访问结构体。
最近无意之中看了一眼pykd,他用重载.操作符的方式访问符号和结构体深深的吸引了我,感觉非常有趣。而python本身依赖比较多,这也促使我拿起之前的代码看了看,并且决定在github上重新建立这个项目叫做luadbg,这次我决定长期维护这个项目,想到新的功能就往里面写,就像我一直维护的0cchext一样。luadbg除了兼容了老dbglua的函数以外,还添加了几个我觉得很方便的类,主要是用重载.操作符的方式来访问模块和结构体的数据,效果如下图所示:
当然,也可以用!luacmd命令进入input模式,从而一条一条的输入语句来测试正确性。
最近和朋友讨论版本号常用的几种规范,前三位<主版本>.<子版本>.<修正版本>基本上一致,不需要详说。主要区别产生在最后一位,有的是build number,有的是时间日期,还有的是git或者svn的revision。我习惯用build number,每次编译都会增加版本号最后一位的数字。但是手动去修改明显不科学也不可靠,所以给和我有一样习惯的朋友分享一个我早年写的python脚本,无论是自己的工具还是公司的产品我一直都在用这个。
用法就是在VS的工程属性Build Event -> Pre Build Event里设置x:\incbuildnum.py $(ProjectDir)$(ProjectName).rc。 |
|
|
说到Rootkit就不能提到他的文件隐藏,Rootkit隐藏文件的方式千奇百怪,这里说其中一个通过NTFS元文件目录无法被普通程序显示的特性隐藏文件的方法。
我们都知道NTFS是有元文件的,比如$MFT(NTFS主文件表),这种文件是我们看不到的,但是系统能访问。同样还有一种元文件目录,这个目录也是看不到的,无论你是否打开了显示系统文件,隐藏文件的选项。那么如果我们把要隐藏的文件放在这种目录下,那么就达到了隐藏的效果。
举个例子 $Extend\$RmMetadata 这个目录。我们可以通过Winhex解析NTFS来读取这个目录的情况,而普通程序不行。这里我们通过这样的代码来创建文件。
|
值得注意的是我们必须用System用户权限去运行这个程序,才能创建文件到元文件目录,这里要用到psexec:
psexec -s C:\0cch\Test.exe
然后我们看看效果
Windows Timer相比大家都用过,WM_TIMER, WM_SYSTIMER, Waitable Timer, Multimedia Timer, Timer Queue Timer,这么多种Timer,给我们变成提供了很大的方便,有窗口无窗口都能自如选择。所以尽量也不要自己再造轮子,用什么Sleep来写Timer。这种“自定义”的Timer肯定是没有由系统内核DPC触发的Timer效率高的。
OK,回到正题,关于Timer的精确度。首先看看SysInternal工具集的clockres的显示:
从图中可以看出,我这个系统的最大精确度15.6毫秒,最小是0.5毫秒,当前是15.6毫秒。默认情况下,Windows会用最大精确度,因为这样可以减少CPU的消耗,而且高精度的定时器,绝大多数程序都不会用到。基于15.6毫秒这个精度,那么我们设置Timer间隔为15.6毫秒以下都是没有意义的,这里再提一下,Sleep函数在内核也是用的定时器,也就是说这个精确度下,Sleep(10)也是没有意义的,间隔会达到15-16毫秒。
当然,我们有的时候也是需要高精度的定时器的,这个时候我们需要设置时间精度。timeBeginPeriod这个函数就可以完成这个任务,这个函数调用了ntdll的NtSetTimerResolution函数,我们也可以直接调用这个ntdll函数,只不过我们需要动态获得这个函数的地址罢了。值得注意的是,并不是你想设置什么精确度都可以,Windows内部实际上维护了一份可以设置的精度列表,他会选择一个和你设置相近的的精度设置上去,这个列表保存在Hal里面。
好了,再说下Windows时钟,Windows时钟更新时间总是用的最大精度,在我个系统上也就是每次更新时间都是间隔15.6毫秒。也就是说如果用GetTickCount来统计性能问题,最大精度也就是15-16毫秒。举个例子,一段代码运行时间不足15.6毫秒,要么统计结果是0,要么是15-16毫秒,时间精度不会影响Windows时钟更新。
最后说下Windows高精度时钟查询的实现,在2000和XP时代,系统用TSC来演算时间,但是那个时候,多核并不支持TSC同步,这回带来一些问题。Vista系统采用了High Precision Event Timer (HPET)或者ACPI Power Management Timer (PM timer),但是这种Timer的延时比较高,当然,这个延时是百纳秒级别的,可以说基本上不会对普通程序有什么影响。之后的系统就使用了固定频率的TSC,这样在多核状态下也能保证同步,而且延时很低。更详细的资料可以参考:https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx
从Win8开始,任务管理已经悄然发生变化了,这篇文章要说的就是结束任务这一个功能。以Win10的任务管理器为主来说明,没有了从窗口关闭进程的标签。取而代之的是一个区分前台和后台程序的进程树。通过这个界面结束进程也不再像以前一样调用User32的EndTask(https://msdn.microsoft.com/en-us/library/windows/desktop/ms633492(v=vs.85).aspx),而是重新规划了一套逻辑。
具体逻辑如下:
1.区分程序类型
2.如果是窗口程序,则给窗口发送WM_SYSCOMMAND+SC_CLOSE结束窗口来结束进程
3.如果是服务程序,则调用ControlService+SERVICE_CONTROL_STOP结束服务来结束进程
4.如果既没有窗口也不是服务的程序,或者说在第2,3步没有结束成功的进程,会调用TerminateProcess来强行结束进程。
5.第五步是和之前结束任务最大的一个区别,以前的任务管理器,如果没能结束进程,例如一些僵尸进程,他就不会做其他动作了,而新的任务管理器为了释放这种进程所占用的内核资源,他还会做另外一些事情,那就是关闭目标进程的所有句柄。使用的方式就是DuplicateHandle+DUPLICATE_CLOSE_SOURCE。这样做的另外一个好处就是,如果顽固进程还在运行,句柄关闭会造成其崩溃而结束。
在Windows 8之前,Shell API对于长路径的文件名的支持并不理想。比如PathAppend这个函数,函数规定pszPath,也就是第一个参数,它的buffer大小必须要能够容纳MAX_PATH个字符。第二个参数pszMore也不能超过MAX_PATH的长度。这样的API不仅不能满足我们对长文件路径需求,同时也可能让我们的软件由于字符串检查不严格出现严重BUG和漏洞。
还好,这个问题在Windows 8以及以后的系统上得到了解决。还是以路径拼接为例。微软向我们介绍了PathCchAppend和PathCchAppendEx函数。其中PathCchAppend函数,增加了cchPath参数,用来指定输出buffer的大小。用这样的方式来加强参数的检查,增加了函数的安全性。而PathCchAppendEx这个函数在PathCchAppend基础上,又加入了dwFlags,现在这个标志只有PATHCCH_ALLOW_LONG_PATHS,意思就是让我们的路径名超过MAX_PATH。
不知道微软设计PathCchAppend和PathCchAppendEx这两个API的时候是怎么样的一个想法,我觉得完全没必要设计成两个函数,一个PathCchAppendEx就足够了。大家是不是也有这个疑问呢?
最后,由于Windows 7现在的使用量还是非常大的,我们也不能因为要使用这些新的API而放弃兼容老版本的Windows。比较合适的做法还是动态导入这些函数,如果成功了就可以使用新的函数,失败就用老的函数。另外值得注意的是,PathCchAppend这类新的函数并不是放在shlwapi.dll里面,而是在kernelbase.dll,动态获取函数的时候需要注意这一点。
用来干什么就不用说了,反正不是什么好事情 =v=
|