将已经注入到目标进程的DLL加载到内存,实现LoadLibrary的功能。
OS:Windows 10 PRO 1709
IDE:Visual Studio 2015 Community
语言:Visual C++
由于直接编写DLL,直接进行反射加载,无法用VS进行调试,所以我之前新建了一个可执行的项目,在该项目中,实现了加载的功能,后续只需将函数导出,和变换下加载的DLL即可。
//caller功能:获取当前指令的下一条指令的地址。
uiLibraryAddress = caller();
while (TRUE)
{
if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE)
{
//pe头偏移RVA
uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
//判断PE头的正确性
if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024)
{
//pe头在内存中的位置
uiHeaderValue += uiLibraryAddress;
//如果找到文件头就退出循环
if (((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE)
break;
}
}
uiLibraryAddress--;
}
我在调试的可执行的Demo中,更改的。
//callAddress:在缓冲区开辟空间的起始地址,在原注入中uiLibraryAddress = caller();0x10偏移是为了模拟寻找起始地址的过程
uiLibraryAddress = callAddress + 0x10;
//caller功能:获取当前指令的下一条指令的地址。
uiLibraryAddress = caller();
while (TRUE)
{
if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE)
{
//pe头偏移RVA
uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
//判断PE头的正确性
if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024)
{
//pe头在内存中的位置
uiHeaderValue += uiLibraryAddress;
//如果找到文件头就退出循环
if (((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE)
break;
}
}
uiLibraryAddress--;
}
我在调试的可执行的Demo中,更改的。
//callAddress:在缓冲区开辟空间的起始地址,在原注入中uiLibraryAddress = caller();0x10偏移是为了模拟寻找起始地址的过程
uiLibraryAddress = callAddress + 0x10;
//callAddress:在缓冲区开辟空间的起始地址,在原注入中uiLibraryAddress = caller();0x10偏移是为了模拟寻找起始地址的过程
uiLibraryAddress = callAddress + 0x10; 模块之间的关系(来自网络,侵删)
uiBaseAddress = __readgsqword(0x60);//peb结构的地址
uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;
uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;
while (uiValueA)
{
//当前模块名地址
uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;
usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;
uiValueC = 0;
//计算模块名的hash
do
{
uiValueC = ror((DWORD)uiValueC);
// normalize to uppercase if the madule name is in lowercase
if (*((BYTE *)uiValueB) >= 'a')
uiValueC += *((BYTE *)uiValueB) - 0x20;
else
uiValueC += *((BYTE *)uiValueB);
uiValueB++;
} while (--usCounter);
//获取目标进程中的接下来需要的函数地址
if ((DWORD)uiValueC == KERNEL32DLL_HASH)
{
uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames);
uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals);
usCounter = 3;
// 找函数
while (usCounter > 0)
{
dwHashValue = hash((char *)(uiBaseAddress + DEREF_32(uiNameArray)));
if (dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH)
{
uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));
if (dwHashValue == LOADLIBRARYA_HASH)
pLoadLibraryA = (LOADLIBRARYA)(uiBaseAddress + DEREF_32(uiAddressArray));
else if (dwHashValue == GETPROCADDRESS_HASH)
pGetProcAddress = (GETPROCADDRESS)(uiBaseAddress + DEREF_32(uiAddressArray));
else if (dwHashValue == VIRTUALALLOC_HASH)
pVirtualAlloc = (VIRTUALALLOC)(uiBaseAddress + DEREF_32(uiAddressArray));
usCounter--;
}
uiNameArray += sizeof(DWORD);
uiNameOrdinals += sizeof(WORD);
}
}
}
uiBaseAddress = __readgsqword(0x60);//peb结构的地址
uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;
uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;
while (uiValueA)
{
//当前模块名地址
uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;
usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;
uiValueC = 0;
//计算模块名的hash
do
{
uiValueC = ror((DWORD)uiValueC);
// normalize to uppercase if the madule name is in lowercase
if (*((BYTE *)uiValueB) >= 'a')
uiValueC += *((BYTE *)uiValueB) - 0x20;
else
uiValueC += *((BYTE *)uiValueB);
uiValueB++;
} while (--usCounter);
//获取目标进程中的接下来需要的函数地址
if ((DWORD)uiValueC == KERNEL32DLL_HASH)
{
uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames);
uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals);
usCounter = 3;
// 找函数
while (usCounter > 0)
{
dwHashValue = hash((char *)(uiBaseAddress + DEREF_32(uiNameArray)));
if (dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH)
{
uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));
if (dwHashValue == LOADLIBRARYA_HASH)
pLoadLibraryA = (LOADLIBRARYA)(uiBaseAddress + DEREF_32(uiAddressArray));
else if (dwHashValue == GETPROCADDRESS_HASH)
pGetProcAddress = (GETPROCADDRESS)(uiBaseAddress + DEREF_32(uiAddressArray));
else if (dwHashValue == VIRTUALALLOC_HASH)
pVirtualAlloc = (VIRTUALALLOC)(uiBaseAddress + DEREF_32(uiAddressArray));
usCounter--;
}
uiNameArray += sizeof(DWORD);
uiNameOrdinals += sizeof(WORD);
}
}
}
uiBaseAddress = __readgsqword(0x60);//peb结构的地址
uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;
uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;
while (uiValueA)
{
//当前模块名地址
uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;
usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;
uiValueC = 0;
//计算模块名的hash
do
{
uiValueC = ror((DWORD)uiValueC);
// normalize to uppercase if the madule name is in lowercase
if (*((BYTE *)uiValueB) >= 'a')
uiValueC += *((BYTE *)uiValueB) - 0x20;
else
uiValueC += *((BYTE *)uiValueB);
uiValueB++;
} while (--usCounter);
//获取目标进程中的接下来需要的函数地址
if ((DWORD)uiValueC == KERNEL32DLL_HASH)
{
uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames);
uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals);
usCounter = 3;
// 找函数
while (usCounter > 0)
{
dwHashValue = hash((char *)(uiBaseAddress + DEREF_32(uiNameArray)));
if (dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH)
{
uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));
if (dwHashValue == LOADLIBRARYA_HASH)
pLoadLibraryA = (LOADLIBRARYA)(uiBaseAddress + DEREF_32(uiAddressArray));
else if (dwHashValue == GETPROCADDRESS_HASH)
pGetProcAddress = (GETPROCADDRESS)(uiBaseAddress + DEREF_32(uiAddressArray));
else if (dwHashValue == VIRTUALALLOC_HASH)
pVirtualAlloc = (VIRTUALALLOC)(uiBaseAddress + DEREF_32(uiAddressArray));
usCounter--;
}
uiNameArray += sizeof(DWORD);
uiNameOrdinals += sizeof(WORD);
}
}
}
在上面的代码中,获取函数地址的部分没有具体写,上一篇帖子中详细的说明了获取的过程,差别就是上一篇帖子中需要将RVA转化为文件偏移。代码中有一些Hash值,这种方法在shellcode中比较常见,shellcode中是为了减小空间,这里除了这个原因,我在用IDA查找信息的时候并不能从字符串中直接找到函数名,也许这也是一个原因。(如有错误,或者其他原因,欢迎指出)。
在上面的代码中,获取函数地址的部分没有具体写,上一篇帖子中详细的说明了获取的过程,差别就是上一篇帖子中需要将RVA转化为文件偏移。代码中有一些Hash值,这种方法在shellcode中比较常见,shellcode中是为了减小空间,这里除了这个原因,我在用IDA查找信息的时候并不能从字符串中直接找到函数名,也许这也是一个原因。(如有错误,或者其他原因,欢迎指出)。
uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
//分配空间,首地址即为DLL加载的基地址
uiBaseAddress = (ULONG_PTR)pVirtualAlloc(NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders;//所有头+节表的大小
uiValueB = uiLibraryAddress;//DLL的起始地址,即缓冲区的起始地址
uiValueC = uiBaseAddress;//dll将被加载的地址的起始地址
//复制头和节表的数据到新开辟的缓冲区
while (uiValueA--)
*(BYTE *)uiValueC++ = *(BYTE *)uiValueB++;
//节表的第一项
uiValueA = ((ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader);
//pe中节的数量
uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections;
while (uiValueE--)
{
//节的虚拟地址
uiValueB = (uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress);
//节的文件偏移地址
uiValueC = (uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData);
//节的大小
uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData;
//拷贝数据
while (uiValueD--)
*(BYTE *)uiValueB++ = *(BYTE *)uiValueC++;
//下一个节
uiValueA += sizeof(IMAGE_SECTION_HEADER);
}
// uiValueB :导入表地址
uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
//基地址+RVA即导入表描述符的地址VA
uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);
//链接库名字
while (((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name)
{
//使用LoadLibraryA将需要的模块加载到内存
uiLibraryAddress = (ULONG_PTR)pLoadLibraryA((LPCSTR)(uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name));
//指向INT的IMAGE_THUNK_DATA的VA
uiValueD = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk);
//要导入IAT的IMAGE_THUNK_DATA结构体
uiValueA = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk);
// 迭代函数,如果没有名,则获取序号
while (DEREF(uiValueA))
{
//在调试过程中发现都是获取的函数序号
// sanity check uiValueD as some compilers only import by FirstThunk
if (uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG)
{
uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
uiExportDir = (uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
uiAddressArray = (uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
uiAddressArray += ((IMAGE_ORDINAL(((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal) - ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->Base) * sizeof(DWORD));
DEREF(uiValueA) = (uiLibraryAddress + DEREF_32(uiAddressArray));
}
else
{
uiValueB = (uiBaseAddress + DEREF(uiValueA));
DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress((HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name);
}
uiValueA += sizeof(ULONG_PTR);
if (uiValueD)//INT
uiValueD += sizeof(ULONG_PTR);
}
uiValueC += sizeof(IMAGE_IMPORT_DESCRIPTOR);
}
//实际装载和建议装载的偏移,原重定位表中的值是以程序建议的装载地址为基址
uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;//程序建议的装载地址
uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (((PIMAGE_DATA_DIRECTORY)uiValueB)->Size)//重定位表大小
{
//重定位表的地址
uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);
while (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock)//重定位块的大小
{
//重定位内存页的起始RVA
uiValueA = (uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress);
//重定位块中的项数(整个块的大小减去结构体的大小,得到重定位项的总大小,除以每个重定位项的大小)
uiValueB = (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC);
//重定位块的第一项
uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION);
//遍历重定位项
while (uiValueB--)
{
//重定位项的高四位代表此重定位项的类型
if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64)
*(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress;
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW)
*(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress;
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH)
*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress);
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW)
*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress);
//下一个重定位项
uiValueD += sizeof(IMAGE_RELOC);
}
//下一个重定位块
uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;
}
}
uiValueA = (uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint);
// We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing.
pNtFlushInstructionCache((HANDLE)-1, NULL, 0);
((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL);//调用入口点
在这种的注入是实现中,很少从外部导入函数,且使用了目标进程的部分导入库和函数,所以在IDA的导入中没有什么有价值的信息。不过,回忆整个流程,我们会发现这种注入有特别的地方,如获取PEB,如图,双重循环获取系统函数等,而且,这种注入由于需要修复的重定位表,也会使用双重循环。
在这种的注入是实现中,很少从外部导入函数,且使用了目标进程的部分导入库和函数,所以在IDA的导入中没有什么有价值的信息。不过,回忆整个流程,我们会发现这种注入有特别的地方,如获取PEB,如图,双重循环获取系统函数等,而且,这种注入由于需要修复的重定位表,也会使用双重循环。
从内存分布看,由于都是新开辟的空间,且需要执行代码,所以权限都为RWX,如果查看内存,小的那段的开头,一定是MZ。
从内存分布看,由于都是新开辟的空间,且需要执行代码,所以权限都为RWX,如果查看内存,小的那段的开头,一定是MZ。
将小的那段内存dump出来,虽然大小和原DLL有稍微的不同,但直接拖到IDA是可以进行分析的,因为那段内存就是dll本身。
将小的那段内存dump出来,虽然大小和原DLL有稍微的不同,但直接拖到IDA是可以进行分析的,因为那段内存就是dll本身。
//caller功能:获取当前指令的下一条指令的地址。
uiLibraryAddress = caller();
while (TRUE)
{
if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE)
{
//pe头偏移RVA
uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
//判断PE头的正确性
if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024)
{
//pe头在内存中的位置
uiHeaderValue += uiLibraryAddress;
//如果找到文件头就退出循环
if (((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE)
break;
}
}
uiLibraryAddress--;
} //callAddress:在缓冲区开辟空间的起始地址,在原注入中uiLibraryAddress = caller();0x10偏移是为了模拟寻找起始地址的过程
uiLibraryAddress = callAddress + 0x10; uiBaseAddress = __readgsqword(0x60);//peb结构的地址
uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;
uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;
while (uiValueA)
{
//当前模块名地址
uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;
usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;
uiValueC = 0;
//计算模块名的hash
do
{
uiValueC = ror((DWORD)uiValueC);
// normalize to uppercase if the madule name is in lowercase
if (*((BYTE *)uiValueB) >= 'a')
uiValueC += *((BYTE *)uiValueB) - 0x20;
else
uiValueC += *((BYTE *)uiValueB);
uiValueB++;
} while (--usCounter);
//获取目标进程中的接下来需要的函数地址
if ((DWORD)uiValueC == KERNEL32DLL_HASH)
{
uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames);
uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals);
usCounter = 3;
// 找函数
while (usCounter > 0)
{
dwHashValue = hash((char *)(uiBaseAddress + DEREF_32(uiNameArray)));
if (dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH)
{
uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));
if (dwHashValue == LOADLIBRARYA_HASH)
pLoadLibraryA = (LOADLIBRARYA)(uiBaseAddress + DEREF_32(uiAddressArray));
else if (dwHashValue == GETPROCADDRESS_HASH)
pGetProcAddress = (GETPROCADDRESS)(uiBaseAddress + DEREF_32(uiAddressArray));
else if (dwHashValue == VIRTUALALLOC_HASH)
pVirtualAlloc = (VIRTUALALLOC)(uiBaseAddress + DEREF_32(uiAddressArray));
usCounter--;
}
uiNameArray += sizeof(DWORD);
uiNameOrdinals += sizeof(WORD);
}
}
} uiHeaderValue = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
//分配空间,首地址即为DLL加载的基地址
uiBaseAddress = (ULONG_PTR)pVirtualAlloc(NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders;//所有头+节表的大小
uiValueB = uiLibraryAddress;//DLL的起始地址,即缓冲区的起始地址
uiValueC = uiBaseAddress;//dll将被加载的地址的起始地址
//复制头和节表的数据到新开辟的缓冲区
while (uiValueA--)
*(BYTE *)uiValueC++ = *(BYTE *)uiValueB++; //节表的第一项
uiValueA = ((ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader);
//pe中节的数量
uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections;
while (uiValueE--)
{
//节的虚拟地址
uiValueB = (uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress);
//节的文件偏移地址
uiValueC = (uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData);
//节的大小
uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData;
//拷贝数据
while (uiValueD--)
*(BYTE *)uiValueB++ = *(BYTE *)uiValueC++;
//下一个节
uiValueA += sizeof(IMAGE_SECTION_HEADER);
} // uiValueB :导入表地址
uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
//基地址+RVA即导入表描述符的地址VA
uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);
//链接库名字
while (((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name)
{
//使用LoadLibraryA将需要的模块加载到内存
uiLibraryAddress = (ULONG_PTR)pLoadLibraryA((LPCSTR)(uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name));
//指向INT的IMAGE_THUNK_DATA的VA
uiValueD = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk);
//要导入IAT的IMAGE_THUNK_DATA结构体
uiValueA = (uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk);
// 迭代函数,如果没有名,则获取序号
while (DEREF(uiValueA))
{
//在调试过程中发现都是获取的函数序号
// sanity check uiValueD as some compilers only import by FirstThunk
if (uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG)
{
uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
uiExportDir = (uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
uiAddressArray = (uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
uiAddressArray += ((IMAGE_ORDINAL(((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal) - ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->Base) * sizeof(DWORD));
DEREF(uiValueA) = (uiLibraryAddress + DEREF_32(uiAddressArray));
}
else
{
uiValueB = (uiBaseAddress + DEREF(uiValueA));
DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress((HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name);
}
uiValueA += sizeof(ULONG_PTR);
if (uiValueD)//INT
uiValueD += sizeof(ULONG_PTR);
}
uiValueC += sizeof(IMAGE_IMPORT_DESCRIPTOR);
} //实际装载和建议装载的偏移,原重定位表中的值是以程序建议的装载地址为基址
uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;//程序建议的装载地址
uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (((PIMAGE_DATA_DIRECTORY)uiValueB)->Size)//重定位表大小
{
//重定位表的地址
uiValueC = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress);
while (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock)//重定位块的大小
{
//重定位内存页的起始RVA
uiValueA = (uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress);
//重定位块中的项数(整个块的大小减去结构体的大小,得到重定位项的总大小,除以每个重定位项的大小)
uiValueB = (((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(IMAGE_RELOC);
//重定位块的第一项
uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION);
//遍历重定位项
while (uiValueB--)
{
//重定位项的高四位代表此重定位项的类型
if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64)
*(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress;
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW)
*(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress;
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH)
*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress);
else if (((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW)
*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress);
//下一个重定位项
uiValueD += sizeof(IMAGE_RELOC);
}
//下一个重定位块
uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;
}
} uiValueA = (uiBaseAddress + ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.AddressOfEntryPoint);
// We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing.
pNtFlushInstructionCache((HANDLE)-1, NULL, 0);
((DLLMAIN)uiValueA)((HINSTANCE)uiBaseAddress, DLL_PROCESS_ATTACH, NULL);//调用入口点
OS:Windows 10 PRO 1709
IDE:Visual Studio 2015 Community
语言:Visual C++
原理:
将已经注入到目标进程的DLL加载到内存,实现LoadLibrary的功能。
步骤:
将已经注入到目标进程的DLL加载到内存,实现LoadLibrary的功能。
这里我先描述下大体的流程,后面会展开。
获取目标进程PEB,从而获取一些需要用到的函数地址,如:VirtualAlloc。
复制PE头,由于PE头的形态并没有像节一样需要展开,所以为复制。
解析PE头,并加载节,与2不一样的是,这里用的是加载,到了节这里,已经在PE头中的信息指定了RVA,所以这里要进行“加载”。
处理导入表,获取导入函数的地址。
处理重定位表,由于基址和默认的加载地址不同,所以需要修改重定位表,否则,程序内的直接寻址会出问题。
调用镜像入口点,到这里,镜像已经加载完毕。
由于直接编写DLL,直接进行反射加载,无法用VS进行调试,所以我之前新建了一个可执行的项目,在该项目中,实现了加载的功能,后续只需将函数导出,和变换下加载的DLL即可。
这里我先描述下大体的流程,后面会展开。
获取目标进程PEB,从而获取一些需要用到的函数地址,如:VirtualAlloc。
复制PE头,由于PE头的形态并没有像节一样需要展开,所以为复制。
解析PE头,并加载节,与2不一样的是,这里用的是加载,到了节这里,已经在PE头中的信息指定了RVA,所以这里要进行“加载”。
处理导入表,获取导入函数的地址。
处理重定位表,由于基址和默认的加载地址不同,所以需要修改重定位表,否则,程序内的直接寻址会出问题。
调用镜像入口点,到这里,镜像已经加载完毕。
由于直接编写DLL,直接进行反射加载,无法用VS进行调试,所以我之前新建了一个可执行的项目,在该项目中,实现了加载的功能,后续只需将函数导出,和变换下加载的DLL即可。
详细步骤:
//caller功能:获取当前指令的下一条指令的地址。
uiLibraryAddress = caller();
while (TRUE)
{
if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE)
{
//pe头偏移RVA
uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
//判断PE头的正确性
if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024)
{
//pe头在内存中的位置
uiHeaderValue += uiLibraryAddress;
//如果找到文件头就退出循环
if (((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE)
break;
}
}
uiLibraryAddress--;
}
我在调试的可执行的Demo中,更改的。
//callAddress:在缓冲区开辟空间的起始地址,在原注入中uiLibraryAddress = caller();0x10偏移是为了模拟寻找起始地址的过程
uiLibraryAddress = callAddress + 0x10;
//caller功能:获取当前指令的下一条指令的地址。
uiLibraryAddress = caller();
while (TRUE)
{
if (((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE)
{
//pe头偏移RVA
uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;
//判断PE头的正确性
if (uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024)
{
//pe头在内存中的位置
uiHeaderValue += uiLibraryAddress;
//如果找到文件头就退出循环
if (((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE)
break;
}
}
uiLibraryAddress--;
}
我在调试的可执行的Demo中,更改的。
//callAddress:在缓冲区开辟空间的起始地址,在原注入中uiLibraryAddress = caller();0x10偏移是为了模拟寻找起始地址的过程
uiLibraryAddress = callAddress + 0x10;
获取目标进程PEB,获取需要的函数地址,需要的函数有:VirtualAlloc(用来为镜像要加载的地址分配空间)、LoadLibraryA(处理导入表)、GetProcAddress(同上)、NtFlushInstructionCache(刷新数据,让CPU执行新指令)。获取PEB的方法:FS:[0x30]和GS:[0x60],前者为32位系统,后者为64位系统。从下面的图中是微软公布的PEB的数据结构(在网络上可以找到更详细的结构),在_PEB_LDR_DATA这个数据结构中,存储着当前进程所加载的模块信息,就是我们想要的,我们需要遍历已经加载的模块,从中找到我们需要的模块,获得以上几个函数的地址。(会在附件中上传详细的PEB图)
提示:在解析PEB的结构的时候,要注意字节对齐 的问题,以前没有注意到结构体的这个问题,算是填了个坑。
PEB及_PEB_LDR_DATA的数据结构
模块之间的关系(来自网络,侵删)
PEB及_PEB_LDR_DATA的数据结构
由以上两图,贴出代码如下:
由以上两图,贴出代码如下: 由以上两图,贴出代码如下:
uiBaseAddress = __readgsqword(0x60);//peb结构的地址
uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;
uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;
while (uiValueA)
{
//当前模块名地址
uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;
usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;
uiValueC = 0;
//计算模块名的hash
do
{
uiValueC = ror((DWORD)uiValueC);
// normalize to uppercase if the madule name is in lowercase
if (*((BYTE *)uiValueB) >= 'a')
uiValueC += *((BYTE *)uiValueB) - 0x20;
else
uiValueC += *((BYTE *)uiValueB);
uiValueB++;
} while (--usCounter);
//获取目标进程中的接下来需要的函数地址
if ((DWORD)uiValueC == KERNEL32DLL_HASH)
{
uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
uiExportDir = (uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress);
uiNameArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNames);
uiNameOrdinals = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfNameOrdinals);
usCounter = 3;
// 找函数
while (usCounter > 0)
{
dwHashValue = hash((char *)(uiBaseAddress + DEREF_32(uiNameArray)));
if (dwHashValue == LOADLIBRARYA_HASH || dwHashValue == GETPROCADDRESS_HASH || dwHashValue == VIRTUALALLOC_HASH)
{
uiAddressArray = (uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY)uiExportDir)->AddressOfFunctions);
uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(DWORD));
if (dwHashValue == LOADLIBRARYA_HASH)
pLoadLibraryA = (LOADLIBRARYA)(uiBaseAddress + DEREF_32(uiAddressArray));
else if (dwHashValue == GETPROCADDRESS_HASH)
pGetProcAddress = (GETPROCADDRESS)(uiBaseAddress + DEREF_32(uiAddressArray));
else if (dwHashValue == VIRTUALALLOC_HASH)
pVirtualAlloc = (VIRTUALALLOC)(uiBaseAddress + DEREF_32(uiAddressArray));
usCounter--;
}
uiNameArray += sizeof(DWORD);
uiNameOrdinals += sizeof(WORD);
}
}
}
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: