一种躲避运行时代码校验的方法

As400 rpgle程序编译错误排查 As400 rpgle程序编译错误排查 1 CRTBNDRPG PGM(mylib/test1 ) SRCFILE(MYSRCLIB/MYSRC) SRCMBR(test1) BNDDIR(mylib/mybnddir) 这里假设我的程序是 test1,用户是user1,编译失败,如下图 2 通过wrksplf 查看对应的程序编译信息 spooled file ... 阅读详情
作 者: NetRoc
时 间: 2008-01-25,15:16
链 接: 
http://bbs.pediy.com/showthread.php?t=58896

一种躲避运行时代码校验的方法


cc682/NetRoc
关键字:代码校验,内存补丁,hook
    我们有时候需要对运行中的程序打内存补丁,或者对它的代码挂一些钩子之类的工作。但是现在相当多软件进行了运行时的代码检测。一旦发现内存中的代码被修改掉,就会进行处理。本文介绍了一种比较特别的办法,用于通过这些检测。
    首先需要说一下做运行时代码校验的方法。一般来说,校验者需要取得当前模块的基地址,通过分析PE结构,获得代码节的偏移和大小,然后对内存中的代码进行CRC或者其他的一些校验。
这其中有个很大的问题,校验者默认了通过这种方式取得的代码节就是当前被使用到的代码,但是事实却不一定如此。一般编译器正常生成的代码,绝大部分跳转和call语句都使用相对地址,因此,我们完全可以把代码节或者整个exe文件映像复制到内存其他地方,并操作进程内的所有线程,使得它执行在新复制的那份代码中。这样,校验者仍然在扫描旧的地址,新的那份我们就可以随意修改了。
    由于一旦进程开始执行,并且创建其他线程之后,我们通过取得线程Context获得的EIP,多半在系统代码中间,所以难以改变。唯一的方法就是,在exe的EntryPoint被执行前,将EntryPoint重定向到新的代码,并Hook CreateThread,将新线程也重新定位。可以通过下面几个步骤来实现:
1、  如果想处理的进程为a.exe,并且a.exe是由b.exe创建的,那么我们需要hook掉b.exe的进程创建函数,一般是CreateProcess。
2、  在Hook的CreateProcess中,以CREATE_SUSPENDED标志创建a.exe。并向a.exe中注入我们的dll,等待这个dll完成处理之后才ResumeThread a.exe的主线程。
3、  注入的dll需要完成几件事。首先要获得主模块的基地址和大小,并分配足够的空间,将原始映像复制过去。然后Hook掉EntryPoint,并Hook CreateThread,最后恢复a.exe主线程。
4、  Hook掉的EntryPoint中,恢复被Hook的EntryPoint代码,防止在后面被检查出来,然后jmp到新分配的代码区域即可。
5、  Hook的CreateThread中,重新计算代码线程函数地址,并修改后创建。这样,所有线程就都在新分配的代码中执行了。
下面是注入的dll的实现代码:

pfnCreateThread g_pCreateThread = ::CreateThread;
PBYTE  g_pbyNewImage = NULL;

#pragma pack(push,1)
typedef struct _PUSH_RETN
{
  BYTE byOpcodePush;//0x68
  DWORD dwRetnAddr;
  BYTE byOpcodeRetn;//0xC3
}PUSH_RETN, *PPUSH_RETN;
#pragma pack(pop)

BYTE g_abyOldEntry[6] = {0};
PBYTE g_pbyOldEntry = 0;
PBYTE g_pbyNewEntry = 0;

//这里使用Detours库Hook掉CreateThread。
BOOL HookThreadCreate()
{
  DetourTransactionBegin();
  DetourUpdateThread( GetCurrentThread());


  if( DetourAttach( &(PVOID&)g_pCreateThread, Hook_CreateThread) != NO_ERROR)
  {
    DebugOut( TEXT( "Hook CreateThread fail/r/n"));
  }

  if( DetourTransactionCommit() != NO_ERROR)
  {
    DebugOut( TEXT( "Hook fail/r/n"));
    return FALSE;
  }
  else
  {
    DebugOut( TEXT( "Hook ok/r/n"));
    return TRUE;
  }
}

