开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

楼主: 笨来无一悟
收起左侧

[易语言纯源码] 【蠢新学汇编】截取 子程序代码 字节集片段 终版

[复制链接]
发表于 前天 20:48 | 显示全部楼层   江苏省连云港市
感谢分享
回复 支持 反对

使用道具 举报

结帖率:80% (4/5)
发表于 前天 19:07 | 显示全部楼层   山东省潍坊市
感谢解答,原来是用来分析的。
回复 支持 反对

使用道具 举报

结帖率:73% (8/11)

签到天数: 3 天

发表于 前天 18:14 | 显示全部楼层   河南省郑州市
本帖最后由 神行 于 2025-4-1 18:17 编辑

不好意思,之前提的意见,我没注意到你对汇编特别感兴趣。我对你的代码进行了改进。你看看如何。主要增加内存安全验证,优化搜索算法(函数头+函数尾双阶段搜索),添加安全计数(头100字节 尾500字节限制),多重失败检查点,无效长度处理,内存分配失败处理,减少不必要的内存访问,优化跳转逻辑,新增内存属性检查,安全长度限制4KB,独立内存分配复制,错误状态返回
  
窗口程序集名保 留  保 留备 注
窗口程序集_启动窗口   
子程序名返回值类型公开备 注
__启动窗口_创建完毕  
变量名类 型静态数组备 注
机器码字节集 
长度整数型 
' 安全获取子程序机器码
机器码 = 取函数机器码_增强版 (&子程序, 长度)
如果真 (取字节集长度 (机器码) = 0)
信息框 ("获取机器码失败!", 0, "错误", )
调试输出 ("获取成功,长度:" + 到文本 (长度), 机器码)

子程序名返回值类型公开备 注
子程序双精度小数型 
参数名类 型参考可空数组备 注
参数一整数型
参数二双精度小数型
返回 (参数一 + 参数二)
子程序名返回值类型公开备 注
取函数机器码_增强版字节集 
参数名类 型参考可空数组备 注
函数指针整数型要获取机器码的函数地址
返回长度整数型返回获取的机器码长度
变量名类 型静态数组备 注
起始偏移整数型 
结束偏移整数型 
安全计数器整数型 
内存保护整数型 
' ================ 安全验证 ================
如果真 (函数指针 = 0 是否可读内存 (函数指针, 1))
返回 ({ })

