吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 52073|回复: 136
收起左侧

[调试逆向] 反内存监视集绵

    [复制链接]
半斤八兩 发表于 2012-7-22 01:40
本帖最后由 半斤八兩 于 2012-7-23 21:51 编辑

反内存监视集绵


/**************************************
/* 作者:半斤八兩
/* 博客:http://hi.baidu.com/bjblcracked
/* 日期:2012-07-22  01:00
/**************************************


IDA,">只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!


   相信不少朋友辛辛苦苦找到的补丁数据,被别人轻而易举的窃取了...
本文就来讲讲怎么防止别人窃取我们的补丁数据...(这也是个矛与盾的话题,本文只是把常见的方法一一列举出来...)


前言:
我们先讲API HOOK.
API HOOK 技术,对于新手来说可能非常的神秘,其实它并不神秘,用一句话来概述就是 [抢在它前面,做些小动作.] 或 [修改EIP] 等.
现在的家用电脑的U普遍都是x86. 在保护模式下,每个进程都是独立的虚拟4GB内存空间.它们彼此都互不干扰.
如果我们想访问或者修改某个进程的某个内存空间.我们就必须得到那个进程的EPROCESS , 然后通过EPROCESS 找到 CR3  再把想要访问或者读写的内存地址按CR4里的PAE位拆分. 然后再把CR3 加上 拆分后的 PDE   加上拆分后的PTE  加上OFFSET.等等...我们才能实现R/W一段内存空间. 但是仅仅知道这些是不行的,因为这些我们用户层是没有权限访问CR3的,因此我们又不得不去了解段机制. 天呐~~ 仅仅一个读写内存就这么麻烦??? 是的,就是有这么麻烦..不过还好.MS的人考虑的周到.他们把这些烦锁的步骤,封装成一个方法.供我们调用.欲称Windows API.




No1: Anti Hook API(先斩后奏)

图片1.png


  上面说的烦锁的过程,我们可以通过2个API轻松实现.
  Kernel32!ReadProcessMemory    // 读指定进程内存
  Kernel32!WriteProcessMemory   // 写指定进程内存
  
  知已知彼 百战不殆!
  通过Kernel32!WriteProcessMemory 就能制作我们的补丁程序了. 如果想知道别人写什么数据,我们可以通过HOOK Kernel32!WriteProcessMemory 这样的函数. 来得到别人的补丁数据.
  这样的偷窃也很好反.我在这里就举两三个小例子.
1) 设置分页属性.

我们知道物理页(PA), 一般有

---------------------------PTE-------------------------------------------------
第0位 P位:    为1时 存在     | 为0时 不存在.  
第1位 R/W位: 为1时 可写     | 为0时 不可写  
第2位 U/S位:  为1时 用户权限 | 为0时 系统权限   
---------------------------------------------------------PTE-------------------

之区分.
这些属性在 PTE(页表项) 对应的 第0位 第1位 第2位.

P位为0时,我们在OD下,访问会显示 "No memory on the specified address"
在Windbg下访问,会显示 "?? ?? ?? ?? ...."

R/W 或 U/S 位为0时, 我们访问,会提示一个 0xC0000005 写,异常.

P位 R/W位和U/S位,我们是没有权限操作的.(本章主要讲解应用层的).
但是 R/W 位, MS的人,也给我们留了一个操作函数.

Kernel32!VirtualProtect    //设置本进程指定低2G内存分页属性(范围4KB 即0x1000)
有了这个函数,我们就可以"间接"的修改R/W位了.

代码:
// 这里是MSG 硬编码,(本人当前机器是XP Sp2 英文系统,硬编码的)
BYTE szWriteBuffer[] = {
  0x6a, 0x40, 0x68, 0xe2, 0x24, 0xa6, 0x7c, 0x68, 0xe2, 0x24, 0xa6, 0x7c, 0x6a, 0x00, 0xe8, 0x0c,
  0xe0, 0x31, 0xfb, 0xc2, 0x10, 0x00, 0x90, 0xb0, 0xeb, 0xbd, 0xef, 0xb0, 0xcb, 0xc1, 0xbd, 0xb7,
  0xb4, 0xb2, 0xb9, 0xb6, 0xa1, 0x00};

HMODULE hModule = GetModuleHandle("Kernel32.dll");

LPVOID lpWriteFun = GetProcAddress(hModule, "WriteProcessMemory");

// 英文计算器
HWND hWnd = ::FindWindow(NULL, "Calculator");
  
DWORD dwPID = 0;
  
GetWindowThreadProcessId(hWnd, &dwPID);
  
HANDLE hTarget = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);

WriteProcessMemory(hTarget, TARGET_ADDRESS, szWriteBuffer, sizeof(szWriteBuffer), NULL);

DWORD dwOldProtect = 0;

VirtualProtect(lpWriteFun, 0x1, PAGE_NOACCESS, &dwOldProtect);
这样做,是在我们执行完补丁后,才修改属性.这样时机得早.否则,一切都是瞎忙活.只要我们时机早.
对方在HOOK 这个函数的时候,就会产生一个 0xC0000005 访问异常. 这样,对方就不能偷窃我
们的数据了.

  试问,如果我们能在对方HOOK之前写入目标数据.我们还修改分页属性, 是不是多此一举?
  这里只是提供一条思路.

检测HOOK指令.

  在前面我们已经说过了, HOOK就是在做某事前,先干些我们自己的事.也就是修改EIP.
  想想修改EIP的,无非就是  JCC   push/ret  call  这几种.
  但是当下系统之多,不得不考虑兼容性. 所以一般HOOK,我们都会HOOK,函数的前5个字节.
为什么正好是前5个字节.不是前4个字节,6个字节呢? 我们来看看 WriteProcessMemory 函数

代码:
7C80220F >   8BFF          mov edi,edi                              ;  // WriteProcessMemory
7C802211     55            push ebp
7C802212     8BEC         mov ebp,esp
7C802214  |.  51            push ecx
7C802215  |.  51            push ecx
7C802216  |.  8B45 0C       mov eax,[arg.2]

我们用JMP指令来修改到任意一个EIP.

7C80220F >  - E9 EBDD7F03   jmp 7FFFFFFF                             ;  // WriteProcessMemory
7C802214  |.  51            push ecx
7C802215  |.  51            push ecx
7C802216  |.  8B45 0C       mov eax,[arg.2]
我们可以看的出来, JMP 指令 0xe9 1个字节, 加上 (目标地址 - 当前地址 - 5) 结果4个字节 ,加起来正好5个字节. 我们就可以用JMP 0xxxxxxxx 来代替前面的5个字节. 这样的,最方便,也最健壮.当然,也最容易被发现了 :(

如此一来,我们只要检测 WriteProcessMemory 第1个字节,是不是为 0xe9, 就可以轻松检测到是否 HOOK.
当然了,此法在这里要排除那些不考虑程序兼容性乱HOOK的朋友.
也要排除那些为了防止被发现HOOK,将所有不同系统的硬编码都加到程序里面判断的朋友.(如此一来,他可以不用再前5个字节修改EIP. 他可以争对不同系统,在不同地方,用不同的方法修改EIP.不一定非要用JCC, 也可以用 push/ret 之类的指令,修改EIP. )

Shadow Function(影子函数)

  相信不少朋友应该看过火影忍者.那里面的鸣人练就了一个本领,[多重影分身] 每当快要被敌人打到的时候,他就会使用多重影分身,将本体藏起来,被敌人打到的,只是一个影分身而已.

  在计算机中,也有这么一个多重影分身术.Shadow Function.. 这个并不是当下什么流行的技术. 当下的一些流行壳,像比如 ZP SE 等知名壳.都有使用Shadow Function技术.

  我们也可以使用这个技术反内存监视.让监视者们,监视我们的影分身.而我们真身写入的数据.敌人浑然不知. 听起来是不是很cool ? 但是,要实现Shadow 必须要熟知PE结构.自己模拟PE装载模块. 像我们小菜不懂PE怎么办?

  在这里,我在教大家一种简单的方法.不知道大家有没有注意,我在前面写的每个函数前面都有加一个模块名. 那个模块名,就是函数的所在模块. 一般都是在 SYSTEM32 目录下.
我们可以直接将他拷贝出来, 改一下名字.然后我们直接调用它,就可以了.

代码:
// 这里是MSG 硬编码,(本人当前机器是XP Sp2 英文系统,硬编码的)
BYTE szWriteBuffer[] = {
  0x6a, 0x40, 0x68, 0xe2, 0x24, 0xa6, 0x7c, 0x68, 0xe2, 0x24, 0xa6, 0x7c, 0x6a, 0x00, 0xe8, 0x0c,
  0xe0, 0x31, 0xfb, 0xc2, 0x10, 0x00, 0x90, 0xb0, 0xeb, 0xbd, 0xef, 0xb0, 0xcb, 0xc1, 0xbd, 0xb7,
  0xb4, 0xb2, 0xb9, 0xb6, 0xa1, 0x00};

void CPage1::OnAntiMonitor() 
{
  HMODULE hModule = LoadLibrary("WriteMem.dll");

  DWORD WriteFun = (DWORD)GetProcAddress(hModule, "WriteProcessMemory");

  BOOL (WINAPI *lpWriteFun)(HANDLE , LPVOID , LPCVOID , SIZE_T , SIZE_T *);

  lpWriteFun = (int (__stdcall *)(void *,void *,const void *,
          unsigned long,unsigned long *))WriteFun;

  // 英文计算器
  HWND hWnd = ::FindWindow(NULL, "Calculator");

  DWORD dwPID = 0;

  GetWindowThreadProcessId(hWnd, &dwPID);

  HANDLE hTarget = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);

  //  WriteProcessMemory
  lpWriteFun(hTarget, TARGET_ADDRESS, szWriteBuffer, sizeof(szWriteBuffer), NULL);
}
这样子,别人再HOOK Kernel32!WriteProcessMemory ,就是HOOK我们的多重影分身了. :)
而我们本体函数,是可以正常的执行,不受任何影响的.

No2: Killer Driver(半路杀出个程咬金)

图片2.png

  前面我们说的都是反应用层的HOOK. 但是,当遇到系统层的,前面的方法都彻底失效了.

  在之前我有写过一个反R0内存监视的小教程.
  http://www.52pojie.cn/thread-154535-1-1.html

  那里面说的方法,只是自动枚举停止按钮的句柄.然后自动按了一下而已.这个方法有很多弊端.
比如监视补丁工具改了一下窗口名. 或者按钮的名字修改了一下,就都无效了.

  今天我们再来说几种新的方法.

1) 利用[停止]按钮句柄,发WM_LBUTTONDOWN消息
2) 定位特征码,枚举所有进程,读取特征码.
3) 检测符号链接
4) 调用IRP派遣消息


1) 利用[停止]按钮句柄,发WM_LBUTTONDOWN消息
第一种方法之前的贴子有讲过,这里就不再赘述了.

定位特征码,枚举所有进程,读取特征码.
  世上的东西都是相生相克的.就像矛与盾一样.没有矛,何来的盾? 没有盾,又何来的矛?
定位特征码的话,我们就必须得有内存监视的程序样本. 否则我们是无法定位其特征码的.
本篇,重点不是讲解如何提取特征码.所以细节忽略...

本篇[内存写入监视器 v2.0]特征数据如下:

004E6716   A1 EC2E4F00     mov eax,dword ptr ds:[4F2EEC]

特征码地址:
004E6716.

特征数据:
0xA1EC2E4F(为了方便,我们就提取四个字节 DWORD 类型,来效验)

代码:
// 特征码地址
#define SIGNATURE_ADDRESS (LPVOID)0x004E6716
// 特征码大小
#define SIGNATURE_SIZE  0x4

  BYTE szSignature[MAXBYTE] = {0};

  HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

  PROCESSENTRY32 Process32 = {0};
  Process32.dwSize = sizeof(PROCESSENTRY32);
  
  BOOL bRect = Process32First(hSnapshot, &Process32);

  while(bRect)
  {
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 
      FALSE, Process32.th32ProcessID);

    if(!hProcess)
    {
      bRect = Process32Next(hSnapshot, &Process32);
      continue;
    }

    ReadProcessMemory(hProcess, SIGNATURE_ADDRESS, 
      szSignature, SIGNATURE_SIZE, NULL);

    if(*szSignature == 0xa1)
    {
      AfxMessageBox("扫描到特征码, 表示检测到内存监视", 
        MB_ICONINFORMATION);

      /*
      ...do
      */
    }

    bRect = Process32Next(hSnapshot, &Process32);
  }
