大白兔联盟

文章搜索
搜索
当前位置:首页 > 前沿技术 > 逆向汇编 > 文章详情

[C++]反射式注入(ManualMap Inject) 2

大白兔    2023-4-10  298  0评论


接上文:https://www.dabaitulm.com/nxhb/1065.html

当我们获取到OPTIONAL_HEADER时,我们就可以拿到第一个需要的东西,也就是DLL的程序入口点,它就是OPTIONAL_HEADER中的AddressOfEntryPoint项。接下来,我们需要从数据目录表中找我们需要的东西,包括重定位表,导入表以及TLS(线程本地存储, Thread Local Storage),当然TLS其实并不是必须的,因为并非所有的DLL都会使用TLS,但为了能写出一个通用的注入器,我们需要考虑TLS的存在。
重定位表为 DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]也就是DataDirectory[5],通过重定位表里的内容判断DLL是否重定位,如果重定位的话需要确认重定位的位置,并且需要定位到对应的重定位项,以下的代码为一个示例:

#define RELOC_FLAG32(RelInfo) ((RelInfo) >> 0x0c == IMAGE_REL_BASED_HIGHLOW)
#define RELOC_FLAG64(RelInfo) ((RelInfo) >> 0x0c == IMAGE_REL_BASED_DIR64)
#ifdef _WIN64
#define RELOC_FLAG RELOC_FLAG64
#else
#define RELOC_FLAG RELOC_FLAG32
#endif
BYTE* LocationDelta = pBase - pOpt->ImageBase;
        // 看dll是否重定位
        if (LocationDelta) {
                if (!pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) {
                        return;
                }
                // 获取重定位表
                /* 重定位表
                typedef struct _IMAGE_BASE_RELOCATION
                {
                        DWORD VirtualAddress; //重定位数据开始的RVA 地址
                        DWORD SizeOfBlock;          //重定位块的长度,标识重定向字段个数
                        // WORD TypeOffset[];      //重定位项数组相对虚拟RVA, 个数动态分配
                }IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION;
                */
                IMAGE_BASE_RELOCATION* pRelocData = (IMAGE_BASE_RELOCATION*)
                        (pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

                while (pRelocData->VirtualAddress) {
                        // 获取重定位项的数量(也就是重定位表的最后一部分,实际上是一个WORD数组)
                        UINT AmountOfEntries = (pRelocData->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
                        // 向后偏移一个重定位项
                        WORD* pRelativeInfo = (WORD*)(pRelocData + 1);
                        for (UINT i = 0; i < AmountOfEntries; ++i, ++pRelativeInfo) {
                                // 判断是否为重定位项
                                if (RELOC_FLAG(*pRelativeInfo)) {
                                        // 定位到当前的重定位项
                                        UINT_PTR* pPatch = (UINT_PTR*)(pBase + pRelocData->VirtualAddress + ((*pRelativeInfo) & 0xfff));
                                        *pPatch += (UINT_PTR)LocationDelta;
                                }
                        }
                        pRelocData = (IMAGE_BASE_RELOCATION*)(((BYTE*)pRelocData) + pRelocData->SizeOfBlock);
                }
        }

接下来是导入表,导入表是非常关键的,通过导入表能否知道我们要注入的DLL具体要导入哪些外部函数,不过考虑到可能会出现没有导入外部函数的可能,所以需要先进行判断,以下为导入表部分的示例,这个示例使用了LoadLibraryA和GetProcAddress获取外部DLL的函数地址,也就是说在这个示例中并没有完全代替使用Windows API,但是对于某些反作弊,做到这部分就已经足够了,当然,之后会给出GetProcAddress的替代方案

 if (pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size)
        {
                /*  导入表基址是PE文件从其他三方程序中导入API,以供本程序调用的机制
                typedef struct _IMAGE_IMPORT_DESCRIPTOR {
                union {
                DWORD Characteristics;
                DWORD OriginalFirstThunk;//指向输入名称表的表(INT)的RVA
                };
                DWORD TimeDateStamp;// 映象绑定前,这个值是0,绑定后是导入模块的时间戳。
                DWORD ForwarderChain;//转发链,如果没有转发器,这个值是-1。
                DWORD Name;//指向导入映像文件的名称
                DWORD FirstThunk;//指向输入地址表的表(IAT)的RVA
                } IMAGE_IMPORT_DESCRIPTOR;
                */
                IMAGE_IMPORT_DESCRIPTOR* pImportDescr = (IMAGE_IMPORT_DESCRIPTOR*)
                        (pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
                // 将所有要导入的外部dll导入,比如ntdll.dll
                while (pImportDescr->Name)
                {
                        char* szMod = (char*)(pBase + pImportDescr->Name);
                        HINSTANCE hDll = _LoadLibraryA(szMod);

                        UINT_PTR* pThunkRef = (UINT_PTR*)(pBase + pImportDescr->OriginalFirstThunk);
                        UINT_PTR* pFuncRef = (UINT_PTR*)(pBase + pImportDescr->FirstThunk);

                        if (!pThunkRef) {
                                pThunkRef = pFuncRef;
                        }

                        for (;*pThunkRef; ++pThunkRef, ++pFuncRef)
                        {
                                // 判断是否按序号导入,不按序号就按名称导入(导入所有的外部函数)
                                if (IMAGE_SNAP_BY_ORDINAL(*pThunkRef)) {
                                        *pFuncRef = _GetProcAddress(hDll, (char*)(*pThunkRef & 0xffff));
                                }
                                else {
                                        IMAGE_IMPORT_BY_NAME* pImport = (IMAGE_IMPORT_BY_NAME*)(pBase + (*pThunkRef));
                                        *pFuncRef = _GetProcAddress(hDll, pImport->Name);
                                }
                        }
                        ++pImportDescr;
                }
        }

接下来是TLS,当然我们也需要最先判断DLL是否启用了TLS,以下是代码示例

if (pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size)
        {
                IMAGE_TLS_DIRECTORY* pTLS = (IMAGE_TLS_DIRECTORY*)
                        (pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress);
                PIMAGE_TLS_CALLBACK* pCallback = (PIMAGE_TLS_CALLBACK*)(pTLS->AddressOfCallBacks);
                // 调用所有的TLS回调函数
                /*
                TLS回调函数是指,每当创建/终止进程的线程时会自动调用执行的函数,创建进程的主线程时也会自动调用回调函数,
                且其调用执行先于EP代码,反调试技术利用的就是TLS回调函数的这一特征
                */
                for (;pCallback && *pCallback; ++pCallback) {
                        (*pCallback)(pBase, DLL_PROCESS_ATTACH, nullptr);
                }
        }

完成这些步骤后,我们可以执行DllMain函数,这个函数的地址就是之前获取到的DLL入口点,这个函数有三个参数,第一个为DLL本身的地址,也就是DOS头的地址,第二个是CALL DllMain的原因,这里我们使用DLL_PROCESS_ATTACH,也就是需要将DLL附加到进程

using f_DLL_ENTRY_POINT = BOOL(WINAPI*)(void* hDll, DWORD dwReadson, void* pReserved);
_DllMain(pBase, DLL_PROCESS_ATTACH, nullptr);

至此,Dll的初始化步骤就已经完成,不过,这种注入方式不适用于所有的进程以及所有的DLL,而且,就算使用这种办法将DLL注入进程并且没有被检测到,也可能会被扫内存模块扫描到异常的模块,所以单在注入环节下功夫是不够的。


免责声明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络收集整理,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。如果您喜欢该程序和内容,请支持正版,购买注册,得到更好的正版服务。我们非常重视版权问题,如有侵权请邮件与我们联系处理。敬请谅解!

挤眼 亲亲 咆哮 开心 想想 可怜 糗大了 委屈 哈哈 小声点 右哼哼 左哼哼 疑问 坏笑 赚钱啦 悲伤 耍酷 勾引 厉害 握手 耶 嘻嘻 害羞 鼓掌 馋嘴 抓狂 抱抱 围观 威武 给力
提交评论

清空信息
关闭评论