採用Native引導方式的.Net加密保護

瑞克-rick發表於2008-02-01

這類加密保護方式屬於整體程式集的加密保護.
這個方法首要解決的問題就是 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程式碼.


相關文章