i wrote this to dissect the file apisetschema.dll some time back found in windows 10 system32 folder
this code is based on Geoff Chappells studies on ApiSetSchema
code in apisetres.cpp
#include "apisetdefs.h"
int main(void)
{
FILE *infile = NULL;
errno_t err = fopen_s(&infile, "c:\\windows\\system32\\apisetschema.dll", "rb");
if (err == 0 && infile != NULL)
{
fseek(infile, 0, SEEK_SET);
size_t siz = fread_s(peheadbuf, BUSIZ, 1, 0x400, infile);
if (siz == 0x400)
{
PIMAGE_DOS_HEADER dhead = (PIMAGE_DOS_HEADER)&peheadbuf;
PIMAGE_NT_HEADERS64 nthead = (PIMAGE_NT_HEADERS64)(peheadbuf + dhead->e_lfanew);
PIMAGE_SECTION_HEADER Section = IMAGE_FIRST_SECTION(nthead);
for (WORD i = 0; i < nthead->FileHeader.NumberOfSections; i++)
{
if ((memcmp(Section->Name, ".apiset", 8)) == 0)
{
fseek(infile, Section->PointerToRawData, SEEK_SET);
siz = fread_s(peheadbuf, BUSIZ, 1, sizeof(APISET_SCHEMA_HEADER_V6), infile);
if (siz == sizeof(APISET_SCHEMA_HEADER_V6))
{
apisethead = (PAPISET_SCHEMA_HEADER_V6)&peheadbuf;
printf("Version Number = %x\n", apisethead->VersionNumber);
printf("MapSize = %x\n", apisethead->Mapsize);
printf("isSealed = %x\n", apisethead->isSealed);
printf("Number of Apisets = %x\n", apisethead->NumAPISets);
printf("Offset to NameSpace Entries = %x\n", apisethead->OffsetNameSpaceEntries);
printf("Offset To hash Entries = %x\n", apisethead->OffsetHashEntries);
printf("Hash Multiplier = %x\n", apisethead->HashMultiplier);
}
DWORD rawdataaddr = Section->PointerToRawData;
DWORD mapoff = rawdataaddr + apisethead->OffsetNameSpaceEntries;
for (unsigned int j = 0; j < apisethead->NumAPISets; j++)
{
DWORD apisetname = (mapoff + (j * sizeof(API_SET_NAMESPACE_ENTRY)));
printf("%x\t", apisetname);
fseek(infile, apisetname, SEEK_SET);
siz = fread_s(apisetnsebuf, BUSIZ, 1, sizeof(API_SET_NAMESPACE_ENTRY), infile);
apinsentry = (PAPI_SET_NAMESPACE_ENTRY)(apisetnsebuf);
printf("%x\t", apinsentry->OffsetApiSetName);
DWORD apivdllname = ((rawdataaddr) + (apinsentry->OffsetApiSetName));
fseek(infile, apivdllname, SEEK_SET);
memset(wbuf,0,sizeof(wbuf));
siz = fread_s(wbuf, WBUSIZ , 1, apinsentry->SizeApiSetName, infile);
printf("%-60.*S\t", (apinsentry->SizeApiSetName), wbuf);
printf("%x\t", apinsentry->OffsetValueEntries);
DWORD apildllname = ((rawdataaddr) + (apinsentry->OffsetValueEntries) );
printf("%x\t", apildllname);
fseek(infile, apildllname, SEEK_SET);
siz = fread_s(apisetvalentbuf, BUSIZ, 1, sizeof(API_SET_VALUE_ENTRY), infile);
apivalentry = (PAPI_SET_VALUE_ENTRY)(apisetvalentbuf);
printf("%x\t" , apivalentry->ValueOffset);
DWORD apivalentname = ((rawdataaddr) + (apivalentry->ValueOffset));
fseek(infile,apivalentname,SEEK_SET);
memset(wbuf,0,sizeof(wbuf));
siz = fread_s(wbuf, WBUSIZ , 1, apivalentry->ValueLength, infile);
printf("%-60.*S\n", (apivalentry->ValueLength)/2, wbuf);
}
};
Section++;
}
}
}
}
header file apisetres.h
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define BUSIZ 0x500
#define WBUSIZ BUSIZ / 2
unsigned char peheadbuf[BUSIZ];
unsigned char apisetnsebuf[BUSIZ];
unsigned char apisetvalentbuf[BUSIZ];
wchar_t wbuf[BUSIZ];
typedef struct _APISET_SCHEMA_HEADER_V6
{
DWORD VersionNumber;
DWORD Mapsize;
DWORD isSealed;
DWORD NumAPISets;
DWORD OffsetNameSpaceEntries;
DWORD OffsetHashEntries;
DWORD HashMultiplier;
} APISET_SCHEMA_HEADER_V6, *PAPISET_SCHEMA_HEADER_V6;
typedef struct _API_SET_NAMESPACE_ENTRY
{
DWORD isSealed;
DWORD OffsetApiSetName;
DWORD SizeApiSetName;
DWORD SizeApiSetNameNoHyphen;
DWORD OffsetValueEntries;
DWORD NumHosts;
} API_SET_NAMESPACE_ENTRY, *PAPI_SET_NAMESPACE_ENTRY;
typedef struct _API_SET_VALUE_ENTRY
{
DWORD Flags;
DWORD NameOffset;
DWORD NameLen;
DWORD ValueOffset;
DWORD ValueLength;
} API_SET_VALUE_ENTRY, *PAPI_SET_VALUE_ENTRY;
PAPISET_SCHEMA_HEADER_V6 apisethead = NULL;
PAPI_SET_NAMESPACE_ENTRY apinsentry = NULL;
PAPI_SET_VALUE_ENTRY apivalentry = NULL;
compiled and linked in win 10 x64 vc 2019 community as x64 binary with
cl /Zi /W4 /analyze /Ehsc /Od apisetres.cpp /link /release
executed to print both virtual dll as well as logical dll names
:\>apisetres.exe > output.txt
:\>head -n 15 output.txt
Version Number = 6
MapSize = 1c6b4
isSealed = 0
Number of Apisets = 37a
Offset to NameSpace Entries = 1c
Offset To hash Entries = 1aae4
Hash Multiplier = 1f
61c 538c api-ms-onecoreuap-print-render-l1-1-0 53d8 59d8 53ec printrenderapihost.dll
634 5418 api-ms-win-appmodel-identity-l1-2-0 5460 5a60 5474 kernel.appcore.dll
64c 5498 api-ms-win-appmodel-runtime-internal-l1-1-7 54f0 5af0 5474 kernel.appcore.dll
664 5504 api-ms-win-appmodel-runtime-l1-1-3 5548 5b48 5474 kernel.appcore.dll
67c 555c api-ms-win-appmodel-state-l1-1-2 559c 5b9c 5474 kernel.appcore.dll
694 55b0 api-ms-win-appmodel-state-l1-2-0 55f0 5bf0 5474 kernel.appcore.dll
6ac 5604 api-ms-win-appmodel-unlock-l1-1-0 5648 5c48 5474 kernel.appcore.dll
6c4 565c api-ms-win-base-bootconfig-l1-1-0 56a0 5ca0 56b4 advapi32.dll
the hash entries ripped and pasted to a file named hashdump
sorted the hashes index wise instead of default name wise
independnatly confirmed first 4 names by hashing them using hash algo described in the study with a python script as below
#index wise sorting of hashkeys from apisetschema.dll raw data
import numpy as np
np.set_printoptions(formatter={'all':lambda x: format(x , '08X')})
a = np.fromfile('hashdump',np.dtype([('hash', '<u4'), ('index', '<u4')]))
b = sorted(a,key = lambda x: x[1])
c = list(filter(None,b))
print( c[0],c[1],c[2],c[3] )
#hashing actual virtual dll names excluding last hyphen
apn = [
"api-ms-onecoreuap-print-render-l1-1",
"api-ms-win-appmodel-identity-l1-2",
"api-ms-win-appmodel-runtime-internal-l1-1",
"api-ms-win-appmodel-runtime-l1-1"
]
for j in range(0,len(apn),1):
hashfactor = 0x1f
hashkey = 0
for i in apn[j]:
hashkey = (hashkey * hashfactor + ord(i)) & 0xffffffff
print("(%08X, %08x)" % (hashkey,j),end =' ')
actual hashes index wise from apisetschema.dll versus algorithmic hashing of names from output.txt
:\>apisethash.py
(BFEC7B66, 00000000) (1079FB19, 00000001) (59E37344, 00000002) (3655E8BE, 00000003)
(BFEC7B66, 00000000) (1079FB19, 00000001) (59E37344, 00000002) (3655E8BE, 00000003)
:\>