他人の空似自作物置場

ReadApiSetSchema.zip/main.cpp


#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <filesystem>
#include <fstream>
#include <optional>

#include <Windows.h>

#include <boost/spirit/home/support/detail/endian.hpp>
#include <boost/utility.hpp>

namespace endian = boost::spirit::endian;

class MzHeader : boost::noncopyable {
public:
   char signature[2];
   endian::ulittle8_t unknown[58];
   endian::ulittle32_t peHeaderAddress;
   bool IsValid() const {
      return std::equal(signature, &signature[_countof(signature)], "MZ");
   }
};

class PeHeader : boost::noncopyable {
public:
   char signature[4];
   endian::ulittle8_t unknown1[2];
   endian::ulittle16_t sectionCount;
   endian::ulittle8_t unknown2[4];
   endian::ulittle32_t symbolTableAddress;
   endian::ulittle32_t symbolTableCount;
   endian::ulittle16_t optionalHeaderSize;
   endian::ulittle16_t attributes;
   bool IsValid() const {
      if (!std::equal(signature, &signature[_countof(signature)], "PE\0\0")) {
         return false;
      }
      return true;
   }
};

class SectionHeader : boost::noncopyable {
public:
   char name[8];
   endian::ulittle32_t virtualSize;
   endian::ulittle32_t virtualAddress;
   endian::ulittle32_t size;
   endian::ulittle32_t address;
   endian::ulittle32_t relocateAddress;
   endian::ulittle8_t unknown[8];
   endian::ulittle32_t flags;
   enum FLAGS {
      FLAGS_WRITE = 0x80000000, FLAGS_READ = 0x40000000, FLAGS_EXECUTE = 0x20000000
   };
};

bool TryExistsSystemDir(const std::filesystem::path &fileName, std::filesystem::path &result) {
   wchar_t dirPath[MAX_PATH];
   if (::GetSystemDirectoryW(dirPath, _countof(dirPath)) == 0) {
      return false;
   }
   result = dirPath;
   return std::filesystem::exists(result / fileName);
}

bool TryExistsSysnativeDir(const std::filesystem::path &fileName, std::filesystem::path &result) {
   wchar_t dirPath[MAX_PATH];
   if (::GetWindowsDirectoryW(dirPath, _countof(dirPath)) == 0) {
      return false;
   }
   result = std::filesystem::path(dirPath) / L"Sysnative";
   return std::filesystem::exists(result / fileName);
}

const SectionHeader *GetSectionHeader(const std::vector<unsigned char> &data, const std::string &name) {
   const unsigned char * const baseAddr = &data.front();
   const MzHeader &mz = *reinterpret_cast<const MzHeader *>(baseAddr);
   const PeHeader &pe = *reinterpret_cast<const PeHeader *>(baseAddr + mz.peHeaderAddress);
   const SectionHeader *sectionList = reinterpret_cast<const SectionHeader *>(baseAddr + mz.peHeaderAddress + sizeof(PeHeader) + pe.optionalHeaderSize);
   for (unsigned int i = 0; i < pe.sectionCount; i++) {
      if (std::equal(name.begin(), name.end(), sectionList[i].name)) {
         return &sectionList[i];
      }
   }
   return NULL;
}

class ApiSetSchemaBase : boost::noncopyable {
public:
   virtual std::optional<std::map<std::wstring, std::wstring> > main(const SectionHeader * const sectionHeader, const unsigned char * const baseAddr) = 0;

   std::optional<std::map<std::wstring, std::wstring> > Read(const std::filesystem::path &path) {
      std::ifstream ifs(path, std::ios::binary);
      if (!ifs.is_open()) {
         return {};
      }
      const unsigned int size = static_cast<unsigned int>(std::filesystem::file_size(path));
      if (size == 0) {
         return {};
      }
      std::vector<unsigned char> data(size);
      ifs.read(reinterpret_cast<char *>(&data.front()), data.size());
      if (!ifs.good()) {
         return {};
      }
      ifs.close();
      const SectionHeader * const sectionHeader = GetSectionHeader(data, ".apiset");
      if (sectionHeader == NULL || sectionHeader->address > data.size() ||sectionHeader->size > data.size() - sectionHeader->address) {
         return {};
      }
      const unsigned char * const baseAddr = &data.front() + sectionHeader->address;
      return main(sectionHeader, baseAddr);
   }

