前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >.NET托管指针

.NET托管指针

作者头像
江湖评谈
发布2024-06-11 17:31:18
620
发布2024-06-11 17:31:18
举报
文章被收录于专栏:天下风云天下风云

前言

尽管大部分时候对于底层更喜欢C/C++和汇编,它们对于软/硬(件)的操控可以精确到bit。但是有些场景依然要用到托管指针,可以混合提高开发效率。本篇简略看下。

例子说明

一个最简单操作即是IntPtr类型,它虽然是一个nint,但却是一个货真价实的指针,类似于C语言的*符号。

声明:

代码语言:javascript
复制
IntPtr ptr;

如果要把托管def函数变成指针:‍

代码语言:javascript
复制
  示例函数:
  public static void def()
  {
      Console.WriteLine("def");
  }
  函数委托:
  delegate void delegatedef();
  委托实例:
  static delegatedef deldef ;

可以

代码语言:javascript
复制
IntPtr f = Marshal.GetFunctionPointerForDelegate(def);

把指针变成托管函数呢?

代码语言:javascript
复制
deldef = Marshal.GetDelegateForFunctionPointer(f, typeof(delegatedef)) as delegatedef;

然后直接调用即可:

代码语言:javascript
复制
deldef();

def函数是静态的,这里在.NET8里面会提示如下错误:

代码语言:javascript
复制
Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.

此时可以通过反射获取函数指针:

代码语言:javascript
复制
 MethodInfo methodInfoo = typeof(Program).GetMethod("def", BindingFlags.Public | BindingFlags.Static);
 IntPtr functionPointer = methodInfoo.MethodHandle.GetFunctionPointer();

然后如上调用即可,这时候就不报错了,代码如下:

代码语言:javascript
复制
deldef = Marshal.GetDelegateForFunctionPointer(functionPointer , typeof(delegatedef)) as delegatedef;
deldef();

上面是一个简单的操作,再来看操作IntPtr指针,,以上面指针functionPointer为例,向IntPtr指针指向的地址赋值:

代码语言:javascript
复制
 Marshal.WriteIntPtr(functionPointer,value);

读取IntPtr指针指向的地址值:

代码语言:javascript
复制
IntPtr ptr = Marshal.ReadIntPtr(functionPointer);

有时候读取/写入的指针指向的内存受到保护,比如不能读或者不能写,或者不能执行,这时候可以用API:VirtualProtect改写IntPtr指向内存的属性:

它的声明如下:

代码语言:javascript
复制
 [DllImport("kernel32.dll", SetLastError = true)]
 private static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);

比如我要让写入functionPointer的地方的内存属性能读,能写,能执行,代码如下,0x40即表示PAGE_EXECUTE_READWRITE。

代码语言:javascript
复制
 VirtualProtect(ptr1, 8, 0x40, out oldProtect);
 Marshal.WriteIntPtr(functionPointer,value);
 VirtualProtect(ptr1, 8, oldProtect, out oldProtect);

综合例子

上面大致介绍了托管指针的操作,下面看一个操作JIT的例子。通过托管和非托管互操,利用托管/非托管指针等知识。

JIT导出函数getJit

代码语言:javascript
复制
[DllImport("clrjit.dll", CallingConvention = CallingConvention.Cdecl)]
 private static extern IntPtr getJit();

读取到JIT的编译函数CompileMethod,然后对这个函数进行def函数替换,也即是上面的functionPointer。这样的话,可以直接操作内存。代码如下:

代码语言:javascript
复制
 IntPtr ptr = getJit();
 uint oldProtect;
 IntPtr ptr1 =  Marshal.ReadIntPtr(Marshal.ReadIntPtr(ptr));
 VirtualProtect(ptr1, 8, 0x40, out oldProtect);
 Marshal.WriteIntPtr(ptr1, functionPointer);
 VirtualProtect(ptr1, 8, oldProtect, out oldProtect);

下面再来看下非托管的C++

代码语言:javascript
复制
//ConsoleApplication4.cpp
#include <cstdint>
#include<Windows.h>

void* GlobalPtr;
DWORD OldProtect;

typedef int(*CompileMethodDelegate)(long long* compHnd,
    long long* methodInfo,
    unsigned             flags,
    uint8_t** entryAddress,
    uint32_t* nativeSizeOfCode);

extern "C"  __declspec(dllexport)  int CompileMethodZhuanZhe(long long* compHnd,
    long long*  methodInfo,
    unsigned             flags,
    uint8_t** entryAddress,
    uint32_t* nativeSizeOfCode)
{
 /*   byte* ilcode = (byte*)(methodInfo+0x02);
    byte* codesize = (byte*)(methodInfo + 0x3);*

    VirtualProtect(GlobalPtr, 8, 0x40,  &OldProtect);
    CompileMethodDelegate def = (CompileMethodDelegate)GlobalPtr;
    VirtualProtect(GlobalPtr, 8, OldProtect,  &OldProtect);
    int nRet = def(compHnd, methodInfo, flags, entryAddress, nativeSizeOfCode);
    return 0;
}

extern "C" __declspec(dllexport) compileMethod_def hook(void * intptr)
{
    VirtualProtect(GlobalPtr, 8, 0x40, &OldProtect);
    GlobalPtr = intptr;
    VirtualProtect(GlobalPtr, 8, OldProtect, &OldProtect);

    CompileMethodDelegate def = &CompileMethodZhuanZhe;
    return def;
}


extern "C" __declspec(dllexport) int Add(int a, int b,int c,int d,int e) {
    //return Compile(a, b+1);
    return 0;
}

此时C#就可以

代码语言:javascript
复制
 [DllImport("ConsoleApplication4.dll", CallingConvention = CallingConvention.Cdecl)]
 private static extern IntPtr hook(IntPtr intptr);

调用

代码语言:javascript
复制
IntPtr ptr = getJit();
uint oldProtect;
ptr1 = Marshal.ReadIntPtr(ptr);
IntPtr ptr2 = Marshal.ReadIntPtr(ptr1);
IntPtr hookptr = hook(ptr2);
VirtualProtect(ptr1, 8, 0x40, out oldProtect);
Marshal.WriteIntPtr(ptr1, hookptr);
VirtualProtect(ptr1, 8, oldProtect, out oldProtect);

这样托管指针,非托管指针,托管/非托管都进行了操作。以上简单的例子。

结尾

托管的指针同样可以达到非托管的效果,但是托管依然需要经过JIT编译,不如非托管来的直接。某些方面可以和非托管形成互补,已完成需要的需求以及项目疑难点,提高效率。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-06-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 江湖评谈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 例子说明
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档