前言:
文章的技巧在DotNet1.0发布那年就已经使用,今天心血来潮,只想更新下2年未更新的BLOG,了以充数罢了.
正文:
Dotnet编译的DLL,EXE等文件,通过混淆可以一定程度保护自己的代码安全,我在此介绍自己一直使用的方式--垫片启动.
运行EXE时,先运行C++代码,再装载DotNet应用程序运行环境,然后运行DotNet程序.
本篇不涉及安全保护,代码混淆,加密等操作,只是一个运行机制模式的介绍,如何保护自己的代码安全,由你自己决定.
C++启动DotNet环境.
VC++本地代码,支持DotNet语言的几个本地函数.
ICorRuntimeHost : 运行主机CLR接口
CorBindToRuntimeEx : 运行环境绑定
_AppDomainPtr : 托管AppDomain指针
_AssemblyPtr : 托管Assembly指针
请注意,mscoree.h头文件的引用
写一段C#代码,编译成EXE.将此EXE放到上面的C++的资源中并编译C++或启动调试.
主题说明:
整个运行流程,就是将托管反射的应用改成由C++代码通过本地函数调用来实现.
其中放进资源文件的EXE或DLL,你可以使用混淆代码并加密它,然后C++读取资源时解密,并装载到程序域中.找到托管代码的入口函数就完成了.
对于C++的资源,有些工具很容易提取,如果不加密,那提取以后还是很容易看到原码的.
注意点说明:
本文并非最安全的保护,由它一个薄弱可攻击的安全漏洞,这是由于JIT决定的.可以采用C++编写JIT拦截代码,看到你运行的托管函数等信息.
所以只能从一定程度上起到保护作用.
文章的技巧在DotNet1.0发布那年就已经使用,今天心血来潮,只想更新下2年未更新的BLOG,了以充数罢了.
正文:
Dotnet编译的DLL,EXE等文件,通过混淆可以一定程度保护自己的代码安全,我在此介绍自己一直使用的方式--垫片启动.
运行EXE时,先运行C++代码,再装载DotNet应用程序运行环境,然后运行DotNet程序.
本篇不涉及安全保护,代码混淆,加密等操作,只是一个运行机制模式的介绍,如何保护自己的代码安全,由你自己决定.
C++启动DotNet环境.
VC++本地代码,支持DotNet语言的几个本地函数.
ICorRuntimeHost : 运行主机CLR接口
CorBindToRuntimeEx : 运行环境绑定
_AppDomainPtr : 托管AppDomain指针
_AssemblyPtr : 托管Assembly指针
请注意,mscoree.h头文件的引用
有效使用这些对象提供的方法,可以反射加载应用程序.
#include "stdafx.h"
#include <excpt.h>
#include <windows.h>
#include <mscoree.h>
#include <assert.h>
#include <stdio.h>
#include <tchar.h>
#include <atlsafe.h>
#include <iostream>
#include <string.h>
#define RC_LENGTH 8
void ExecuteAssembly();
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
ExecuteAssembly();
PostQuitMessage(0);
return 0;
}
void ExecuteAssembly()
{
//垫片方式启动托管入口
LPWSTR pszVer = L"v2.0.50727"; // .NET Fx 3.5 needs CLR 2.0
LPWSTR pszFlavor = L"svr"; //svr:多核优化处理GC, wks:总是执行单核GC处理;
ICorRuntimeHost *pClrHost = NULL;
HRESULT hr = ::CorBindToRuntimeEx(
pszVer,
pszFlavor,
STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_CONCURRENT_GC,
CLSID_CorRuntimeHost,
IID_ICorRuntimeHost,
(void **)&pClrHost);
pClrHost->Start(); // 启动CLR
_AppDomainPtr pDefaultDomain = NULL;
IUnknownPtr pAppDomainPunk = NULL;
hr = pClrHost->GetDefaultDomain(&pAppDomainPunk);
hr = pAppDomainPunk->QueryInterface(__uuidof(_AppDomain),(void**) &pDefaultDomain);
//资源读取,并装载主程序依赖的引用库(第三方不能商业发布的组件,你可以考虑放在其中)
CReaderRc *rcDll = new CReaderRc(IDR_SPCLRASM1, _T("spclrasm"), pDefaultDomain);
/*
可以将你的代码作为C++的资源,一起编译成EXE
*/
//托管的主程序集
_AssemblyPtr ExeAssembly;
hr = pDefaultDomain->Load_3(*(rcDll->getByteArray()).GetSafeArrayPtr(), &ExeAssembly);
delete rcDll;
/*
rcDll->getByteArray() 是应用程序程序集文件的字节数组,由CReaderRc负责读取.
你完全可以在这个里面,采用自己的加/解密算法,操作文件.
*/
//找到入口方法,并调用执行
_MethodInfo* EntryPointInfo;
hr = ExeAssembly->get_EntryPoint(&EntryPointInfo);
_variant_t thisPointer, ReturnValue;
CComSafeArray<BSTR> Parameters; //main方法的参数
hr = EntryPointInfo->Invoke_3(thisPointer, *Parameters.GetSafeArrayPtr() , &ReturnValue);
}
写一段C#代码,编译成EXE.将此EXE放到上面的C++的资源中并编译C++或启动调试.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
主题说明:
整个运行流程,就是将托管反射的应用改成由C++代码通过本地函数调用来实现.
其中放进资源文件的EXE或DLL,你可以使用混淆代码并加密它,然后C++读取资源时解密,并装载到程序域中.找到托管代码的入口函数就完成了.
对于C++的资源,有些工具很容易提取,如果不加密,那提取以后还是很容易看到原码的.
注意点说明:
本文并非最安全的保护,由它一个薄弱可攻击的安全漏洞,这是由于JIT决定的.可以采用C++编写JIT拦截代码,看到你运行的托管函数等信息.
所以只能从一定程度上起到保护作用.