• 跟着病毒学技术--学习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);
                  }
           }
  • 相关阅读:
    PostgreSQL数据库管理:定期vacuum
    关于压力测试的思路
    作业自动提示功能设计思路
    This system is not registered with RHN
    读《高性能建站指南》(上)
    Ubuntu下PostgreSQL数据库集群(PL/Proxy)配置方法
    PG SQL数据库读写分离的思路
    ASP.NET知识点的明晰(非原创,东拼西凑的,向这些内容的原创者致敬)
    [转]Effective C#原则4:用条件属性而不是#if
    如何在Word文档中插入一条分隔线?
  • 原文地址:https://www.cnblogs.com/Toring/p/6886661.html
Copyright © 2020-2023  润新知