// 监测当前模块是否是在2gb虚拟内存空间之上 // 这部分的地址内存是属于win32进程共享的 if (!isnt() && ((dword)hmodule >= 0x80000000)) { _assert(false); setlasterrorex(error_invalid_handle, sle_error); return false; } // 清零 if (paorigfuncs) memset(paorigfuncs, null, sizeof(proc));
// 调用getnamedimportdescriptor()函数,来得到hmodule -- 即我们需要 // 截获的函数所在的dll模块的引入描述符(import descriptor) pimage_import_descriptor pimportdesc = getnamedimportdescriptor(hmodule, szimportmodule); if (pimportdesc == null) return false; // 若为空,则模块未被当前进程所引入
// 从dll模块中得到原始的thunk信息,因为pimportdesc->firstthunk数组中的原始信息已经 // 在应用程序引入该dll时覆盖上了所有的引入信息,所以我们需要通过取得pimportdesc->originalfirstthunk // 指针来访问引入函数名等信息 pimage_thunk_data porigthunk = makeptr(pimage_thunk_data, hmodule, pimportdesc->originalfirstthunk);
// 从pimportdesc->firstthunk得到image_thunk_data数组的指针,由于这里在dll被引入时已经填充了 // 所有的引入信息,所以真正的截获实际上正是在这里进行的 pimage_thunk_data prealthunk = makeptr(pimage_thunk_data, hmodule, pimportdesc->firstthunk);
// 穷举image_thunk_data数组,寻找我们需要截获的函数,这是最关键的部分! while (porigthunk->u1.function) { // 只寻找那些按函数名而不是序号引入的函数 if (image_ordinal_flag != (porigthunk->u1.ordinal & image_ordinal_flag)) { // 得到引入函数的函数名 pimage_import_by_name pbyname = makeptr(pimage_import_by_name, hmodule, porigthunk->u1.addressofdata);
// 如果函数名以null开始,跳过,继续下一个函数 if (’/0’ == pbyname->name[0]) continue;
// bdohook用来检查是否截获成功 bool bdohook = false;
// 检查是否当前函数是我们需要截获的函数 if ((pahookfunc.szfunc[0] == pbyname->name[0]) && (strcmpi(pahookfunc.szfunc, (char*)pbyname->name) == 0)) { // 找到了! if (pahookfunc.pproc) bdohook = true; } if (bdohook) { // 我们已经找到了所要截获的函数,那么就开始动手吧 // 首先要做的是改变这一块虚拟内存的内存保护状态,让我们可以自由存取 memory_basic_information mbi_thunk; virtualquery(prealthunk, &mbi_thunk, sizeof(memory_basic_information)); _assert(virtualprotect(mbi_thunk.baseaddress, mbi_thunk.regionsize, page_readwrite, &mbi_thunk.protect));
// 保存我们所要截获的函数的正确跳转地址 if (paorigfuncs) paorigfuncs = (proc)prealthunk->u1.function;
// 将image_thunk_data数组中的函数跳转地址改写为我们自己的函数地址! // 以后所有进程对这个系统函数的所有调用都将成为对我们自己编写的函数的调用 prealthunk->u1.function = (pdword)pahookfunc.pproc;
// 操作完毕!将这一块虚拟内存改回原来的保护状态 dword dwoldprotect; _assert(virtualprotect(mbi_thunk.baseaddress, mbi_thunk.regionsize, mbi_thunk.protect, &dwoldprotect)); setlasterror(error_success); return true; } } // 访问image_thunk_data数组中的下一个元素 porigthunk++; prealthunk++; } return true; }
// getnamedimportdescriptor函数的实现 pimage_import_descriptor getnamedimportdescriptor(hmodule hmodule, lpcstr szimportmodule) { // 检测参数 _assert(szimportmodule); _assert(hmodule); if ((szimportmodule == null) || (hmodule == null)) { _assert(false); setlasterrorex(error_invalid_parameter, sle_error); return null; }
// 得到dos文件头 pimage_dos_header pdosheader = (pimage_dos_header) hmodule;
// 检测是否mz文件头 if (isbadreadptr(pdosheader, sizeof(image_dos_header)) || (pdosheader->e_magic != image_dos_signature)) { _assert(false); setlasterrorex(error_invalid_parameter, sle_error); return null; }
// 取得pe文件头 pimage_nt_headers pntheader = makeptr(pimage_nt_headers, pdosheader, pdosheader->e_lfanew);
// 检测是否pe映像文件 if (isbadreadptr(pntheader, sizeof(image_nt_headers)) || (pntheader->signature != image_nt_signature)) { _assert(false); setlasterrorex(error_invalid_parameter, sle_error); return null; }
// 检查pe文件的引入段(即 .idata section) if (pntheader->optionalheader.datadirectory[image_directory_entry_import].virtualaddress == 0) return null;
// 得到引入段(即 .idata section)的指针 pimage_import_descriptor pimportdesc = makeptr(pimage_import_descriptor, pdosheader, pntheader->optionalheader.datadirectory[image_directory_entry_import].virtualaddress);
// 穷举pimage_import_descriptor数组寻找我们需要截获的函数所在的模块 while (pimportdesc->name) { pstr szcurrmod = makeptr(pstr, pdosheader, pimportdesc->name); if (stricmp(szcurrmod, szimportmodule) == 0) break; // 找到!中断循环 // 下一个元素 pimportdesc++; }
// 如果没有找到,说明我们寻找的模块没有被当前的进程所引入! if (pimportdesc->name == null) return null;
// 返回函数所找到的模块描述符(import descriptor) return pimportdesc; }
// isnt()函数的实现 bool isnt() { osversioninfo stosvi; memset(&stosvi, null, sizeof(osversioninfo)); stosvi.dwosversioninfosize = sizeof(osversioninfo); bool bret = getversionex(&stosvi); _assert(true == bret); if (false == bret) return false; return (ver_platform_win32_nt == stosvi.dwplatformid); } /////////////////////////////////////////////// end //////////////////////////////////////////////////////////////////////
不知道在这篇文章问世之前,有多少朋友尝试过去实现“鼠标屏幕取词”这项充满了挑战的技术,也只有尝试过的朋友才能体会到其间的不易,尤其在探索api函数的截获时,手头的几篇资料没有一篇是涉及到关键代码的,重要的地方都是一笔代过,msdn更是显得苍白而无力,也不知道除了image_import_descriptor和image_thunk_data,微软还隐藏了多少秘密,好在硬着头皮还是把它给攻克了,希望这篇文章对大家能有所帮助。 
说明:本教程来源互联网或网友上传或出版商,仅为学习研究或媒体推广,wanshiok.com不保证资料的完整性。
2/2 首页 上一页 1 2 |