三叶草 发表于 2021-7-14 11:20:19

[VB]调用vc Dll出错

模块代码:

Public Declare Function HookApi Lib "D:\HookApi.dll" (ByVal hWnd As Long, ByVal ModuleName As String, ByVal FunctionName As String, ByVal FnctionAddr As Long) As Boolean

Public Declare Sub UnHookApi Lib "D:\HookApi.dll" ()

Public Declare Sub HookOn Lib "D:\HookApi.dll" ()

Public Declare Sub HookOff Lib "D:\HookApi.dll" ()

Public Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hWnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal uType As Long) As Long

Public Function My_MessageBox(ByVal hWnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal uType As Long) As Long

Debug.Print lpText & vbCrLf & lpCaption

HookOff

My_MessageBox = MessageBox(hWnd, lpText, lpCaption, uType)

HookOn

End Function

窗体代码:

Private Sub Command1_Click()

MsgBox HookApi(Me.hWnd, "user32", "MessageBoxA", AddressOf My_MessageBox)

End Sub

Private Sub Command2_Click()

UnHookApi

End Sub

Private Sub Command4_Click()

MessageBox Me.hWnd, "Hi", "Hello", vbOK

End Sub

运行时总出现对话框Error:cannot get Function

求解释!

三叶草 发表于 2021-7-14 11:26:04

HookApi.dll代码:

HookApi.h:

// HookApi.h : HookApi DLL 的主头文件
//

#pragma once

#ifndef __AFXWIN_H__
        #error "在包含此文件之前包含“stdafx.h”以生成 PCH 文件"
#endif

#include "resource.h"                // 主符号


// CHookApiApp
// 有关此类实现的信息,请参阅 HookApi.cpp
//

class CHookApiApp : public CWinApp
{
public:
        CHookApiApp();

// 重写
public:
        virtual BOOL InitInstance();

        int ExitInstance();

        DECLARE_MESSAGE_MAP()
};
#defineUM_WNDTITLE WM_USER+100 //自定义消息

//全局共享变量
#pragma data_seg(".Share")
HWND g_hWnd = NULL;//主窗口句柄;
HHOOK hhk = NULL;        //鼠标钩子句柄;
HINSTANCE hInst = NULL;//本dll实例句柄;
#pragma data_seg()
#pragma comment(linker, "/section:.Share,rws")

HANDLE hProcess = NULL;
BOOL bIsInjected = FALSE;

PTR oldFunc = NULL; //用于保存原函数地址
FARPROC pfFunc = NULL;//指向原函数地址的远指针
BYTE OldCodeA; //老的系统API入口代码
BYTE NewCodeA; //要跳转的API代码 (jmp xxxx)
PTR FunctionAddress;

//安装钩子
_declspec(dllexport) BOOL WINAPI _stdcall HookApi(HWND hWnd);
//卸载钩子
_declspec(dllexport) VOID WINAPI _stdcall UnHookApi();

HookApi.cpp:

// HookApi.cpp : 定义 DLL 的初始化例程。
//

#include "stdafx.h"
#include "HookApi.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CHookApiApp

BEGIN_MESSAGE_MAP(CHookApiApp, CWinApp)
END_MESSAGE_MAP()


// CHookApiApp 构造

CHookApiApp::CHookApiApp()
{
        // TODO:在此处添加构造代码,
        // 将所有重要的初始化放置在 InitInstance 中
}


// 唯一的一个 CHookApiApp 对象

CHookApiApp theApp;

//开启钩子的函数
void HookOn()
{
        ASSERT(hProcess != NULL);

        DWORD dwTemp = 0, dwOldProtect, dwRet = 0, dwWrite;

        VirtualProtectEx(hProcess, pfFunc, 5, PAGE_READWRITE, &dwOldProtect);
        dwRet = WriteProcessMemory(hProcess, pfFunc, NewCodeA, 5, &dwWrite);
        if (0 == dwRet || 0 == dwWrite)
        {
                TRACE("啊,写入失败");
        }
        VirtualProtectEx(hProcess, pfFunc, 5, dwOldProtect, &dwTemp);
}

//关闭钩子的函数
void HookOff()
{
        ASSERT(hProcess != NULL);

        DWORD dwTemp = 0, dwOldProtect = 0, dwRet = 0, dwWrite = 0;

        //恢复原API入口
        VirtualProtectEx(hProcess, pfFunc, 5, PAGE_READWRITE, &dwOldProtect);
        dwRet = WriteProcessMemory(hProcess, pfFunc, OldCodeA, 5, &dwWrite);
        if (0 == dwRet || 0 == dwWrite)
        {
                TRACE("啊,写入失败");
        }
        VirtualProtectEx(hProcess, pfFunc, 5, dwOldProtect, &dwTemp);
}