我们利用MS给出的进程快照 Kernel32!CreateToolhelp32Snapshot 得到进程PID
利用 Kernel32!OpenProcess 打开进程句柄
利用 Kernel32!ReadProcessMemory 读取特征码.

优点:
此法,简单,实用. :)

缺点:
对于线程 或者堆中, 动态分配的代码,不适宜.


检测符号链接
  既然是检测驱动,那我们也要简单的知道一些驱动相关的信息.
驱动大体分为两类,

一类是PNP(Plug-and-play)类型, 这类多半是USB. 摄像头, 狗, U盾 等等,即插即用类型的.
一类是NT 类型. 非即插即用驱动.

  这个工具很显然是后者,即NT式驱动.
应用程序想要和驱动程序之间进行"通信".驱动程序就会暴露一个符号链接,让应用程序去访问它.

我们可以通过 Kernel32!CreateFile 来访问这个符号链接.
我们先用OD载入 [内存写入监视器 V2.0.exe]  

代码:
00400154 >  8725 D45F6200   xchg dword ptr ds:[625FD4],esp           ; // AddressOfEntryPoint
0040015A    61              popad
0040015B    94              xchg eax,esp
0040015C    55              push ebp
0040015D    A4              movs byte ptr es:[edi],byte ptr ds:[esi]
0040015E    B6 80           mov dh,80
然后我们下一个 CreateFileA/W 断点. F9, 让程序跑起来.
这时候单字节的API断下来了,我们看一下堆栈信息.

代码:
0013FDC8   004E55DD  /CALL to CreateFileA from 内存写入.004E55D8
0013FDCC   004E64C4  |FileName = "MemWrite.sys"
0013FDD0   C0000000  |Access = GENERIC_READ|GENERIC_WRITE
0013FDD4   00000001  |ShareMode = FILE_SHARE_READ
0013FDD8   00000000  |pSecurity = NULL
0013FDDC   00000002  |Mode = CREATE_ALWAYS
0013FDE0   00000000  |Attributes = 0
0013FDE4   00000000  \hTemplateFile = NULL
我们重点看这一个地址.

0013FDCC   004E64C4  |FileName = "MemWrite.sys"

这就是他的驱动程序了.这个驱动加载完毕,就删除了,我们找不到它.这里我们就无视它了.
我们重点是要找到这个驱动暴露给应用程序的符号链接.

我们继续 F9 运行程序. 然后程序又断下来了.我们继续看堆栈信息.

代码:
0013FDB0   004E5E4F  /CALL to CreateFileA from 内存写入.004E5E4A
0013FDB4   00B338FC  |FileName = "\\.\MemWrite"
0013FDB8   C0000000  |Access = GENERIC_READ|GENERIC_WRITE
0013FDBC   00000000  |ShareMode = 0
0013FDC0   00000000  |pSecurity = NULL
0013FDC4   00000003  |Mode = OPEN_EXISTING
0013FDC8   00000080  |Attributes = NORMAL
0013FDCC   00000000  \hTemplateFile = NULL
这一下我们就看到了暴露的符号链接了.

0013FDB4   00B338FC  |FileName = "\\.\MemWrite"

"\\.\MemWrite" 这个就是NT驱动暴露给客户端的符号链接.

下面,我们就可以跨进程用同样的方法来调用它.
在这里,可能有的朋友好奇了,为什么跨进程还能调用它.
原因很简单, 每个进程虽然都是"独立的", 但是这里的独立的,是指低2G内存空间是独立的.
但是高2G是共享的.而符号链接,就保存在高2G内存空间中,所以,我们任何一个进程,都是可以访问这个符号链接的.

代码:
  HANDLE hSymbolic = CreateFile("\\\\.\\MemWrite", 
    GENERIC_READ, FILE_SHARE_READ,
    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

  if(INVALID_HANDLE_VALUE != hSymbolic)
  {
    AfxMessageBox("检测到符号链接...:)", MB_ICONERROR);
    /*
    do...
    */
  }
  else
  {
    AfxMessageBox("没有发现符号链接~~~:)", MB_ICONINFORMATION);
  }
优点:
屡试不爽. :)

缺点:
如果驱动暴露在外的符号链接修改了,或者改成随机的,那此法无效 :(


调用IRP派遣消息
IRP派遣消息是通过 Kernel32!DeviceIoControl 函数实现的.
我们再来用OD运行内存监视器.我们随便选择一个计算器程序, 然后再OD命令行处,下 DeviceIoControl 断点. 然后我们点监视器上的 [开始监控]按钮.

程序断点,堆栈信息:
0013FBEC   004E672A  /CALL to DeviceIoControl from 内存写入.004E6725
0013FBF0   000000C0  |hDevice = 000000C0 (window)
0013FBF4   5800C007  |IoControlCode = 5800C007
0013FBF8   004FF9E0  |InBuffer = 内存写入.004FF9E0
0013FBFC   00000004  |InBufferSize = 4
0013FC00   00000000  |OutBuffer = NULL
0013FC04   00000000  |OutBufferSize = 0
0013FC08   0013FC34  |pBytesReturned = 0013FC34
0013FC0C   00000000  \pOverlapped = NULL

再运行.又中断.

代码:
0013FBEC   004E674C  /CALL to DeviceIoControl from 内存写入.004E6747
0013FBF0   000000C0  |hDevice = 000000C0 (window)
0013FBF4   5800C00B  |IoControlCode = 5800C00B
0013FBF8   00000000  |InBuffer = NULL
0013FBFC   00000000  |InBufferSize = 0
0013FC00   00000000  |OutBuffer = NULL
0013FC04   00000000  |OutBufferSize = 0
0013FC08   0013FC34  |pBytesReturned = 0013FC34
0013FC0C   00000000  \pOverlapped = NULL
程序中断两次,说明,客户端通知驱动开启监视,要通知两次才能完成开启.下面,我们再点[停止监控]按钮.

程序又中断了,堆栈信息:

代码:
0013FBFC   004E68B7  /CALL to DeviceIoControl from 内存写入.004E68B2
0013FC00   000000C0  |hDevice = 000000C0 (window)
0013FC04   5800C00F  |IoControlCode = 5800C00F
0013FC08   00000000  |InBuffer = NULL
0013FC0C   00000000  |InBufferSize = 0
0013FC10   00000000  |OutBuffer = NULL
0013FC14   00000000  |OutBufferSize = 0
0013FC18   0013FC38  |pBytesReturned = 0013FC38
0013FC1C   00000000  \pOverlapped = NULL
我们对比一下三次,可以看出,除了  IoControlCode  InBuffer  InBufferSize  pBytesReturned  这四个参数不一样外,其它的参数,全部是一样的.
InBuffer  InBufferSize
这两个,前面一个是传进去要监控的PID值, 以及大小.我们无视之.
pBytesReturned  是一个返回值,我们也无视. 重要的就是 IoControlCode  .

IoControlCode  我们可以称之为 消息码 或者 控制码. 其值是固定不变的,不会因为机器重启,或者换了个平台, 而改变控制码.

有过windows开发经验的,都知道windows消息机制. 而这个 IoControlCode 就相当于是Windows中的,自定义消息. 驱动那头,就相当于是一个处理消息的过程函数.

这边发送什么样的消息码过去.驱动那边,就会有怎样的反应.
当我们点 [停止监控] 时,发送的控制码是 IoControlCode = 5800C00F
知道了,[停止监控] 的 "控制码"了, 我们自己也可以发送这个消息过去了.

这里,大家要注意的是, DeviceIoControl 的第一个参数 hDevice = 000000C0 (window)
他的值是 CreateFile 的返回值, 大家要注意,必须是 MemWrite.sys 的文件句柄才行.

但是,问题又来了.我们如何发送这个控制码呢? 我们也可以跨进程发送这个控制码吗?
答案,当然是可行的喽~~ :)

代码:
  HANDLE hSymbolic = CreateFile("\\\\.\\MemWrite", 
    GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

  if(INVALID_HANDLE_VALUE != hSymbolic)
  {
    AfxMessageBox("检测到符号链接...:)", MB_ICONERROR);

    DWORD dwCount = 0;

    DeviceIoControl(hSymbolic, 0x5800C00F, NULL, 0, 
      NULL, 0, &dwCount, NULL);
  }
  else
  {
    AfxMessageBox("没有发现符号链接~~~:)", MB_ICONINFORMATION);
  }
主要是作者在驱动层没有做严格判断.所以我们可以跨进程发送 [停止监控] 的IRP请求.

No3: CRC(1 byte Cyclic Redundancy Check)
  
图片3.png


  对付这类的内存监视,有点恶心.
为什么说它恶心呢?因为它是扫描目标进程代码段,看是否有修改过的.如果有修改过的,则直接显示出来.这样的内存扫描工具,我们就没有办法吗?

  兵来将挡,水来土淹.
  对于这样的程序,还是有不少办法的.

1) 异常页
2) 复制术
3) 一字节异常数据


异常页
  这类的内存监视工具,很显然就是利用我们前面讲到的,跨进程内存存取的方式实现的. 那么自然就扫不了要调用 Kernel32!ReadProcessMemory 函数. 将目标进程的代码全部读取出来, 保存到内存空间, 或保存在磁盘上.等.

  我们来看看这个函数的参数.