//Hook的CreateThread里面,重新计算lpStartAddress地址,并按这个地址来创建
HANDLE WINAPI Hook_CreateThread(
                LPSECURITY_ATTRIBUTES lpThreadAttributes,
                SIZE_T dwStackSize,
                LPTHREAD_START_ROUTINE lpStartAddress,
                LPVOID lpParameter,
                DWORD dwCreationFlags,
                LPDWORD lpThreadId
                )
{
  PBYTE pfn = (PBYTE)lpStartAddress;
  HMODULE hMod = ::GetModuleHandle( NULL);
  pfn = g_pbyNewImage + (pfn - (PBYTE)hMod);
  HANDLE hThread = g_pCreateThread( lpThreadAttributes, dwStackSize, (LPTHREAD_START_ROUTINE)pfn, lpParameter, dwCreationFlags, lpThreadId);
  return hThread;
}

//Hook掉的入口点,恢复旧代码并跳转到新分配的代码空间
__declspec( naked ) VOID Hook_EntryPoint()
{
  //_asm int 3
  for ( DWORD i = 0; i < sizeof(g_abyOldEntry); i++)
  {//恢复旧的代码
    g_pbyOldEntry[i] = g_abyOldEntry[i];
  }
  _asm jmp g_pbyNewEntry;
}