void Inject()
{

        if (!bIsInjected)
        {
                bIsInjected = TRUE;//保证只调用1次

                if (pfFunc == NULL)
                {
                        MessageBox(NULL, _T("cannot get Function"), _T("error"), 0);
                        return;
                }

                _asm
                {
                        lea edi, OldCodeA
                        mov esi, pfFunc
                                cld
                                movsd
                                movsb
                }

                NewCodeA = 0xe9;
                _asm
                {
                        lea eax, FunctionAddress
                        mov ebx, pfFunc
                                sub eax, ebx
                                sub eax, 5
                                mov dword ptr, eax
                }

                HookOn();
        }
}

// CHookApiApp 初始化

BOOL CHookApiApp::InitInstance()
{
        CWinApp::InitInstance();
        hInst = AfxGetInstanceHandle();
        DWORD dwPid = ::GetCurrentProcessId();
        hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, 0, dwPid);

        Inject();

        return TRUE;
}

LRESULT CALLBACK MouseProc(
        int nCode,      // hook code
        WPARAM wParam,// message identifier
        LPARAM lParam   // mouse coordinates
        )
{
        if (nCode == HC_ACTION)
        {
                //将钩子所在窗口句柄发给主程序
                ::SendMessage(g_hWnd, UM_WNDTITLE, wParam, (LPARAM)(((PMOUSEHOOKSTRUCT)lParam)->hwnd));
        }
        return CallNextHookEx(hhk, nCode, wParam, lParam);
}


//安装钩子
BOOL WINAPI HookApi(HWND hWnd,LPCWSTR ModuleName,LPCSTR FunctionName,PTR FunctionAddr)
{
        //获取函数
        HMODULE hmod = ::LoadLibrary(ModuleName);
        oldFunc = GetProcAddress(hmod, FunctionName);
        pfFunc = (FARPROC)oldFunc;
        g_hWnd = hWnd;
        FunctionAddress = FunctionAddr;
        hhk = ::SetWindowsHookEx(WH_MOUSE, MouseProc, hInst, 0);
        if (hhk == NULL)
        {
                return FALSE;
        }
        else
        {
                return TRUE;
        }
}

//卸载钩子
VOID WINAPI UnHookApi()
{
        HookOff();

        if (hhk != NULL)
        {
                UnhookWindowsHookEx(hhk);
                FreeLibrary(hInst);
        }
}

//dll退出时
int CHookApiApp::ExitInstance()
{
        HookOff();

        return TRUE;
}

三叶草 发表于 2021-7-14 11:28:55

本帖最后由 三叶草 于 2021-7-14 11:29 编辑

然后我在习惯性地按Ctrl+S时,也出现了此对话框:

三叶草 发表于 2021-7-14 11:31:16

还有一张:

唐凌 发表于 2021-7-14 20:07:00

你的`HookApi`函数定义成了
```C
BOOL WINAPI HookApi(HWND hWnd,LPCWSTR ModuleName,LPCSTR FunctionName,PTR FunctionAddr)
```
其中`ModuleName`和`FunctionName`两个,一个用的是unicode字符串,一个用的是ansi字符串。VB6里调用API的时候会把字符串自动转换成ansi。
就是因为字符串编码问题导致你`LoadLibrary`失败。
如果你要让VB6按字符串传参数,那就必须全部用ansi字符串。换言之就是把`ModuleName`的类型换成`LPCSTR`。
然后把`LoadLibrary`强行指定为`LoadLibraryA`或者`GetModuleHandleA`,只接受ansi字符串参数的版本。
如果不想改VC里的代码,那就在VB里把`HookApi`的代码定义成
```vb
Public Declare Function HookApi Lib "D:\HookApi.dll" (ByVal hWnd As Long, ByVal ModuleName As Long, ByVal FunctionName As String, ByVal FnctionAddr As Long) As Boolean
```
然后这么调用:
``` vb
Call HookApi(Me.hWnd, StrPtr("user32"), "MessageBoxA", AddressOf My_MessageBox)
```

三叶草 发表于 2021-7-15 16:48:23

我按你说的做,结果还是不行,而且...

三叶草 发表于 2021-7-15 16:50:20

我用我另外一个程序调用了MessageBox,崩了,任务管理器都关不掉的那种

唐凌 发表于 2021-7-15 17:01:23

三叶草 发表于 2021-7-15 16:50
我用我另外一个程序调用了MessageBox,崩了,任务管理器都关不掉的那种

那就挂调试器看看崩哪里了呗,学一些windbg调试起来就简单多了

三叶草 发表于 2021-7-15 20:23:45

tangptr@126.com 发表于 2021-7-15 17:01
那就挂调试器看看崩哪里了呗,学一些windbg调试起来就简单多了

