采用Native 引导方式的.Net加密保护

文/rick  出处/博客园

这类加密保护方式属于整体程序集的加密保护.
这个方法首要解决的问题就是 native code 和 .Net Code如何交互.
这里介绍三种实现方式.

1. C++/CLI 实现.
这个比较简单了,会C++/CLI一下子就能完成了.
Loader是由C++/CLI实现的.运行时通过解码程序集通过反射载入然后运行.
void InvokeAssemblyResource()
{

try
{
  byte[] pBuf = GetDecryptedResource(); 
  Assembly^ asm = Assembly::Load(pBuf);
  asm->EntryPoint->Invoke(nullptr,nullptr);
}
catch(Exception^ ex)
{
  MessageBox::Show(ex->Message);
}


}

2. 利用C#导出Com接口和native code交互.
Loader由C#和native code两部分组成.
C#部分代码
public interface IInvokeAssembly
{
void LoadAndExecute(byte[] pBuf);
};

public class CInvokeAssembly : IInvokeAssembly
{
public CInvokeAssembly()
{
}
public void LoadAndExecute(byte[] pBuf)
{
  try
  {
  Assembly asm = Assembly.Load(pBuf);
  asm.EntryPoint.Invoke(null,null);
  }
  catch(Exception ex)
  {
  MessageBox.Show(ex.Message);
  }
}
}

这里导出的 IInvokeAssembly 接口,将在native code中使用.
native code 部分
void InvokeAssemblyResource()
{
IInvokeAssemblyPtr pInvoker; //COM Pointer to the .Net Interface

if(FAILED(pInvoker.CreateInstance(CLSID_CInvokeAssembly)))
{
  MessageBox(NULL,_T("Unable to Create Invoke Assembly Object !!"),_T("Error"),MB_OK|MB_ICONERROR);
  return;
}

HRSRC hRC = FindResource(NULL,MAKEINTRESOURCE(IDR_EMBEDDED_ASSEMBLY),"RT_EMBEDDED_ASSEMBLY");
HGLOBAL hRes = LoadResource(NULL,hRC);
DWORD dwSize = SizeofResource(NULL,hRC);

SAFEARRAY* pSA = NULL;

if(NULL !=(pSA = SafeArrayCreateVector(VT_UI1, 0, dwSize)))
{
  LPVOID pBuf = NULL;

  if(FAILED(SafeArrayAccessData(pSA,&pBuf)))
  MessageBox(NULL,_T("Unable to Access SafeArray Data"), _T("Error"),MB_OK|MB_ICONERROR);
  else
  {
  LPVOID hAsm = LockResource(hRes);

  memcpy(pBuf, hAsm, dwSize);
 
  UnlockResource(hRes);
 
  SafeArrayUnaccessData(pSA);
  }

  pInvoker->LoadAndExecute(pSA); //Invoke the Reflection to load and Execute our Byte[]
}
else
  MessageBox(NULL,_T("Unable to Allocate Memory"),_T("Memory Allocate Error"),MB_OK|MB_ICONERROR);

if(pSA) SafeArrayDestroy(pSA);
}


这里还有一个问题,loader是两部分.加密的程序集可以作为资源签入到native code loader中.但是C#部分怎么处理?
一个比较隐蔽的方式是,在安装程序时将它安装到gac中.

3 是利用 CLR-Hosting 接口. 可以参考msdn中 本地接口部分的文档.
bool InvokeAssemblyResource()

CComPtr<ICorRuntimeHost> spRuntimeHost;
CComPtr<_AppDomain> spAppDomain;
CComPtr<IUnknown> spUnk;

bool bSuccess = false;

if(FAILED(CorBindToRuntimeEx( NULL, // Latest Version by Default
    L"wks",  // Workstation build
    STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN,
    CLSID_CorRuntimeHost ,
    IID_ICorRuntimeHost ,
    (void**)&spRuntimeHost)))
{
  gErrMsg = _T("Unable to Bind CLR");
  return false;
}
if(FAILED(spRuntimeHost->Start()))
{
  gErrMsg = _T("Unable to Start CLR");
  return false;
}
do
{
  if(FAILED(spRuntimeHost->GetDefaultDomain(&spUnk)))
  {
  gErrMsg = _T("Unable to GetDefaultDomain");
  break;
  }
  if(FAILED(spUnk->QueryInterface(&spAppDomain.p)))
  {
  gErrMsg = _T("Unable to Query AppDomain Interface");
  break;
  }

  SAFEARRAY* pSA = GetDecryptedResource();
  if(pSA)
  {
  try
  {    // Invoke the Entry Point with No Arguments
    spAppDomain->Load_3(pSA)->EntryPoint->Invoke_3(_variant_t(), NULL);
    bSuccess = true; // Everything Went Fine !!
  }
  catch(_com_error ex)
  {
    gErrMsg = ex.ErrorMessage();
  }

  SafeArrayDestroy(pSA);
  pSA = NULL;
  }
}while(false);

if(FAILED(spRuntimeHost->Stop()))
{
  gErrMsg = _T("Unable to Stop CLR");
  return false;
}

return bSuccess;
}

一般这类加密工具都会选择第三种实现方式.如 .Net Reactor.

不过单纯的整体加密保护安装性是很低的,可以配合一些其它的方式来提高保护强度,如 .Net Reactor的 NecroBit.
因为在Load时很容易被dump出程序集.这种方式就是让Load时载入的程序集不是完整的(除去了IL代码部分,NecroBit).
然后在Load完成后,程序集执行之前,还原IL代码.

 感谢原创者的辛勤劳动,希望对您有所帮助,转载请注明原出处。
 您可能对 [Visual Studio.NET] 的这些文章也感兴趣:

Entity Framework之Entity SQL(四) 集合运算符
SVN-WINDOWS 服务器架设和管理
XNA中的HLSL简单应用
Enterprise Library深入解析与灵活应用(6):自己动手创建迷你版AOP框架
db4o发布7.2,出现.NET 3.5版本,支持LINQ
jQuery实例: ToolTip的实现
Visual Studio插件大搜索(60+)
Bringing Scripting back to VB
安装中文VS2008 SP1 和.NETFRAMEWORK 3.5SP1后智能提示是英文的解决办法
攻击方式学习之(2) - SQL注入(SQL Injection)