Windows Process Analysis Tools (Windows程序分析工具)
主要功能:查看进程,线程,模块,堆内存,窗口,挂起线程,结束线程,卸载模块,CPU使用率,内存使用率,附加pe文件解析,支持查看导入表,导出表,重定位表,资源表,TLS表,延迟加载等..
附加功能:dll注入(win32/x64),无模块Pe加载注入,R3 inlineHook实现进程隐藏,内置反汇编引擎实现代码注入
其他功能:分析文件MD5,一键隐藏窗口(alt+win+z),设置进程优先级,托盘管理,增删用户,内存清理,窗口重置,等等无聊的功能都写里面了...
学习了小半年的编程了 写出来还是有点成就感的
涉及的知识【MFC编程,Windows核心编程,C++,注入和hook】
========================================================
用到的Api:
EnumWindows(枚举顶层窗口);GetWindowLong;GetClassLong;TerminateProcess;Thread32First;OpenThread;ResumeThread;UpdateWindow;CreateToolhelp32Snapshot;IsWow64Process;QueryDosDevice;GetLogicalDriveStrings;SuspendThread;SetPriorityClass;OpenProcess;VirtualAllocEx;WriteProcessMemory;CreateRemoteThread;AfxBeginThread;FindWindow;GetProcAddress;....就不都列举出来了 详情看代码吧。 我就把隐藏进程,内存加载,代码注入的核心代码贴出来吧
========================================================华丽的***======================================================
进程隐藏:
(进程隐藏采用了R3 层Hook ZwQuerySystemInformation 这个函数,这个函数是任务管理器,Proexp,Promon,一类进程管理所获取进程的一个函数底层调用的是这个函数,我们要先把DLL
注入到任务管理器中,然后在dll里面实现inlineHook 这个api 改前五个字节跳到dll中自己的NewZwQuerySystemInformation的函数中,把要隐藏的进程从单链表中剔去,
一点都不新鲜的东西咯.. 详情代码请参考逆向工程核心原理,含有要提一点想隐藏进程是进程不在任务管理器显示,所以我们hook的是任务管理器ZwQuerySystemInformation 函数,因为书中的测试环境是Win7 x32所以他的任务管理器也是32位的,所以该方法对Win7 x64 六十四位的任务管理器无效,因为他们的函数参数入栈方式不同,要写段七个字节的内联汇编.... 有兴趣可以私信交流下
)
Demo:
#include "windows.h"
#include "tchar.h"
#define STATUS_SUCCESS (0x00000000L)
typedef LONG NTSTATUS;
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
} SYSTEM_INFORMATION_CLASS;
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset;
ULONG NumberOfThreads;
BYTE Reserved1[48];
PVOID Reserved2[3];
HANDLE UniqueProcessId;
PVOID Reserved3;
ULONG HandleCount;
BYTE Reserved4[4];
PVOID Reserved5[11];
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER Reserved6[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
typedef NTSTATUS(WINAPI *PFZWQUERYSYSTEMINFORMATION)
(SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);
#define DEF_NTDLL ("ntdll.dll")
#define DEF_ZWQUERYSYSTEMINFORMATION ("ZwQuerySystemInformation")
// global variable (in sharing memory)
#pragma comment(linker, "/SECTION:.SHARE,RWS")
#pragma data_seg(".SHARE")
TCHAR g_szProcName[MAX_PATH] = { 0, };
#pragma data_seg()
// global variable
BYTE g_pOrgBytes[5] = { 0, };
NTSTATUS WINAPI NewZwQuerySystemInformation(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength)
{
NTSTATUS status;
FARPROC pFunc;
PSYSTEM_PROCESS_INFORMATION pCur, pPrev;
char szProcName[MAX_PATH] = { 0, };
// 先脱钩
unhook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION, g_pOrgBytes);
// o调用kernrl的ZwQuerySystemInformation
pFunc = GetProcAddress(GetModuleHandleA(DEF_NTDLL),
DEF_ZWQUERYSYSTEMINFORMATION);
status = ((PFZWQUERYSYSTEMINFORMATION)pFunc)
(SystemInformationClass, SystemInformation,
SystemInformationLength, ReturnLength);
if (status != STATUS_SUCCESS)
goto __NTQUERYSYSTEMINFORMATION_END;
// 对 SystemProcessInformation 操作
if (SystemInformationClass == SystemProcessInformation)
{
// SYSTEM_PROCESS_INFORMATION类型强转
// pCur 为单链表的头节点
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while (TRUE)
{
// 比较进程名称
// g_szProcName = 要隐藏的进程名称
if (pCur->Reserved2[1] != NULL)
{
if (!_tcsicmp((PWSTR)pCur->Reserved2[1], g_szProcName))
{
// 删除隐藏进程的节点
if (pCur->NextEntryOffset == 0)
pPrev->NextEntryOffset = 0;
else
pPrev->NextEntryOffset += pCur->NextEntryOffset;
}
else
pPrev = pCur;
}
if (pCur->NextEntryOffset == 0)
break;
//指向链表的下一项
pCur = (PSYSTEM_PROCESS_INFORMATION)
((ULONG)pCur + pCur->NextEntryOffset);
}
}
__NTQUERYSYSTEMINFORMATION_END:
// 二次勾取api
hook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION,
(PROC)NewZwQuerySystemInformation, g_pOrgBytes);
return status;
}
BOOL hook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes)
{
FARPROC pfnOrg;
DWORD dwOldProtect, dwAddress;
BYTE pBuf[5] = { 0xE9, 0, };
PBYTE pByte;
// 获取ZwQuerySystemInformation地址
pfnOrg = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pfnOrg;
// 判断第一个字节是否已被修改
if (pByte[0] == 0xE9)
return FALSE;
// 修改内存属性 前额五个字节为可写
VirtualProtect((LPVOID)pfnOrg, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 保存修改之前的五个字节
memcpy(pOrgBytes, pfnOrg, 5);
// JMP 的地址
dwAddress = (DWORD)pfnNew - (DWORD)pfnOrg - 5;
memcpy(&pBuf[1], &dwAddress, 4);
// 修改五个字节
memcpy(pfnOrg, pBuf, 5);
// 恢复内存属性
VirtualProtect((LPVOID)pfnOrg, 5, dwOldProtect, &dwOldProtect);
return TRUE;
}
BOOL unhook_by_code(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes)
{
FARPROC pFunc;
DWORD dwOldProtect;
PBYTE pByte;
// 获取api地址
pFunc = GetProcAddress(GetModuleHandleA(szDllName), szFuncName);
pByte = (PBYTE)pFunc;
//判断是否挂钩
if (pByte[0] != 0xE9)
return FALSE;
// 修改内存属性
VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 卸掉钩子
memcpy(pFunc, pOrgBytes, 5);
// 还原内存属性
VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);
return TRUE;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
// #2. API Hooking
case DLL_PROCESS_ATTACH:
hook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION,
(PROC)NewZwQuerySystemInformation, g_pOrgBytes);
break;
// #3. API Unhooking
case DLL_PROCESS_DETACH:
unhook_by_code(DEF_NTDLL, DEF_ZWQUERYSYSTEMINFORMATION,
g_pOrgBytes);
break;
}
return TRUE;
}
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void SetProcName(LPCTSTR szProcName)
{
_tcscpy_s(g_szProcName, szProcName);
}
#ifdef __cplusplus
}
#endif
Dll注入:
我的程序是Win32位的,所以只能注入Win32的程序,我通过在Win32唤起Win64的程序,FindWindow,得到窗口句柄然后发送消息,Win64程序我用了窗口隐藏,调用后即可销毁,这是注入的方法,借鉴一篇看雪无模块加载注入DLL我简单的说下我的理解,也算对他那篇文章做下解释吧..
它的文章地址:
http://bbs.pediy.com/showthread.php?p=1243996
Nomal Dll Inject Demo:
//创建一个进程互斥量 防止无模块DLL多次注入
BOOL IsMutexExist(char* pstrMutex)
{
BOOL bRet = FALSE;
HANDLE hMutex = NULL;
hMutex = CreateMutexA(NULL, TRUE, pstrMutex);
if ( hMutex )
{
if ( GetLastError() == ERROR_ALREADY_EXISTS )
bRet = TRUE;
ReleaseMutex(hMutex);
CloseHandle(hMutex);
}
else
{
bRet = TRUE;
}
return bRet;
}
//调用LoadPE.cpp里的函数,自行处理PE加载,把DLL在新申请的内存加载起来,并执行入口函数
void LaunchNoModule()
{
LaunchDll(dllModuleName,NO_MODULE_MARK);
}
unsigned int __stdcall NoModuleThread(void* lpParameter)
{
while (TRUE)
{
Sleep(1000);
OutputDebugString("helloAgain~");
}
return TRUE;
}
//调用LoadPE.cpp里的函数,自行处理PE加载,把DLL在新申请的内存加载起来,并执行入口函数
void NoModuleEntryCall(HMODULE hModule, DWORD ul_reason_for_call, char* pstrModuleName)
{
char szMutexName[MAX_PATH];
wsprintf(szMutexName,"HelloAgain~%d",GetCurrentProcessId());
g_hMutex = CreateMutex(NULL, TRUE, szMutexName);
char szLog[MAX_PATH] = {0};
wsprintf(szLog,"NoModuleEntryCall Module Start:%p",hModule);
OutputDebugString(szLog);
//下面为正常Dll功能代码
CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)NoModuleThread,NULL,NULL,NULL);
}
BOOL ChooseSub(HMODULE hModule, DWORD ul_reason_for_call, char* pstrModuleName)
{
BOOL bRet = FALSE;
GetModuleFileNameA(NULL, exeModuleName, MAX_PATH);
if ( ul_reason_for_call == NO_MODULE_MARK )
strcpy(dllModuleName, pstrModuleName);
else
GetModuleFileName(hModule, dllModuleName, MAX_PATH);
if ( ul_reason_for_call == NO_MODULE_MARK )
{
NoModuleEntryCall(hModule, DLL_PROCESS_ATTACH, 0);
bRet = TRUE;
}
else
{
LaunchNoModule();
bRet = FALSE;
}
return bRet;
}
//ul_reason_for_call等于NO_MODULE_MARK时,DllMain已经是在新申请的内存中运行
//DllMain返回值是个很关键的地方,平常写DLLMain的时候不会注意,都是直接返回TRUE,没有去关注FALSE的情况
//ul_reason_for_call等于DLL_PROCESS_ATTACH时,DllMain返回FALSE会使DLL自行卸载
//利用这一点再结合PE自行加载,就可以实现无模块注入了
//当然,这样的DLL,如果由程序内部加载,也是以无模块的形式存在的
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
BOOL bRet = FALSE;
if ( ul_reason_for_call == DLL_PROCESS_ATTACH || ul_reason_for_call == NO_MODULE_MARK )
{
char szMutexName[MAX_PATH];
wsprintf(szMutexName,"yanshier2013nomoduleinject%d",GetCurrentProcessId());
if ( IsMutexExist(szMutexName))
return FALSE;
bRet = ChooseSub(hModule, ul_reason_for_call, (char *)lpReserved);
}
else
{
if ( ul_reason_for_call == DLL_PROCESS_DETACH)
{
ReleaseMutex(g_hMutex);
CloseHandle(g_hMutex);
bRet = TRUE;
}
}
return bRet;
}
下面着重看下它的Pe加载过程
BOOL PELoader(char *lpStaticPEBuff, PVOID& pExecuMem)
{
long lPESignOffset = *(long *)(lpStaticPEBuff + 0x3c);
IMAGE_NT_HEADERS *pINH = (IMAGE_NT_HEADERS *)(lpStaticPEBuff + lPESignOffset);
long lImageSize = pINH->OptionalHeader.SizeOfImage;
char *lpDynPEBuff = (char *)VirtualAlloc(NULL,lImageSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
//申请一块SizeOfImage的内存
if(lpDynPEBuff == NULL)
{
return FALSE;
}
memset(lpDynPEBuff, 0, lImageSize);
long lSectionNum = pINH->FileHeader.NumberOfSections;
IMAGE_SECTION_HEADER *pISH = (IMAGE_SECTION_HEADER *)((char *)pINH + sizeof(IMAGE_NT_HEADERS));
memcpy(lpDynPEBuff, lpStaticPEBuff, pISH->VirtualAddress);
long lFileAlignMask = pINH->OptionalHeader.FileAlignment - 1;
long lSectionAlignMask = pINH->OptionalHeader.SectionAlignment - 1;
//按节表拷贝,重定位IAT
for(int nIndex = 0; nIndex < lSectionNum; nIndex++, pISH++)
{
memcpy(lpDynPEBuff + pISH->VirtualAddress, lpStaticPEBuff + pISH->PointerToRawData, pISH->SizeOfRawData);
}
if(pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size > 0)
{
IMAGE_IMPORT_DESCRIPTOR *pIID = (IMAGE_IMPORT_DESCRIPTOR *)(lpDynPEBuff + \
pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
//
for(; pIID->Name != NULL; pIID++)
{
IMAGE_THUNK_DATA *pITD = (IMAGE_THUNK_DATA *)(lpDynPEBuff + pIID->FirstThunk);
char* pLoadName = lpDynPEBuff + pIID->Name;
HINSTANCE hInstance = LoadLibrary(pLoadName);
if(hInstance == NULL)
{
//Free掉这块内存,然后然后执行下面的函数 将ul_reason_for_call 标记成NO_MODULE_MARK
VirtualFree(lpDynPEBuff,lImageSize,MEM_DECOMMIT);
return FALSE;
}
for(; pITD->u1.Ordinal != 0; pITD++)
{
FARPROC fpFun;
if(pITD->u1.Ordinal & IMAGE_ORDINAL_FLAG32)
{
fpFun = GetProcAddress(hInstance, (LPCSTR)(pITD->u1.Ordinal & 0x0000ffff));
}
else
{
IMAGE_IMPORT_BY_NAME * pIIBN = (IMAGE_IMPORT_BY_NAME *)(lpDynPEBuff + pITD->u1.Ordinal);
fpFun = GetProcAddress(hInstance, (LPCSTR)pIIBN->Name);
}
if(fpFun == NULL)
{
delete lpDynPEBuff;
return false;
}
pITD->u1.Ordinal = (long)fpFun;
}
}
}
套路大概就是:第一次进入DllMain 实现PE加载,在PE加载前申请一块内存把自己Copy进去,然后释放当前的dll,进入下一个函数 返回值用No_Modal_Mark标记然后就举着No_Modal_Mark的牌子用申请的那块内存来搞事情了...
============================================================================================================
代码注入:
反汇编引擎,开源的网上很多我下了一个做外挂用的.. 然后解释下文件写入和远程代码注入这里的内容吧
此函数只需要传进来被注入进程的PID,汇编代码还是要自己去手动定位函数基址,毕竟不是shellcode,其他用法和网上代码注入器都是一样的。
void CAsmInJectDlg::InjectBin(DWORD pid)
{
if (!AdjustPr())
{
MessageBox("提权失败");
return;
}
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, pid);
if (!hProcess)
{
MessageBox("OpenProcess失败");
return;
}
//LPVOID pParam = VirtualAllocEx(hProcess, NULL, 4, MEM_COMMIT, PAGE_READWRITE);
LPVOID pAddr = VirtualAllocEx(hProcess, NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!pAddr)
{
MessageBox("VirtualAllocEx失败");
return;
}
CString tmp, str;
tmp.Format("开辟的虚拟地址:%08X", pAddr);
OutputDebugString(tmp);
char error[256] = { 0 };
int count = m_Asm.GetLineCount();
BYTE buf[4096] = { 0 };
int j = 0;
int m = 0;
for (int i = 0; i < count; i++)
{
char cmd[256] = { 0 };
int len = m_Asm.GetLine(i, cmd, 256);
if (len == 0) continue;
cmd[len] = '\0';
t_asmmodel t_asm;
j = m_asm.Assemble(cmd, (DWORD)pAddr + j, &t_asm, 0, 4, error);
if (j <= 0)
{
tmp.Format("error=\"%s\"", error);
OutputDebugString(tmp);
}
for (int k = 0; k<j; k++)
{
buf[m] = (BYTE)t_asm.code[k]; //按字节进行opcode转换
tmp.Format("%02X", buf[m]);
str = str + tmp;
m = m + 1;
}
OutputDebugString(str);
str = "";
}
buf[m] = 0x0c2;//ret 4
buf[m + 1] = 0x04;
buf[m + 2] = 0x00;
if (!WriteProcessMemory(hProcess, pAddr, buf, 4096, NULL))
{
MessageBox("内存写入失败");
return;
}
DWORD dwThreadID;
DWORD dwParam = 0;
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pAddr, NULL, 0, &dwThreadID);
if (!hRemoteThread)
{
MessageBox("远程线程创建失败");
return;
}
CloseHandle(hRemoteThread);
CloseHandle(hProcess);
}
开发环境:vs2013 Win7 X64 SP1
项目文件:
AAA: 32位realse Unicode字符集
AsmInJect: 32位realse 多字节字符集 [代码注入器]
HidensDll: 32位realse Unicode字符集 [要注入进32位任务管理器的dll]
TestDll: 32/64 多字节字符集 [Pe加载注入的dll,32还是64自行选择/*通常情况32位程序只能往32位程序注入32位dll x64也一个道理,*/]
x64tool: 64位 Debug Unicode字符集 [注入64位程序使用的]
大概就这些,lz也是重复造轮子.. 新手上路 多多指教
源码放出来了,给需要的人做个参考
[课程]Android-CTF解题方法汇总!