最近在WIN7下跑原来写的一个壳。发现自己写的GetProcAddress出错。分析了ntdll里的LdrGetProcedureAddress函数。查了一些资料发现。Win7引入了ApiSetMap的机制。它把一些系统低级别的一些API分为几个类型并且把kernel32.dll与advapi32.dll的一部分函数放到kernelbase.dll里。而归类的API名由api-ms-xxx字符串的dll库引入。这些DLL在系统上并不存在而只是一个索引类似的东西存在于引出表中。起一个重定向的问题。
在每一个进程的PEB+0x38偏移处就是ApiSetMap字段了。翻阅了一些文档,这个东西是存在于ApiSetMap.dll中的.apimap节中而此节是在WIN7启动时在启动1阶段被加载到系统空间,而0x38是这个的用户空间映射(这些我没有去验证,有时间的朋友可以自行去验证一下)。这个结构是这样的。
// ApiSetMap结构
typedef struct _API_SET_MAP_HEADER {
__dword dwVersionNumber;
__dword dwNumberOfApiSetModules;
} API_SET_MAP_HEADER, *PAPI_SET_MAP_HEADER;
typedef struct _API_SET_MAP_ENTRY {
__dword dwNameOfApiSetModuleRVA;
__dword dwSizeOfName;
__dword dwHostModulesRVA;
} API_SET_MAP_ENTRY, *PAPI_SET_MAP_ENTRY;
typedef struct _API_SET_MAP_HOST_HEADER {
__dword dwNumberOfHosts;
} API_SET_MAP_HOST_HEADER, *PAPI_SET_MAP_HOST_HEADER;
typedef struct _API_SET_MAP_HOST_ENTRY {
__dword dwNameOfImportModuleRVA;
__dword dwSizeOfImportModuleName;
__dword dwNameOfHostRVA;
__dword dwSizeOfHostName;
} API_SET_MAP_HOST_ENTRY, *PAPI_SET_MAP_HOST_ENTRY;
由两个头 两个Entry组成。只有一个ApiSetMapHeader,紧随其后的是ApiSetMapHeader.dwNumberOfApiSetModules个ApiSetMapEntry而在此结构ApiSetMapEntry.dwHostModulesRVA又是ApiSetMapHostHeader结构的相对于ApiSetMapHeader的偏移。而在ApiSetMapHostHeader下又有ApiSetMapHostHeader.dwNumberOfHosts个ApiSetMapHostEntry结构,在这个结构中存在着一些对应。现在发现最多是2个HostEntry结构,最少是1个。
而我们要找的就是ImportMoude的名字与我们原先加载DLL的名字一样下面的HostModule的名字。大多数都是kernel32.dll -> kernelbase.dll的映射。
如果只有1个HostEntry那么ImportModule名为空。而HostModule为kernel32.dll,这个应该是为advapi32.dll准备的。advapi32.dll的一部分函数被重定向到kernel32.dll里了。
我语言组织的不太好。下面直接给出代码,一看就明白了。
// 获取ApiSetMap
PAPI_SET_MAP_HEADER __API__ GetApiSetMapHeader() {
PAPI_SET_MAP_HEADER pApiSetMap = NULL;
__memory pPeb = NULL;
// 检查是否是Vista以上系统
if (GetWindowsVersion() != WIN_VISTA)
return NULL;
pPeb = (__memory)__NtCurrentPeb__();
pApiSetMap = (PAPI_SET_MAP_HEADER)*((__address *)(pPeb + 0x38));
return pApiSetMap;
}
// 私有函数这里声明一下,底下函数有要用
FARPROC __INTERNAL_FUNC__ xLdrGetExportByName(__memory pBaseAddress, __memory pHashPoint, __integer iHashSize, FPHashFunc pHashFunc, FARPROC fpLoadLibraryA);
FARPROC __INTERNAL_FUNC__ xLdrGetExportByOrdinal(__memory pBaseAddress, __word wOrdinal, FPHashFunc pHashFunc, FARPROC fpLoadLibraryA);
__INLINE__ FARPROC __INTERNAL_FUNC__ xLdrFixupForward(__memory pBaseAddress, __memory pForwardName, __word wOrdinal, FPHashFunc pHashFunc, FARPROC fpLoadLibraryA) {
__char NameBuffer[128] = {0};
__wchar NameOfLib[128] = {0};
__memory pPoint = NULL;
__memory hModule = NULL;
__byte HashValue[1024] = {0};
__integer iHashValueSize = 0;
FARPROC pFunction = NULL;
// 获取加载动态库的地址
FPLoadLibraryA pLoadLibraryA = (FPLoadLibraryA)fpLoadLibraryA;
// 取出DLL与引出的函数名
__logic_strcpy__(NameBuffer, pForwardName);
pPoint = __logic_strchr__(NameBuffer, '.');
if (pPoint) {
__char *pProcName = NULL;
__integer iProcNameSize = 0;
*pPoint = 0;//使用0字符将DLL与函数名隔开,替代'.'
// 检查是否是ApiSetMap的函数
__logic_str2lower__(NameBuffer);//转换为小写
if (__logic_strncmp__(NameBuffer, "api-", 4) == 0) {
// 寻找API SET MAP
PAPI_SET_MAP_HEADER pApiSetMapHeader = NULL;
PAPI_SET_MAP_ENTRY pApiSetMapEntry = NULL;
__dword dwNumberOfApiSetMapEntry = 0;
__dword dwNumberOfHostEntry = 0;
__wchar *pApiSetMapEntryName = NULL;
__integer iSizeOfApiSetMapEntryName = 0;
__wchar *pHostMapName = NULL;
__integer iSizeOfHostMapName = 0;
__wchar *pImportMapName = NULL;
__integer iSizeOfImportMapName = 0;
PAPI_SET_MAP_HOST_HEADER pApiSetMapHostHeader = NULL;
PAPI_SET_MAP_HOST_ENTRY pApiSetMapHostEntry = NULL;
__integer i = 0, j = 0;
__char *pNameBuffer = NULL;
// 获取要匹配的库名
pNameBuffer = (__char *)(NameBuffer + 4);
pApiSetMapHeader = GetApiSetMapHeader();
if (!pApiSetMapHeader) return NULL;
pApiSetMapEntry = (PAPI_SET_MAP_ENTRY)((__memory)pApiSetMapHeader + sizeof(API_SET_MAP_HEADER));
dwNumberOfApiSetMapEntry = pApiSetMapHeader->dwNumberOfApiSetModules;
for (i = 0; i < dwNumberOfApiSetMapEntry; i++, pApiSetMapEntry++) {
__char AnsiNameOfLib[128] = {0};
pApiSetMapEntryName = (__wchar *)((__memory)pApiSetMapHeader + pApiSetMapEntry->dwNameOfApiSetModuleRVA);
iSizeOfApiSetMapEntryName = pApiSetMapEntry->dwSizeOfName;
// 转换成小写
__logic_memset__(NameOfLib, 0, sizeof(__wchar) * 128);
__logic_memset__(AnsiNameOfLib, 0, 128);
__logic_tcsncpy__(NameOfLib, pApiSetMapEntryName, iSizeOfApiSetMapEntryName);
__logic_tcs2lower_n_(NameOfLib, iSizeOfApiSetMapEntryName / sizeof(__tchar));
__logic_tcs2str_n__(NameOfLib, AnsiNameOfLib, iSizeOfApiSetMapEntryName / sizeof(__tchar));
// 对比是否是要查找的Lib
if (__logic_strcmp__(AnsiNameOfLib, pNameBuffer) == 0) {
__integer iCount = 0;
PLDR_MODULE pLdrImportModule = NULL;
__char szImportModuleName[128] = {0};
PUNICODE_STRING pCurrImportName = NULL;
pApiSetMapHostHeader = (PAPI_SET_MAP_HOST_HEADER)((__memory)pApiSetMapHeader + pApiSetMapEntry->dwHostModulesRVA);
dwNumberOfHostEntry = pApiSetMapHostHeader->dwNumberOfHosts;
pApiSetMapHostEntry = (PAPI_SET_MAP_HOST_ENTRY)((__memory)pApiSetMapHostHeader + sizeof(API_SET_MAP_HOST_HEADER));
// 找到当前模块的引入模块
pLdrImportModule = GetExistModuleInLoadModuleList(pBaseAddress);
pCurrImportName = &(pLdrImportModule->BaseDllName);
// 如果dwNumberOfHostEntry == 1
if (dwNumberOfHostEntry == 1) {
// Host
pHostMapName = (__wchar *)((__memory)pApiSetMapHeader + pApiSetMapHostEntry->dwNameOfHostRVA);
iSizeOfHostMapName = pApiSetMapHostEntry->dwSizeOfHostName;
iCount = iSizeOfHostMapName / sizeof(__tchar);
// 转换
__logic_tcs2str_n__(pHostMapName, NameBuffer, iCount);
NameBuffer[iCount] = '\0';
// 跳转到
goto _load_library_get_address;
} else {
// 遍历Host映射
for (j = 0; j < dwNumberOfHostEntry; j++, pApiSetMapHostEntry++) {
__wchar ImportModule[128] = {0};
__wchar ImportModule2[128] = {0};
// Import
pImportMapName = (__wchar *)((__memory)pApiSetMapHeader + pApiSetMapHostEntry->dwNameOfImportModuleRVA);
iSizeOfImportMapName = pApiSetMapHostEntry->dwSizeOfImportModuleName;
iCount = iSizeOfImportMapName / sizeof(__tchar);
// 如果引入模块名长度为0
if (iSizeOfImportMapName == 0)
continue;
// Host
pHostMapName = (__wchar *)((__memory)pApiSetMapHeader + pApiSetMapHostEntry->dwNameOfHostRVA);
iSizeOfHostMapName = pApiSetMapHostEntry->dwSizeOfHostName;
__logic_tcsncpy__(ImportModule, pImportMapName, iCount);
__logic_tcs2lower_n_(ImportModule, iCount);
ImportModule[iCount] = _T('\0');
__logic_tcsncpy__(ImportModule2, pCurrImportName->Buffer, iCount);
__logic_tcs2lower_n_(ImportModule2, iCount);
ImportModule2[iCount] = _T('\0');
// 比对Import模块的名字, 如果相等则获取
if (__logic_tcsncmp__(pImportMapName, ImportModule2, iCount) == 0) {
iCount = iSizeOfHostMapName / sizeof(__tchar);
__logic_tcs2str_n__(pHostMapName, NameBuffer, iCount);
NameBuffer[iCount] = '\0';
goto _load_library_get_address;
}/* end if */
}/* end for */
}/* end else */
}/* end if */
}/* end for */
} else {
_load_library_get_address:
// 加载动态库
hModule = (__memory)pLoadLibraryA(NameBuffer);
if (!hModule) return NULL;
// 产生哈希值
pProcName = (__char *)(pPoint + 1);
iProcNameSize = __logic_strlen__(pPoint + 1);
iHashValueSize = pHashFunc((__memory)pProcName, iProcNameSize, HashValue);
return xLdrGetExportByName((__memory)hModule, (__memory)HashValue, iHashValueSize, pHashFunc, fpLoadLibraryA);
}/* end else */
}/* end if */
return NULL;
}
FARPROC __INTERNAL_FUNC__ xLdrGetExportByOrdinal(__memory pBaseAddress, __word wOrdinal, FPHashFunc pHashFunc, FARPROC fpLoadLibraryA) {
PIMAGE_EXPORT_DIRECTORY pExportDir;
__integer iExportDirSize;
__dword **pExFunctionsPoint;
FARPROC pFunction;
PIMAGE_DATA_DIRECTORY pExportDataDirectory = ExistDataDirectory(pBaseAddress, IMAGE_DIRECTORY_ENTRY_EXPORT);
pExportDir = (PIMAGE_EXPORT_DIRECTORY)(pBaseAddress + pExportDataDirectory->VirtualAddress);
if (!pExportDir)
return NULL;
iExportDirSize = pExportDataDirectory->Size;
pExFunctionsPoint = (__dword **)__RvaToVa__(pBaseAddress, pExportDir->AddressOfFunctions);
pFunction = (FARPROC)(0 != pExFunctionsPoint[wOrdinal - pExportDir->Base]
? __RvaToVa__(pBaseAddress, pExFunctionsPoint[wOrdinal - pExportDir->Base])
: NULL);
if (((__address)pFunction >= (__address)pExportDir) &&
((__address)pFunction < (__address)pExportDir + (__address)iExportDirSize))
pFunction = xLdrFixupForward(pBaseAddress, (__memory)pFunction, wOrdinal, pHashFunc, fpLoadLibraryA);
return pFunction;
}
FARPROC __INTERNAL_FUNC__ xLdrGetExportByName(__memory pBaseAddress, __memory pHashPoint, __integer iHashSize, FPHashFunc pHashFunc, FARPROC fpLoadLibraryA) {
__word wOrdinal = 0;
__integer iDirCount = 0;
__address *pAddrTable = NULL;
__address addrAddr = 0;
__offset ofRVA = 0;
__integer iExpDataSize = 0;
__integer i = 0;
PIMAGE_EXPORT_DIRECTORY pEd = NULL;
PIMAGE_NT_HEADERS pNt = NULL;
PIMAGE_DATA_DIRECTORY pExportDataDirectory = NULL;
if (pBaseAddress == NULL) return NULL;
pNt = GetNtHeader(pBaseAddress);
iDirCount = pNt->OptionalHeader.NumberOfRvaAndSizes;
if (iDirCount < IMAGE_NUMBEROF_DIRECTORY_ENTRIES) return FALSE;
pExportDataDirectory = ExistDataDirectory(pBaseAddress, IMAGE_DIRECTORY_ENTRY_EXPORT);
if(!pExportDataDirectory)
return NULL;//确定引出表
iExpDataSize = pExportDataDirectory->Size;
// 从引出表获取函数地址
pEd = (PIMAGE_EXPORT_DIRECTORY)__RvaToVa__(pBaseAddress, pExportDataDirectory->VirtualAddress);
/*
* 获取序数引出的函数
*/
if (HIWORD((__dword)pHashPoint)==0) {//以序号引出
wOrdinal = (__word)(LOWORD((__dword)pHashPoint)) - pEd->Base;
} else {
__integer iCount = 0;
__dword *pdwNamePtr = NULL;
__word *pwOrdinalPtr = NULL;
iCount = (__integer)(pEd->NumberOfNames);
pdwNamePtr = (__dword *)__RvaToVa__(pBaseAddress, pEd->AddressOfNames);
pwOrdinalPtr = (__word *)__RvaToVa__(pBaseAddress, pEd->AddressOfNameOrdinals);
for(i = 0; i < iCount; i++) {
__byte HashValue[1024];
__char *svName = NULL;
__integer iHashValueSize = 0;
svName = (__char *)__RvaToVa__(pBaseAddress, *pdwNamePtr);
iHashValueSize = pHashFunc(svName, __logic_strlen__(svName), HashValue);//进行哈希计算
if (iHashValueSize == iHashSize) {
if (__logic_memcmp__(HashValue, pHashPoint, iHashSize) == 0) {
wOrdinal = *pwOrdinalPtr;
break;
}
}
pdwNamePtr++;
pwOrdinalPtr++;
}
if (i == iCount) return NULL;
}
pAddrTable=(__address *)__RvaToVa__(pBaseAddress, pEd->AddressOfFunctions);
ofRVA = pAddrTable[wOrdinal];
addrAddr = (__address)__RvaToVa__(pBaseAddress, ofRVA);
/*
* 最终判断是否是中间跳转
*/
if (((__address)addrAddr >= (__address)pEd) &&
((__address)addrAddr < (__address)pEd + (__address)iExpDataSize))
return xLdrFixupForward(pBaseAddress, (__memory)addrAddr, wOrdinal, pHashFunc, fpLoadLibraryA);
return (FARPROC)addrAddr;
}
__INLINE__ FARPROC __INTERNAL_FUNC__ xLdrGetProcedureAddress(__memory pBaseAddress, __memory pHashPoint, __integer iHashSize, FPHashFunc pHashFunc, FARPROC fpLoadLibraryA) {
FARPROC pProcedureAddress = NULL;
__dword dwOrdinal = (__dword)pHashPoint;
if (HIWORD((__dword)pHashPoint)) {
// 通过名字引出
pProcedureAddress = xLdrGetExportByName(pBaseAddress, pHashPoint, iHashSize, pHashFunc, fpLoadLibraryA);
} else {
// 通过序数
dwOrdinal &= 0x0000FFFF;
pProcedureAddress = xLdrGetExportByOrdinal(pBaseAddress, (__word)dwOrdinal, pHashFunc, fpLoadLibraryA);
}
return pProcedureAddress;
}
我逆向的是win7下ntdll中的位于0x77F1E681 -> 0x77F1E6F1 -> 0x77F22193 这三个函数就是主要ntdll实现对ApiSetMap的解析了。
一个邪恶的想法是是否可以在内核里直接劫持这片空间。把他们的映射关系修改我们自己的DLL。谁有时间研究一下可行性。
[课程]Android-CTF解题方法汇总!