[2024]基于C++的混淆壳(二) IAT隐藏 huoji 混淆,壳,IAT 2024-03-08 228 次浏览 0 次点赞 ### 做壳步骤 创建区段 遍历所有指令 识别指令类型 加花/混淆/处理跳转表 <-先说到这里 处理原来老的区段 处理PE入口点 处理PE头 ### IAT隐藏 我们希望我们的被加壳的程序IAT被隐藏掉,所以先遍历老的,保存为一个数组 ```cpp const auto peBase = peFile->get_buffer()->data(); const auto sectionBase = peBase + newSection->VirtualAddress; PIMAGE_DATA_DIRECTORY importDirectory = &((PIMAGE_NT_HEADERS64)(peBase + ((PIMAGE_DOS_HEADER)peBase)->e_lfanew)) ->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; if (importDirectory->Size > 0) { // 获取第一个导入描述符 PIMAGE_IMPORT_DESCRIPTOR importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(peBase + importDirectory->VirtualAddress); while (importDescriptor->Name != 0) { // 获取IAT PIMAGE_THUNK_DATA64 thunk = (PIMAGE_THUNK_DATA64)(peBase + importDescriptor->FirstThunk); while (thunk->u1.AddressOfData != 0) { LPCSTR function_name; bool isOrdinal = (thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG); if (isOrdinal) { //function_name = (LPCSTR)(thunk->u1.Ordinal & 0xFFFF); function_name = NULL; } else { PIMAGE_IMPORT_BY_NAME functionName = (PIMAGE_IMPORT_BY_NAME)(peBase + thunk->u1.AddressOfData); function_name = (LPCSTR)functionName->Name; } if (function_name) { char* dllName = (char*)(peBase + importDescriptor->Name); // 将信息添加到IatTable _BuildIAT entry; entry.functionName = std::string(function_name); entry.dllName = std::string(dllName); entry.inCodeSectionAddress = (uint64_t)thunk->u1.Function; IatTable.push_back(entry); } // 移动到下一个thunk thunk++; } // 移动到下一个导入描述符 importDescriptor++; } } ``` 这边省略了Ordinal,因为我实在是懒得处理了,想处理找blackbone的代码 ### 建表,复制 弄完后,建立一个表,存一些基本结构 ```cpp struct _iatTable { uint64_t functionAddress; char dllName[64]; char functionName[64]; }; struct _initTable { size_t iatTableSize; //保持第一个 uint32_t fnGetProcAddressOffset; uint32_t fnGetKernel32BaseOffset; uint32_t entryPoint; _iatTable iatTable[1]; }; ``` 把老的一坨IAT复制过来 ```cpp // 复制初始化信息到壳段头部 const auto copyiedSize = sizeof(_initTable) + (sizeof(_iatTable) * IatTable.size()); _initTable* initTable = (_initTable*)malloc(copyiedSize); if (initTable == nullptr) { // 这不是 驱动,没有低资源模式. __debugbreak(); } memset(initTable, 0x0, copyiedSize); auto startBase = (uint64_t)sectionBase; /* initTable shellcode_init */ _initTable* insertTable = (_initTable*)(sectionBase); for (size_t i = 0; i < IatTable.size(); i++) { const auto entry = &IatTable[i]; // 不可能大于64字节 if (entry->dllName.size() > sizeof(initTable->iatTable[i].dllName)) { __debugbreak(); } memcpy(initTable->iatTable[i].dllName, entry->dllName.c_str(), entry->dllName.size()); memcpy(initTable->iatTable[i].functionName, entry->functionName.c_str(), entry->functionName.size()); entry->buildAddress = (uint64_t) & (&insertTable->iatTable[i])->functionAddress; //printf("build address :%p \n", entry->buildAddress); } ``` 这样我们就把IAT的信息存到区段的头部了 区段开始 -> ->->init table结构 ->->IAT结构 ->->->->dll名字 ->->->->函数名字 ->->->->call的地址 至于怎么修复,下一章会说,还有一个重要的是,在修复重定位的时候 检查是不是来自iat的call,如果是 改为到指定的iattable1的call地址 ```cpp // 3. 在保护区域里面,就修正相对跳转地址为保护区域地址 // 如果当前正在拷贝一个call 0x123456 if (item.callType == _callType::kIMM) { // 看看是不是在保护区里面 for (auto& buildIns : sourceFunctionInsn.build_ins) { // 如果call的地址是被移动到保护区的地址 if (buildIns.codeSectionAddress == item.calladdress) { // 修正他; const auto nextAddress = item.inNewSectoinAddress + item.insSize; const auto newOffset = buildIns.newSectionAddress - nextAddress; memcpy((void*)(item.inNewSectoinAddress + getInsCopyLength(item.cs_ins)), &newOffset, 4); item.isFixed = true; break; } } // 不在重新计算位置 if (item.isFixed == false) { const auto nextAddress = item.inNewSectoinAddress + item.insSize; const auto newOffset = item.calladdress - nextAddress; memcpy((void*)(item.inNewSectoinAddress + getInsCopyLength(item.cs_ins)), &newOffset, 4); item.isFixed = true; // __debugbreak(); } continue; } ``` 其他的mov lea 也一样处理.不再叙述 本文由 huoji 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。 点赞 0
还不快抢沙发