代码:
BOOL ReadProcessMemory(
  HANDLE hProcess,              // handle to the process
  LPCVOID lpBaseAddress,        // base of memory area
  LPVOID lpBuffer,              // data buffer
  SIZE_T nSize,                 // number of bytes to read
  SIZE_T * lpNumberOfBytesRead  // number of bytes read
);
  我们可以看出,并没有"权限" 这一个参数.那么我们可以设想.如果我们把 0x00400000 这个地址开始的分页属性修改成 PAGE_NOACCESS(没有访问权限) 那么这个内存扫描工具是不是就会报错了?  这个想都不用想,当然是不可能的了.

  事实上,正常的内存遍历工具,应该都会先调用 Kernel32!VirtualQueryEx 查看一下,分页是否可读或可写权. 再决定是否调用 Kernel32!ReadProcessMemory 读取此分页. 当我分析此程序时, 下 Kernel32!VirtualQueryEx 断点,并没有断点, 当然,函数也没有 Sahdow 处理. 看来此内存监视工具,写的还是不够完善.

  知道此程序的BUG,那么,我们就可以做手脚了.如何做手脚呢?
  很简单,我们可以在 Magic Jump 那个分页属性,修改成 PAGE_NOACCESS. 这样不是就大功告成了?  是不是感觉哪里还有问题? 没错, 不仅有问题,而且问题还很严重. 如果属性改成无权限,那么,被打补丁的目标程序,自己执行到那段代码的时候,也是要访问,并且执行的啊,执行到我们修改成无访问权限的那段,代码,不是要抛一个0xc0000005 访问异常?
  
  是吖,就是这个问题,很纠结. 因此, 我们最多也只能修改 0x00400000 这一个分页.这个页从磁盘映射到内存空间,囊括了 DOS头和PE头和节等信息.我们修改属性是没有问题. 但是其它代码修改了属性,我们不能保证他不会被目标程序本身执行.

  所以我们只能找100% 不会被目标程序本身 访问, 执行 的分页来修改其属性. 这样做,也只是能做一些干扰码. 因为这款内存扫描工具,没有判断分页属性,直接读内存,所以结果读了没有内存访问权限的分页,就会返回很多干扰码. 其中本小节开始的截图,就是读取的干扰码.

修改分页属性,看似没有什么实质帮助,其实可以用在其他地方,本小节,只是起一个抛砖引玉的作用.

再附上代码:
// exception page
void CPage3::OnAntiMonitor4() 
{
  DWORD dwOldProtect = 0;

  VirtualProtect((LPVOID)0x00400000, 1, PAGE_READWRITE, &dwOldProtect);

  *(PBYTE)(0x00400000) = 0x11;
  
  // 注释本行,就会被内存监视扫描到
  VirtualProtect((LPVOID)0x00400000, 1, PAGE_NOACCESS, &dwOldProtect);
}
优点:
好像木有啥优点.  :(
当然了,也不是没有优点,如果可以和下面说的 1字节异常 结合起来使用,就真是完美搭配了.:)

缺点:
只适合于这类没有调用 Kernel32!VirtualQueryEx 函数,判断分页属性的,内存监视工具



