2014年7月27日 星期日

继续记录Hook遇到的坑:兼容模式

Windows的兼容模式在apphelp.dll、AcLayers.dll、AcGernel.dll Hook了一堆一堆的函数。win7采用的方法是iathook。
冲突点如下:
由于我们的实现机制,兼容模式的iathook优先于我,先将CreateFileA、CreateFileW、GetProcAddress等,覆盖指针为iatCreateFileA、iatCreateFileW、iatGetProcAddress(我忘了实际的符号叫啥了,这些名字是我瞎起的,用于区分hook前后)等。
于是我本来通过GetProcAddress获取CreateFileW的地址然后inlinehook的流程由于iat被修改,变成了通过iatGetProcAddress获取到iatCreateFileW的地址然后inlinehook。
 如此一来CreateFileA的调用路径就变成了这样:
CreateFileA == iatCreateFileA  ->CreateFileA->CreateFileW
走不到我的hook里面去了。
相比浏览器那恐怖的hook环境,这坑真的很好填,我的方法是通过LdrGetProcedureAddress获取到真实的CreateFileW地址然后hook,毕竟不是写攻防代码,兼容性优先了。
说到浏览器了,要不哪天有时间了翻翻某个亏本了的工程代码,总结下浏览器(IE、Chrome、Firefox)hook里遇到的坑,和这几篇博文合并成一篇Hook遇坑大总结?

2014年7月22日 星期二

备忘:关于VB程序报错Out of memory

msvbvm60.dll写的很屎,有一个报错函数写死了报Out of memory,今天就遇到的情况明明是hook的伪SetFilePointer写错了,在某些情况成功操作但是函数返回失败,结果msvbvm60.dll搞了个Out of memory,真够坑爹的。
所以,VB6程序报错Out of memory等于报错Error,除了说明发生了错误之外什么都不能说明 ,memory什么的就当它是个屁好了。

不用任何API获取本可执行文件的ImageBase(dll hModule)

一句话:
extern "C" IMAGE_DOS_HEADER __ImageBase;
这两天读msvcrt源码所得,以后dll获取自身的hmodule再也不用VitualQuery了

2014年7月20日 星期日

VS2013写的dll被内存加载失败原因一例

  在公司写的某dll终于完成了,和公司的壳一同使用时,公司的一种壳可以稳定的从内存加载使用我写的dll,而另一种时好时坏(大多数情况都无 法正常加载),在我来这里工作之前,这两个壳在内存加载他们使用低版本VS编译出的dll时都可以正常工作,我因为使用了一些C++11语法特性,实在不 想做降版这蛋疼事,调试分析了这其中的原因。
  经过一番调试,终于把前因后果捋顺了:
· ->DLLEntryPoint:走到_DllMainCRTStartup
· _DllMainCRTStartup->_CRT_INIT:初始化CRT环境
· _CRT_INIT->_cinit:这还真不知道是初始化啥
· 其中_cinit中有段代码是这样写的:
if (_FPinit != NULL &&
     _IsNonwritableInCurrentImage((PBYTE)&_FPinit))
{
      //这里代码具体记不清楚了,是调用了一个函数初始化浮点数运算相关环境
}

代码意思比较好懂,如果_FPinit不为零同时这个变量在当前映像中是可写入的就初始化浮点数运算环境。而问题就出在_IsNonwritableInCurrentImage的判断上了:
BOOL __cdecl _IsNonwritableInCurrentImage(char *pTarget)
{
  _IMAGE_SECTION_HEADER *pTargetSectoinHeader; 
  BOOL bResult = FALSE;
  if ( _ValidateImageBase((char *)0x10000000)
&& (pTargetSectoinHeader = 
  _FindPESection((char *)0x10000000, (unsigned int)(pTarget - 0x10000000))) != 0 )
  {
    bResult = ~(pTargetSectoinHeader->Characteristics >> 31) & 1;
  }
  return bResult;
}


