ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 802|回复: 3

[分享] 【仅32位】【另类函数指针】函数获取自身所在模块和函数名

[复制链接]

TA的精华主题

TA的得分主题

发表于 2022-11-21 13:55 | 显示全部楼层 |阅读模式
老外很厉害,多年前我就想要的东西,有了VB6版本

顺势迁移了一个VBA 32位版本,用到的技术如下:
1、函数指针,本方法仅用于32位,64位需要用其他方法
2、内存查找未导出函数

声明:
1、目前应该能用于VBA6和VBA7,如果你的32位Office不能用,请上传相应VBE6.dll或VBE7.dll
2、仅提供一种新玩法,不保证所有人电脑都能用,水平有限,也不一定能给所有人解决问题
3、欢迎上传移植成功的64位版本

代码如下:
'mCallingProcName.bas
  1. Option Explicit
  2. Option Base 0

  3. Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleW" (ByVal lpModuleName As Long) As Long
  4. Private Declare Sub memcpy Lib "kernel32" Alias "RtlMoveMemory" (dst As Any, src As Any, ByVal Length As Long)
  5. '函数原型
  6. Private Function EbSetMode(ByVal Addr As Long, ByVal Mode As Long) As Long
  7. End Function
  8. Private Function EbGetCallstackCount(ByVal Addr As Long, ByRef lCount As Long) As Long
  9. End Function
  10. Private Function EbGetCallstackFunction(ByVal Addr As Long, _
  11.                          ByVal lIndex As Long, _
  12.                          ByVal pProject As Long, _
  13.                          ByVal pModule As Long, _
  14.                          ByVal pFunction As Long, _
  15.                          ByRef lRet As Long) As Long
  16. End Function

  17. Sub Test()
  18.     MsgBox GetCallingProcName()
  19.     End
  20. End Sub

  21. Public Function GetCallingProcName(Optional ByVal lReserved As Long) As String
  22.     Dim lStackCount As Long
  23.     Dim sProject As String
  24.     Dim sModule As String
  25.     Dim sFunction As String
  26.     Dim hEbSetMode As Long
  27.     Dim hEbGetCallstackCount As Long
  28.     Dim hEbGetCallstackFunction As Long
  29.     Dim hVbe            As Long
  30.     Dim pSection        As Long
  31.     Dim pStartScan      As Long
  32.     Dim pEndScan        As Long
  33.     Dim e_lfanew        As Long
  34.     Dim iNumOfSec       As Integer
  35.     Dim iOptSize        As Integer
  36.     Dim lIndex          As Long
  37.     Dim cName           As Currency
  38. #If VBA7 Then
  39.     hVbe = GetModuleHandle(StrPtr("VBE7"))
  40. #ElseIf VBA6 Then
  41.     hVbe = GetModuleHandle(StrPtr("VBE6"))
  42. #End If
  43.     If hVbe = 0 Then Exit Function
  44. #If VBA6 Or VBA7 Then
  45.     memcpy e_lfanew, ByVal hVbe + &H3C, 4
  46.     memcpy iNumOfSec, ByVal hVbe + e_lfanew + 6, 2
  47.     memcpy iOptSize, ByVal hVbe + e_lfanew + &H14, 2
  48.     pSection = hVbe + e_lfanew + &H18 + iOptSize
  49.     For lIndex = 0 To iNumOfSec - 1
  50.         memcpy cName, ByVal pSection, 8
  51.         If cName = 50023612.1134@ Then
  52.             memcpy pStartScan, ByVal pSection + &HC, 4
  53.             memcpy pEndScan, ByVal pSection + &H8, 4
  54.             pStartScan = pStartScan + hVbe
  55.             pEndScan = pEndScan + pStartScan - 1
  56.             Exit For
  57.         End If
  58.         pSection = pSection + &H28
  59.     Next
  60.     If pStartScan = 0 Or pEndScan = 0 Then Exit Function
  61. #End If
  62.    
  63. #If VBA7 Then
  64.     hEbSetMode = SearchFunction(pStartScan, pEndScan, "8B FF 53 55 56 57 E8 XX XX XX XX 8B 3D XX XX XX XX 8B F0 33 ED 8B DD 83 XX 01 75 0C E8 XX XX XX XX 85 C0 74 XX 6A 02 5E", 0)
  65.     hEbGetCallstackCount = SearchFunction(pStartScan, pEndScan, "E8 XX XX XX XX 83 E8 00 74 XX 48 74 XX 48 75 XX A1 XX XX XX XX 8B 4C 24 04 83 21 00 05 XX XX XX XX 74 XX FF 01 8B 00 85 C0 75 XX EB XX", 0)
  66.     hEbGetCallstackFunction = SearchFunction(pStartScan, pEndScan, "81 EC XX XX XX XX A1 XX XX XX XX 33 C4 89 XX XX XX 8B 84 XX XX XX 00 00 53 8B XX XX XX XX 00 00 55 33 ED 56 89 XX XX XX 8B 84 XX XX XX 00 00 57 8B XX XX XX XX 00 00 89 44", 0)
  67. #ElseIf VBA6 Then
  68.     hEbSetMode = SearchFunction(pStartScan, pEndScan, "55 8B EC 51 56 57 E8 XX XX XX XX XX XX XX XX 8B 35 XX XX XX XX 8B F8 83 XX 01 75 0C E8 XX XX XX XX 85 C0 74 XX 6A 02 5F", 0)
  69.     hEbGetCallstackCount = SearchFunction(pStartScan, pEndScan, "E8 XX XX XX XX 83 E8 00 74 XX 48 74 XX 48 75 XX A1 XX XX XX XX 8B 4C 24 04 05 XX XX XX XX 83 21 00 EB XX FF 01 8B 00 85 C0 7C XX EB XX", 0)
  70.     hEbGetCallstackFunction = SearchFunction(pStartScan, pEndScan, "55 8B EC 81 EC XX XX XX XX A1 XX XX XX XX 33 C5 89 XX XX 8B XX XX 53 8B XX XX 89 XX XX 8B XX XX 56 33 F6 89 XX XX 8B XX XX 57 89 XX XX 8B XX XX 33 FF 89 XX XX 89 XX XX 89", 0)
  71. #End If

  72.     PatchFunc AddressOf EbSetMode
  73.     PatchFunc AddressOf EbGetCallstackCount
  74.     PatchFunc AddressOf EbGetCallstackFunction
  75.    
  76.     EbSetMode hEbSetMode, 2
  77.     If EbGetCallstackCount(hEbGetCallstackCount, lStackCount) >= 0 Then
  78.         If lStackCount > 1 Then
  79.             If EbGetCallstackFunction(hEbGetCallstackFunction, 1, VarPtr(sProject), VarPtr(sModule), VarPtr(sFunction), 0) >= 0 Then
  80.                 GetCallingProcName = sModule & "::" & sFunction
  81.             End If
  82.         End If
  83.     End If
  84.     EbSetMode hEbSetMode, 1
  85. End Function

  86. Private Sub PatchFunc(ByVal Addr As Long)
  87.     memcpy Addr, ByVal Addr + &H16, 4
  88.     memcpy ByVal Addr, &HFF505958, 4
  89.     memcpy ByVal Addr + 4, &HE1, 4
  90. End Sub