复制术
  复制术? 听起来很cool.. 名字是本人随便起的, 并不是什么专业术语,勿较真. :)
  相信有不少玩Hacker的朋友在渗透踩点某伺服器的时候,为了避免被IDS检测到,都会在人流峰高潮的节假日的时候,进行渗透踩点.这样才能尽量避免触发警报.
  而本节的内存监视工具,就相当于是夜间工作的IDS. 专门监视代码段. 如果我们在半夜突然像伺服器发送一个握手协议.就能被IDS盯死. 但是,如果我们在双休日的时候,WEB访问量巨增的时候,在数亿个合法包里面,藏匿我们自己的踩点包,还是可以轻而易举的绕过去的.
  在这里, 代码段  ==  夜间踩点   那么,反过来,    双休踩点  ==  ????

  双休踩点, 就相当于是 进程中的 堆空间 或 线程中. 变动的 无规则的.
我们从本节的图片中,可以看出,此内存监视工具,缺省设置的扫描地址,就是 0x00400000 - 0x00490000   那么,我们可以大胆的设为,在低2G内存空间中, 除了这上面的缺省范围的内存空间外,其它的内存空间,都会活动的. 无规则的

  说到这里,相信还有不少人有点迷糊,不知道 所谓的 "活动的" "无规则的" 到底是什么意思.

  其实意思就是说,被经常被读 或被写 或被访问, 或被修改 的代码段. 如果搜索那段"活动的" 地址页. 那么,程序扫描出的结果数量,将是非常 "惊人"的.

  那么,清楚了这些,我们就可以把我们要修改数据的那段上下文, 或者子函数,全部拷贝一份出来.到我们的堆空间,或者线程中. 那么,就能避免被内存监视工具发现.
  现在还最后剩一个问题.就是如何将EIP,指向我们的复制函数里面?

我们可以暂停目标进程,设置其上下文, 或者干脆创建一个远程线程函数. :)


代码:
void Hello(int nValue)
{
  if(nValue == 0x520)
  {
    MessageBox(NULL, "Register successful~", 
      "By 半斤八兩", MB_ICONINFORMATION);
  }
  else
  {
    MessageBox(NULL, "Register failure~", 
      "By 半斤八兩", MB_ICONERROR);
  }
}

void Shadow(int nValue)
{
  MessageBox(NULL, "successful~", 
    "By 半斤八兩", MB_ICONINFORMATION);
}

// shadow page
void CPage3::OnAntiMonitor() 
{
  AfxMessageBox("||||使用复制术前||||", MB_ICONINFORMATION);
  
  Hello(0x0);
  
  AfxMessageBox("--==使用复制术后==--", MB_ICONINFORMATION);
  
  Shadow(0x0);
}

// 修改EIP的,我就不添加了.
一字节异常
  One Byte Patch. 看到这个字节.相信大家,第1时间想到的就是. 会不会是 0x74 改成 0x75
0x85 改换 0x84  或者改成 NOP 这类的 单字节指令呢?
  当然不是了.这里的1字节,肯定会被补丁检测到的.但是,这是一个无用的字节,我们可以改成 0xcc .. 有的人就会想了, 0xcc 那不是 int3异常吗? 答案是的. 我们的目的就是让程序产生异常. 产生异常后,我们再接管异常处理. 即可. 听起来,是不是很神奇.~~
  MS给出了多种异常处理的方案.其中常用的就三种.

VEH  UEF  SEH (详细的区别和功能大家可以GG,这里就不赘述了.)

其中SEH 和 UEF  一个范围限制,一个权限限制.所以本章使用 VEH.向量异常处理.

代码:
// exception handler
LONG WINAPI VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
  if(ExceptionInfo->ExceptionRecord->ExceptionCode == 0x80000003)
  {
    MessageBox(NULL, "Exception Handler~", 
      "By 半斤八兩", MB_ICONINFORMATION);

    ExceptionInfo->ContextRecord->Eip = 0x60000008;

    return EXCEPTION_CONTINUE_EXECUTION;
  }

  return EXCEPTION_CONTINUE_SEARCH;
}

