通用且简单的扩充和改造PE中的call的功能的方法

关于PE病毒编写的学习(六)——关于PE文件结构操作的程序编写   对PE文件结构的各个值定义和作用,这里不提了,网上资源很多,百度一下就好了。所以,本章只说一下,作为代码编写者对PE文件结构操作的方法和技巧。还是通过改进代码,来体会一下吧。  你应该记得前面的BOOL IsPEFile(HANDLE hFIle) 这个函数吧,它的作用是判断文件是否为PE格式文件。它把文件句柄作为参数,虽然许多函数需要文件句柄这个参数,但是作为对PE文件结构操作的函数,这样做是不恰当的,因为如果这样做就要频繁的使用SetFilePointer()、ReadFile()、WriteFil 阅读详情
技术基础:会编写加壳程序、了解PE结构、X86指令系统。
开发工具:MASM、VC++

1、我们有时候在改造一个PE的时候,对其中的某些call要做大量的改动,这时
候,往往通过手动增加一个节,在这个节中完成这个call的改造,再返回原来的地方继续执行。此法虽然可行,但工作量实在太大,且调试十分不方便。

2、“通用简单的方法”之实现原理:
  使用jmp far address 和 call far address的机器码长度相同,且仅仅第一字节不同的原则,jmp第一字节是0xE9,而call第一字节是0xE8,后面4字节相同。
  这种方法就是:通过给PE加个小壳,同时在加壳的时候,将PE中要改造的call 修改成调用外壳中的call,在外壳中的call中调用原来PE中的call,而外壳中的call我们在编写的时候较容易实现功能扩充和改造。
  在外壳中的call中,我们有两种扩充功能的方式:
  (1)在调用原来PE中的call之前扩充功能;
  (2)在调用原来PE中的call之后扩充功能;
  扩充功能可以直接在外壳call中实现,但较难调试和复杂。
  简单通用的方法是这个外壳中的新call仅仅完成:
    1、加载一个外部补丁DLL;
    2、调用这个外部补丁DLL中的函数,完成原来PE中的call的功能扩充和改造。
    3、调用原来PE中的call的;
  是先调用原来PE中的call还是后调用,视具体需要决定。这样我们就可以很简单的写扩充的功能代码了,因为这个外部补丁DLL可以用高级语言来写,且调试十分简单。

3、外壳中函数的具体实现:
  (一) 首先,在外壳中设定几个变量:
  (1) 保存外部DLL的各个函数地址的变量(DWORD):
    pfnExtPatchFun_1--pfnExtPatchFun_n,n为多大具体看要改造PE中几个函数。
  (2) 保存原来PE中的call的“地址差”变量(DWORD):
    dwOrgCallDifference_1--dwOrgCallDifference_n,n为多大具体看要改造PE中几个函数。
    dwOrgCallDifference_x = 原来call的机器码的后4个字节(双字)。
  (3) 保存原来PE中的call的RVA地址的变量(DWORD):
    dwOrgCallRVA_1--dwOrgCallRVA_n,n为多大具体看要改造PE中几个函数。
  (4) 保存外部DLL的hModule的变量(DWORD):
    hExtPatchDll。
  ;-----------------------------------------------------------------
  举例说明:
    如:原来PE的ImageBase=0x00400000,且有下面代码(IDA显示):
      .
      .
      004010CB 50           push   eax
      004010CC 57           push   edi
      004010CD E8 CE 08 00 00   call   sub_4019A0 ;需要改造的call,注意机器码后4字节
      004010D2 85 C0         test   eax, eax
      .
      .
    则:dwOrgCallRVA_1 = 004010CD - 0x00400000 = 0x000010CD
      dwOrgCallDifference_1 = 0x000008CE
  ;------------------------------------------------------------------
  ;
  (二) 在外壳程序中编写这些call,需要改造几个函数,就有几个call,格式如下:
