如何将 DLL 函数调用/DllImport 从 vb6 转换为 C# .NET

How to translate DLL Function Call / DllImport from vb6 to C# .NET

VB6 中的代码工作得非常好,我相信 dll 是为了速度而编写的,我正在尝试在 C# 或 VB.NET 中移植调用它,无论哪个我不介意。我试过了许多不同的技巧都不起作用,它的调用方式应该类似于使用偏移量传递的数组地址调用 RltMoveMemory (CopyMemory) 的方式。

VB6 代码如下所示

Public Type REGTYPE
  REG_Kind As Byte  ' ;1=8 bits \ 2=16 bits \ 3=32 bits \ 4=MMX \ 5=XMM \ 6=Float stack \ 7=Segment \ 8=Debug \ 9=Control \ 10=Test
  REG_Ptr_Kind As Byte  ' ;1=Byte PTR \ 2=Word PTR \ 3=Dword PTR \ 4=Qword PTR \ 5=mmword ptr \ 6=xmmword ptr \ 7=FWord PTR \ 8=tbyte ptr \ 9=null ptr (LEA)
  REG_Type As Byte '  ;0-7= direct register index \ 16 register=byte && 7 \ 32 register=(byte && 63)/8 \ 64=[32/16 address only] \ 128=[using x86 relatives]
  REG_BaseAsReg As Byte ' ? ;1=Register only (BASE exposed)!
End Type

Public Type REGSTRUCT
 SEG_TYPE As Long
 Base As Long
 INDEX As Long
 SCALE As Long
 DISPLACEMENTS As Long
 DISPLACEMENT_TYPE As Long
 REG_Kind As REGTYPE
 PTR_TYPE As Long
End Type

Public Type IMMSTRUCT
 VALUE_LO As Long
 VALUE_HI As Long
 VALUE_TYPE As Long '     1=Byte \ 2=Word \ 4=Dword \ 8=ByteToWord \ 16=ByteToDword \ 32=AbsJump \ 64=ShortJump \ 128=LongJump
End Type

Public Type DisAsmStruct
 Instruction_Prefix As Long
 Instruction As Long
 Reg1 As REGSTRUCT
 Reg2 As REGSTRUCT
 Reg_Reg As Long '1=from ptr
 Imm As IMMSTRUCT
 Instruction_Length As Long
End Type

'return buffer length
Declare Function DisAssemble Lib "disASM" (Data As Any, ByVal BaseAddress As Long, DisAsmString As Any, DisAsmS As Any, ByVal DisasmOpt As Long) As Long

假设您将文件作为二进制文件加载到,结构将像这样初始化

Dim FDATA() as byte
Dim DisA As DisAsmStruct

DLL 调用将是

BufferLength = DisAssemble(FDATA(CNT), BaseAddress + CNT, ByVal Opcodes, DisA, 0)

CNT 值是一个计数器,它会在调用后根据发现反汇编指令的长度递增

CNT = CNT + DisA.Instruction_Length

DLL Function OpCodes 中的其他值是写入字符串内存,其中打印了人类可读的 asm 代码的字符串。 DisA 结构也由相同的 DLL 函数调用填充。 BufferLength 被 return 编辑为一个函数 return 值我认为我不会有任何问题,它用于 trim OpCodes 字符串到以后适当的字符串大小。

操作码是这样声明的。

Dim Opcodes     As String
Opcodes = String(128, 0)

这是我目前在 C# 中尝试的方法