   static std::optional<std::map<std::wstring, std::wstring> > ReadAll(const std::filesystem::path &path);
};

class ApiSetSchema7 : public ApiSetSchemaBase {
private:
   class Header : boost::noncopyable {
   public:
      endian::ulittle32_t schemaVersion;
      endian::ulittle32_t count;
   };
   class ForwardHeader : boost::noncopyable {
   public:
      endian::ulittle32_t dllNameOffset;
      endian::ulittle32_t dllNameLength;
      endian::ulittle32_t forwardInfoOffset;
   };
   class ForwardInfo : boost::noncopyable {
   public:
      endian::ulittle32_t type;
      endian::ulittle32_t destDllNameOffset2;
      endian::ulittle32_t unknown;
      endian::ulittle32_t destDllNameOffset;
      endian::ulittle32_t destDllNameLength;
   };

protected:
   virtual std::optional<std::map<std::wstring, std::wstring> > main(const SectionHeader * const sectionHeader, const unsigned char * const baseAddr) {
      if (sectionHeader->size < sizeof(Header)) {
         return {};
      }
      const Header &header = *reinterpret_cast<const Header *>(baseAddr);
      if (header.schemaVersion != 2 || header.count * sizeof(ForwardHeader) > sectionHeader->size) {
         return {};
      }
      const ForwardHeader * const forwordheaderList = reinterpret_cast<const ForwardHeader *>(baseAddr + sizeof(header));
      std::map<unsigned int, std::wstring> nameList;
      std::list<std::pair<std::wstring, unsigned int> > dest2List;
      std::map<std::wstring, std::wstring> result;
      for (unsigned int i = 0; i < header.count; i++) {
         const ForwardHeader &forward = forwordheaderList[i];
         if (forward.dllNameOffset > sectionHeader->size || forward.dllNameLength > sectionHeader->size - forward.dllNameOffset ||
            forward.forwardInfoOffset > sectionHeader->size || sizeof(ForwardInfo) > sectionHeader->size - forward.forwardInfoOffset)
         {
            return {};
         }
         const std::wstring dllName = L"Api-" + std::wstring(reinterpret_cast<const wchar_t *>(baseAddr + forward.dllNameOffset), forward.dllNameLength / sizeof(wchar_t));
         const ForwardInfo &info = *reinterpret_cast<const ForwardInfo *>(baseAddr + forward.forwardInfoOffset);
         if (info.destDllNameOffset > sectionHeader->size || info.destDllNameLength > sectionHeader->size - info.destDllNameOffset) {
            return {};
         }
         nameList.insert(std::make_pair(forward.dllNameOffset, dllName));
         if (info.type == 1) {
            const std::wstring destDllName(reinterpret_cast<const wchar_t *>(baseAddr + info.destDllNameOffset), info.destDllNameLength / sizeof(wchar_t));
            result.insert(std::make_pair(dllName, destDllName));
         } else if (info.type == 2) {
            dest2List.push_back(std::make_pair(dllName, info.destDllNameOffset2));
         } else {
            return {};
         }
      }
      while (!dest2List.empty()) {
         std::pair<std::wstring, unsigned int> dest2 = dest2List.front();
         dest2List.pop_front();
         const std::map<unsigned int, std::wstring>::const_iterator it = nameList.find(dest2.second);
         if (it == nameList.end()) {
            return {};
         }
         const std::map<std::wstring, std::wstring>::const_iterator dest = result.find(it->second);
         if (dest == result.end()) {
            dest2List.push_back(dest2);
            continue;
         }
         result.insert(std::make_pair(dest2.first, dest->second));
      }
      return result;
   }
};