;*****************************************************************************
;---------------------------------------------------;
; 外壳中的call,用于替换原来PE中的call           ;
;---------------------------------------------------;
align 4
wjq_API_SMC_Label_1:
GeneralPEShellCall_1   proc
;{
  ;原PE中的call的入口参数,我们通过堆栈传递到Dll中的新call,此时需注意EBP在本call中不要改变。
  @nPara1 EQU [ebp+0ch] ;原来PE中的Call的参数,也可以通过esp取得
  @nPara2 EQU [ebp+8h]
  ;
  pusha
  call GetPatchDllFunctions ;获取外部DLL中的所有函数地址并保存
  call GetAddressDifference ;获取地址差
  .if [pfnExtPatchFun_1+eax] != 0 ;如果功能函数1实现了,就调用
    push @nPara1 ;原来PE中的Call的参数
    push @nPara2
    call [pfnExtPatchFun_1+eax] ;调用外部DLL中的补丁call
  .endif
  ;------------调用原来的call-------------------------
  call GetAddressDifference ;获取地址差
  lea esi,[TempCall_1+eax] ;现在call的VA
  sub esi,[Image_Base+eax] ;转换成RVA
  sub esi,[dwOrgCallRVA_1+eax] ;减去原来PE中的call的RVA = 两个call的地址差
  ;
  mov ebx,[dwOrgCallDifference_1+eax] ;原来call xxxxxxxx 指令的偏移差
  .if ebx < 0 ;负数,原来call向上调用
    add ebx,esi
  .else     ;正数,原来call向下调用
    sub ebx,esi
  .endif
  ;
  mov [TempCallDifference_1+eax],ebx ;修正后的差值
  popa
  ;
  ;下面等同于一个 jmp far xxxxxxxx 语句,调用原来的call
  ;这里之所以采用这种方式,而不是用call,是为了通用,因为PE中的call有些是__stdcall,有些是___cdecl,如果一概采用call实现,要考虑出栈问题,不是很通用了。
TempCall_1       db 0E9h
TempCallDifference_1 dd 0
  ret
;}
GeneralPEShellCall_1 endp

;---------------------------------------------------------------------
; 获取外部DLL中的所有函数地址并保存
; 此函数中使用的W_LoadLibraryA等W_开始的函数,是外壳自建引入表是实现的
;---------------------------------------------------------------------
GetPatchDllFunctions proc
  pusha
  call GetAddressDifference
  mov edx,eax
  .if [hExtPatchDll+edx] == 0 ;如果已经装载了,就直接返回。
    ;
    lea ebx,[pExtPatchDll+edx]
    push edx ;保存地址差
    ;--------------------------
    push ebx
    call [W_LoadLibraryA+edx]
    ;--------------------------
    pop edx ;恢复地址差
    mov [hExtPatchDll+edx],eax
    .if eax != 0
      ;-------------;
      ; function_1 ;
      ;-------------;
      push   edx ;保存地址差
      lea   ebx,[pExtPatchFun_1+edx]
      push   ebx ; 功能名的字符偏移
      push   dword ptr [hExtPatchDll+edx] ; 模块的句柄
      call   [W_GetProcAddress+edx] ; 调用Kernel32!GetpProcAddress以获得功能调用地址
      pop   edx ;恢复地址差
      mov   [pfnExtPatchFun_1+edx],eax
      ;-------------;
      ; function_2 ;
      ;-------------;
      push   edx ;保存地址差
      lea   ebx,[pExtPatchFun_2+edx]
      push   ebx ; 功能名的字符偏移
      push   dword ptr [hExtPatchDll+edx] ; 模块的句柄
      call   [W_GetProcAddress+edx] ; 调用Kernel32!GetpProcAddress以获得功能调用地址
      pop   edx ;恢复地址差
      mov   [pfnExtPatchFun_2+edx],eax
      .
      .
      ;-直到取得n个函数地址。
    .endif
  .endif
  popa
  ret
GetPatchDllFunctions endp

;-------------------------------------------------------------------
;该函数返回:地址差
;-------------------------------------------------------------------
align 4
GetAddressDifference proc
  call @F
@@:
  pop eax         ;获得实际偏移
  sub eax, offset @B ;减去偏移=地址差
  ret
GetAddressDifference endp
;*****************************************************************************

4、加壳部分的实现:
  相对来说,加壳部分的实现加简单,只要按照上面计算call的偏移差的方法,计算出新的偏移差,改写这个call的机器码的后4字节为新的偏移差,使其调用我们外壳中的call就可以了。
;
具体计算过程如下:
mov eax,dwOrgCallRVA_1   ;原来PE中call处的RVA,对应上面例子就是:dwOrgCallRVA_1 = 0x000010CD
mov edx,Sheller_RVA     ;外壳程序入口的RVA
add edx,API_CALL_ADJUST_1 ;API_CALL_ADJUST_1 = wjq_API_SMC_Label_1 - Sheller_Start
sub edx,eax   ;差值
sub edx,5   ;减去Call指令的长度5
mov eax,edx   ;此时后的eax就是要修改的原PE中的call的后4字节(偏移差),对应上面例子就是:004010CD E8 CE 08 00 00 的 CE 08 00 00,变为双字就是0x000008CE,这个值改为eax中的值。

需要注意的是:API_CALL_ADJUST_1的计算要使用wjq_API_SMC_Label_1标号,而不是函数名字GeneralPEShellCall_1。


5、外部DLL的编写格式(使用高级语言,快速编写,十分方便的调试):
//具体参数格式,请根据原来PE中的call的入口参数确定
//调用约定请参照外壳中的call,主要看出栈规则,我用的是__stdcall,也可以用___cdecl,主要根据外壳call中的使用方式,这个不能错,否则堆栈就乱套了。
extern "C" DWORD __stdcall ExtPatchFun_1(DWORD nPara1,DWORD nPara2)
{
//实现功能扩充。。。
return;
}
//
extern "C" DWORD __stdcall ExtPatchFun_2(DWORD nPara1,DWORD nPara2)
{
//实现功能扩充。。。
return;
}

6、关于补丁DLL的卸载
  我们在外壳call中只加载外部DLL,但不去卸载。这会不会有问题呢?回答是否定的,因为PE在结束进程时候,会自动卸载所有加载到本进程的DLL,所以,不用关心这个DLL不会卸载掉。而且,我们通过只加载方式使用这个DLL,会给我们带来很大方便,因为,只要在一处加载了这个外部DLL,我们就可以在原PE的任何地方调用其中的函数。

7、上面介绍的是先调用功能扩展部分,再调用原来的call。反之实现的方法类似,可自行发挥。

排版不是很好,凑合着看吧。。呵呵。
                                    Spring.W 
西门子S7-1200与S7-200SMART的PROFINET通讯实战:从GSD文件生成到数据监控 本文详细解析了西门子S7-1200与S7-200SMART之间实现PROFINET通讯的完整实战流程。核心内容包括将S7-200SMART配置为智能IO设备、生成并安装GSD文件、在TIA Portal中进行网络组态与地址映射,最终实现双向数据交换与监控。该方案为整合新旧设备、构建灵活高效的工业自动化系统提供了可靠路径。 阅读详情

相关推荐

飞腾Ubantu22.04.3安装OpenNebula测试

因OpenneBula官方镜像源只有AMD架构的镜像包不存在ARM的镜像包,借此用源码编译进行测试。

坚持就是胜利的博客 1009

使用PE Explorer修改pe文件中图片、字符串资源

使用PE Explorer修改pe文件中图片、字符串资源,以windows扫雷游戏为例

qqww155的专栏 3908

用废旧汽车电池DIY家用UPS:手把手教你低成本搭建Wi-Fi不断电系统

本文详细介绍了如何利用废旧汽车电池DIY低成本家用UPS系统,确保Wi-Fi设备在断电时持续供电。通过精选材料、电路搭建和安全防护,实现高效稳定的电源转换与智能切换,成本仅为市售UPS的三分之一。特别适合家庭办公和远程工作者提升网络供电可靠性。

weixin_29200485的博客 463

打造快速关机迷你程序(改造PE文件---手搓)

晚上没什么事情做,正好又断网了,无聊就做了个迷你型的程序。起初是想用纯手搓,C32结合OD来做,但是输入表那一块我想不起来了,又不能上网查,只好偷个懒,用VC先做个典型的小文件,然后我再去修改。这个小东西只有一个功能,快速关机,相信很多朋友都知道XP系统的任务管理器,有一个关机的

SomeTimes 1577

基于ZYNQ7020芯片(黑金开发板AX7020)之AXI-FULL DDR的PL与PS交互操作。

在该工程中我们使用了PS端的串口接收功能,PS端串口接收和发送分别有一个64字节的FIFO,但是因为串口接收阈值设置为16,所以固定接收16个字节数据后才会发生中断。此测试工程得到以前知识的总结和各大网友的支持,只希望可以为学习的你更进一步,大家一起学习才是真的开心。加油,国产芯片之路。

landyjzlai的博客 3189

VS2022命令行编译C++报错?一招解决‘fatal error C1034: iostream: 不包括路径集‘

本文针对VS2022命令行编译C++时常见的'fatal error C1034: iostream: 不包括路径集'错误,深入剖析了其根源在于环境变量缺失。文章提供了永久配置用户环境变量(PATH、INCLUDE、LIB)的一劳永逸解决方案,确保在任何终端中都能直接使用cl命令进行编译,并给出了进阶配置与常见错误排查指南。

weixin_29011667的博客 304

深入剖析PE文件(续1)---扩展知识

 不赖猴的笔记,转载请注明出处。一、        Windows加载器加载器读取一个PE文件的过程如下:1. 先读入PE文件的DOS头,PE头和Section头。2. 然后根据PE头里的ImageBase所定义的加载地址是否可用,如果已被其他模块占用,则重新分配一块空间。3. 根据Section头部的信息,把文件的各个Section映射到分配的空间,并根据各个Section定