//Hook掉入口点
VOID HookEntryPoint()
{
  DebugOut( _T( "In HookEntryPoint/r/n"));

  HMODULE hMod = ::GetModuleHandle( NULL);
  PIMAGE_DOS_HEADER pstDosHeader = (PIMAGE_DOS_HEADER)hMod;
  PIMAGE_NT_HEADERS pstHeader = (PIMAGE_NT_HEADERS)((PBYTE)hMod + pstDosHeader->e_lfanew);
  DWORD dwEntryRVA = pstHeader->OptionalHeader.AddressOfEntryPoint;
  PBYTE pbyRVA = (PBYTE)(hMod) + dwEntryRVA;
  PPUSH_RETN pstHook = (PPUSH_RETN)pbyRVA;
  memcpy( g_abyOldEntry, pbyRVA, sizeof(PUSH_RETN));
  pstHook->byOpcodePush = 0x68;
  pstHook->dwRetnAddr = (DWORD)Hook_EntryPoint;
  pstHook->byOpcodeRetn = 0xC3;

  g_pbyOldEntry = pbyRVA;
  g_pbyNewEntry = g_pbyNewImage + dwEntryRVA;
  DebugOut( _T("New image base = 0x%X, New EntryPoint = 0x%X/r/n /
        Old image base = 0x%X, Old EntryPoint = 0x%X/r/n"), g_pbyNewImage, g_pbyNewEntry, hMod, g_pbyOldEntry);
  DebugOut( _T( "HookEntryPoint OK/r/n"));
}

//在DllMain里面Process Attach的时候调用
VOID OnAttachProcess()
{
  HMODULE hMod = ::GetModuleHandle( NULL);
  DWORD dwSize = GetImageSize();
  g_pbyNewImage = new BYTE[dwSize];

  DWORD dwOldProtect = 0;
  VirtualProtect( g_pbyNewImage, dwSize, PAGE_EXECUTE_READWRITE, &dwOldProtect);
  VirtualProtect( (LPVOID)hMod, dwSize, PAGE_READWRITE, &dwOldProtect);
  memcpy( g_pbyNewImage, (LPVOID)hMod, dwSize);

  HookEntryPoint();
  HookThreadCreate();
}

//获得主模块映像大小
DWORD GetImageSize()
{
  HANDLE hShot = NULL;
  BOOL blResult = FALSE;
  MODULEENTRY32 stInfo = {0};
  stInfo.dwSize = sizeof( MODULEENTRY32);
  TCHAR tszTmp[MAX_PATH] = {0};

  ::GetModuleFileName( GetModuleHandle(NULL), tszTmp, MAX_PATH);
  _tcslwr( tszTmp);
  size_t s = _tcslen(tszTmp);
  for ( DWORD i = 0; i < s; i++)
  {
    if ( tszTmp[s - i] == '//')
    {
      s = s - i + 1;
      break;
    }
  }
  hShot = ::CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, ::GetCurrentProcessId());
  if ( INVALID_HANDLE_VALUE == hShot)
  {
    DebugOut( _T( "CreateToolhelp32Snapshot fail.Err=%d/r/n"), GetLastError());
    return 0;
  }

  blResult = ::Module32First( hShot, &stInfo);
  while ( blResult)
  {
    _tcslwr( stInfo.szModule);
    if ( _tcscmp( stInfo.szModule, tszTmp + s) == 0)
    {
      CloseHandle( hShot);
      return stInfo.modBaseSize;
    }
    blResult = ::Module32Next( hShot, &stInfo);
  }
  CloseHandle( hShot);
  return 0;
}
     这种方法对加壳之后的exe作用有限,因为Hook的并不是真实的OEP。这样做常常会造成壳执行过程中,或者跳转到OEP之后迅速崩溃。本文仅仅是介绍一种思想,有时候巧妙的构思可以使得复杂问题很快得到解决。
    改良之后,这个技巧可以用到不少地方,呵呵
从AS400上导出RPGLE程序代码 打开IBM Personal Communications 1.定位需要导出的RPGLE程序 1.1打开我们要导出的RPGLE程序,这是为了在第二个步骤的时候方便设置参数。 1.2或者你也可以不用打开你的RPGLE程序,如果你熟悉你的库和里面的文件的话。 2.设置导出属性 菜单栏中:操作—>接受来自主机的文件,进入下面的窗口: 图1 2.1 在“库/文件(成员)”下的空白处按 阅读详情

相关推荐

文件上传校验绕过总结

文件上传校验 前端javascript校验(一般只校验后缀名) 服务端校验 文件头content-type字段校验(image/gif) 文件内容头校验(GIF89a) 后缀名黑名单校验 后缀名白名单校验 WAF设备校验(根据不同的WAF产品而定) 其他绕过方式 1.客户端校验         ...

小水池 5730

AS400-RPG-01

RPG 的全称:Report Program Generator能 COPY、修改、编译源代码(RPGLE、CLP),并能运行编译后的程序能 COPY、修改、编译文件(PF、LF、PRTF、DSPF)对数据文件(PF)有简单的认识(FIELD -> RECORD -> PF),并知道 LF 与 PF 的对应关系编写 HELLO WROLD0003.00 C0004.00 C调用程序 call + 程序名 屏幕反白出现 “HELLO WORLD”字样。

码农成长记 1925

web渗透—xss攻击防御

1、基于特征的防御 XSS漏洞和著名的SQL注入漏洞一样,都是利用了Web页面的编写不完善,所以每一个漏洞所利用和针对的弱点都不尽相同。这就给XSS漏洞防御带来了困难:不可能以单一特征来概括所有XSS攻击。 传统XSS防御多采用特征匹配方式,在所有提交的信息中都进行匹配检查。对于这种类型的XSS攻击,采用的模式匹配方法一般会需要对“javascript”这个关键字进行检索,一旦发现提交信息中包

dongxie_tk的博客 1511

AS400 之RPGLE Part 1

RPG在写程序之前关于程序开发,想强调的是:业务流程先于程序——程序是给计算机看的,流程是人执行的;程序是死的,而流程则是一个相对模糊的东西。所以在写程序前,要先弄清楚自己到底要做什么,然后将自己的思想表达给计算机——这个过程就是CODING。RPG的长处在于数据库处理,界面交互能力倒是其次。所以最开始需要了解一些关于数据库文件的相关知识(这里就只强调跟400有关的)。在下面的操作中,假设朋友已经会用STRPDM,会文件的COPY,修改。数据文件PF:COPY一个已经有的比较快点。然后修改一下里面的数据,就

weixin_41447442的博客 2496

安卓逆向实战:LSPatch如何绕过签名校验与内存检测(附检测方法)

本文深入剖析了安卓热修复工具LSPatch的核心隐匿机制,重点解析其如何通过劫持AppComponentFactory、匿名内存加载和伪造LoadedApk等技术,绕过签名校验与内存检测。同时,文章提供了从Java层到Native层的多层次、可操作的检测方法论,包括检查类加载器、扫描匿名内存区域以及监控关键系统调用,为安卓应用安全加固与逆向分析提供了实战指南。

weixin_29327525的博客 377

RPGLE 事务处理程序实例代码

如何使用AS400 中RPGLE 的事务控制操作与隔离级别

一种躲避运行时代码校验的方法(Reload And Run)

转自看雪。    我们有时候需要对运行中的程序打内存补丁,或者对它的代码挂一些钩子之类的工作。但是现在相当多软件进行了运行时的代码检测。一旦发现内存中的代码被修改掉,就会进行处理。本文介绍了一种比较特别的办法,用于通过这些检测。     首先需要说一下做运行时代码校验的方法。一般来说,校验者需要取得当前模块的基地址,通过分析PE结构,获得代码节的偏移和大小,然后对内存中的代码进行CRC

zfdyq 2708

CH341的I2C流模式(StreamI2C)到底怎么用?从EEPROM读写到自定义设备,一篇讲清参数配置逻辑

本文详细解析了CH341的I2C流模式(StreamI2C)的使用方法,从EEPROM读写到自定义设备的参数配置逻辑。通过深入分析I2C协议基础、StreamI2C参数映射关系及典型设备配置示例,帮助开发者快速掌握StreamI2C的应用技巧,解决实际开发中的常见问题。

weixin_30932215的博客 447

Linux Rootkit躲避内核检测

Rootkit在登堂入室并得手后,还要记得把门锁上。 如果我们想注入一个Rootkit到内核,同时不想被侦测到,那么我们需要做的是精妙的隐藏,并保持低调静悄悄,这个话题我已经谈过了,诸如进程摘链,TCP链接摘链潜伏等等,详情参见: https://blog.youkuaiyun.com/dog250/article/details/105371830 https://blog.youkuaiyun.com/dog250/...

TCP/IP 5354

宝塔面板添加站点及运营商SSL免费证书的申请与使用

本文详细介绍在宝塔面板下如何正确添加站点、配置站点,及在运营商如何申请免费SSL证书,将其配置到宝塔站点里。 给你的网站加一把锁,教你免费SSL证书申请,配置到自己的网站中。 环境:Ubuntu server 18 工具:宝塔控制面板 平台:阿里云 Web服务器:Nginx 宝塔:https://www.bt.cn/ 阿里云:https://www.aliyun.com/ 腾讯云:https://cloud.tencent.com/ 要求: 1.宝塔控制面板搭建完毕。 2掌握域名解析的原理。 3.了解ht

贺兰山长老 5058

opencv-meshgrid

使用opencv::repeat函数和std::iota函数完成meshgrid功能。 X Y Z 通俗的说就是根据Z的大小生成X矩阵,Y矩阵

yuegooxi的博客 1607

小游戏制作QQ宠物系列 ----躲避老鼠

点击鼠标左键,控制左侧人物跳跃躲避右侧老鼠的撞击,成功躲避老鼠的撞击则积分+1;老鼠的速度是随机的。本文章是小游戏制作系列,小游戏制作QQ宠物系列 ----躲避老鼠。js代码重点方法(未优化版)

weixin_45101949的博客 600

从零开始,手把手教你用RTL代码设计一个简单的8位CPU(附状态图与ALU设计)

本文详细介绍了如何从零开始设计一个简单的8位CPU,包括状态机设计、ALU实现与RTL编码实战。通过具体的指令集分析、状态图设计和Verilog代码示例,帮助读者深入理解CPU工作原理,并掌握RTL代码编写技巧。特别适合计算机系统学习者和硬件设计爱好者。

weixin_30847063的博客 320

十六、代码校验(6)

● 剖析和优化 ○ 优化准则 ● 风格检测 ● 静态错误分析 ● 代码重审 ● 结对编程 ● 重构 ○ 重构基石 ● 持续集成 ● 本章小结

走在bug的路上 6793

FineReport 动态列切换统计维度

目标: 自己选择统计维度进行数据的汇总统计数据集参数实现动态列。

Davina_yu的博客 1053

工业级实战:C#实现欧姆龙PLC FINS-TCP通信(协议解析+完整源码+避坑指南)

// FINS主命令码+子命令码枚举(工业级常用指令)ReadData = 0x0101, // 读取数据WriteData = 0x0102, // 写入数据ReadPlcStatus = 0x0601, // 读取PLC状态ResetPlc = 0x0605 // 复位PLC(慎用)/// 欧姆龙PLC数据区枚举(工业常用)CIO = 0x30, // CIO区(核心数据区)WR = 0x31, // 工作区HR = 0x32, // 保持区。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 656
上一篇: Rootkit Unhooker驱动逆向分析
下一篇: 也谈rootkit 注册表信息隐藏
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值