windows逆向

DLL注入

消息钩子

SetWindowsHookExA

HHOOK SetWindowsHookExA(
[in] int idHook,//消息类型
[in] HOOKPROC lpfn,//钩子函数
[in] HINSTANCE hmod,//钩子函数所在dll
[in] DWORD dwThreadId//挂钩线程id
);

os会给每一个出发消息钩子的进程强制加载钩子函数所在dll

远程线程注入

OpenProcess 打开注入进程

VirtualAllocEx 申请空间写入要加载的dll名

在本进程获取kernel32!loadlibrary地址

CreateRemoteThread+loadlibrary+dll名

注册表

AppInit_DLLs

修改pe iat表

原iat表项是否够大,不够找个空闲位置或者增大最后一个section,或者添加一个新section,移动iat

导入绑定表清零(DataDirectory第11项)

添加iat项 (name iat int)

添加的iat的位置要有可写权限或者位于DataDirectory[12] iat表中

DLL卸载

枚举进程模块找到模块基址

OpenProcess

获取kernel32!freelibrary

CreateRemoteThread+freelirary+模块基址

代码注入

数据和代码分别注入

  • data_addr:数据可以是loadlibrary和getprocaddress的函数地址和函数参数需要的字符串
  • proc_addr:参数是data_addr 利用loadlibrary和getprocaddress主动获取函数地址去调用

OpenProcess

VirtualAllocEx

WriteProcessMemory

CreateRemoteThread(proc_addr,data_addr)

api hook

调试 hook

findwindow->GetWindowThreadProcessId->DebugActiveProcess——>WaitForDebugEvent

  • CREATE_PROCESS_DEBUG_EVENT时从DebugEvent.u.CreateProcessInfo中获取进程句柄 api开头改为0xcc的int3断点,并保存开头字节
  • EXCEPTION_DEBUG_EVENT时从DebugEvent.u.Exception.ExceptionRecord获得PEXCEPTION_RECORE 判断是否位EXCEPTION_BREAKPOINT断点,再判断发送异常的地址是否是我们hook api的地址,利用GetThreadContext等做hook操作

IAT hook

利用dll注入加载我们的dll,dll里通过getmodulehandle(null)获取exe模块基址+偏移找到要hook的api所在的iat表,把iat里的内容改为我们的代理函数地址

热补丁

对于绝大多数api 开头都有一条mov edi,edi的指令的无意义指令

image-20240405190939120

这样调用原api直接调用push ebp处的代码就可以了,就不用频繁进行脱钩处理,从而导致多线程是访问冲突问题

反调试

TLS回调函数

tls再进程创建执行ep前调用/终止后调用,有线程创建前/终止后调用,可以用来反调试

typedef VOID (NTAPI *PIMAGE_TLS_CALLBACK)
(
PVOID DllHandle,//模块加载地址
DWORD Reason,//TLS回调函数被调用的原因
PVOID Reserved
);
其中reason有以下
#define DLL_PROCESS_ATTACH 1
#define DLL_THREAD_ATTACH 2
#define DLL_THREAD_DETACH 3
#define DLL_PROCESS_DETACH 0
#pragma comment(linker, "/INCLUDE:__tls_used")
#pragma data_seg(".CRT$XLX")
PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { TLS_CALLBACK1, TLS_CALLBACK2, 0 };
#pragma data_seg()

SEH反调试

seh里检测

或者配合int3(或者单步调试eflag的tf标志位),正常状态int3 出发异常进行seh机制恢复异常,调试状态int3抛给调试器处理

peb

beingdebugged标志

被调试堆内存未使用部分是全部为0xfeeefeee peb.ldr就是堆内存

processheap.flags和processheap.forceflags

NtGlobalFlag

NtQueryInformationProcess

ProcessDebugPort(0x7) //CheckRemoteDebuggerPresent内部调用找个实现
ProcessDebugObjectHandle(0x1E)
ProcessDebugFlags(0x1F)


NtQueryInformationProcess(GetCurrentProcess(), 0x7, &debugPort, sizeof(debugPort), NULL);//非调试为0 否则-1
NtQueryInformationProcess(GetCurrentProcess(), 0x1E, &hdebugObject, sizeof(hdebugObject), NULL);//非调试为0
NtQueryInformationProcess(GetCurrentProcess(), 0x1f, &bdebugFlag, sizeof(bdebugFlag), NULL);//非调试为1

NtQueryObject

检测是否有调试对象

SetInformationThread(GetCurrentThread(), 0x11, 0, 0);

分离调试并终止进程

系统痕迹

窗口名

进程名

用户名

虚拟机进程

路径名

SetUnhandledExceptionFilter

调用 NtQueryInformationProcess查询Proccessdebug值,查询当前进程是否处于调试状态。如果程序正常运行,则运行顶层异常处理器(顶层异常里我们解决异常),如果处于调试状态,则将异常抛给调试器。

执行时间校验

int2D

正常状态走seh处理异常,有调试器调试器接管后会跳过int2d下面的一字节,并继续执行

代码校验和

硬件断点,软件断点

API重定向

调试状态运行程序

可以把被调试进程的异常处理代码写到调试器里

子进程

挂起模式创建子进程,手动修改子进程ep再恢复

pe镜像切换

挂起创建的进程,刚开始ebx寄存器指向peb,eip指向ntdll.dll!_RtlUserThreadStart eax指向返回地址即镜像的ep,所以需要把eax给为替换镜像的入口

ZwUnmapViewOfSection