A00553344的专栏 3216

【开源】STM32+ESP8266+MQTT多传感器数据上云OneNET(易拓展,带操作系统FreeRTOS)

【开源】STM32+ESP8266+MQTT多传感器数据上云OneNET(易拓展) 文章目录【开源】STM32+ESP8266+MQTT多传感器数据上云OneNET(易拓展)1. 相关连接1.1 本项目相关连接1.2 无操作系统简易版(旧版,有OneNET云平台设备创建和应用配置,本文不再赘述):2. 具体功能3. 硬件环境4. 云平台环境配置5. 接线6. 功能展示6.1 数据流6.2 APP应用管理6.3 串口数据7. 源码详解7.1 源码文件解析7.2 服务器与wifi配置相关7.3 源码框架7.4

ssssadw的博客 2万+

JS中的call()方法和apply()方法用法总结

JacvaScript中的call()方法和apply()方法, 1. 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。 2. 相同点:这两个方法的作用是一样的。都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。

Hei, Welcome To vickie's Blog,Good Good Study, Day Day Up ~ 18万+

PSO-BP和BP多输入多输出回归预测模型 matlab (多输入多输出)

PSO-BP和BP多输入多输出回归预测模型 matlab (多输入多输出)

算法如诗的博客 257

锐捷 MPLS VPN 配置

ISP内部配置 , PE设备和CE设备之间的接口不开启 ( 只在物理接口上配置 , loopback 口不配置 )最后一步关闭 ipv4 单播 , 如果你发现路由奇奇怪怪的 , 可以尝试一下。both --> 同时配置 import export 值。绑定后接口会重置 , 且接口的路由会转到VRF路由表中。这里基本上绑定了 vrf 实例的接口都需要宣告进去。vpnv 4 通道是用来传递 mpls 路由的。先建立邻居管理,建立VPNV4通道。这里要做双向引入 , 才会有路由。export 导出值。

dekangzou的博客 1710

JS中的call()方法和apply()方法和slice()用法总结

1. 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。 2. 相同点:这两个方法的作用是一样的。 都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。 一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。 call()方法使用示例: //例1 &lt;......

Jack huang 的技术笔记 822

【论文汇总】Diffusion Models视频生成/视频编辑/可控视频生成/跨模态视频生成

本文总结了Diffusion Models视频生成领域相关的工作,目前共收录142篇,持续更新中。

沉迷单车的追风少年 1671

JS中的call()方法和apply()方法

转发——https://blog.youkuaiyun.com/ganyingxie123456/article/details/70855586 JacvaScript中的call()方法和apply()方法,而在某些时候这两个方法还确实是十分重要的,那么就让我总结这两个方法的使用和区别吧。 1. 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。 2. 相同点:这两个方法的作用...

进击的葱 549

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Js apply方法和call方法的详解

一开始看到javascript的函数apply和call方法的时候,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这里我做如下笔记,希望和大家分享… 如有什么不对的或者说法不明确的地方希望读者多多提一些意见,以便和大家共同提高… 主要我是要解决一下几个问题: 1,apply和call的区别在哪里 2, 什么情况下用apply,什么情况下用call 3,apply的其他巧妙用法(一般在什么情况下可以使用apply) 一、定义: apply:方法能

qq_44552416的博客 1394

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

【JavaScript】call()方法和apply()方法用法总结

什么是call()和apply(): 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。 1.apply与call是更改对象的内部指针,即改变对象的this指向的内容; 2.call与apply的第一个参数都是要传入给当前对象的对象,及函数内部的this,后面的参数都是传递给当前对象的参数。 call(): 语法:obj1.call(obj2[,param1,param2,...

Dream_Lee的博客 1354

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

call和calling的用法_call()方法和apply()方法用法总结

1. 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。2. 相同点:这两个方法的作用是一样的。都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。call()方法使用示例://例1window.color= 'red';...

weixin_39935654的博客 489

call()方法和apply()方法总结

1. 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。2. 相同点:这两个方法的作用是一样的。都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。call方法使用示例://例1window.color='red';doc...

weixin_39034984的博客 466

call()方法和apply()方法用法总结

1. 每个函数都包含两个非继承而来的方法:call()方法和apply()方法。 2. 相同点:这两个方法的作用是一样的。 都是在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。 一般来说,this总是指向调用某个方法的对象,但是使用call()和apply()方法时,就会改变this的指向。 call()方法使用示例: //例1 ...

Lerch_Wang的博客 339
上一篇: 木马免杀花指令的写法
下一篇: PE引入表修改实战
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值