class ApiSetSchema81 : public ApiSetSchemaBase {
private:
   class Header : boost::noncopyable {
   public:
      endian::ulittle32_t schemaVersion;
      endian::ulittle64_t size;
      endian::ulittle32_t count;
   };
   class ForwardHeader : boost::noncopyable {
   public:
      endian::ulittle32_t type; // 3:ext- 1:api-
      endian::ulittle32_t dllNameOffset1;
      endian::ulittle32_t dllNameLength;
      endian::ulittle32_t dllNameOffset2;
      endian::ulittle32_t unknown; // 必ずdllNameLength-8の値
      endian::ulittle32_t forwardInfoOffset;
   };
   class ForwardInfo : boost::noncopyable {
   public:
      endian::ulittle32_t unknown1; // 常に0
      endian::ulittle32_t type; // 大体1、たまに2
      endian::ulittle32_t unknown2; // 常に0
      endian::ulittle32_t unknown3; // 常に0
      endian::ulittle32_t unknown4; // 常に0
      endian::ulittle32_t destDllNameOffset;
      endian::ulittle32_t destDllNameLength;
   };

protected:
   virtual std::optional<std::map<std::wstring, std::wstring> > main(const SectionHeader * const sectionHeader, const unsigned char * const baseAddr) {
      if (sectionHeader->size < sizeof(Header)) {
         return {};
      }
      const Header &header = *reinterpret_cast<const Header *>(baseAddr);
      if (header.schemaVersion != 4 || header.count * sizeof(ForwardHeader) > sectionHeader->size || header.size != sectionHeader->virtualSize) {
         return {};
      }
      const ForwardHeader * const forwordheaderList = reinterpret_cast<const ForwardHeader *>(baseAddr + sizeof(header));
      std::map<unsigned int, std::wstring> nameList;
      std::list<std::pair<std::wstring, unsigned int> > dest2List;
      std::map<std::wstring, std::wstring> result;
      for (unsigned int i = 0; i < header.count; i++) {
         const ForwardHeader &forward = forwordheaderList[i];
         if (forward.dllNameOffset1 > sectionHeader->size || forward.dllNameOffset2 > sectionHeader->size ||
            forward.dllNameLength > sectionHeader->size - forward.dllNameOffset1 || forward.dllNameLength > sectionHeader->size - forward.dllNameOffset2 ||
            forward.forwardInfoOffset > sectionHeader->size || sizeof(ForwardInfo) > sectionHeader->size - forward.forwardInfoOffset)
         {
            return {};
         }
         const std::wstring dllNameRaw(reinterpret_cast<const wchar_t *>(baseAddr + forward.dllNameOffset1), forward.dllNameLength / sizeof(wchar_t));
         const std::wstring dllName = (forward.type == 1 ? L"api-" : L"ext-") + dllNameRaw + L".dll";
         const ForwardInfo &info = *reinterpret_cast<const ForwardInfo *>(baseAddr + forward.forwardInfoOffset);
         if (info.destDllNameOffset > sectionHeader->size || info.destDllNameLength > sectionHeader->size - info.destDllNameOffset) {
            return {};
         }
         const std::wstring destDllName = (info.destDllNameOffset == 0) ?
               L"Null(Definition only" :
               std::wstring(reinterpret_cast<const wchar_t *>(baseAddr + info.destDllNameOffset), info.destDllNameLength / sizeof(wchar_t));
         result.insert(std::make_pair(dllName, destDllName));
      }
      return result;
   }
};