我所有的失败尝试

    //return buffer length
    [DllImport("disASM.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    public static extern int DisAssemble(IntPtr Data, uint BaseAddress, IntPtr DisAsmString, IntPtr DisAsmS, uint DisasmOpt);

    [DllImport("disASM.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    public static extern int DisAssemble(IntPtr Data, uint BaseAddress, string DisAsmString, DisAsmStruct DisAsmS, uint DisasmOpt);

    [DllImport("disASM.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    public static extern short DisAssemble(
      [MarshalAs(UnmanagedType.LPArray)] [In] ref byte[] data, 
      uint BaseAddress,
      ref string DisAsmString,
      ref DisAsmStruct DisAsmS,
      uint DisasmOpt);

    [DllImport("disASM.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    public static extern short DisAssemble(
      [MarshalAs(UnmanagedType.LPArray)] [In] ref byte[] data, 
      uint BaseAddress,
      ref string DisAsmString,
      ref DisAsmStruct DisAsmS,
      uint DisasmOpt);

    [DllImport("disASM.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    public static extern short DisAssemble(
      [MarshalAs(UnmanagedType.LPArray)] [In] ref byte[] data, 
      uint BaseAddress,
      [MarshalAs(UnmanagedType.LPStr)]  ref string DisAsmString,
      [MarshalAs(UnmanagedType.LPStruct)]ref DisAsmStruct DisAsmS,
      uint DisasmOpt);

//this looks like the best one
        [DllImport("disASM.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
        public static extern int DisAssemble(IntPtr Data, uint BaseAddress, out string DisAsmString, out DisAsmStruct DisAsmS, uint DisasmOpt);

我做不到

    [DllImport("disASM.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
    public static extern short DisAssemble(
      byte* data, 
      uint BaseAddress,
      string* DisAsmString,
      DisAsmStruct* DisAsmS,
      uint DisasmOpt);

因为它会给出错误指针和固定大小的缓冲区只能在不安全的上下文中使用

最接近的尝试是这个,它没有用,但我觉得它已经足够好了,因为指针被公开并使用 CNT 计数器正确递增,只是由于 .NET i 中的一些保护而没有通过不知道。

unsafe
{ 
    fixed(byte* test = &GLOBALDATA.FDATA[CNT])
    {
        IntPtr codeBuf = new IntPtr((void*)test);
        BufferLength = ModuleASM.DisAssemble(codeBuf, BaseAddress + CNT, out Opcodes, out DisA, 0);
    }
}

网上研究说我喜欢这个(当然行不通)

GCHandle fileDataPointered = GCHandle.Alloc(GLOBALDATA.FDATA[CNT], GCHandleType.Pinned);
BufferLength = ModuleASM.DisAssemble(fileDataPointered.AddrOfPinnedObject(), BaseAddress + CNT, ref Opcodes, ref DisA, 0);
//BufferLength = ModuleASM.DisAssemble(fileDataPointered.AddrOfPinnedObject(), BaseAddress + CNT, Opcodes, DisA, 0);
fileDataPointered.Free();

在线研究示例编译正常但与不安全代码示例类似的错误

An unhandled exception of type 'System.AccessViolationException' occurred in {}.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

有人在 google link 上推荐了一个工具,我尝试称为 P/Invoke Interop Assistant,它可以根据将 .DLL 文件加载到其中来生成 DLLImport。我从中得到一个错误,.dll 文件缺少程序集清单。

您可能在 32 位和 64 位平台上遇到问题。 VB6 仅是 32 位的,所以 DisAssemble.dll 是一个 32 位的 dll。

.Net同时支持32位和64位,默认为AnyCpu。 AnyCpu 将 运行 在 64 位操作系统上作为 64 位,这很可能是这种情况。

尝试将目标平台更改为 x86。

您不能在 64 位进程中加载​​ 32 位 dll。此外,在 64 位 CLR 上 运行ning 时 IntPtr 为 8 字节,在 32 位 CLR 上 运行ning 时为 4 字节。

耶!我得到它来调用。

问题是 .NET string 与 vb6 String 不同 String 我不知道确切的区别..但是 vb6

Dim OpCodes As String

改变 C#

string OpCodes;

IntPtr OpCodes;

完全解决了这个问题,是的,它不是很漂亮,但我现在必须把它从指针中取出回到字符串,我现在就开始处理。

起初我想也许我必须将所有变量移到不安全范围内。我这样做了,不知道它是否有助于修复。但我认为这并不重要,是的,我测试了不安全范围外的变量仍然可以正常工作!

完整的工作代码如下所示,好吧,它不适用于字符串。我会坚持下去,也修复了这个问题

[DllImport("disASM.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public static extern int DisAssemble(IntPtr Data, uint BaseAddress, out IntPtr DisAsmString, out DisAsmStruct DisAsmS, uint DisasmOpt);


unsafe
{
    string Opcodes = new string((char)0, 128);
    IntPtr OpcodesTest;
    ModuleASM.DisAsmStruct DisAa = new ModuleASM.DisAsmStruct();
    fixed (byte* dataPtr = &GLOBALDATA.FDATA[CNT])
    {
        IntPtr dataBuf = new IntPtr((void*)dataPtr);
        BufferLength = ModuleASM.DisAssemble(dataBuf, BaseAddress + CNT, out OpcodesTest, out DisAa, 0);

        //Kinda like it.. need more characters like, PUSH ECX
        ASCIIEncoding.ASCII.GetString(BitConverter.GetBytes(OpcodesPtr.ToInt64())) //return "PUSH[=13=][=13=][=13=][=13=]"

        byte testbbb = Marshal.ReadByte(OpcodesTest); //fail error
        string testa = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(OpcodesTest)); //fail error
        string testb = Marshal.PtrToStringAnsi(OpcodesTest); //blank return
        string testc = Marshal.PtrToStringUni(OpcodesTest); //fail error
        string testd = Marshal.PtrToStringUni(Marshal.ReadIntPtr(OpcodesTest)); //fail error
        string teste = Marshal.PtrToStringAuto(Marshal.ReadIntPtr(OpcodesTest)); //fail error
        string testf = Marshal.PtrToStringAuto(OpcodesTest); //fail error
    }
}