啊这,我不会winbdg

Golden Blonde 发表于 2021-7-15 20:27:55

你HOOK自己进程内的函数为啥要用C写?VB一样可以调用VirtualProtect和WriteProcessMemory。

如果要HOOK其它进程内部的函数,那么地址必须要在其它进程里,不能是你进程里的地址。

三叶草 发表于 2021-7-15 22:50:59

我用的是全局钩子,VB写不了

唐凌 发表于 2021-7-16 03:23:05

三叶草 发表于 2021-7-15 20:23
啊这,我不会winbdg

我猜到你不会,所以用了“学”这个字。
https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/getting-started-with-windbg
用windbg调试vb6程序的时候别忘了在编译前给项目勾选“创建符号调试信息”,它会额外生成一个pdb文件有助于你用windbg玩源码级调试。

Golden Blonde 发表于 2021-7-16 05:27:36

三叶草 发表于 2021-7-15 22:50
我用的是全局钩子,VB写不了

你的VC-DLL到底是啥功能?我乍一看是API HOOK,怎么又看到了消息钩子?

系统消息 发表于 2021-7-16 09:55:43

三叶草 发表于 2021-7-15 20:23
啊这,我不会winbdg

VS可以附加调试到进程,选择附加到VB6IDE进程上,你就可以调试你自己写的dll,可以在源码里面下断点。

系统消息 发表于 2021-7-16 09:58:28

tangptr@126.com 发表于 2021-7-14 20:07
你的`HookApi`函数定义成了
```C
BOOL WINAPI HookApi(HWND hWnd,LPCWSTR ModuleName,LPCSTR FunctionN ...

VB6还是tlb的方式调用C函数方便,我只需要在tlb内部声明LPCSTR和LPCWSTR,VB6调用就会自动根据参数传ANSI还是Unicode进去。

三叶草 发表于 2021-7-16 15:18:44

tangptr@126.com 发表于 2021-7-16 03:23
我猜到你不会,所以用了“学”这个字。
https://docs.microsoft.com/en-us/windows-hardware/drivers/deb ...

是这个吗?

三叶草 发表于 2021-7-16 15:19:54

美俪女神 发表于 2021-7-16 05:27
你的VC-DLL到底是啥功能?我乍一看是API HOOK,怎么又看到了消息钩子?

消息钩子只是个摆设,真正起作用的是Inject()

Golden Blonde 发表于 2021-7-16 19:56:22

三叶草 发表于 2021-7-16 15:19
消息钩子只是个摆设,真正起作用的是Inject()

你具体要实现什么功能?

三叶草 发表于 2021-7-19 16:16:53

Hook Api,把线程注入到所有进程中,修改该进程Api的头部,使其跳转到我自己编写的函数中,拦截函数执行

0xAA55 发表于 2021-7-19 17:20:57

三叶草 发表于 2021-7-19 16:16
Hook Api,把线程注入到所有进程中,修改该进程Api的头部,使其跳转到我自己编写的函数中,拦截函数执行 ...

VC部分竟然写了个类。定睛一看,class CHookApiApp : public CWinApp。为了搞个Hook你这竟然用MFC,这是干屌?

void Inject(),我看到了内联汇编的操作,5字节Hook。这十分令人怀念Windows XP。强烈推荐了解Detours库,我就先不说什么你那个强行Hook的操作过于“线程不安全”了,槽点太多了。

然后我注意到:

FARPROC pfFunc = NULL;//指向原函数地址的远指针

(都1202年了还“FARPROC”,1986年就淘汰了的东西)

在void Inject()里,你判断pfFunc为NULL时弹窗,内容是"cannot get Function"。

转到VB6的代码:Public Declare Function HookApi Lib "D:\HookApi.dll" (ByVal hWnd As Long, ByVal ModuleName As String, ByVal FunctionName As String, ByVal FnctionAddr As Long) As Boolean这是一个经典的VB6调用API时初学者容易遇到的坑。观察你的VC函数原型:BOOL WINAPI HookApi(HWND hWnd,LPCWSTR ModuleName,LPCSTR FunctionName,PTR FunctionAddr);你的ModuleName是宽字符,你的FunctionName不是,但是这两个在你的VB6声明里都是ByVal xxx As String,你的VB6程序无法区分其中两者。要知道:VB6调用API的时候,String类型会受到自动的Unicode转Ansi处理。

因此你的 ModuleName 应该被定义为 ByVal ModuleName As Long ,然后传参的时候,传递你的模块名的字符串指针,使用 StrPtr() 传参,可以避免隐藏的字符串转换过程。

页: [1] 2
查看完整版本: [VB]调用vc Dll出错