class ApiSetSchema10 : public ApiSetSchema81 {
private:
   class Header : boost::noncopyable {
   public:
      endian::ulittle32_t schemaVersion;
      endian::ulittle64_t size;
      endian::ulittle32_t count;
      endian::ulittle32_t unknown1;
      endian::ulittle32_t unknownOffset;
      endian::ulittle32_t unknown2;
   };
   class ForwardHeader : boost::noncopyable {
   public:
      endian::ulittle32_t type; // 大体1、たまに0
      endian::ulittle32_t dllNameOffset;
      endian::ulittle32_t dllNameLength;
      endian::ulittle32_t unknown1; // 必ずdllNameLength-4の値
      endian::ulittle32_t forwardInfoOffset;
      endian::ulittle32_t unknown2; // 大体1、たまに2
   };
   class ForwardInfo : boost::noncopyable {
   public:
      endian::ulittle32_t unknown1; // ほぼ0、たまに何か入ってる
      endian::ulittle32_t unknown2; // 常に0
      endian::ulittle32_t unknown3; // 常に0
      endian::ulittle32_t destDllNameOffset;
      endian::ulittle32_t destDllNameLength;
   };

protected:
   virtual std::optional<std::map<std::wstring, std::wstring> > main(const SectionHeader * const sectionHeader, const unsigned char * const baseAddr) {
      if (sectionHeader->size < sizeof(Header)) {
         return {};
      }
      const Header &header = *reinterpret_cast<const Header *>(baseAddr);
      if (header.schemaVersion != 6 || header.count * sizeof(ForwardHeader) > sectionHeader->size || header.size != sectionHeader->virtualSize) {
         return {};
      }
      const ForwardHeader * const forwordheaderList = reinterpret_cast<const ForwardHeader *>(baseAddr + sizeof(header));
      std::map<unsigned int, std::wstring> nameList;
      std::list<std::pair<std::wstring, unsigned int> > dest2List;
      std::map<std::wstring, std::wstring> result;
      for (unsigned int i = 0; i < header.count; i++) {
         const ForwardHeader &forward = forwordheaderList[i];
         if (forward.dllNameOffset > sectionHeader->size || forward.dllNameLength > sectionHeader->size - forward.dllNameOffset ||
            forward.forwardInfoOffset > sectionHeader->size || sizeof(ForwardInfo) > sectionHeader->size - forward.forwardInfoOffset)
         {
            return {};
         }
         const std::wstring dllNameRaw(reinterpret_cast<const wchar_t *>(baseAddr + forward.dllNameOffset), forward.dllNameLength / sizeof(wchar_t));
         const std::wstring dllName = dllNameRaw + L".dll";
         const ForwardInfo &info = *reinterpret_cast<const ForwardInfo *>(baseAddr + forward.forwardInfoOffset);
         if (info.destDllNameOffset > sectionHeader->size || info.destDllNameLength > sectionHeader->size - info.destDllNameOffset) {
            return {};
         }
         const std::wstring destDllName = (info.destDllNameOffset == 0) ?
               L"Null(Definition only" :
               std::wstring(reinterpret_cast<const wchar_t *>(baseAddr + info.destDllNameOffset), info.destDllNameLength / sizeof(wchar_t));
         result.insert(std::make_pair(dllName, destDllName));
      }
      return result;
   }
};

std::optional<std::map<std::wstring, std::wstring> > ApiSetSchemaBase::ReadAll(const std::filesystem::path &path) {
   auto result = ApiSetSchema7().Read(path);
   if (result) {
      return result;
   }
   result = ApiSetSchema81().Read(path);
   if (result) {
      return result;
   }
   result = ApiSetSchema10().Read(path);
   if (result) {
      return result;
   }
   return {};
}

std::filesystem::path getApiSetSchemaPath() {
   const std::filesystem::path fileName = L"apisetschema.dll";
   std::filesystem::path dir;
   if (!TryExistsSystemDir(fileName, dir)) {
      if (!TryExistsSysnativeDir(fileName, dir)) {
         return {};
      }
   }
   return dir / fileName;
}


bool run(const std::filesystem::path &path = getApiSetSchemaPath()) {
   const std::optional<std::map<std::wstring, std::wstring> > list = ApiSetSchemaBase::ReadAll(path);
   if (!list) {
      return false;
   }
   for (const auto &pair : *list) {
      std::wcout << pair.first << " => " << pair.second << std::endl;
   }
   return true;
}

int main(const unsigned int argc, const char * const * argv) {
   const bool result = (argc == 2) ? run(argv[1]) : run();
   std::cin.get();
   return result ? 0 : 1;
}