复制代码

'mSearchFunc.bas
  1. Option Explicit
  2. Option Base 0

  3. Private Const FADF_AUTO As Long = 1

  4. Private Type SAFEARRAYBOUND
  5.     cElements As Long
  6.     lLbound As Long
  7. End Type

  8. Private Type SAFEARRAY
  9.     cDims As Integer
  10.     fFeatures As Integer
  11.     cbElements As Long
  12.     cLocks As Long
  13.     pvData As Long
  14.     Bounds As SAFEARRAYBOUND
  15. End Type

  16. Private Declare Sub DupArray Lib "kernel32" _
  17.             Alias "RtlMoveMemory" ( _
  18.             ByRef Destination() As Any, _
  19.             ByRef pSA As Any, _
  20.             Optional ByVal Length As Long = 4)

  21. Public Function SearchFunction(ByVal pStartScan As Long, ByVal pEndScan As Long, ByVal strHex As String, Optional ByVal offset As Long = 0) As Long
  22.     Dim bData() As Byte
  23.     Dim tSAMap As SAFEARRAY
  24.     Dim lIndex As Long

  25.     tSAMap.cbElements = 1
  26.     tSAMap.cDims = 1
  27.     tSAMap.fFeatures = FADF_AUTO
  28.     tSAMap.Bounds.cElements = CLng(pEndScan - pStartScan) + 1
  29.     tSAMap.pvData = pStartScan
  30.     DupArray bData, VarPtr(tSAMap)

  31.     Dim bTemplate() As Byte
  32.     Dim bMask() As Byte

  33.     Dim arrTemp
  34.     Dim i As Long
  35.     arrTemp = Split(strHex)

  36.     ReDim bTemplate(UBound(arrTemp) - LBound(arrTemp)) As Byte
  37.     ReDim bMask(UBound(arrTemp) - LBound(arrTemp)) As Byte

  38.     For i = LBound(arrTemp) To UBound(arrTemp)
  39.         If arrTemp(i) <> "XX" Then
  40.             bMask(i) = 1
  41.             bTemplate(i) = Val("&H" & arrTemp(i))
  42.         End If
  43.     Next i

  44.     lIndex = FindSignature(bData(), bTemplate(), bMask())
  45.     DupArray bData, 0@
  46.     If lIndex = -1 Then Exit Function
  47.     SearchFunction = pStartScan + lIndex + offset
  48. End Function

  49. Public Function FindSignature(ByRef bData() As Byte, ByRef bSignature() As Byte, ByRef bMask() As Byte) As Long
  50.     Dim lDataIndex As Long
  51.     Dim lSignIndex As Long
  52.     lDataIndex = 0: lSignIndex = 0
  53.     Do While lDataIndex <= UBound(bData)
  54.         If bData(lDataIndex) = bSignature(lSignIndex) Or bMask(lSignIndex) = 0 Then
  55.             lSignIndex = lSignIndex + 1
  56.             If lSignIndex > UBound(bSignature) Then               '
  57.                 FindSignature = lDataIndex - UBound(bSignature)
  58.                 Exit Function
  59.             End If
  60.         Else
  61.             If lSignIndex Then
  62.                 lDataIndex = lDataIndex - lSignIndex + 1
  63.                 lSignIndex = 0
  64.             End If
  65.         End If
  66.         lDataIndex = lDataIndex + 1
  67.     Loop
  68.     FindSignature = -1
  69. End Function
复制代码

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-11-22 19:01 | 显示全部楼层
使用案例:
Sub Test()
    On Error GoTo ErrH
1   Dim a As Long
2   a = 1 / 0
    GoTo lExit:
ErrH:
    MsgBox Err.Number & vbCrLf & Err.Description & vbCrLf & Erl() & vbCrLf & GetCallingProcName()  '这里还可以写日志
    Resume Next
lExit:
End Sub

TA的精华主题

TA的得分主题

发表于 2022-11-23 09:06 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-11-23 19:15 来自手机 | 显示全部楼层
本帖最后由 loquat 于 2022-11-23 19:44 编辑

目前来看vba 64应该有难度,总是把堆栈干爆
猜测是不是64位的这几个api有外部依赖。
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-20 22:37 , Processed in 0.025574 second(s), 9 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

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