最近逆了一下WannaCry病毒,发现里边加载动态库是自己实现的,所以我也学着实现了一下。
0x001 读取动态库到内存
首先,需要将目标动态库读取的到内存,然后再进行下一步工作。
HANDLE hDll = CreateFile(L"..//Debug//TestDll.dll", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); if (hDll == INVALID_HANDLE_VALUE) { printf("打开文件错误:%d ", GetLastError()); } DWORD dwFileSize = 0; dwFileSize = GetFileSize(hDll, NULL); if (dwFileSize <= 0) { printf("获得文件大小失败! "); return -1; } unsigned char* DllBuffer = (unsigned char*)malloc(dwFileSize); DWORD dwDataLength = 0; if (!ReadFile(hDll, DllBuffer, dwFileSize, &dwDataLength, NULL)) { printf("读取文件错误:%d ", GetLastError()); return -1; } HMEMORYMODULE hModule = MemoryLoadLibrary(DllBuffer, dwDataLength); pfnTestProc TestProc = (pfnTestProc)MemoryGetProcAddress(hModule, "TestProc"); TestProc(); MemoryFreeLibrary(hModule);
0x002 在目标加载地址处申请内存空间
实现LoadLirbrary其实就是将Dll文件由文件格式映射为内存格式,我们首先要做的就是在Dll建议加载地址处申请内存空间。
//从Dll建议加载基地址处申请内存空间 Code = (unsigned char *)MyAllocMemory((LPVOID)(Nt_Header->OptionalHeader.ImageBase), AlignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, UserData); if (Code == NULL) { //如果不成功,则随意找个地儿加载 Code = (unsigned char *)MyAllocMemory(NULL, AlignedImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE, UserData); if (Code == NULL) { SetLastError(ERROR_OUTOFMEMORY); return NULL; } }
这个申请多大呢?实际上要考虑系统的内存页大小,对齐粒度。
GetNativeSystemInfo(&SystemInfo); AlignedImageSize = AlignValueUp(Nt_Header->OptionalHeader.SizeOfImage, SystemInfo.dwPageSize); static inline uintptr_t AlignValueDown(uintptr_t Value, uintptr_t Alignment) { return Value & ~(Alignment - 1); }
0x003 拷贝PE头部信息到目标地址
接下来我们要做的便是将PE头(Dos头+Nt头,具体结构可参考PE权威指南)拷贝到指定地区。
//申请头部空间 Headers = (unsigned char *)MyAllocMemory(Code, Nt_Header->OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE, UserData); //将Dll中的Nt头拷贝到指定加载内存处 memcpy(Headers, Dos_Header, Nt_Header->OptionalHeader.SizeOfHeaders); hModule->NtHeaders = (PIMAGE_NT_HEADERS)&((const unsigned char *)(Headers))[Dos_Header->e_lfanew]; // 更新Dll加载基地址 #ifdef _WIN64 hModule->NtHeaders->OptionalHeader.ImageBase = (DWORD64)Code; #else hModule->NtHeaders->OptionalHeader.ImageBase = (DWORD)Code; #endif // _WIN64
0x004 拷贝节区信息到目标地址
static BOOL CopySections(const unsigned char *Data, size_t Size, PIMAGE_NT_HEADERS Nt_Headers, PMEMORYMODULE Module) { int i, SizeOfSection; unsigned char *CodeBase = Module->pCodeBase; unsigned char *SectionBase; //节区地址 PIMAGE_SECTION_HEADER Section_Header = IMAGE_FIRST_SECTION(Module->NtHeaders); for (i = 0; i<Module->NtHeaders->FileHeader.NumberOfSections; i++, Section_Header++) { //这里有节区,但是没内容 if (Section_Header->SizeOfRawData == 0) { //节大小为0的情况下,直接按照粒度对齐 SizeOfSection = Nt_Headers->OptionalHeader.SectionAlignment; if (SizeOfSection > 0) { SectionBase = (unsigned char *)Module->MyAlloc(CodeBase + Section_Header->VirtualAddress, SizeOfSection, MEM_COMMIT, PAGE_READWRITE, Module->pUserData); if (SectionBase == NULL) { return FALSE; } SectionBase = CodeBase + Section_Header->VirtualAddress; Section_Header->Misc.PhysicalAddress = (DWORD)((uintptr_t)SectionBase & 0xffffffff); memset(SectionBase, 0, SizeOfSection); } // 节区为空 continue; } if (!CheckSize(Size, Section_Header->PointerToRawData + Section_Header->SizeOfRawData)) { return FALSE; } //拷贝节区内容 SectionBase = (unsigned char *)Module->MyAlloc(CodeBase + Section_Header->VirtualAddress, Section_Header->SizeOfRawData, MEM_COMMIT, PAGE_READWRITE, Module->pUserData); if (SectionBase == NULL) { return FALSE; } SectionBase = CodeBase + Section_Header->VirtualAddress; memcpy(SectionBase, Data + Section_Header->PointerToRawData, Section_Header->SizeOfRawData); Section_Header->Misc.PhysicalAddress = (DWORD)((uintptr_t)SectionBase & 0xffffffff); } return TRUE; }
0x005 调整重定向信息
重定向表是一个特别重要的结构,我们要先弄好它,否则接下来的各种数据地址有可能会错误。
// 调整重定向数据位置 LocationDelta = (ptrdiff_t)(hModule->NtHeaders->OptionalHeader.ImageBase - Nt_Header->OptionalHeader.ImageBase); if (LocationDelta != 0) { hModule->bIsRelocated = PerformBaseRelocation(hModule, LocationDelta); } //重定向 static BOOL PerformBaseRelocation(PMEMORYMODULE Module, ptrdiff_t Delta) { unsigned char *CodeBase = Module->pCodeBase; PIMAGE_BASE_RELOCATION Relocation; //获得重定向表目录项 PIMAGE_DATA_DIRECTORY BaseRelocDirectory = GET_HEADER_DICTIONARY(Module, IMAGE_DIRECTORY_ENTRY_BASERELOC); if (BaseRelocDirectory->Size == 0) { return (Delta == 0); } Relocation = (PIMAGE_BASE_RELOCATION)(CodeBase + BaseRelocDirectory->VirtualAddress); while (Relocation->VirtualAddress > 0) { DWORD i; unsigned char *RelocationBase = CodeBase + Relocation->VirtualAddress; //重定向表RVA unsigned short *RelocationInfo = (unsigned short*)OffsetPointer(Relocation, IMAGE_SIZEOF_BASE_RELOCATION); for (i = 0; i<((Relocation->SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, RelocationInfo++) { // 高4位为Type int Type = *RelocationInfo >> 12; // 低12位为偏移 int Offset = *RelocationInfo & 0xfff; switch (Type) { case IMAGE_REL_BASED_ABSOLUTE: //这个无意义,仅对齐用 break; case IMAGE_REL_BASED_HIGHLOW: //32位都需要修正 { DWORD *PatchAddrHL = (DWORD *)(RelocationBase + Offset); *PatchAddrHL += (DWORD)Delta; } break; #ifdef _WIN64 case IMAGE_REL_BASED_DIR64: //64位 { ULONGLONG *PatchAddr64 = (ULONGLONG *)(RelocationBase + Offset); *PatchAddr64 += (ULONGLONG)Delta; } break; #endif default: break; } } // 下一个重定向Block Relocation = (PIMAGE_BASE_RELOCATION)OffsetPointer(Relocation, Relocation->SizeOfBlock); } return TRUE; }
0x006 建立导入表
//导入表 static BOOL BuildImportTable(PMEMORYMODULE Module) { unsigned char *CodeBase = Module->pCodeBase; PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor; BOOL bOk = TRUE; //获得导入表表项 PIMAGE_DATA_DIRECTORY ImportDirectory = GET_HEADER_DICTIONARY(Module, IMAGE_DIRECTORY_ENTRY_IMPORT); if (ImportDirectory->Size == 0) { return TRUE; } ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(CodeBase + ImportDirectory->VirtualAddress); //导出表RVA for (; !IsBadReadPtr(ImportDescriptor, sizeof(IMAGE_IMPORT_DESCRIPTOR)) && ImportDescriptor->Name; ImportDescriptor++) { uintptr_t *ThunkRef; FARPROC *FuncRef; HCUSTOMMODULE *v1; HCUSTOMMODULE hModule = Module->MyLoadLibrary((LPCSTR)(CodeBase + ImportDescriptor->Name), Module->pUserData); if (hModule == NULL) { SetLastError(ERROR_MOD_NOT_FOUND); bOk = FALSE; break; } v1 = (HCUSTOMMODULE *)realloc(Module->pModules, (Module->nNumberOfModules + 1)*(sizeof(HCUSTOMMODULE))); if (v1 == NULL) { Module->MyFreeLibrary(hModule, Module->pUserData); SetLastError(ERROR_OUTOFMEMORY); bOk = FALSE; break; } Module->pModules = v1; Module->pModules[Module->nNumberOfModules++] = hModule; if (ImportDescriptor->OriginalFirstThunk) { //注:导入表双桥结构,OriginalFirstThunk指向INT表,FirstThunk指向IAT表,最终两个表中的表项指向同一个函数地址 ThunkRef = (uintptr_t *)(CodeBase + ImportDescriptor->OriginalFirstThunk); //INT FuncRef = (FARPROC *)(CodeBase + ImportDescriptor->FirstThunk); //IAT } else { // 无INT,有的程序只保留一个桥,如Borland公司的Tlink只保留桥2 ThunkRef = (uintptr_t *)(CodeBase + ImportDescriptor->FirstThunk); FuncRef = (FARPROC *)(CodeBase + ImportDescriptor->FirstThunk); } for (; *ThunkRef; ThunkRef++, FuncRef++) { if (IMAGE_SNAP_BY_ORDINAL(*ThunkRef)) { *FuncRef = Module->MyGetProcAddress(hModule, (LPCSTR)IMAGE_ORDINAL(*ThunkRef), Module->pUserData); } else { //INT PIMAGE_IMPORT_BY_NAME ThunkData = (PIMAGE_IMPORT_BY_NAME)(CodeBase + (*ThunkRef)); *FuncRef = Module->MyGetProcAddress(hModule, (LPCSTR)&ThunkData->Name, Module->pUserData); } if (*FuncRef == 0) { bOk = FALSE; break; } } if (!bOk) { //如果不成功,释放动态库 Module->MyFreeLibrary(hModule, Module->pUserData); SetLastError(ERROR_PROC_NOT_FOUND); break; } } return bOk; }
0x007 调整节属性
//更改内存页属性等 static BOOL FinalizeSection(PMEMORYMODULE Module, PSECTIONFINALIZEDATA SectionData) { DWORD dwProtect, dwOldProtect; BOOL bExecutable; BOOL bReadable; BOOL bWriteable; if (SectionData->Size == 0) { return TRUE; } //节区数据在进程启动后将被丢弃,如.reloc if (SectionData->dwCharacteristics & IMAGE_SCN_MEM_DISCARDABLE) { //节区数据不再需要就释放 if (SectionData->lpAddress == SectionData->lpAlignedAddress && (SectionData->bIsLast || Module->NtHeaders->OptionalHeader.SectionAlignment == Module->dwPageSize || (SectionData->Size % Module->dwPageSize) == 0) ) { Module->MyFree(SectionData->lpAddress, SectionData->Size, MEM_DECOMMIT, Module->pUserData); } return TRUE; } // bExecutable = (SectionData->dwCharacteristics & IMAGE_SCN_MEM_EXECUTE) != 0; //可执行 bReadable = (SectionData->dwCharacteristics & IMAGE_SCN_MEM_READ) != 0; //可读 bWriteable = (SectionData->dwCharacteristics & IMAGE_SCN_MEM_WRITE) != 0; //可写 dwProtect = ProtectionFlags[bExecutable][bReadable][bWriteable]; if (SectionData->dwCharacteristics & IMAGE_SCN_MEM_NOT_CACHED) //节区数据不会经过缓存 { dwProtect |= PAGE_NOCACHE; } // 改变内存页属性 if (VirtualProtect(SectionData->lpAddress, SectionData->Size, dwProtect, &dwOldProtect) == 0) { return FALSE; } return TRUE; } //将一些变量由文件中数值转化为内存中数值 static BOOL FinalizeSections(PMEMORYMODULE Module) { int i; PIMAGE_SECTION_HEADER Section_Header = IMAGE_FIRST_SECTION(Module->NtHeaders); #ifdef _WIN64 //有可能超32位 uintptr_t ImageOffset = ((uintptr_t)Module->NtHeaders->OptionalHeader.ImageBase & 0xffffffff00000000); #else static const uintptr_t ImageOffset = 0; #endif //将文件中的属性转化为内存中的属性 SECTIONFINALIZEDATA SectionData; SectionData.lpAddress = (LPVOID)((uintptr_t)Section_Header->Misc.PhysicalAddress | ImageOffset); SectionData.lpAlignedAddress = AlignAddressDown(SectionData.lpAddress, Module->dwPageSize); SectionData.Size = GetRealSectionSize(Module, Section_Header); SectionData.dwCharacteristics = Section_Header->Characteristics; SectionData.bIsLast = FALSE; Section_Header++; // 依次更改各个节中属性 for (i = 1; i<Module->NtHeaders->FileHeader.NumberOfSections; i++, Section_Header++) { LPVOID SectionAddress = (LPVOID)((uintptr_t)Section_Header->Misc.PhysicalAddress | ImageOffset); //将文件RVA转为内存RVA LPVOID AlignedAddress = AlignAddressDown(SectionAddress, Module->dwPageSize); //将节区对齐粒度改为系统内存页大小 SIZE_T SectionSize = GetRealSectionSize(Module, Section_Header); //将文件中节区大小转为内存中节区大小 if (SectionData.lpAlignedAddress == AlignedAddress || (uintptr_t)SectionData.lpAddress + SectionData.Size >(uintptr_t) AlignedAddress) { // 节的数据在进程启动后将丢弃 if ((Section_Header->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0 || (SectionData.dwCharacteristics & IMAGE_SCN_MEM_DISCARDABLE) == 0) { SectionData.dwCharacteristics = (SectionData.dwCharacteristics | Section_Header->Characteristics) & ~IMAGE_SCN_MEM_DISCARDABLE; } else { SectionData.dwCharacteristics |= Section_Header->Characteristics; } SectionData.Size = (((uintptr_t)SectionAddress) + ((uintptr_t)SectionSize)) - (uintptr_t)SectionData.lpAddress; continue; } if (!FinalizeSection(Module, &SectionData)) { return FALSE; } SectionData.lpAddress = SectionAddress; SectionData.lpAlignedAddress = AlignedAddress; SectionData.Size = SectionSize; SectionData.dwCharacteristics = Section_Header->Characteristics; } SectionData.bIsLast = TRUE; if (!FinalizeSection(Module, &SectionData)) { return FALSE; } return TRUE; }
0x008 实现TLS回调函数
因为TLS回调函数会在DllEntry函数之前运行,所以要先实现这个函数
//执行TLS回调函数 static BOOL ExecuteTLS(PMEMORYMODULE Module) { unsigned char *CodeBase = Module->pCodeBase; PIMAGE_TLS_DIRECTORY TLSDirectory; PIMAGE_TLS_CALLBACK* CallBack; PIMAGE_DATA_DIRECTORY Directory = GET_HEADER_DICTIONARY(Module, IMAGE_DIRECTORY_ENTRY_TLS); if (Directory->VirtualAddress == 0) { return TRUE; } TLSDirectory = (PIMAGE_TLS_DIRECTORY)(CodeBase + Directory->VirtualAddress); CallBack = (PIMAGE_TLS_CALLBACK *)TLSDirectory->AddressOfCallBacks; if (CallBack) { while (*CallBack) { //当进程开始时执行 (*CallBack)((LPVOID)CodeBase, DLL_PROCESS_ATTACH, NULL); CallBack++; } } return TRUE; }
0x009 最终在函数入口处执行代码
//获得Dll函数执行入口地址 if (hModule->NtHeaders->OptionalHeader.AddressOfEntryPoint != 0) { if (hModule->bIsDLL) { //函数执行入口 DllEntryProc DllEntry = (DllEntryProc)(LPVOID)(Code + hModule->NtHeaders->OptionalHeader.AddressOfEntryPoint); BOOL bOk = DllEntry((HMODULE)Code, DLL_PROCESS_ATTACH, 0); //BOOL bOk = (*DllEntry)((HINSTANCE)Code, DLL_PROCESS_ATTACH, 0); if (!bOk) { SetLastError(ERROR_DLL_INIT_FAILED); goto error; } hModule->bInitialized = TRUE; } else { hModule->ExeEntry = (ExeEntryProc)(LPVOID)(Code + hModule->NtHeaders->OptionalHeader.AddressOfEntryPoint); } }