在Python中使用.NET的反射特性
· 阅读需 3 分钟
RT,最近在研究从.NET 生成的动态链接库里提取需要的常量(比如一些加密 Key),用 Python 可以实现一定程度的自动化。
使用反射需要你知道常量原本的命名空间,如果不知道可以用ILSpy
、dnSpy
、dotPeek
这类工具先手动反编译找一下。
主要是要通过 Python 来实现动态链接以及自动化读取,如果常量经常发生变化或者有特殊需求就很有点用了。
总而言之,先上一段 dotPeek 反编译出来的 C#代码作为示例:
using XXX2.Util;
using System.Security.Cryptography;
using System.Text;
#nullable disable
namespace Net
{
internal class CipherAES : Singleton<CipherAES>
{
private static readonly int BLOCK_SIZE = 128;
private static readonly int KEY_SIZE = 256;
private static readonly string AesKey = "This is an AesKey";
private static readonly string AesIV = "This is an AesIv";
public static byte[] Encrypt(byte[] data)
{
// ......
}
public static byte[] Decrypt(byte[] encryptData)
{
// ......
}
}
}
我们的目标是通过 Python 脚本自动获取其中的加密常量用于自己的仿真客户端,这时我们需要使用pythonnet
这个库:
poetry add pythonnet
新建一个.py
脚本,随便叫什么都行,我给个peek.py
吧。
# 引入pythonnet以及System包
import clr
# 因为IDE一般没法直接认clr加载进来的.net库,所以可以加上下面这行把IDE标红消掉,眼不见为净。
# noinspection PyUnresolvedReferences
from System import Reflection
assembly = Reflection.Assembly.LoadFile("X:\xxx\xxx\xxx.dll") # 必须使用绝对路径!
我现在已经知道了我要的常量存在于Net.CipherAES
类中,那么直接对其进行 GetType
:
cipher_aes_type = assembly.GetType('Net.CipherAES')
然后就可以利用 GetField
方法查找常量所在的位置了:
if cipher_aes_type is not None: # 可以通过这个方法来判断是否能够定位这个类
# 获取常量值
aes_key = cipher_aes_type.GetField('AesKey', Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic).GetValue(None)
完整代码:
assembly = Reflection.Assembly.LoadFile("X:\xxx\xxx\xxx.dll")
try:
cipher_aes_type = assembly.GetType('Net.CipherAES')
if cipher_aes_type is not None:
print(f"Found type: {cipher_aes_type.FullName}")
# 获取常量值
block_size = cipher_aes_type.GetField('BLOCK_SIZE',
Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic).GetValue(
None)
key_size = cipher_aes_type.GetField('KEY_SIZE',
Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic).GetValue(
None)
aes_key = cipher_aes_type.GetField('AesKey',
Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic).GetValue(
None)
aes_iv = cipher_aes_type.GetField('AesIV',
Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic).GetValue(
None)
print(f"BLOCK_SIZE: {block_size}")
print(f"KEY_SIZE: {key_size}")
print(f"AesKey2: {aes_key}")
print(f"AesIV: {aes_iv}")
except Exception as e:
print(f"Exception occurred: {e}")