// function : magic jump 0x74 jexx
void IsOneByteException(int nParameter)
{
  if(nParameter)
  {
    MessageBox(NULL, "Register successful~", 
      "By 半斤八兩", MB_ICONINFORMATION);
  }
  else
  {
    MessageBox(NULL, "Register failure~", 
      "By 半斤八兩", MB_ICONERROR);
  }
}

// one byte exception
void CPage3::OnAntiMonitor3() 
{
  LPVOID lpMem = VirtualAlloc((LPVOID)0x60000000, 0x1000, 
    MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

  LPVOID (WINAPI *AddVectoredExceptionHandler)(ULONG, LPVOID);

  HMODULE hModule = GetModuleHandle("Kernel32.dll");

  AddVectoredExceptionHandler = 
    (void *(__stdcall *)(unsigned long,void *))
    GetProcAddress(hModule, "AddVectoredExceptionHandler");

  // 这里是异常处理, 注释掉这里,程序会异常.
  AddVectoredExceptionHandler(1, VectoredHandler);

  memcpy(lpMem, IsOneByteException, 0x1000);

  DWORD dwAddress = 0x60000000;

  // 这里是模拟的Patch,注释掉这里,将注册失败
  *(PBYTE)(dwAddress+6) = 0xcc;

  _asm 
  {
    push 0
    mov eax, dwAddress
    call eax
  }

  VirtualFree(lpMem, 0, MEM_RELEASE);
}
// 本例因没有对Debug 跳转表做处理,所以只适合Release 编译,


我们来看一下运行结果.

图片4.png


在这里,我们看到了一片的数据,而没有看到 0xcc, 这个原因,我们前面有分析过了,因为本内存监视程序
在跨进程取内存时 没有使用 Kernel32!VirtualQuery 做检查分页属性,所以会读取一片错误信息.弱暴了~~~


End...

测试Bin的源码.7z (322.96 KB, 下载次数: 240)
反内存监视集绵.doc (203.5 KB, 下载次数: 214)
内存监视器.7z (526.2 KB, 下载次数: 905)
内存写入监视器 V2.0.exe.7z (603.96 KB, 下载次数: 677)
文章中的测试Bin.7z (456.07 KB, 下载次数: 139)



免费评分

参与人数 9热心值 +9 收起 理由
liushuaijie123 + 1 已答复!
loop_ + 1 谢谢@Thanks!
ttcctt + 1 热心回复!
蚯蚓翔龙 + 1 好文
wuaipojie + 1 谢谢@Thanks!
qishbi + 1 鼓励转贴优秀软件安全工具和文档!
Ruin + 1 感谢发布原创作品,[吾爱破解论坛]因你更精.
Peace + 1 我很赞同!
Sound + 1 欢迎分析讨论交流,[吾爱破解论坛]有你更精.

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

头像被屏蔽
苏烟式 发表于 2014-7-22 18:44

一点都看不懂 八爷直接说怎么防止E语言内存补丁被偷取代码不就好了嘛
cqwzhc 发表于 2015-2-4 22:37
好厉害!看懂了一些.大概是因为没有戳中兴趣点!
对HOOK很感兴趣.其它的就略深了点.
Chief 发表于 2012-7-22 01:42
a13686593572 发表于 2012-7-22 01:43
本帖最后由 a13686593572 于 2012-7-22 02:10 编辑

不爽啊,这样都被抢了沙发,作为一个夜猫子~~
果断将网页保存下来!
 楼主| 半斤八兩 发表于 2012-7-22 01:55
天呐~~
这贴子编辑的..
大家看文档吧.
怎么排版都排不好.

点评

老湿,据说使用code代码可以使排版更加轻松  发表于 2012-7-23 05:51
1354669803 发表于 2012-7-22 02:07
一点都看不懂 八爷直接说怎么防止E语言内存补丁被偷取代码不就好了嘛
boteli 发表于 2012-7-22 02:36
八爷的东西,不是我们这等人可以看的。
wantdns 发表于 2012-7-22 03:00
好像很厉害
yyjpcx 发表于 2012-7-22 03:03
有讲易语言的HOOK  ?
elfchery 发表于 2012-7-22 04:02
看来排版确实难啊
rmknct 发表于 2012-7-22 06:48
前排。加油,哥
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-19 20:52

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表