跟着病毒学技术--学习WannaCry自己实现LoadLirbrary

最近逆了一下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);
              }
       }
原文地址:https://www.cnblogs.com/Toring/p/6886661.html