这个函数先调用_ValidateImageBase判断了当前映像基址是否为合法的PE文件头,然后调用_FindPESection找出变量所在的区段的区段头,检查区段头中记录的该区段是否具有写入权限,返回结果。代码中写死的0x10000000地址都会在dll加载时系统通过重定位表将这些地址改写为映像基址。
但是在_ValidateImageBase判断头两个字节是否为MZ时程序就非法内存访问然后崩溃了。
经过了与维护壳的同事沟通确认,产生问题的壳在最初分析完我的dll之后就把PE头抹掉了,使用了一个自定义的结构体代替PE头记录了部分PE头内的信息供内存加载dll初始化时使用,在为我的dll代码申请内存空间时根本就没有留出PE头的空,当然更没有把PE头还原回去
内存空间就成了这样:
未分配的内存,本应为PE
DLL申请的内存
ImageBase
于是经过重定位表重定位后传递给_ValidateImageHeader的映像基址就指向了一处非法地址,最终产生了非法内存访问。在极其偶然的情况下该地址可能指向了另一块内存区域的一部分,指向的内容正好不是MZ开头,而这个dll又凑巧不需要初始化浮点数运算环境,才会出现偶尔可以正常启动的情况。
至此这个问题的前因后果已经搞明白,具体该如何解决还等和领导沟通后再定吧。
--8:28 PM 7/21/2014
解决方案:
方法一(最省事相对稳定):由于_IsNonwritableInCurrentImage总共就判断两个变量,所以可以根据自身程序情况写个代替函数写死了,修改工程设置人为规定EntryPoint,调用_CRT_INIT初始化之前修改内存权限把自己写的函数memcpy过去。
方法二(相对省事可能不稳定):修改工程设置人为规定EntryPoint,然后参照IDA F5VS安装目录下的crt源代码,写自己的_DllMainCRTStartup_CRT_INIT_cinit。不稳定因素有二:1.由于msvcrt.lib有两个obj都有某全局变量,所以实现_CRT_INIT时对着两个全局变量是没法初始化了2.可能正是由于不稳定因素1,调用初始化环境的函数会失败。
方法三(最稳定最费时):搞个自己的msvcrt.lib

2014年7月2日 星期三

Hook CloseHandle时遇到程序退出时卡死的问题原因及解决方案

  实际上这个问题我很久之前就遇到过了,在Hook CloseHandle函数的情况下,点击关闭按钮后程序要等一段时间才能够关闭,当初只当是虚拟机性能差造成的延迟,直到前几天我在新公司写Hook代码在实机上测试才发现问题并不是我之前认为的那样。

问题现象:
  • 在Hook CloseHandle函数的情况下,点击关闭按钮后要等一段时间进程才能够退出。
问题原因:
  • 在程序退出的过程中,全局对象的生存期已经结束,系统在进行进一步收尾工作时调用了CloseHanle,这时在伪函数内访问了已经销毁的全局对象,导致异常产生。
解决方案:
  • 动态申请在伪函数内需要访问到的全局对象,将对象保存在堆内。
  此时对象保存在堆内而非栈内,在程序退出全局变量生存期结束时不会调用析构函数。同时,虽然用于保存该对象地址的指针/引用虽然也超过生存期,但是由于没有被复写,也不存在析构函数,所以可以访问。

分析过程:
  首先尝试在进程等待退出时间段内使用Process Explorer查看该进程各个线程的调用堆栈情况,初步判断原因。
  现象:一用Process Explorer查看该进程线程情况,一切换到该进程属性的Threads标签页程序立刻退出。
  现象分析:Process Explorer查询线程堆栈调用了dbghelp.dll内相关函数,可能程序遇到的情况如果不挂调试器就不继续执行了。
  尝试:先用Process Explorer查看该进程属性的Threads项,关闭程序,让异常现象产生
  现象:从调用堆栈可以看出,现场内存在未处理的异常。
  猜测:【猜测内容同问题原因】(运气不错,一下猜中了)
  编写粗糙的测试代码:
  BOOL bDestoryed = FALSE;
  Class fdsa{
  public:
    fdsa(){};
    ~fdsa(){bDestoryed = TRUE};
  }
  fdsa xxx;
    同时在CloseHandle的伪函数内判断bDestoryed是否被赋值为TRUE。

  最终确定猜测成立。