' ================ 内存属性检查 ================
内存保护 = 取内存保护属性 (函数指针)
如果真 (位与 (内存保护, #PAGE_EXECUTE_READ ) = 0)
返回 ({ })

' ================ 汇编实现 ================
置入代码 ({ 139, 69, 8, 137, 69, 252, 51, 210, 137, 85, 248 })
' MOV EAX, [EBP+8]    ; 获取函数指针
' MOV [EBP-4], EAX    ; 起始偏移 = 函数地址
' XOR EDX, EDX        ; EDX = 0 (安全计数器)
' MOV [EBP-8], EDX    ; 结束偏移 = 0
' ================ 查找函数头 ================
置入代码 ({ 139, 77, 252, 138, 1, 60, 85, 117, 3, 235, 16, 144 })
' HEADER_SEARCH:
' MOV ECX, [EBP-4]    ; ECX = 当前地址
' MOV AL, [ECX]       ; 读取字节
' CMP AL, 0x55        ; 检查PUSH EBP
' JNE NOT_HEADER
' JMP HEADER_FOUND
' NOT_HEADER:
' NOP
置入代码 ({ 255, 69, 252, 255, 69, 244, 131, 125, 244, 100, 124, 229 })
' INC DWORD [EBP-4]   ; 地址+1
' INC DWORD [EBP-12]  ; 安全计数器+1
' CMP DWORD [EBP-12], 100 ; 最多检查100字节
' JL HEADER_SEARCH
置入代码 ({ 233, 88, 0, 0, 0 })
' JMP FAIL_EXIT       ; 未找到函数头
置入代码 ({ 131, 193, 1, 137, 77, 252 })
' HEADER_FOUND:
' ADD ECX, 1          ; 跳过函数头
' MOV [EBP-4], ECX    ; 更新起始地址
' ================ 查找函数尾 ================
置入代码 ({ 139, 85, 252, 137, 85, 248, 51, 210, 137, 85, 244 })
' MOV EDX, [EBP-4]    ; EDX = 起始地址
' MOV [EBP-8], EDX    ; 初始化结束地址
' XOR EDX, EDX        ; 清空EDX
' MOV [EBP-12], EDX   ; 重置安全计数器
置入代码 ({ 139, 117, 248, 138, 6, 60, 139, 117, 19 })
' TAIL_SEARCH:
' MOV ESI, [EBP-8]    ; ESI = 当前地址
' MOV AL, [ESI]       ; 读取字节
' CMP AL, 0x8B        ; 检查MOV EBP,ESP
' JNE NEXT_BYTE
置入代码 ({ 128, 126, 1, 229, 117, 12, 128, 126, 2, 93, 117, 8 })
' CMP BYTE [ESI+1], 0xE5 ; 检查完整指令
' JNE NEXT_BYTE
' CMP BYTE [ESI+2], 0x5D ; 检查POP EBP
' JNE NEXT_BYTE
置入代码 ({ 128, 126, 3, 194, 116, 8, 128, 126, 3, 195, 116, 4 })
' CMP BYTE [ESI+3], 0xC2 ; RETN xxxx
' JE TAIL_FOUND
' CMP BYTE [ESI+3], 0xC3 ; RETN
' JE TAIL_FOUND
置入代码 ({ 255, 69, 248, 255, 69, 244, 131, 125, 244, 500, 124, 204 })
' NEXT_BYTE:
' INC DWORD [EBP-8]   ; 地址+1
' INC DWORD [EBP-12]  ; 计数器+1
' CMP DWORD [EBP-12], 500 ; 最多检查500字节
' JL TAIL_SEARCH
置入代码 ({ 139, 77, 252, 43, 77, 248, 137, 77, 240 })
' TAIL_FOUND:
' MOV ECX, [EBP-8]    ; 结束地址
' SUB ECX, [EBP-4]    ; 计算长度
' MOV [EBP-16], ECX   ; 保存长度
' ================ 长度验证 ================
置入代码 ({ 131, 249, 0, 126, 29, 129, 249, 0, 16, 0, 0, 127, 22 })
' CMP ECX, 0          ; 检查长度有效性
' JLE INVALID_LEN
' CMP ECX, 4096       ; 最大4KB
' JG INVALID_LEN
置入代码 ({ 139, 85, 252, 139, 77, 248, 43, 202, 137, 77, 240, 233, 5, 0, 0, 0 })
' MOV EDX, [EBP-4]    ; 起始地址
' MOV ECX, [EBP-8]    ; 结束地址
' SUB ECX, EDX        ; 计算实际长度
' MOV [EBP-16], ECX   ; 保存长度
' JMP COPY_CODE
置入代码 ({ 51, 201, 137, 77, 240 })
' INVALID_LEN:
' XOR ECX, ECX        ; 长度置0
' MOV [EBP-16], ECX
' ================ 复制机器码 ================
置入代码 ({ 139, 117, 252, 139, 125, 240, 133, 255, 116, 19 })
' COPY_CODE:
' MOV ESI, [EBP-4]    ; 源地址
' MOV EDI, [EBP-16]   ; 长度
' TEST EDI, EDI       ; 检查长度
' JZ FAIL_EXIT
置入代码 ({ 106, 0, 255, 117, 240, 232, 40, 0, 0, 0, 137, 69, 232 })
' PUSH 0              ; 内存属性
' PUSH [EBP-16]       ; 长度
' CALL AllocMem       ; 调用内存分配
' MOV [EBP-24], EAX   ; 保存分配的内存地址
置入代码 ({ 139, 77, 240, 139, 117, 252, 139, 125, 232, 243, 164 })
' MOV ECX, [EBP-16]   ; 长度
' MOV ESI, [EBP-4]    ; 源地址
' MOV EDI, [EBP-24]   ; 目标地址
' REP MOVSB           ; 复制机器码
' ================ 返回结果 ================
置入代码 ({ 139, 69, 232, 233, 10, 0, 0, 0 })
' MOV EAX, [EBP-24]   ; 返回内存地址
' JMP SUCCESS_EXIT
置入代码 ({ 51, 192 })
' FAIL_EXIT:
' XOR EAX, EAX        ; 返回0表示失败
置入代码 ({ 139, 77, 240, 137, 77, 236 })
' SUCCESS_EXIT:
' MOV ECX, [EBP-16]   ; 获取长度
' MOV [EBP-20], ECX   ; 保存到局部变量
如果真 (是否为空 (返回长度))
返回长度 = 取变量数据地址_整数 (取变量地址 (起始偏移)取变量地址 (结束偏移))

返回 (指针到字节集 (取变量数据地址_整数 (起始偏移), 取变量数据地址_整数 (结束偏移 - 起始偏移)))

点评

安全+1   贵州省毕节市  发表于 前天 18:42
回复 支持 反对

使用道具 举报

结帖率:50% (1/2)

签到天数: 3 天

发表于 前天 17:03 | 显示全部楼层   广东省汕头市
请教大神:为什么使用置入代码命令运行 这个程序里“调试输出 (截取)” 获得的字节集,会没有反应?
(我把子程序的内容改为调试输出(“aaaaaaaaa”))
该怎样执行截取到的代码?

点评

又比如 你执行一个类的方法 就可以扣代码分析出 类方法的指针这么获取的 类方法的那个也是这样抠出来的   贵州省毕节市  发表于 前天 19:02
截取代码一般是拿来这么用的 比如易语言写的比较函数 扣汇编出来会发现多了两个不必要的分支 于是就可以手动改得更高效 又避免了从0全程敲汇编   贵州省毕节市  发表于 前天 19:01
你把你截取之前的代码发出来看看咋回事 涉及跳转的地方 要修改跳转的位置   贵州省毕节市  发表于 前天 18:41
回复 支持 反对

使用道具 举报

结帖率:50% (1/2)

签到天数: 3 天

发表于 前天 16:56 | 显示全部楼层   广东省汕头市

感谢大神分享~!
回复 支持 反对

使用道具 举报

结帖率:80% (4/5)
发表于 前天 16:04 | 显示全部楼层   山东省潍坊市
截取的字节集,再用置入代码 命令去调用么?
还是有别的用途?如果能在发一个怎么调用这个 截取字节集的方法就好了。。

点评

截取出来主要是为了分析 取出来的代码 如果涉及跳转什么的 跳转的地址需要修改 改了才能调用   贵州省毕节市  发表于 前天 16:16
回复 支持 反对

使用道具 举报

结帖率:73% (8/11)

签到天数: 3 天

发表于 前天 15:29 | 显示全部楼层   河南省郑州市
本帖最后由 神行 于 2025-4-1 15:37 编辑

我给你提点建议吧
1.你使用了大量的硬编码汇编指令(置入代码),这些指令高度依赖x86架构和特定编译器生成的函数序言/结尾,而不同编译器或优化级别可能生成不同的函数开头/结尾模式
2.安全性问题:直接操作内存地址和机器码可能不安全,没有检查内存访问是否越界
3.变量使用问题:截取长度 变量在函数开始时声明,但实际值是在汇编代码中通过MOV DWORD [EBP-4],ECX设置的,这种隐式的变量赋值方式容易出错
4.第一段汇编代码在寻找函数入口点,跳过可能的跳转指令(0xE8), 第二段汇编代码在查找函数结尾的典型模式(恢复栈帧和返回指令), 第三段汇编代码处理无参数情况的返回指令(0xC3),这种技术通常用于低级编程或hook操作,但在实际使用环境中使用时需要非常非常谨慎。

这种技术通常用于低级编程或hook操作,但在生产环境中使用时需要非常谨慎
建议:添加错误检查机制,确保内存访问安全,考虑使用更可靠的方式确定函数边界,添加注释说明这种技术的限制和假设,如果可能,使用更高级的API来实现类似功能,检查子针到字节集函数是否能正确处理获取的长度

我基于你本来的逻辑,大致修改了一下,添加了一些必要的安全检查和错误处理,但是使用这种接近硬件或机器层面的代码编写手段在实际使用过程中依然存在很大风险,不建议实际中使用,特别是一些特别安全敏感的环境:

  

窗口程序集名保 留  保 留备 注
窗口程序集_启动窗口    

子程序名返回值类型公开备 注
__启动窗口_创建完毕   
变量名类 型静态数组备 注
截取结果字节集  
安全标志逻辑型  

' 添加安全检查和错误处理
安全标志 =
截取结果 = 截取代码 (到整数 (&子程序), 安全标志)
如果真 (安全标志)
调试输出 ("安全截取成功", 取字节集长度 (截取结果), "字节")
.否则
信息框 ("函数截取失败,可能是不支持的函数格式或内存访问错误", 0, "错误", )
.如果真结束

子程序名返回值类型公开备 注
子程序双精度小数型  
参数名类 型参考可空数组备 注
参数一整数型
参数二双精度小数型

返回 (参数一 + 参数二)

子程序名返回值类型公开备 注
截取代码字节集  
参数名类 型参考可空数组备 注
截取地址整数型
是否安全逻辑型
变量名类 型静态数组备 注
截取长度整数型  
最大长度整数型  
起始地址整数型  
结束地址整数型  

' 初始化安全标志
是否安全 =

' 设置最大允许截取长度(4KB)
最大长度 = 4096

' 验证地址是否有效
如果真 (截取地址 ≤ 0 截取地址 ≥ 2147483647)
返回 ({ })
.如果真结束

起始地址 = 截取地址

' 查找函数入口点(更安全的方式)
置入代码 ({ 139, 69, 8, 128, 56, 85, 116, 19, 139, 77, 8, 65, 128, 57, 232, 117, 250, 139, 65, 1, 141, 92, 1, 5, 137, 93, 8 })
' MOV EAX,[EBP+8]   ; 获取截取地址
' CMP BYTE [EAX],85 ; 检查是否是push ebp(0x55)
' JE 跳过
' MOV ECX,[EBP+8]   ; 不是标准开头,查找call指令(0xE8)
' 入口循环:
' INC ECX
' CMP BYTE [ECX],232
' JNE 入口循环
' MOV EAX,[ECX+1]   ; 获取call目标偏移
' LEA EBX,[ECX+EAX+5] ; 计算实际地址
' MOV [EBP+8],EBX   ; 更新起始地址
' 跳过:

' 验证新地址是否有效
如果真 (起始地址 ≤ 0 起始地址 ≥ 2147483647)
返回 ({ })
.如果真结束

' 查找函数结束(添加边界检查)
置入代码 ({ 139, 77, 8, 137, 77, 252, 139, 85, 12, 138, 17, 65, 128, 250, 139, 117, 243, 128, 121, 255, 229, 117, 238, 128, 121, 254, 93, 117, 231, 128, 121, 253, 194, 117, 7, 131, 193, 3, 137, 77, 252, 235, 18 })
' MOV ECX,[EBP+8]   ; 起始地址
' MOV [EBP-4],ECX   ; 保存到截取长度(作为结束地址)
' MOV EDX,[EBP+12]  ; 最大长度
' 查找循环:
' MOV DL,[ECX]
' INC ECX
' CMP DL,139        ; 检查mov ebp,esp(0x8B)
' JNE 查找循环
' CMP BYTE [ECX-1],229 ; 0xE5
' JNE 查找循环
' CMP BYTE [ECX-2],93  ; 0x5D(pop ebp)
' JNE 查找循环
' CMP BYTE [ECX-3],194 ; 0xC2(retn)
' JNE 检查无参
' ADD ECX,3         ; 调整到retn之后
' MOV [EBP-4],ECX   ; 更新结束地址
' JMP 完成检查
' 检查无参:
置入代码 ({ 128, 121, 253, 195, 117, 213, 131, 193, 3, 137, 77, 252 })
' CMP BYTE [ECX-3],195 ; 0xC3(ret)
' JNE 查找循环
' ADD ECX,3         ; 调整到ret之后
' MOV [EBP-4],ECX   ; 更新结束地址
' 完成检查:

' 计算实际长度
截取长度 = 结束地址 - 起始地址

' 验证长度是否合理
如果真 (截取长度 ≤ 0 截取长度 > 最大长度)
返回 ({ })
.如果真结束

' 设置安全标志
是否安全 =

' 返回截取的代码
返回 (指针到字节集 (起始地址, 截取长度))



所以,为了你的CPU硬件安全,我对这个代码又进行了一次改进。这次不使用可以直接操作CPU指令的这些低级代码,大致思路,没有经过测试。
  
窗口程序集名保 留  保 留备 注
高级函数分析器   
变量名类 型数组备 注
内存保护属性整数型  
最大扫描长度整数型4096
最小函数长度整数型16

子程序名返回值类型公开备 注
初始化分析器逻辑型 
内存保护属性 = 取内存保护属性 (GetCurrentProcess ())
最大扫描长度 = 4096
最小函数长度 = 16
返回 真
子程序名返回值类型公开备 注
安全截取函数代码字节集 
参数名类 型参考可空数组备 注
函数指针整数型
结果状态整数型0=成功 1=无效指针 2=内存不可读 3=超出范围 4=模式不匹配
变量名类 型静态数组备 注
函数长度整数型 
内存信息内存基本信息 
安全缓冲区字节集 
' 1. 参数验证
如果真 (函数指针 ≤ 0)
结果状态 = 1
返回 ({ })

' 2. 检查内存可读性
内存信息 = 取内存信息 (函数指针)
如果真 (位与 (内存信息.保护, 内存保护属性.PAGE_READABLE) = 0)
结果状态 = 2
返回 ({ })

' 3. 智能分析函数边界
函数长度 = 智能分析函数长度 (函数指针)
如果真 (函数长度 < 最小函数长度 函数长度 > 最大扫描长度)
结果状态 = 3
返回 ({ })

' 4. 安全复制函数代码
安全缓冲区 = 取空白字节集 (函数长度)
复制内存 (取变量数据地址 (安全缓冲区), 函数指针, 函数长度)
' 5. 验证复制内容
如果真 (校验字节集 (安全缓冲区))
结果状态 = 4
返回 ({ })

结果状态 = 0
返回 安全缓冲区
子程序名返回值类型公开备 注
智能分析函数长度整数型 
参数名类 型参考可空数组备 注
起始地址整数型
变量名类 型静态数组备 注
当前地址整数型 
扫描计数器整数型 
指令分析器整数型 
当前地址 = 起始地址
指令分析器 = 创建指令分析器 ()
' 使用启发式规则分析函数边界
判断循环首 (当前地址 - 起始地址 < 最大扫描长度)
扫描计数器 = 扫描计数器 + 1
' 使用指令分析器判断是否到达函数结尾
如果 (分析指令 (指令分析器, 当前地址) = "RET")
如果 (验证函数结尾 (当前地址))
跳出循环 ()


' 检查常见函数序言
如果真 (扫描计数器 = 1 分析指令 (指令分析器, 当前地址) ≠ "PUSH EBP")
当前地址 = 查找真实入口 (当前地址)

当前地址 = 当前地址 + 取指令长度 (指令分析器)
释放指令分析器 (指令分析器)
' 确保长度有效
如果 (当前地址 - 起始地址 ≥ 最小函数长度 当前地址 - 起始地址 ≤ 最大扫描长度)
返回 (当前地址 - 起始地址)
返回 (0)

' 验证标准的函数结尾序列
返回 (取字节集长度 (指针到字节集 (地址, 4)) = 4 且
指针到字节集 (地址, 4){ 139, 229, 93, 195 }) ' MOV ESP,EBP; POP EBP; RET
子程序名返回值类型公开备 注
验证函数结尾逻辑型 
参数名类 型参考可空数组备 注
地址整数型

子程序名返回值类型公开备 注
查找真实入口整数型 
参数名类 型参考可空数组备 注
疑似地址整数型
变量名类 型静态数组备 注
当前地址整数型 
' 处理编译器优化情况下的函数入口
当前地址 = 疑似地址
判断循环首 (当前地址 - 疑似地址 < 32) ' 在32字节范围内查找
如果 (指针到字节集 (当前地址, 2){ 85, 139 }) ' PUSH EBP; MOV EBP,ESP
返回 (当前地址)
当前地址 = 当前地址 + 1

判断循环尾
返回 (疑似地址) ' 找不到则返回原地址
子程序名返回值类型公开备 注
校验字节集逻辑型 
参数名类 型参考可空数组备 注
代码块字节集
变量名类 型静态数组备 注
校验和整数型 
i整数型 
' 验证代码块有效性
计次循环首 (取字节集长度 (代码块), i)
校验和 = 位异或 (校验和, 取字节集数据 (代码块, i, #字节型 ))
计次循环尾
' 简单校验和验证
返回 (校验和 ≠ 0)


.子程序

  





点评

查了一下 { 139, 229, 93, 194 } 对应 -1034033781 差不多一个G了 很难有这么大的跳转 所以 几乎不会出现误判   贵州省毕节市  发表于 前天 16:28
出错的概率是 2147483647 分之 2   贵州省毕节市  发表于 前天 16:21
我拿VC6 和 VC2022对比了 生成的机器码是一样的 唯一可能导致误判的只有一个地方 跳转的某个地址 正好是 139,229,93,194 误判顶多少截取 不会崩溃的   贵州省毕节市  发表于 前天 16:19
回复 支持 反对

使用道具 举报

签到天数: 2 天

发表于 前天 14:20 | 显示全部楼层   陕西省*
支持开源~!感谢分享
回复 支持 反对

使用道具 举报

签到天数: 3 天

发表于 前天 13:01 | 显示全部楼层   广东省东莞市
谢谢分享
回复 支持 反对

使用道具 举报

结帖率:80% (4/5)

签到天数: 2 天

发表于 前天 12:35 高大上手机用户 | 显示全部楼层   广东省佛山市
感谢分享,
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 793400750,邮箱:wp@125.la
网站简介:精易论坛成立于2009年,是一个程序设计学习交流技术论坛,隶属于揭阳市揭东区精易科技有限公司所有。
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

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