仅学习,如有错误请指出…………
在加壳软件中,ntdll.dll里的有些API会被加壳软件hook,导致我们在做补丁无法正常执行
如:有的程序会hook NtGetContextThread和NtSetContextThread,导致无法下硬件断点,也有的会hook NtProtectVirtualMemory,无法修改内存属性,就不能修改汇编代码等……
Nt或Zw开头的函数执行的代码是一样的,他们所指向的函数地址都是同一个,具体有什么不同,请自行搜索查看,我这里使用Nt函数进行编写。
原理:
1、利用CreateFile创建文件
char dllPath[MAX_PATH];
GetSystemDirectoryA(dllPath, MAX_PATH);
strcat_s(dllPath, MAX_PATH, "\\ntdll.dll");//拼接系统目录ntdll.dll路径
HANDLE hFile = CreateFileA(dllPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
if (hFile == INVALID_HANDLE_VALUE) return functionAddress;
2、把系统目录里ntdll.dll读入内存
//把ntdll读入进内存
DWORD dwRead = 0;
DWORD dwSize = GetFileSize(hFile, &dwRead);
BYTE* pBuff = new BYTE[dwSize];
RtlZeroMemory(pBuff, sizeof(pBuff));
ReadFile(hFile, pBuff, dwSize, &dwRead, NULL);
3、用程序里的Nt函数地址找到读入内存的ntdll.dll的foa地址
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
#ifdef _WIN64
PIMAGE_NT_HEADERS64 pNtHeader = (PIMAGE_NT_HEADERS64)(pDosHeader->e_lfanew + ((ULONG_PTR)pDosHeader));
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(((ULONG_PTR)(pNtHeader)) + sizeof(IMAGE_NT_HEADERS64));
#else
PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + ((ULONG_PTR)pDosHeader));
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(((ULONG_PTR)(pNtHeader)) + sizeof(IMAGE_NT_HEADERS32));
#endif
ULONG_PTR foaAddress = 0;
//Nt函数地址 - ntdll基址 = rva
ULONG_PTR rva = apiAddress - (ULONG_PTR)dllHandle;
for (WORD i = 0; i < pNtHeader->FileHeader.NumberOfSections; ++i)
{
if (rva > pSectionHeader[i].VirtualAddress && rva < pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData)
{
//找到foa地址
foaAddress = rva - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData;
break;
}
}
4、使用foa地址去读取出opcode,32位程序opcode码,va地址和foa不一致,需要处理,64位无需处理
//opcode读取
int opcodeOffset = 0;
#ifdef _WIN64
while (true)
{
//x64位opcode读取
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + opcodeOffset));
if (*(BYTE*)(pBuff + foaAddress + opcodeOffset) == 0xC3) //遇到ret指令结束
{
//C3 ret
//CD 2E int 2E
//C3 ret
//遇到C3后还要追加int 2E ret指令
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + ++opcodeOffset)); //CD
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + ++opcodeOffset)); //2E
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + ++opcodeOffset)); //C3
break;
}
++opcodeOffset;
}
#else
while (true)
{
//x86位opcode读取
if (*(BYTE*)(pBuff + foaAddress + opcodeOffset) == 0xC2 && opcodeOffset > 10) //遇到ret指令结束
{
//C2 2000 ret 20
//遇到C2后还要追加返回值opcode
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + opcodeOffset)); //C2
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + ++opcodeOffset)); //20
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + ++opcodeOffset)); //00
break;
}
//x86 opcode在8、9位时foa值与va值不同,需要特殊处理
else if (opcodeOffset == 8 || opcodeOffset == 9)
{
functionBytes.push_back(*(BYTE*)(apiAddress + opcodeOffset));
}
else
{
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + opcodeOffset));
}
++opcodeOffset;
}
#endif
5、申请内存把opcode码写入到新内存
//申请堆空间执行汇编代码
int sizeCustomFunction = functionBytes.size();
functionAddress = (BYTE*)VirtualAlloc(0, sizeCustomFunction, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
for (int j = 0; j < sizeCustomFunction; j++)
{
//写入opcode到新内存,执行汇编代码
*(functionAddress + j) = functionBytes[j];
}
6、使用演示
//NtGetContextThread
typedef BOOL(NTAPI* PNtGetContextThread)(HANDLE hThread, LPCONTEXT lpContext);
PNtGetContextThread pZwGetContextThread;
BOOL MyNtGetContextThread(HANDLE hThread, LPCONTEXT lpContext)
{
pZwGetContextThread = (PNtGetContextThread)CustomNtFunction("NtGetContextThread");
return pZwGetContextThread(hThread, lpContext);
}
//NtSetContextThread
typedef BOOL(CALLBACK* PNtSetContextThread)(HANDLE hThread, LPCONTEXT lpContext);
PNtSetContextThread pZwSetContextThread;
BOOL MyNtSetContextThread(HANDLE hThread, LPCONTEXT lpContext)
{
pZwGetContextThread = (PNtSetContextThread)CustomNtFunction("NtSetContextThread");
return pZwGetContextThread(hThread, lpContext);
}
//NtProtectVirtualMemory
typedef BOOL(NTAPI* PNtProtectVirtualMemory)(HANDLE hProcess, PVOID* lpAddress, PSIZE_T dwSize, ULONG flNewProtect, PULONG lpflOldProtect);
PNtProtectVirtualMemory pNtProtectVirtualMemory;
BOOL MyNtProtectVirtualMemory(HANDLE hProcess, PVOID* lpAddress, PSIZE_T dwSize, ULONG flNewProtect, PULONG lpflOldProtect)
{
// 将函数名转换为地址并执行
pNtProtectVirtualMemory = (PNtProtectVirtualMemory)CustomNtFunction("NtProtectVirtualMemory");
return pNtProtectVirtualMemory(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect);
}
完整代码如下:
BYTE* CustomNtFunction(const char* functionName)
{
std::vector<BYTE> functionBytes;
BYTE* functionAddress = NULL;
char dllPath[MAX_PATH];
GetSystemDirectoryA(dllPath, MAX_PATH);
strcat_s(dllPath, MAX_PATH, "\\ntdll.dll");//拼接系统目录ntdll.dll路径
HANDLE hFile = CreateFileA(dllPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
if (hFile == INVALID_HANDLE_VALUE) return functionAddress;
HMODULE dllHandle = LoadLibraryA(dllPath);
ULONG_PTR apiAddress = (ULONG_PTR)GetProcAddress(dllHandle, functionName);
//把ntdll读入进内存
DWORD dwRead = 0;
DWORD dwSize = GetFileSize(hFile, &dwRead);
BYTE* pBuff = new BYTE[dwSize];
RtlZeroMemory(pBuff, sizeof(pBuff));
ReadFile(hFile, pBuff, dwSize, &dwRead, NULL);
//通过apiAddress地址获取函数的foa地址
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
#ifdef _WIN64
PIMAGE_NT_HEADERS64 pNtHeader = (PIMAGE_NT_HEADERS64)(pDosHeader->e_lfanew + ((ULONG_PTR)pDosHeader));
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(((ULONG_PTR)(pNtHeader)) + sizeof(IMAGE_NT_HEADERS64));
#else
PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + ((ULONG_PTR)pDosHeader));
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(((ULONG_PTR)(pNtHeader)) + sizeof(IMAGE_NT_HEADERS32));
#endif
ULONG_PTR foaAddress = 0;
//Nt函数地址 - ntdll基址 = rva
ULONG_PTR rva = apiAddress - (ULONG_PTR)dllHandle;
for (WORD i = 0; i < pNtHeader->FileHeader.NumberOfSections; ++i)
{
if (rva > pSectionHeader[i].VirtualAddress && rva < pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData)
{
//找到foa地址
foaAddress = rva - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData;
break;
}
}
//opcode读取
int opcodeOffset = 0;
#ifdef _WIN64
while (true)
{
//x64位opcode读取
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + opcodeOffset));
if (*(BYTE*)(pBuff + foaAddress + opcodeOffset) == 0xC3) //遇到ret指令结束
{
//C3 ret
//CD 2E int 2E
//C3 ret
//遇到C3后还要追加int 2E ret指令
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + ++opcodeOffset)); //CD
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + ++opcodeOffset)); //2E
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + ++opcodeOffset)); //C3
break;
}
++opcodeOffset;
}
#else
while (true)
{
//x86位opcode读取
if (*(BYTE*)(pBuff + foaAddress + opcodeOffset) == 0xC2 && opcodeOffset > 10) //遇到ret指令结束
{
//C2 2000 ret 20
//遇到C2后还要追加返回值opcode
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + opcodeOffset)); //C2
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + ++opcodeOffset)); //20
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + ++opcodeOffset)); //00
break;
}
//x86 opcode在8、9位时foa值与va值不同,需要特殊处理
else if (opcodeOffset == 8 || opcodeOffset == 9)
{
functionBytes.push_back(*(BYTE*)(apiAddress + opcodeOffset));
}
else
{
functionBytes.push_back(*(BYTE*)(pBuff + foaAddress + opcodeOffset));
}
++opcodeOffset;
}
#endif
//申请堆空间执行汇编代码
int sizeCustomFunction = functionBytes.size();
functionAddress = (BYTE*)VirtualAlloc(0, sizeCustomFunction, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
for (int j = 0; j < sizeCustomFunction; j++)
{
//写入opcode到新内存,执行汇编代码
*(functionAddress + j) = functionBytes[j];
}
delete[] pBuff; pBuff = NULL;
CloseHandle(hFile);
return functionAddress;
}
//NtProtectVirtualMemory
typedef BOOL(NTAPI* PNtProtectVirtualMemory)(HANDLE hProcess, PVOID* lpAddress, PSIZE_T dwSize, ULONG flNewProtect, PULONG lpflOldProtect);
PNtProtectVirtualMemory pNtProtectVirtualMemory;
BOOL MyNtProtectVirtualMemory(HANDLE hProcess, PVOID* lpAddress, PSIZE_T dwSize, ULONG flNewProtect, PULONG lpflOldProtect)
{
// 将函数名转换为地址并执行
pNtProtectVirtualMemory = (PNtProtectVirtualMemory)CustomNtFunction("NtProtectVirtualMemory");
return pNtProtectVirtualMemory(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect);
}
最后,实战一个VMP加壳的程序,此程序正常情况下无法修改内存属性
程序下载地址:https://nns.lanzouu.com/iiMLu2ee6sxi
补丁工程文件:
winmm补丁工程文件.zip
(176.86 KB, 下载次数: 34, 售价: 15 CB吾爱币)