add Obfuz Unity Project

before-split
walon 2025-05-17 14:53:51 +08:00
parent eb0542ed0f
commit 50fb2274f1
156 changed files with 17805 additions and 392 deletions

400
.gitignore vendored
View File

@ -3,398 +3,14 @@
## ##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files **/Library/
*.rsuser **/Logs/
*.suo **/Temp/
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio) **/.vs/
*.userprefs **/bin/
**/obj/
# Mono auto generated files
mono_crash.*
# Build results *.vsconfig
[Dd]ebug/ **/UserSettings/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml

View File

@ -0,0 +1,17 @@
test stack trace
UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
Obfus2.TestStackTrace:Stack3()
Obfus2.NestedClass`1:Stack2(TestStackTrace, Int32[], List`1, Banana)
Obfus2.TestStackTrace:Stack1(Int64, UInt64, Single, Double, String, Object)
Obfus2.TestStackTrace:Stack0(Byte, SByte, Int16, UInt16, Int32, UInt32)
Tests.TC_StackTrace:PrintStackTrace()
System.Reflection.RuntimeMethodInfo:InternalInvoke(Object, Object[], Exception&)
System.Reflection.RuntimeMethodInfo:Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
System.Reflection.MethodBase:Invoke(Object, Object[])
SharpUnit.TestCase:Run(TestResult)
SharpUnit.TestSuite:Run(TestResult)
TestRunner:Run()
Bootstrap:Start()

View File

@ -0,0 +1,17 @@
test stack trace
UnityEngine.DebugLogHandler:Internal_Log(LogType, LogOption, String, Object)
UnityEngine.DebugLogHandler:LogFormat(LogType, Object, String, Object[])
UnityEngine.Logger:Log(LogType, Object)
UnityEngine.Debug:Log(Object)
F.g:A()
F.G:a(g, Int32[], List`1, Banana)
F.g:a(Int64, UInt64, Single, Double, String, Object)
F.g:b(Byte, SByte, Int16, UInt16, Int32, UInt32)
Tests.TC_StackTrace:PrintStackTrace()
System.Reflection.RuntimeMethodInfo:InternalInvoke(Object, Object[], Exception&)
System.Reflection.RuntimeMethodInfo:Invoke(Object, BindingFlags, Binder, Object[], CultureInfo)
System.Reflection.MethodBase:Invoke(Object, Object[])
SharpUnit.TestCase:Run(TestResult)
SharpUnit.TestSuite:Run(TestResult)
TestRunner:Run()
Bootstrap:Start()

1095
Obfuz/Obfuz.Editor.csproj Normal file

File diff suppressed because it is too large Load Diff

881
Obfuz/Obfuz.Runtime.csproj Normal file
View File

@ -0,0 +1,881 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Generated file, do not modify, your changes will be overwritten (use AssetPostprocessor.OnGeneratedCSProject) -->
<PropertyGroup>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.20506</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<RootNamespace></RootNamespace>
<ProjectGuid>{1FE2F0BF-BDED-ACA3-1B41-08AC3002B1B6}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<AssemblyName>Obfuz.Runtime</AssemblyName>
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<BaseDirectory>.</BaseDirectory>
</PropertyGroup>
<PropertyGroup>
<NoWarn>0169;USG0001</NoWarn>
<DefineConstants>UNITY_2022_3_11;UNITY_2022_3;UNITY_2022;UNITY_5_3_OR_NEWER;UNITY_5_4_OR_NEWER;UNITY_5_5_OR_NEWER;UNITY_5_6_OR_NEWER;UNITY_2017_1_OR_NEWER;UNITY_2017_2_OR_NEWER;UNITY_2017_3_OR_NEWER;UNITY_2017_4_OR_NEWER;UNITY_2018_1_OR_NEWER;UNITY_2018_2_OR_NEWER;UNITY_2018_3_OR_NEWER;UNITY_2018_4_OR_NEWER;UNITY_2019_1_OR_NEWER;UNITY_2019_2_OR_NEWER;UNITY_2019_3_OR_NEWER;UNITY_2019_4_OR_NEWER;UNITY_2020_1_OR_NEWER;UNITY_2020_2_OR_NEWER;UNITY_2020_3_OR_NEWER;UNITY_2021_1_OR_NEWER;UNITY_2021_2_OR_NEWER;UNITY_2021_3_OR_NEWER;UNITY_2022_1_OR_NEWER;UNITY_2022_2_OR_NEWER;UNITY_2022_3_OR_NEWER;PLATFORM_ARCH_64;UNITY_64;UNITY_INCLUDE_TESTS;ENABLE_AR;ENABLE_AUDIO;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_EVENT_QUEUE;ENABLE_MICROPHONE;ENABLE_MULTIPLE_DISPLAYS;ENABLE_PHYSICS;ENABLE_TEXTURE_STREAMING;ENABLE_VIRTUALTEXTURING;ENABLE_LZMA;ENABLE_UNITYEVENTS;ENABLE_VR;ENABLE_WEBCAM;ENABLE_UNITYWEBREQUEST;ENABLE_WWW;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_EDITOR_GAME_SERVICES;ENABLE_UNITY_GAME_SERVICES_ANALYTICS_SUPPORT;ENABLE_CLOUD_LICENSE;ENABLE_EDITOR_HUB_LICENSE;ENABLE_WEBSOCKET_CLIENT;ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_ANIMATION_JOBS;ENABLE_MANAGED_AUDIO_JOBS;ENABLE_MANAGED_UNITYTLS;INCLUDE_DYNAMIC_GI;ENABLE_SCRIPTING_GC_WBARRIERS;PLATFORM_SUPPORTS_MONO;RENDER_SOFTWARE_CURSOR;ENABLE_VIDEO;ENABLE_ACCELERATOR_CLIENT_DEBUGGING;ENABLE_NAVIGATION_PACKAGE_DEBUG_VISUALIZATION;ENABLE_NAVIGATION_HEIGHTMESH_RUNTIME_SUPPORT;ENABLE_NAVIGATION_UI_REQUIRES_PACKAGE;PLATFORM_STANDALONE;TEXTCORE_1_0_OR_NEWER;PLATFORM_STANDALONE_WIN;UNITY_STANDALONE_WIN;UNITY_STANDALONE;ENABLE_RUNTIME_GI;ENABLE_MOVIES;ENABLE_NETWORK;ENABLE_NVIDIA;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_OUT_OF_PROCESS_CRASH_HANDLER;ENABLE_CLUSTER_SYNC;ENABLE_CLUSTERINPUT;PLATFORM_UPDATES_TIME_OUTSIDE_OF_PLAYER_LOOP;GFXDEVICE_WAITFOREVENT_MESSAGEPUMP;PLATFORM_INITIALIZES_MEMORY_MANAGER_EXPLICITLY;ENABLE_MONO;NET_4_6;NET_UNITY_4_8;ENABLE_PROFILER;DEBUG;TRACE;UNITY_ASSERTIONS;UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_BURST_AOT;UNITY_TEAM_LICENSE;ENABLE_CUSTOM_RENDER_TEXTURE;ENABLE_DIRECTOR;ENABLE_LOCALIZATION;ENABLE_SPRITES;ENABLE_TERRAIN;ENABLE_TILEMAP;ENABLE_TIMELINE;ENABLE_LEGACY_INPUT_MANAGER;TEXTCORE_FONT_ENGINE_1_5_OR_NEWER;CSHARP_7_OR_LATER;CSHARP_7_3_OR_NEWER</DefineConstants>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>Temp\bin\Debug\</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>Temp\bin\Release\</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<NoConfig>true</NoConfig>
<NoStdLib>true</NoStdLib>
<AddAdditionalExplicitAssemblyReferences>false</AddAdditionalExplicitAssemblyReferences>
<ImplicitlyExpandNETStandardFacades>false</ImplicitlyExpandNETStandardFacades>
<ImplicitlyExpandDesignTimeFacades>false</ImplicitlyExpandDesignTimeFacades>
</PropertyGroup>
<PropertyGroup>
<ProjectTypeGuids>{E097FAD1-6243-4DAD-9C02-E9B9EFC3FFC1};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<UnityProjectGenerator>Package</UnityProjectGenerator>
<UnityProjectGeneratorVersion>2.0.23</UnityProjectGeneratorVersion>
<UnityProjectGeneratorStyle>Legacy</UnityProjectGeneratorStyle>
<UnityProjectType>Game:1</UnityProjectType>
<UnityBuildTarget>StandaloneWindows64:19</UnityBuildTarget>
<UnityVersion>2022.3.11f1</UnityVersion>
</PropertyGroup>
<ItemGroup>
<Analyzer Include="d:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\Extensions\Microsoft\Visual Studio Tools for Unity\Analyzers\Microsoft.Unity.Analyzers.dll" />
<Analyzer Include="D:\UnityHubs\2022.3.11f1\Editor\Data\Tools\Unity.SourceGenerators\Unity.SourceGenerators.dll" />
<Analyzer Include="D:\UnityHubs\2022.3.11f1\Editor\Data\Tools\Unity.SourceGenerators\Unity.Properties.SourceGenerator.dll" />
</ItemGroup>
<ItemGroup>
<Compile Include="Packages\com.code-philosophy.obfuz\Runtime\ObfuzIgnoreAttribute.cs" />
<Compile Include="Packages\com.code-philosophy.obfuz\Runtime\EncryptionService.cs" />
<Compile Include="Packages\com.code-philosophy.obfuz\Runtime\IEncryptor.cs" />
<Compile Include="Packages\com.code-philosophy.obfuz\Runtime\NullEncryptor.cs" />
<Compile Include="Packages\com.code-philosophy.obfuz\Runtime\ConstUtility.cs" />
<Compile Include="Packages\com.code-philosophy.obfuz\Runtime\EncryptorBase.cs" />
<Compile Include="Packages\com.code-philosophy.obfuz\Runtime\EncryptionScope.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Packages\com.code-philosophy.obfuz\Runtime\Obfuz.Runtime.asmdef" />
</ItemGroup>
<ItemGroup>
<Reference Include="UnityEngine">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.ARModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.ARModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.AccessibilityModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.AccessibilityModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.AssetBundleModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.AssetBundleModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.AudioModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.AudioModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.ClusterInputModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.ClusterInputModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.ClusterRendererModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.ClusterRendererModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.ContentLoadModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.ContentLoadModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CrashReportingModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.CrashReportingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.DSPGraphModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.DSPGraphModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.GIModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.GIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.GameCenterModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.GameCenterModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.GridModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.GridModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.HotReloadModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.HotReloadModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.IMGUIModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.IMGUIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.ImageConversionModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.ImageConversionModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.InputModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.InputModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.InputLegacyModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.InputLegacyModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.JSONSerializeModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.JSONSerializeModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.LocalizationModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.LocalizationModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PerformanceReportingModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.PerformanceReportingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.ProfilerModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.ProfilerModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.PropertiesModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.PropertiesModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.SharedInternalsModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.SharedInternalsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.SpriteMaskModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.SpriteMaskModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.SpriteShapeModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.SpriteShapeModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.StreamingModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.StreamingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.SubstanceModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.SubstanceModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TLSModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.TLSModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextCoreFontEngineModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.TextCoreFontEngineModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextCoreTextEngineModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.TextCoreTextEngineModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.TextRenderingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UnityAnalyticsCommonModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.UnityAnalyticsCommonModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UnityConnectModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.UnityConnectModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UnityCurlModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.UnityCurlModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UnityTestProtocolModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.UnityTestProtocolModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.UnityWebRequestModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestAssetBundleModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.UnityWebRequestAssetBundleModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestAudioModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.UnityWebRequestAudioModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestWWWModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.VFXModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.VFXModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.VirtualTexturingModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEngine.VirtualTexturingModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.CoreModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.DeviceSimulatorModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.DeviceSimulatorModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.DiagnosticsModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.DiagnosticsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.EditorToolbarModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.EditorToolbarModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.GraphViewModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.GraphViewModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.PresetsUIModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.PresetsUIModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.QuickSearchModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.QuickSearchModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.SceneTemplateModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.SceneTemplateModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.SceneViewModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.SceneViewModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.TextCoreFontEngineModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.TextCoreFontEngineModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.TextCoreTextEngineModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.TextCoreTextEngineModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.UIBuilderModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.UIBuilderModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.UIElementsModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.UIElementsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.UIElementsSamplesModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.UIElementsSamplesModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.UnityConnectModule">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEngine\UnityEditor.UnityConnectModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.Graphs">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\Managed\UnityEditor.Graphs.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.WebGL.Extensions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\PlaybackEngines\WebGLSupport\UnityEditor.WebGL.Extensions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.Android.Extensions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\PlaybackEngines\AndroidPlayer\UnityEditor.Android.Extensions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.iOS.Extensions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\PlaybackEngines\iOSSupport\UnityEditor.iOS.Extensions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.AppleTV.Extensions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\PlaybackEngines\AppleTVSupport\UnityEditor.AppleTV.Extensions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.WindowsStandalone.Extensions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\PlaybackEngines\WindowsStandaloneSupport\UnityEditor.WindowsStandalone.Extensions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.LinuxStandalone.Extensions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\PlaybackEngines\LinuxStandaloneSupport\UnityEditor.LinuxStandalone.Extensions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="dnlib">
<HintPath>Packages\com.code-philosophy.obfuz\Plugins\dnlib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.iOS.Extensions.Xcode">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\PlaybackEngines\AppleTVSupport\UnityEditor.iOS.Extensions.Xcode.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.iOS.Extensions.Xcode">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\PlaybackEngines\iOSSupport\UnityEditor.iOS.Extensions.Xcode.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEditor.iOS.Extensions.Common">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\PlaybackEngines\iOSSupport\UnityEditor.iOS.Extensions.Common.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.Android.Types">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\PlaybackEngines\AndroidPlayer\Unity.Android.Types.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.Android.Gradle">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\PlaybackEngines\AndroidPlayer\Unity.Android.Gradle.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Unity.Android.GradleProject">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\PlaybackEngines\AndroidPlayer\Unity.Android.GradleProject.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="mscorlib">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\mscorlib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Core">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime.Serialization">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.Runtime.Serialization.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Xml">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.Xml.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Xml.Linq">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.Xml.Linq.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Numerics">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.Numerics.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Numerics.Vectors">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.Numerics.Vectors.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.Http">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.Net.Http.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.IO.Compression">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.IO.Compression.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.CSharp">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Microsoft.CSharp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Data">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.Data.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Data.DataSetExtensions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.Data.DataSetExtensions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Drawing">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.Drawing.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.IO.Compression.FileSystem">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.IO.Compression.FileSystem.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ComponentModel.Composition">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.ComponentModel.Composition.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Transactions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\System.Transactions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Win32.Primitives">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\Microsoft.Win32.Primitives.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="netstandard">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\netstandard.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.AppContext">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.AppContext.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Buffers">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Buffers.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Collections.Concurrent">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Collections.Concurrent.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Collections">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Collections.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Collections.NonGeneric">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Collections.NonGeneric.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Collections.Specialized">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Collections.Specialized.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ComponentModel.Annotations">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ComponentModel.Annotations.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ComponentModel">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ComponentModel.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ComponentModel.EventBasedAsync">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ComponentModel.EventBasedAsync.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ComponentModel.Primitives">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ComponentModel.Primitives.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ComponentModel.TypeConverter">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ComponentModel.TypeConverter.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Console">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Console.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Data.Common">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Data.Common.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Diagnostics.Contracts">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Diagnostics.Contracts.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Diagnostics.Debug">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Diagnostics.Debug.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Diagnostics.FileVersionInfo">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Diagnostics.FileVersionInfo.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Diagnostics.Process">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Diagnostics.Process.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Diagnostics.StackTrace">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Diagnostics.StackTrace.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Diagnostics.TextWriterTraceListener">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Diagnostics.TextWriterTraceListener.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Diagnostics.Tools">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Diagnostics.Tools.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Diagnostics.TraceSource">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Diagnostics.TraceSource.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Drawing.Primitives">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Drawing.Primitives.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Dynamic.Runtime">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Dynamic.Runtime.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Globalization.Calendars">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Globalization.Calendars.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Globalization">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Globalization.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Globalization.Extensions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Globalization.Extensions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.IO.Compression.ZipFile">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.IO.Compression.ZipFile.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.IO">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.IO.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.IO.FileSystem">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.IO.FileSystem.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.IO.FileSystem.DriveInfo">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.IO.FileSystem.DriveInfo.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.IO.FileSystem.Primitives">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.IO.FileSystem.Primitives.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.IO.FileSystem.Watcher">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.IO.FileSystem.Watcher.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.IO.IsolatedStorage">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.IO.IsolatedStorage.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.IO.MemoryMappedFiles">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.IO.MemoryMappedFiles.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.IO.Pipes">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.IO.Pipes.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.IO.UnmanagedMemoryStream">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.IO.UnmanagedMemoryStream.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Linq">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Linq.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Linq.Expressions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Linq.Expressions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Linq.Parallel">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Linq.Parallel.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Linq.Queryable">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Linq.Queryable.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Memory">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Memory.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.Http.Rtc">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Net.Http.Rtc.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.NameResolution">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Net.NameResolution.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.NetworkInformation">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Net.NetworkInformation.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.Ping">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Net.Ping.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.Primitives">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Net.Primitives.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.Requests">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Net.Requests.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.Security">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Net.Security.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.Sockets">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Net.Sockets.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.WebHeaderCollection">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Net.WebHeaderCollection.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.WebSockets.Client">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Net.WebSockets.Client.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Net.WebSockets">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Net.WebSockets.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ObjectModel">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ObjectModel.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Reflection.DispatchProxy">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Reflection.DispatchProxy.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Reflection">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Reflection.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Reflection.Emit">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Reflection.Emit.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Reflection.Emit.ILGeneration">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Reflection.Emit.ILGeneration.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Reflection.Emit.Lightweight">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Reflection.Emit.Lightweight.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Reflection.Extensions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Reflection.Extensions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Reflection.Primitives">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Reflection.Primitives.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Resources.Reader">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Resources.Reader.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Resources.ResourceManager">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Resources.ResourceManager.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Resources.Writer">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Resources.Writer.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime.CompilerServices.VisualC">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Runtime.CompilerServices.VisualC.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Runtime.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime.Extensions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Runtime.Extensions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime.Handles">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Runtime.Handles.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime.InteropServices">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Runtime.InteropServices.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime.InteropServices.RuntimeInformation">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime.InteropServices.WindowsRuntime">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Runtime.InteropServices.WindowsRuntime.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime.Numerics">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Runtime.Numerics.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime.Serialization.Formatters">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Runtime.Serialization.Formatters.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime.Serialization.Json">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Runtime.Serialization.Json.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime.Serialization.Primitives">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Runtime.Serialization.Primitives.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Runtime.Serialization.Xml">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Runtime.Serialization.Xml.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Security.Claims">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Security.Claims.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Algorithms">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Security.Cryptography.Algorithms.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Csp">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Security.Cryptography.Csp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Encoding">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Security.Cryptography.Encoding.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Security.Cryptography.Primitives">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Security.Cryptography.Primitives.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Security.Cryptography.X509Certificates">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Security.Cryptography.X509Certificates.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Security.Principal">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Security.Principal.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Security.SecureString">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Security.SecureString.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ServiceModel.Duplex">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ServiceModel.Duplex.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ServiceModel.Http">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ServiceModel.Http.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ServiceModel.NetTcp">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ServiceModel.NetTcp.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ServiceModel.Primitives">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ServiceModel.Primitives.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ServiceModel.Security">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ServiceModel.Security.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Text.Encoding">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Text.Encoding.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Text.Encoding.Extensions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Text.Encoding.Extensions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Text.RegularExpressions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Text.RegularExpressions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Threading">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Threading.Overlapped">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Overlapped.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Threading.Tasks">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Tasks.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Tasks.Extensions.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Threading.Tasks.Parallel">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Tasks.Parallel.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Threading.Thread">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Thread.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Threading.ThreadPool">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.ThreadPool.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Threading.Timer">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Threading.Timer.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.ValueTuple">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.ValueTuple.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Xml.ReaderWriter">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Xml.ReaderWriter.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Xml.XDocument">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Xml.XDocument.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Xml.XmlDocument">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Xml.XmlDocument.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Xml.XmlSerializer">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Xml.XmlSerializer.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Xml.XPath">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Xml.XPath.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Xml.XPath.XDocument">
<HintPath>D:\UnityHubs\2022.3.11f1\Editor\Data\UnityReferenceAssemblies\unity-4.8-api\Facades\System.Xml.XPath.XDocument.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="GenerateTargetFrameworkMonikerAttribute" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

26
Obfuz/Obfuz.sln Normal file
View File

@ -0,0 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Obfuz.Editor", "Obfuz.Editor.csproj", "{61F9FC55-17B9-1093-9FB3-FCD0720BA981}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Obfuz.Runtime", "Obfuz.Runtime.csproj", "{1FE2F0BF-BDED-ACA3-1B41-08AC3002B1B6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{61F9FC55-17B9-1093-9FB3-FCD0720BA981}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{61F9FC55-17B9-1093-9FB3-FCD0720BA981}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61F9FC55-17B9-1093-9FB3-FCD0720BA981}.Release|Any CPU.ActiveCfg = Release|Any CPU
{61F9FC55-17B9-1093-9FB3-FCD0720BA981}.Release|Any CPU.Build.0 = Release|Any CPU
{1FE2F0BF-BDED-ACA3-1B41-08AC3002B1B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1FE2F0BF-BDED-ACA3-1B41-08AC3002B1B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1FE2F0BF-BDED-ACA3-1B41-08AC3002B1B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1FE2F0BF-BDED-ACA3-1B41-08AC3002B1B6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,400 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml

View File

@ -0,0 +1,267 @@
using dnlib.DotNet;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using UnityEditor.VersionControl;
using UnityEngine;
namespace Obfuz.Conf
{
public interface IRule<T>
{
void InheritParent(T parentRule);
}
public interface IMethodRule<R> where R: IRule<R>
{
string Name { get; set; }
NameMatcher NameMatcher { get; set; }
R Rule { get; set; }
}
public abstract class MethodRuleBase<R> : IMethodRule<R> where R : IRule<R>
{
public string Name { get; set; }
public NameMatcher NameMatcher { get; set; }
public R Rule { get; set; }
}
public interface ITypeRule<T, R> where T: IMethodRule<R> where R : IRule<R>
{
string Name { get; set; }
NameMatcher NameMatcher { get; set; }
R Rule { get; set; }
List<T> Methods { get; set; }
}
public abstract class TypeRuleBase<T, R> : ITypeRule<T, R> where T : IMethodRule<R> where R : IRule<R>
{
public string Name { get; set; }
public NameMatcher NameMatcher { get; set; }
public R Rule { get; set; }
public List<T> Methods { get; set; }
}
public interface IAssemblyRule<TType, TMethod, TRule> where TType : ITypeRule<TMethod, TRule> where TMethod : IMethodRule<TRule> where TRule : IRule<TRule>
{
string Name { get; set; }
TRule Rule { get; set; }
List<TType> Types { get; set; }
}
public abstract class AssemblyRuleBase<TType, TMethod, TRule> : IAssemblyRule<TType, TMethod, TRule> where TType : ITypeRule<TMethod, TRule> where TMethod : IMethodRule<TRule> where TRule : IRule<TRule>
{
public string Name { get; set; }
public TRule Rule { get; set; }
public List<TType> Types { get; set; }
}
public class XmlAssemblyTypeMethodRuleParser<TAssembly, TType, TMethod, TRule>
where TMethod : IMethodRule<TRule>, new()
where TType : ITypeRule<TMethod, TRule>, new()
where TAssembly : IAssemblyRule<TType, TMethod, TRule>, new()
where TRule : IRule<TRule>, new()
{
private readonly HashSet<string> _toObfuscatedAssemblyNames;
private readonly Func<string, XmlElement, TRule> _ruleParser;
private readonly Action<string, XmlElement> _unknownNodeTypeHandler;
private readonly Dictionary<string, TAssembly> _assemblySpecs = new Dictionary<string, TAssembly>();
public XmlAssemblyTypeMethodRuleParser(IEnumerable<string> toObfuscatedAssemblyNames, Func<string, XmlElement, TRule> ruleParser, Action<string, XmlElement> unknownNodeTypeHandler)
{
_toObfuscatedAssemblyNames = new HashSet<string>(toObfuscatedAssemblyNames);
_ruleParser = ruleParser;
_unknownNodeTypeHandler = unknownNodeTypeHandler;
}
public Dictionary<string, TAssembly> AssemblySpecs => _assemblySpecs;
public void LoadConfigs(IEnumerable<string> configFiles)
{
foreach (var configFile in configFiles)
{
LoadConfig(configFile);
}
}
public void LoadConfig(string configFile)
{
if (string.IsNullOrEmpty(configFile))
{
throw new Exception($"Invalid xml file {configFile}, file name is empty");
}
Debug.Log($"ConfigurableObfuscationPolicy::LoadConfig {configFile}");
var doc = new XmlDocument();
doc.Load(configFile);
var root = doc.DocumentElement;
if (root.Name != "obfuz")
{
throw new Exception($"Invalid xml file {configFile}, root name should be 'obfuz'");
}
foreach (XmlNode node in root.ChildNodes)
{
if (!(node is XmlElement ele))
{
continue;
}
switch (ele.Name)
{
case "assembly":
{
TAssembly assSpec = ParseAssembly(configFile, ele);
_assemblySpecs.Add(assSpec.Name, assSpec);
break;
}
default:
{
if (_unknownNodeTypeHandler == null)
{
throw new Exception($"Invalid xml file {configFile}, unknown node {ele.Name}");
}
_unknownNodeTypeHandler(configFile, ele);
break;
}
}
}
}
private TAssembly ParseAssembly(string configFile, XmlElement ele)
{
var assemblySpec = new TAssembly();
string name = ele.GetAttribute("name");
if (!_toObfuscatedAssemblyNames.Contains(name))
{
throw new Exception($"Invalid xml file {configFile}, assembly name {name} isn't in toObfuscatedAssemblyNames");
}
if (_assemblySpecs.ContainsKey(name))
{
throw new Exception($"Invalid xml file {configFile}, assembly name {name} is duplicated");
}
assemblySpec.Name = name;
assemblySpec.Rule = _ruleParser(configFile, ele);
var types = new List<TType>();
assemblySpec.Types = types;
foreach (XmlNode node in ele.ChildNodes)
{
if (!(node is XmlElement childEle))
{
continue;
}
switch (childEle.Name)
{
case "type":
{
types.Add(ParseType(configFile, childEle));
break;
}
default:
{
throw new Exception($"Invalid xml file, unknown node {childEle.Name}");
}
}
}
return assemblySpec;
}
private TType ParseType(string configFile, XmlElement element)
{
var typeSpec = new TType();
string name = element.GetAttribute("name");
typeSpec.Name = name;
typeSpec.NameMatcher = new NameMatcher(name);
typeSpec.Rule = _ruleParser(configFile, element);
var methods = new List<TMethod>();
typeSpec.Methods = methods;
foreach (XmlNode node in element.ChildNodes)
{
if (!(node is XmlElement ele))
{
continue;
}
switch (ele.Name)
{
case "method":
{
methods.Add(ParseMethod(configFile, ele));
break;
}
default:
{
throw new Exception($"Invalid xml file, unknown node {ele.Name}");
}
}
}
return typeSpec;
}
private TMethod ParseMethod(string configFile, XmlElement element)
{
var methodSpec = new TMethod();
string name = element.GetAttribute("name");
methodSpec.Name = name;
methodSpec.NameMatcher = new NameMatcher(name);
methodSpec.Rule = _ruleParser(configFile, element);
return methodSpec;
}
public TRule GetMethodRule(MethodDef method, TRule defaultRule)
{
var assemblyName = method.DeclaringType.Module.Assembly.Name;
if (!_assemblySpecs.TryGetValue(assemblyName, out var assSpec))
{
return defaultRule;
}
string declaringTypeName = method.DeclaringType.FullName;
foreach (var typeSpec in assSpec.Types)
{
if (typeSpec.NameMatcher.IsMatch(declaringTypeName))
{
foreach (var methodSpec in typeSpec.Methods)
{
if (methodSpec.NameMatcher.IsMatch(method.Name))
{
return methodSpec.Rule;
}
}
return typeSpec.Rule;
}
}
return assSpec.Rule;
}
public void InheritParentRules(TRule defaultRule)
{
foreach (TAssembly assSpec in _assemblySpecs.Values)
{
assSpec.Rule.InheritParent(defaultRule);
foreach (TType typeSpec in assSpec.Types)
{
typeSpec.Rule.InheritParent(assSpec.Rule);
foreach (TMethod methodSpec in typeSpec.Methods)
{
methodSpec.Rule.InheritParent(typeSpec.Rule);
}
}
}
}
}
}

View File

@ -0,0 +1,208 @@
using dnlib.DotNet;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using UnityEditor.VersionControl;
using UnityEngine;
namespace Obfuz.Conf
{
public class XmlFieldRuleParser<R> where R : class, new()
{
private readonly HashSet<string> _toObfuscatedAssemblyNames;
private readonly Func<string, XmlElement, R> _ruleParser;
private readonly Action<string, XmlElement> _unknownNodeTypeHandler;
private readonly Dictionary<string, AssemblySpec> _assemblySpecs = new Dictionary<string, AssemblySpec>();
private class FieldSpec
{
public string Name { get; set; }
public NameMatcher NameMatcher { get; set; }
public R Rule { get; set; }
}
private class TypeSpec
{
public string Name { get; set; }
public NameMatcher NameMatcher { get; set; }
public List<FieldSpec> Fields { get; set; }
}
private class AssemblySpec
{
public string Name { get; set; }
public List<TypeSpec> Types { get; set; }
}
public XmlFieldRuleParser(IEnumerable<string> toObfuscatedAssemblyNames, Func<string, XmlElement, R> ruleParser, Action<string, XmlElement> unknownNodeTypeHandler)
{
_toObfuscatedAssemblyNames = new HashSet<string>(toObfuscatedAssemblyNames);
_ruleParser = ruleParser;
_unknownNodeTypeHandler = unknownNodeTypeHandler;
}
public void LoadConfigs(IEnumerable<string> configFiles)
{
foreach (var configFile in configFiles)
{
LoadConfig(configFile);
}
}
public void LoadConfig(string configFile)
{
if (string.IsNullOrEmpty(configFile))
{
throw new Exception($"Invalid xml file {configFile}, file name is empty");
}
var doc = new XmlDocument();
doc.Load(configFile);
var root = doc.DocumentElement;
if (root.Name != "obfuz")
{
throw new Exception($"Invalid xml file {configFile}, root name should be 'obfuz'");
}
foreach (XmlNode node in root.ChildNodes)
{
if (!(node is XmlElement ele))
{
continue;
}
switch (ele.Name)
{
case "assembly":
{
AssemblySpec assSpec = ParseAssembly(configFile, ele);
_assemblySpecs.Add(assSpec.Name, assSpec);
break;
}
default:
{
if (_unknownNodeTypeHandler == null)
{
throw new Exception($"Invalid xml file {configFile}, unknown node {ele.Name}");
}
_unknownNodeTypeHandler(configFile, ele);
break;
}
}
}
}
private AssemblySpec ParseAssembly(string configFile, XmlElement ele)
{
var assemblySpec = new AssemblySpec();
string name = ele.GetAttribute("name");
if (!_toObfuscatedAssemblyNames.Contains(name))
{
throw new Exception($"Invalid xml file {configFile}, assembly name {name} isn't in toObfuscatedAssemblyNames");
}
if (_assemblySpecs.ContainsKey(name))
{
throw new Exception($"Invalid xml file {configFile}, assembly name {name} is duplicated");
}
assemblySpec.Name = name;
var types = new List<TypeSpec>();
assemblySpec.Types = types;
foreach (XmlNode node in ele.ChildNodes)
{
if (!(node is XmlElement childEle))
{
continue;
}
switch (childEle.Name)
{
case "type":
{
types.Add(ParseType(configFile, childEle));
break;
}
default:
{
throw new Exception($"Invalid xml file, unknown node {childEle.Name}");
}
}
}
return assemblySpec;
}
private TypeSpec ParseType(string configFile, XmlElement element)
{
var typeSpec = new TypeSpec();
string name = element.GetAttribute("name");
typeSpec.Name = name;
typeSpec.NameMatcher = new NameMatcher(name);
var fields = new List<FieldSpec>();
typeSpec.Fields = fields;
foreach (XmlNode node in element.ChildNodes)
{
if (!(node is XmlElement ele))
{
continue;
}
switch (ele.Name)
{
case "field":
{
fields.Add(ParseField(configFile, ele));
break;
}
default:
{
throw new Exception($"Invalid xml file, unknown node {ele.Name}");
}
}
}
return typeSpec;
}
private FieldSpec ParseField(string configFile, XmlElement element)
{
var fieldSpec = new FieldSpec();
string name = element.GetAttribute("name");
fieldSpec.Name = name;
fieldSpec.NameMatcher = new NameMatcher(name);
fieldSpec.Rule = _ruleParser(configFile, element);
return fieldSpec;
}
public R GetFieldRule(FieldDef field)
{
var assemblyName = field.DeclaringType.Module.Assembly.Name;
if (!_assemblySpecs.TryGetValue(assemblyName, out var assSpec))
{
return null;
}
string declaringTypeName = field.DeclaringType.FullName;
foreach (var typeSpec in assSpec.Types)
{
if (typeSpec.NameMatcher.IsMatch(declaringTypeName))
{
foreach (var fieldSpec in typeSpec.Fields)
{
if (fieldSpec.NameMatcher.IsMatch(field.Name))
{
return fieldSpec.Rule;
}
}
}
}
return null;
}
}
}

View File

@ -0,0 +1,547 @@
using dnlib.DotNet;
using Obfuz.ObfusPasses;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using UnityEngine;
namespace Obfuz
{
public class ConfigurablePassPolicy
{
class PassRule
{
public ObfuscationPassType? enablePasses;
public ObfuscationPassType? disablePasses;
public ObfuscationPassType? addPasses;
public ObfuscationPassType? removePasses;
public ObfuscationPassType finalPasses;
public void InheritParent(PassRule parentRule, ObfuscationPassType globalEnabledPasses)
{
finalPasses = parentRule.finalPasses;
if (enablePasses != null)
{
finalPasses = enablePasses.Value;
}
if (disablePasses != null)
{
finalPasses = ~disablePasses.Value;
}
if (addPasses != null)
{
finalPasses |= addPasses.Value;
}
if (removePasses != null)
{
finalPasses &= ~removePasses.Value;
}
finalPasses &= globalEnabledPasses;
}
}
class SpecBase
{
public string name;
public NameMatcher nameMatcher;
public PassRule rule;
}
class MethodSpec : SpecBase
{
}
class FieldSpec : SpecBase
{
}
class PropertySpec : SpecBase
{
}
class EventSpec : SpecBase
{
}
class TypeSpec : SpecBase
{
public List<FieldSpec> fields = new List<FieldSpec>();
public List<MethodSpec> methods = new List<MethodSpec>();
public List<PropertySpec> properties = new List<PropertySpec>();
public List<EventSpec> events = new List<EventSpec>();
}
class AssemblySpec
{
public string name;
public NameMatcher nameMatcher;
public PassRule rule;
public List<TypeSpec> types = new List<TypeSpec>();
}
private readonly ObfuscationPassType _enabledPasses;
private readonly HashSet<string> _toObfuscatedAssemblyNames;
private readonly List<AssemblySpec> _assemblySpecs = new List<AssemblySpec>();
private readonly PassRule _defaultPassRule;
private string _curLoadingConfig;
public ConfigurablePassPolicy(IEnumerable<string> toObfuscatedAssemblyNames, ObfuscationPassType enabledPasses, List<string> configFiles)
{
_toObfuscatedAssemblyNames = new HashSet<string>(toObfuscatedAssemblyNames);
_enabledPasses = enabledPasses;
_defaultPassRule = new PassRule { finalPasses = enabledPasses };
LoadConfigs(configFiles);
InheritParentRules(enabledPasses);
}
private void LoadConfigs(IEnumerable<string> configFiles)
{
foreach (var configFile in configFiles)
{
LoadConfig(configFile);
}
}
private void InheritParentRules(ObfuscationPassType enablePasses)
{
var defaultRule = new PassRule
{
enablePasses = enablePasses,
finalPasses = enablePasses,
};
foreach (AssemblySpec assSpec in _assemblySpecs)
{
assSpec.rule.InheritParent(defaultRule, enablePasses);
foreach (TypeSpec typeSpec in assSpec.types)
{
typeSpec.rule.InheritParent(assSpec.rule, enablePasses);
foreach (FieldSpec fieldSpec in typeSpec.fields)
{
fieldSpec.rule.InheritParent(typeSpec.rule, enablePasses);
}
foreach (MethodSpec methodSpec in typeSpec.methods)
{
methodSpec.rule.InheritParent(typeSpec.rule, enablePasses);
}
foreach (PropertySpec propertySpec in typeSpec.properties)
{
propertySpec.rule.InheritParent(typeSpec.rule, enablePasses);
}
foreach (EventSpec eventSpec in typeSpec.events)
{
eventSpec.rule.InheritParent(typeSpec.rule, enablePasses);
}
}
}
}
public void LoadConfig(string configFile)
{
if (string.IsNullOrEmpty(configFile))
{
throw new Exception($"Invalid xml file {configFile}, file name is empty");
}
_curLoadingConfig = configFile;
Debug.Log($"ConfigurablePassPolicy::LoadConfig {configFile}");
var doc = new XmlDocument();
doc.Load(configFile);
var root = doc.DocumentElement;
if (root.Name != "obfuz")
{
throw new Exception($"Invalid xml file {configFile}, root name should be 'obfuz'");
}
foreach (XmlNode node in root.ChildNodes)
{
if (!(node is XmlElement ele))
{
continue;
}
switch (ele.Name)
{
case "assembly":
{
AssemblySpec assSpec = ParseAssembly(ele);
_assemblySpecs.Add(assSpec);
break;
}
default:
{
throw new Exception($"Invalid xml file {configFile}, unknown node {ele.Name}");
}
}
}
}
(bool, ObfuscationPassType) ParseObfuscationType(string obfuscationPassTypesStr)
{
bool delta = false;
if (obfuscationPassTypesStr[0] == '+' || obfuscationPassTypesStr[0] == '-')
{
delta = true;
obfuscationPassTypesStr = obfuscationPassTypesStr.Substring(1);
}
ObfuscationPassType passType = ObfuscationPassType.None;
foreach (var passName in obfuscationPassTypesStr.Split('|'))
{
if (Enum.TryParse< ObfuscationPassType>(passName, out var pass))
{
passType |= pass;
}
else
{
throw new Exception($"Invalid xml file {_curLoadingConfig}, unknown pass type {passName}");
}
}
return (delta, passType);
}
private PassRule ParseRule(XmlElement ele)
{
var r = new PassRule();
if (ele.HasAttribute("enable"))
{
string enablePassStr = ele.GetAttribute("enable");
if (string.IsNullOrEmpty(enablePassStr))
{
throw new Exception($"Invalid xml file {_curLoadingConfig}, enable attribute is empty");
}
var (delta, passType) = ParseObfuscationType(enablePassStr);
if (delta)
{
r.addPasses = passType;
}
else
{
r.enablePasses = passType;
}
}
if (ele.HasAttribute("disable"))
{
string disablePassStr = ele.GetAttribute("disable");
if (string.IsNullOrEmpty(disablePassStr))
{
throw new Exception($"Invalid xml file {_curLoadingConfig}, disable attribute is empty");
}
var (delta, passType) = ParseObfuscationType(disablePassStr);
if (delta)
{
r.removePasses = passType;
}
else
{
r.disablePasses = passType;
}
}
if (r.enablePasses != null && (r.disablePasses != null || r.addPasses != null || r.removePasses != null))
{
throw new Exception($"Invalid xml file {_curLoadingConfig}, enable and disable can't be used together");
}
if (r.disablePasses != null && (r.enablePasses != null || r.addPasses != null || r.removePasses != null))
{
throw new Exception($"Invalid xml file {_curLoadingConfig}, disable and enable can't be used together");
}
return r;
}
private AssemblySpec ParseAssembly(XmlElement ele)
{
var assemblySpec = new AssemblySpec();
string name = ele.GetAttribute("name");
if (!_toObfuscatedAssemblyNames.Contains(name))
{
throw new Exception($"Invalid xml file {_curLoadingConfig}, assembly name {name} isn't in toObfuscatedAssemblyNames");
}
assemblySpec.name = name;
assemblySpec.nameMatcher = new NameMatcher(name);
assemblySpec.rule = ParseRule(ele);
var types = assemblySpec.types;
foreach (XmlNode node in ele.ChildNodes)
{
if (!(node is XmlElement childEle))
{
continue;
}
switch (childEle.Name)
{
case "type":
{
types.Add(ParseType(childEle));
break;
}
default:
{
throw new Exception($"Invalid xml file, unknown node {childEle.Name}");
}
}
}
return assemblySpec;
}
private TypeSpec ParseType(XmlElement element)
{
var typeSpec = new TypeSpec();
string name = element.GetAttribute("name");
typeSpec.name = name;
typeSpec.nameMatcher = new NameMatcher(name);
typeSpec.rule = ParseRule(element);
List<FieldSpec> fields = typeSpec.fields;
List<MethodSpec> methods = typeSpec.methods;
List<PropertySpec> properties = typeSpec.properties;
List<EventSpec> events = typeSpec.events;
foreach (XmlNode node in element.ChildNodes)
{
if (!(node is XmlElement ele))
{
continue;
}
switch (ele.Name)
{
case "field":
{
fields.Add(ParseField(ele));
break;
}
case "method":
{
methods.Add(ParseMethod(ele));
break;
}
case "property":
{
properties.Add(ParseProperty(ele));
break;
}
case "event":
{
events.Add(ParseEvent(ele));
break;
}
default:
{
throw new Exception($"Invalid xml file, unknown node {ele.Name}");
}
}
}
return typeSpec;
}
private void ParseSpecObject(XmlElement element, SpecBase obj)
{
string name = element.GetAttribute("name");
obj.name = name;
obj.nameMatcher = new NameMatcher(name);
obj.rule = ParseRule(element);
}
private FieldSpec ParseField(XmlElement element)
{
var fieldSpec = new FieldSpec();
ParseSpecObject(element, fieldSpec);
return fieldSpec;
}
private MethodSpec ParseMethod(XmlElement element)
{
var methodSpec = new MethodSpec();
ParseSpecObject(element, methodSpec);
return methodSpec;
}
private PropertySpec ParseProperty(XmlElement element)
{
var propertySpec = new PropertySpec();
ParseSpecObject(element, propertySpec);
return propertySpec;
}
private EventSpec ParseEvent(XmlElement element)
{
var eventSpec = new EventSpec();
ParseSpecObject(element, eventSpec);
return eventSpec;
}
private readonly Dictionary<ModuleDef, (AssemblySpec, PassRule)> _modulePassRuleCaches = new Dictionary<ModuleDef, (AssemblySpec, PassRule)>();
private readonly Dictionary<TypeDef, (TypeSpec, PassRule)> _typePassRuleCaches = new Dictionary<TypeDef, (TypeSpec, PassRule)>();
private readonly Dictionary<MethodDef, (MethodSpec, PassRule)> _methodPassRuleCaches = new Dictionary<MethodDef, (MethodSpec, PassRule)>();
private readonly Dictionary<FieldDef, (FieldSpec, PassRule)> _fieldPassRuleCaches = new Dictionary<FieldDef, (FieldSpec, PassRule)>();
private readonly Dictionary<PropertyDef, (PropertySpec, PassRule)> _propertyPassRuleCaches = new Dictionary<PropertyDef, (PropertySpec, PassRule)>();
private readonly Dictionary<EventDef, (EventSpec, PassRule)> _eventPassRuleCaches = new Dictionary<EventDef, (EventSpec, PassRule)>();
private (AssemblySpec, PassRule) GetAssemblySpec(ModuleDef module)
{
if (!_modulePassRuleCaches.TryGetValue(module, out var result))
{
result = (null, _defaultPassRule);
string assName = module.Assembly.Name;
foreach (var ass in _assemblySpecs)
{
if (ass.nameMatcher.IsMatch(assName))
{
result = (ass, _defaultPassRule);
break;
}
}
_modulePassRuleCaches.Add(module, result);
}
return result;
}
private (TypeSpec, PassRule) GetTypeSpec(TypeDef type)
{
if (!_typePassRuleCaches.TryGetValue(type, out var result))
{
var assResult = GetAssemblySpec(type.Module);
result = (null, assResult.Item2);
if (assResult.Item1 != null)
{
string typeName = type.FullName;
foreach (var typeSpec in assResult.Item1.types)
{
if (typeSpec.nameMatcher.IsMatch(typeName))
{
result = (typeSpec, typeSpec.rule);
break;
}
}
}
_typePassRuleCaches.Add(type, result);
}
return result;
}
private (MethodSpec, PassRule) GetMethodSpec(MethodDef method)
{
if (!_methodPassRuleCaches.TryGetValue(method, out var result))
{
var typeResult = GetTypeSpec(method.DeclaringType);
result = (null, typeResult.Item2);
if (typeResult.Item1 != null)
{
string methodName = method.Name;
foreach (var methodSpec in typeResult.Item1.methods)
{
if (methodSpec.nameMatcher.IsMatch(methodName))
{
result = (methodSpec, methodSpec.rule);
break;
}
}
}
_methodPassRuleCaches.Add(method, result);
}
return result;
}
private (FieldSpec, PassRule) GetFieldSpec(FieldDef field)
{
if (!_fieldPassRuleCaches.TryGetValue(field, out var result))
{
var typeResult = GetTypeSpec(field.DeclaringType);
result = (null, typeResult.Item2);
if (typeResult.Item1 != null)
{
string fieldName = field.Name;
foreach (var fieldSpec in typeResult.Item1.fields)
{
if (fieldSpec.nameMatcher.IsMatch(fieldName))
{
result = (fieldSpec, fieldSpec.rule);
break;
}
}
}
_fieldPassRuleCaches.Add(field, result);
}
return result;
}
private (PropertySpec, PassRule) GetPropertySpec(PropertyDef property)
{
if (!_propertyPassRuleCaches.TryGetValue(property, out var result))
{
var typeResult = GetTypeSpec(property.DeclaringType);
result = (null, typeResult.Item2);
if (typeResult.Item1 != null)
{
string propertyName = property.Name;
foreach (var propertySpec in typeResult.Item1.properties)
{
if (propertySpec.nameMatcher.IsMatch(propertyName))
{
result = (propertySpec, propertySpec.rule);
break;
}
}
}
_propertyPassRuleCaches.Add(property, result);
}
return result;
}
private (EventSpec, PassRule) GetEventSpec(EventDef eventDef)
{
if (!_eventPassRuleCaches.TryGetValue(eventDef, out var result))
{
var typeResult = GetTypeSpec(eventDef.DeclaringType);
result = (null, typeResult.Item2);
if (typeResult.Item1 != null)
{
string eventName = eventDef.Name;
foreach (var eventSpec in typeResult.Item1.events)
{
if (eventSpec.nameMatcher.IsMatch(eventName))
{
result = (eventSpec, eventSpec.rule);
break;
}
}
}
_eventPassRuleCaches.Add(eventDef, result);
}
return result;
}
public ObfuscationPassType GetAssemblyObfuscationPasses(ModuleDef module)
{
return GetAssemblySpec(module).Item2.finalPasses;
}
public ObfuscationPassType GetTypeObfuscationPasses(TypeDef type)
{
return GetTypeSpec(type).Item2.finalPasses;
}
public ObfuscationPassType GetMethodObfuscationPasses(MethodDef method)
{
return GetMethodSpec(method).Item2.finalPasses;
}
public ObfuscationPassType GetFieldObfuscationPasses(FieldDef field)
{
return GetFieldSpec(field).Item2.finalPasses;
}
public ObfuscationPassType GetPropertyObfuscationPasses(PropertyDef property)
{
return GetPropertySpec(property).Item2.finalPasses;
}
public ObfuscationPassType GetEventObfuscationPasses(EventDef eventDef)
{
return GetEventSpec(eventDef).Item2.finalPasses;
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.Editor
{
public static class ConstValues
{
public const string ObfuzInternalSymbolNamePrefix = "$Obfuz$";
}
}

View File

@ -0,0 +1,344 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Editor;
using Obfuz.Emit;
using Obfuz.Utils;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Assertions;
namespace Obfuz.Data
{
public class ModuleConstFieldAllocator : IGroupByModuleEntity
{
private ModuleDef _module;
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private readonly RvaDataAllocator _rvaDataAllocator;
private readonly GroupByModuleEntityManager _moduleEntityManager;
private EncryptionScopeInfo _encryptionScope;
private RandomCreator _randomCreator;
private IEncryptor _encryptor;
private TypeDef _holderTypeDef;
class ConstFieldInfo
{
public FieldDef field;
public object value;
}
class AnyComparer : IEqualityComparer<object>
{
public new bool Equals(object x, object y)
{
if (x is byte[] xBytes && y is byte[] yBytes)
{
return StructuralComparisons.StructuralEqualityComparer.Equals(xBytes, yBytes);
}
return x.Equals(y);
}
public static int ComputeHashCode(object obj)
{
return HashUtil.ComputePrimitiveOrStringOrBytesHashCode(obj);
}
public int GetHashCode(object obj)
{
return ComputeHashCode(obj);
}
}
private readonly Dictionary<object, ConstFieldInfo> _allocatedFields = new Dictionary<object, ConstFieldInfo>(new AnyComparer());
private readonly Dictionary<FieldDef, ConstFieldInfo> _field2Fields = new Dictionary<FieldDef, ConstFieldInfo>();
private readonly List<TypeDef> _holderTypeDefs = new List<TypeDef>();
private bool _done;
public ModuleConstFieldAllocator(EncryptionScopeProvider encryptionScopeProvider, RvaDataAllocator rvaDataAllocator, GroupByModuleEntityManager moduleEntityManager)
{
_encryptionScopeProvider = encryptionScopeProvider;
_rvaDataAllocator = rvaDataAllocator;
_moduleEntityManager = moduleEntityManager;
}
public void Init(ModuleDef mod)
{
_module = mod;
_encryptionScope = _encryptionScopeProvider.GetScope(mod);
_randomCreator = _encryptionScope.localRandomCreator;
_encryptor = _encryptionScope.encryptor;
}
const int maxFieldCount = 1000;
private TypeSig GetTypeSigOfValue(object value)
{
if (value is int)
return _module.CorLibTypes.Int32;
if (value is long)
return _module.CorLibTypes.Int64;
if (value is float)
return _module.CorLibTypes.Single;
if (value is double)
return _module.CorLibTypes.Double;
if (value is string)
return _module.CorLibTypes.String;
if (value is byte[])
return new SZArraySig(_module.CorLibTypes.Byte);
throw new NotSupportedException($"Unsupported type: {value.GetType()}");
}
private ConstFieldInfo CreateConstFieldInfo(object value)
{
if (_holderTypeDef == null || _holderTypeDef.Fields.Count >= maxFieldCount)
{
_module.EnableTypeDefFindCache = false;
ITypeDefOrRef objectTypeRef = _module.Import(typeof(object));
_holderTypeDef = new TypeDefUser($"{ConstValues.ObfuzInternalSymbolNamePrefix}ConstFieldHolder${_holderTypeDefs.Count}", objectTypeRef);
_module.Types.Add(_holderTypeDef);
_holderTypeDefs.Add(_holderTypeDef);
_module.EnableTypeDefFindCache = true;
}
var field = new FieldDefUser($"{ConstValues.ObfuzInternalSymbolNamePrefix}RVA_Value{_holderTypeDef.Fields.Count}", new FieldSig(GetTypeSigOfValue(value)), FieldAttributes.Static | FieldAttributes.Private | FieldAttributes.InitOnly);
field.DeclaringType = _holderTypeDef;
return new ConstFieldInfo
{
field = field,
value = value,
};
}
private FieldDef AllocateAny(object value)
{
if (_done)
{
throw new Exception("can't Allocate after done");
}
if (!_allocatedFields.TryGetValue(value, out var field))
{
field = CreateConstFieldInfo(value);
_allocatedFields.Add(value, field);
_field2Fields.Add(field.field, field);
}
return field.field;
}
public FieldDef Allocate(int value)
{
return AllocateAny(value);
}
public FieldDef Allocate(long value)
{
return AllocateAny(value);
}
public FieldDef Allocate(float value)
{
return AllocateAny(value);
}
public FieldDef Allocate(double value)
{
return AllocateAny(value);
}
public FieldDef Allocate(string value)
{
return AllocateAny(value);
}
public FieldDef Allocate(byte[] value)
{
return AllocateAny(value);
}
private DefaultMetadataImporter GetModuleMetadataImporter()
{
return _moduleEntityManager.GetDefaultModuleMetadataImporter(_module, _encryptionScopeProvider);
}
private void CreateCCtorOfRvaTypeDef(TypeDef type)
{
var cctor = new MethodDefUser(".cctor",
MethodSig.CreateStatic(_module.CorLibTypes.Void),
MethodImplAttributes.IL | MethodImplAttributes.Managed,
MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Private);
cctor.DeclaringType = type;
//_rvaTypeDef.Methods.Add(cctor);
var body = new CilBody();
cctor.Body = body;
var ins = body.Instructions;
//IMethod method = _module.Import(typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("InitializeArray", new[] { typeof(Array), typeof(RuntimeFieldHandle) }));
//Assert.IsNotNull(method);
DefaultMetadataImporter importer = GetModuleMetadataImporter();
// TODO. obfuscate init codes
foreach (var field in type.Fields)
{
ConstFieldInfo constInfo = _field2Fields[field];
IRandom localRandom = _randomCreator(HashUtil.ComputePrimitiveOrStringOrBytesHashCode(constInfo.value));
int ops = EncryptionUtil.GenerateEncryptionOpCodes(localRandom, _encryptor, 4);
int salt = localRandom.NextInt();
switch (constInfo.value)
{
case int i:
{
int encryptedValue = _encryptor.Encrypt(i, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
ins.Add(Instruction.CreateLdcI4(ops));
ins.Add(Instruction.CreateLdcI4(salt));
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaInt));
break;
}
case long l:
{
long encryptedValue = _encryptor.Encrypt(l, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
ins.Add(Instruction.CreateLdcI4(ops));
ins.Add(Instruction.CreateLdcI4(salt));
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaLong));
break;
}
case float f:
{
float encryptedValue = _encryptor.Encrypt(f, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
ins.Add(Instruction.CreateLdcI4(ops));
ins.Add(Instruction.CreateLdcI4(salt));
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaFloat));
break;
}
case double d:
{
double encryptedValue = _encryptor.Encrypt(d, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
ins.Add(Instruction.CreateLdcI4(ops));
ins.Add(Instruction.CreateLdcI4(salt));
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaDouble));
break;
}
case string s:
{
byte[] encryptedValue = _encryptor.Encrypt(s, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
Assert.AreEqual(encryptedValue.Length, rvaData.size);
ins.Add(Instruction.CreateLdcI4(encryptedValue.Length));
ins.Add(Instruction.CreateLdcI4(ops));
ins.Add(Instruction.CreateLdcI4(salt));
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaString));
break;
}
case byte[] bs:
{
byte[] encryptedValue = _encryptor.Encrypt(bs, 0, bs.Length, ops, salt);
Assert.AreEqual(encryptedValue.Length, bs.Length);
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
ins.Add(Instruction.CreateLdcI4(bs.Length));
ins.Add(Instruction.CreateLdcI4(ops));
ins.Add(Instruction.CreateLdcI4(salt));
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaBytes));
break;
}
default: throw new NotSupportedException($"Unsupported type: {constInfo.value.GetType()}");
}
ins.Add(Instruction.Create(OpCodes.Stsfld, field));
}
ins.Add(Instruction.Create(OpCodes.Ret));
}
public void Done()
{
if (_done)
{
throw new Exception("Already done");
}
_done = true;
foreach (var typeDef in _holderTypeDefs)
{
CreateCCtorOfRvaTypeDef(typeDef);
}
}
}
public class ConstFieldAllocator
{
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private readonly RvaDataAllocator _rvaDataAllocator;
private readonly GroupByModuleEntityManager _moduleEntityManager;
public ConstFieldAllocator(EncryptionScopeProvider encryptionScopeProvider, RvaDataAllocator rvaDataAllocator, GroupByModuleEntityManager moduleEntityManager)
{
_encryptionScopeProvider = encryptionScopeProvider;
_rvaDataAllocator = rvaDataAllocator;
_moduleEntityManager = moduleEntityManager;
}
private ModuleConstFieldAllocator GetModuleAllocator(ModuleDef mod)
{
return _moduleEntityManager.GetEntity<ModuleConstFieldAllocator>(mod, () => new ModuleConstFieldAllocator(_encryptionScopeProvider, _rvaDataAllocator, _moduleEntityManager));
}
public FieldDef Allocate(ModuleDef mod, int value)
{
return GetModuleAllocator(mod).Allocate(value);
}
public FieldDef Allocate(ModuleDef mod, long value)
{
return GetModuleAllocator(mod).Allocate(value);
}
public FieldDef Allocate(ModuleDef mod, float value)
{
return GetModuleAllocator(mod).Allocate(value);
}
public FieldDef Allocate(ModuleDef mod, double value)
{
return GetModuleAllocator(mod).Allocate(value);
}
public FieldDef Allocate(ModuleDef mod, byte[] value)
{
return GetModuleAllocator(mod).Allocate(value);
}
public FieldDef Allocate(ModuleDef mod, string value)
{
return GetModuleAllocator(mod).Allocate(value);
}
public void Done()
{
foreach (var moduleAllocator in _moduleEntityManager.GetEntities<ModuleConstFieldAllocator>())
{
moduleAllocator.Done();
}
}
}
}

View File

@ -0,0 +1,354 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Emit;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.Assertions;
namespace Obfuz.Data
{
public struct RvaData
{
public readonly FieldDef field;
public readonly int offset;
public readonly int size;
public RvaData(FieldDef field, int offset, int size)
{
this.field = field;
this.offset = offset;
this.size = size;
}
}
public class ModuleRvaDataAllocator : GroupByModuleEntityBase
{
// randomized
const int maxRvaDataSize = 0x1000;
private ModuleDef _module;
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private readonly GroupByModuleEntityManager _moduleEntityManager;
private EncryptionScopeInfo _encryptionScope;
private IRandom _random;
class RvaField
{
public FieldDef holderDataField;
public FieldDef runtimeValueField;
public int encryptionOps;
public uint size;
public List<byte> bytes;
public int salt;
public void FillPaddingToSize(int newSize)
{
for (int i = bytes.Count; i < newSize; i++)
{
bytes.Add(0xAB);
}
}
public void FillPaddingToEnd()
{
// fill with random value
for (int i = bytes.Count; i < size; i++)
{
bytes.Add(0xAB);
}
}
}
private readonly List<RvaField> _rvaFields = new List<RvaField>();
private RvaField _currentField;
private TypeDef _rvaTypeDef;
private readonly Dictionary<int, TypeDef> _dataHolderTypeBySizes = new Dictionary<int, TypeDef>();
private bool _done;
public ModuleRvaDataAllocator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager)
{
_encryptionScopeProvider = encryptionScopeProvider;
_moduleEntityManager = moduleEntityManager;
}
public override void Init(ModuleDef mod)
{
_module = mod;
_encryptionScope = _encryptionScopeProvider.GetScope(mod);
_random = _encryptionScope.localRandomCreator(HashUtil.ComputeHash(mod.Name));
}
private (FieldDef, FieldDef) CreateDataHolderRvaField(TypeDef dataHolderType)
{
if (_rvaTypeDef == null)
{
_module.EnableTypeDefFindCache = false;
//_rvaTypeDef = _module.Find("$ObfuzRVA$", true);
//if (_rvaTypeDef != null)
//{
// throw new Exception($"can't obfuscate a obfuscated assembly");
//}
ITypeDefOrRef objectTypeRef = _module.Import(typeof(object));
_rvaTypeDef = new TypeDefUser("$Obfuz$RVA$", objectTypeRef);
_module.Types.Add(_rvaTypeDef);
_module.EnableTypeDefFindCache = true;
}
var holderField = new FieldDefUser($"$RVA_Data{_rvaFields.Count}", new FieldSig(dataHolderType.ToTypeSig()), FieldAttributes.InitOnly | FieldAttributes.Static | FieldAttributes.HasFieldRVA);
holderField.DeclaringType = _rvaTypeDef;
var runtimeValueField = new FieldDefUser($"$RVA_Value{_rvaFields.Count}", new FieldSig(new SZArraySig(_module.CorLibTypes.Byte)), FieldAttributes.Static);
runtimeValueField.DeclaringType = _rvaTypeDef;
return (holderField, runtimeValueField);
}
private TypeDef GetDataHolderType(int size)
{
size = (size + 15) & ~15; // align to 6 bytes
if (_dataHolderTypeBySizes.TryGetValue(size, out var type))
return type;
var dataHolderType = new TypeDefUser($"$ObfuzRVA$DataHolder{size}", _module.Import(typeof(ValueType)));
dataHolderType.Layout = TypeAttributes.ExplicitLayout;
dataHolderType.PackingSize = 1;
dataHolderType.ClassSize = (uint)size;
_dataHolderTypeBySizes.Add(size, dataHolderType);
_module.Types.Add(dataHolderType);
return dataHolderType;
}
private static int AlignTo(int size, int alignment)
{
return (size + alignment - 1) & ~(alignment - 1);
}
private RvaField CreateRvaField(int size)
{
TypeDef dataHolderType = GetDataHolderType(size);
var (holderDataField, runtimeValueField) = CreateDataHolderRvaField(dataHolderType);
var newRvaField = new RvaField
{
holderDataField = holderDataField,
runtimeValueField = runtimeValueField,
size = dataHolderType.ClassSize,
bytes = new List<byte>((int)dataHolderType.ClassSize),
encryptionOps = _random.NextInt(),
salt = _random.NextInt(),
};
_rvaFields.Add(newRvaField);
return newRvaField;
}
private RvaField GetRvaField(int preservedSize, int alignment)
{
if (_done)
{
throw new Exception("can't GetRvaField after done");
}
Assert.IsTrue(preservedSize % alignment == 0);
// for big size, create a new field
if (preservedSize >= maxRvaDataSize)
{
return CreateRvaField(preservedSize);
}
if (_currentField != null)
{
int offset = AlignTo(_currentField.bytes.Count, alignment);
int expectedSize = offset + preservedSize;
if (expectedSize <= _currentField.size)
{
_currentField.FillPaddingToSize(offset);
return _currentField;
}
_currentField.FillPaddingToEnd();
}
_currentField = CreateRvaField(maxRvaDataSize);
return _currentField;
}
public RvaData Allocate(int value)
{
RvaField field = GetRvaField(4, 4);
int offset = field.bytes.Count;
Assert.IsTrue(offset % 4 == 0);
field.bytes.AddRange(BitConverter.GetBytes(value));
return new RvaData(field.runtimeValueField, offset, 4);
}
public RvaData Allocate(long value)
{
RvaField field = GetRvaField(8, 8);
int offset = field.bytes.Count;
Assert.IsTrue(offset % 8 == 0);
field.bytes.AddRange(BitConverter.GetBytes(value));
return new RvaData(field.runtimeValueField, offset, 8);
}
public RvaData Allocate(float value)
{
RvaField field = GetRvaField(4, 4);
int offset = field.bytes.Count;
Assert.IsTrue(offset % 4 == 0);
field.bytes.AddRange(BitConverter.GetBytes(value));
return new RvaData(field.runtimeValueField, offset, 4);
}
public RvaData Allocate(double value)
{
RvaField field = GetRvaField(8, 8);
int offset = field.bytes.Count;
Assert.IsTrue(offset % 8 == 0);
field.bytes.AddRange(BitConverter.GetBytes(value));
return new RvaData(field.runtimeValueField, offset, 8);
}
public RvaData Allocate(string value)
{
byte[] bytes = Encoding.UTF8.GetBytes(value);
return Allocate(bytes);
}
public RvaData Allocate(byte[] value)
{
RvaField field = GetRvaField(value.Length, 1);
int offset = field.bytes.Count;
field.bytes.AddRange(value);
return new RvaData(field.runtimeValueField, offset, value.Length);
}
private void CreateCCtorOfRvaTypeDef()
{
if (_rvaTypeDef == null)
{
return;
}
ModuleDef mod = _rvaTypeDef.Module;
var cctorMethod = new MethodDefUser(".cctor",
MethodSig.CreateStatic(_module.CorLibTypes.Void),
MethodImplAttributes.IL | MethodImplAttributes.Managed,
MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Private);
cctorMethod.DeclaringType = _rvaTypeDef;
//_rvaTypeDef.Methods.Add(cctor);
var body = new CilBody();
cctorMethod.Body = body;
var ins = body.Instructions;
DefaultMetadataImporter importer = _moduleEntityManager.GetDefaultModuleMetadataImporter(mod, _encryptionScopeProvider);
foreach (var field in _rvaFields)
{
// ldc
// newarr
// dup
// stsfld
// ldtoken
// RuntimeHelpers.InitializeArray(array, fieldHandle);
ins.Add(Instruction.Create(OpCodes.Ldc_I4, (int)field.size));
ins.Add(Instruction.Create(OpCodes.Newarr, field.runtimeValueField.FieldType.Next.ToTypeDefOrRef()));
ins.Add(Instruction.Create(OpCodes.Dup));
ins.Add(Instruction.Create(OpCodes.Dup));
ins.Add(Instruction.Create(OpCodes.Stsfld, field.runtimeValueField));
ins.Add(Instruction.Create(OpCodes.Ldtoken, field.holderDataField));
ins.Add(Instruction.Create(OpCodes.Call, importer.InitializedArrayMethod));
// EncryptionService.DecryptBlock(array, field.encryptionOps, field.salt);
ins.Add(Instruction.CreateLdcI4(field.encryptionOps));
ins.Add(Instruction.Create(OpCodes.Ldc_I4, field.salt));
ins.Add(Instruction.Create(OpCodes.Call, importer.DecryptBlock));
}
ins.Add(Instruction.Create(OpCodes.Ret));
}
private void SetFieldsRVA()
{
foreach (var field in _rvaFields)
{
Assert.IsTrue(field.bytes.Count <= field.size);
if (field.bytes.Count < field.size)
{
field.FillPaddingToEnd();
}
byte[] data = field.bytes.ToArray();
_encryptionScope.encryptor.EncryptBlock(data, field.encryptionOps, field.salt);
field.holderDataField.InitialValue = data;
}
}
public void Done()
{
if (_done)
{
throw new Exception("can't call Done twice");
}
_done = true;
SetFieldsRVA();
CreateCCtorOfRvaTypeDef();
}
}
public class RvaDataAllocator
{
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private readonly GroupByModuleEntityManager _moduleEntityManager;
public RvaDataAllocator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager)
{
_encryptionScopeProvider = encryptionScopeProvider;
_moduleEntityManager = moduleEntityManager;
}
private ModuleRvaDataAllocator GetModuleRvaDataAllocator(ModuleDef mod)
{
return _moduleEntityManager.GetEntity<ModuleRvaDataAllocator>(mod, () => new ModuleRvaDataAllocator(_encryptionScopeProvider, _moduleEntityManager));
}
public RvaData Allocate(ModuleDef mod, int value)
{
return GetModuleRvaDataAllocator(mod).Allocate(value);
}
public RvaData Allocate(ModuleDef mod, long value)
{
return GetModuleRvaDataAllocator(mod).Allocate(value);
}
public RvaData Allocate(ModuleDef mod, float value)
{
return GetModuleRvaDataAllocator(mod).Allocate(value);
}
public RvaData Allocate(ModuleDef mod, double value)
{
return GetModuleRvaDataAllocator(mod).Allocate(value);
}
public RvaData Allocate(ModuleDef mod, string value)
{
return GetModuleRvaDataAllocator(mod).Allocate(value);
}
public RvaData Allocate(ModuleDef mod, byte[] value)
{
return GetModuleRvaDataAllocator(mod).Allocate(value);
}
public void Done()
{
foreach (var allocator in _moduleEntityManager.GetEntities<ModuleRvaDataAllocator>())
{
allocator.Done();
}
}
}
}

View File

@ -0,0 +1,295 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.Emit
{
public class BasicBlock
{
public readonly List<Instruction> instructions = new List<Instruction>();
public readonly List<BasicBlock> inBlocks = new List<BasicBlock>();
public readonly List<BasicBlock> outBlocks = new List<BasicBlock>();
public bool inLoop;
public void AddTargetBasicBlock(BasicBlock target)
{
if (!outBlocks.Contains(target))
{
outBlocks.Add(target);
}
if (!target.inBlocks.Contains(this))
{
target.inBlocks.Add(this);
}
}
}
public class BasicBlockCollection
{
private readonly MethodDef _method;
private readonly List<BasicBlock> _blocks = new List<BasicBlock>();
private readonly Dictionary<Instruction, BasicBlock> _inst2BlockMap = new Dictionary<Instruction, BasicBlock>();
public IList<BasicBlock> Blocks => _blocks;
public BasicBlockCollection(MethodDef method)
{
_method = method;
HashSet<Instruction> splitPoints = BuildSplitPoint(method);
BuildBasicBlocks(method, splitPoints);
BuildInOutGraph(method);
var loopBlocks = FindLoopBlocks(_blocks);
foreach (var block in loopBlocks)
{
block.inLoop = true;
}
}
public BasicBlock GetBasicBlockByInstruction(Instruction inst)
{
return _inst2BlockMap[inst];
}
private HashSet<Instruction> BuildSplitPoint(MethodDef method)
{
var insts = method.Body.Instructions;
var splitPoints = new HashSet<Instruction>();
foreach (ExceptionHandler eh in method.Body.ExceptionHandlers)
{
if (eh.TryStart != null)
{
splitPoints.Add(eh.TryStart);
}
if (eh.TryEnd != null)
{
splitPoints.Add(eh.TryEnd);
}
if (eh.HandlerStart != null)
{
splitPoints.Add(eh.HandlerStart);
}
if (eh.HandlerEnd != null)
{
splitPoints.Add(eh.HandlerEnd);
}
if (eh.FilterStart != null)
{
splitPoints.Add(eh.FilterStart);
}
}
for (int i = 0, n = insts.Count; i < n; i++)
{
Instruction curInst = insts[i];
Instruction nextInst = i + 1 < n ? insts[i + 1] : null;
switch (curInst.OpCode.FlowControl)
{
case FlowControl.Branch:
{
if (nextInst != null)
{
splitPoints.Add(nextInst);
}
break;
}
case FlowControl.Cond_Branch:
{
if (nextInst != null)
{
splitPoints.Add(nextInst);
}
if (curInst.Operand is Instruction targetInst)
{
splitPoints.Add(targetInst);
}
else if (curInst.Operand is Instruction[] targetInsts)
{
foreach (var target in targetInsts)
{
splitPoints.Add(target);
}
}
break;
}
case FlowControl.Return:
{
if (nextInst != null)
{
splitPoints.Add(nextInst);
}
break;
}
case FlowControl.Throw:
{
if (nextInst != null)
{
splitPoints.Add(nextInst);
}
break;
}
}
}
return splitPoints;
}
private void BuildBasicBlocks(MethodDef method, HashSet<Instruction> splitPoints)
{
var insts = method.Body.Instructions;
BasicBlock curBlock = new BasicBlock();
foreach (Instruction inst in insts)
{
if (splitPoints.Contains(inst) && curBlock.instructions.Count > 0)
{
_blocks.Add(curBlock);
curBlock = new BasicBlock();
}
curBlock.instructions.Add(inst);
_inst2BlockMap.Add(inst, curBlock);
}
if (curBlock.instructions.Count > 0)
{
_blocks.Add(curBlock);
}
}
private void BuildInOutGraph(MethodDef method)
{
var insts = method.Body.Instructions;
for (int i = 0, n = _blocks.Count; i < n; i++)
{
BasicBlock curBlock = _blocks[i];
BasicBlock nextBlock = i + 1 < n ? _blocks[i + 1] : null;
Instruction lastInst = curBlock.instructions.Last();
switch (lastInst.OpCode.FlowControl)
{
case FlowControl.Branch:
{
Instruction targetInst = (Instruction)lastInst.Operand;
BasicBlock targetBlock = GetBasicBlockByInstruction(targetInst);
curBlock.AddTargetBasicBlock(targetBlock);
break;
}
case FlowControl.Cond_Branch:
{
if (lastInst.Operand is Instruction targetInst)
{
BasicBlock targetBlock = GetBasicBlockByInstruction(targetInst);
curBlock.AddTargetBasicBlock(targetBlock);
}
else if (lastInst.Operand is Instruction[] targetInsts)
{
foreach (var target in targetInsts)
{
BasicBlock targetBlock = GetBasicBlockByInstruction(target);
curBlock.AddTargetBasicBlock(targetBlock);
}
}
else
{
throw new Exception("Invalid operand type for conditional branch");
}
if (nextBlock != null)
{
curBlock.AddTargetBasicBlock(nextBlock);
}
break;
}
case FlowControl.Return:
case FlowControl.Throw:
{
break;
}
}
}
}
private static HashSet<BasicBlock> FindLoopBlocks(List<BasicBlock> allBlocks)
{
// Tarjan算法找强连通分量
var sccList = FindStronglyConnectedComponents(allBlocks);
// 筛选有效循环
var loopBlocks = new HashSet<BasicBlock>();
foreach (var scc in sccList)
{
// 有效循环需满足以下条件之一:
// 1. 分量包含多个块
// 2. 单个块有自环(跳转自己)
if (scc.Count > 1 ||
(scc.Count == 1 && scc[0].outBlocks.Contains(scc[0])))
{
foreach (var block in scc)
{
loopBlocks.Add(block);
}
}
}
return loopBlocks;
}
private static List<List<BasicBlock>> FindStronglyConnectedComponents(List<BasicBlock> allBlocks)
{
int index = 0;
var stack = new Stack<BasicBlock>();
var indexes = new Dictionary<BasicBlock, int>();
var lowLinks = new Dictionary<BasicBlock, int>();
var onStack = new HashSet<BasicBlock>();
var sccList = new List<List<BasicBlock>>();
foreach (var block in allBlocks.Where(b => !indexes.ContainsKey(b)))
{
StrongConnect(block);
}
return sccList;
void StrongConnect(BasicBlock v)
{
indexes[v] = index;
lowLinks[v] = index;
index++;
stack.Push(v);
onStack.Add(v);
foreach (var w in v.outBlocks)
{
if (!indexes.ContainsKey(w))
{
StrongConnect(w);
lowLinks[v] = System.Math.Min(lowLinks[v], lowLinks[w]);
}
else if (onStack.Contains(w))
{
lowLinks[v] = System.Math.Min(lowLinks[v], indexes[w]);
}
}
if (lowLinks[v] == indexes[v])
{
var scc = new List<BasicBlock>();
BasicBlock w;
do
{
w = stack.Pop();
onStack.Remove(w);
scc.Add(w);
} while (!w.Equals(v));
sccList.Add(scc);
}
}
}
}
}

View File

@ -0,0 +1,193 @@
using dnlib.DotNet;
using System;
using UnityEngine.Assertions;
namespace Obfuz.Emit
{
public class EncryptionServiceMetadataImporter
{
private readonly ModuleDef _module;
private readonly Type _encryptionServiceType;
private IMethod _encryptBlock;
private IMethod _decryptBlock;
private IMethod _encryptInt;
private IMethod _decryptInt;
private IMethod _encryptLong;
private IMethod _decryptLong;
private IMethod _encryptFloat;
private IMethod _decryptFloat;
private IMethod _encryptDouble;
private IMethod _decryptDouble;
private IMethod _encryptString;
private IMethod _decryptString;
private IMethod _encryptBytes;
private IMethod _decryptBytes;
private IMethod _decryptFromRvaInt;
private IMethod _decryptFromRvaLong;
private IMethod _decryptFromRvaFloat;
private IMethod _decryptFromRvaDouble;
private IMethod _decryptFromRvaString;
private IMethod _decryptFromRvaBytes;
public IMethod EncryptBlock => _encryptBlock;
public IMethod DecryptBlock => _decryptBlock;
public IMethod EncryptInt => _encryptInt;
public IMethod DecryptInt => _decryptInt;
public IMethod EncryptLong => _encryptLong;
public IMethod DecryptLong => _decryptLong;
public IMethod EncryptFloat => _encryptFloat;
public IMethod DecryptFloat => _decryptFloat;
public IMethod EncryptDouble => _encryptDouble;
public IMethod DecryptDouble => _decryptDouble;
public IMethod EncryptString => _encryptString;
public IMethod DecryptString => _decryptString;
public IMethod EncryptBytes => _encryptBytes;
public IMethod DecryptBytes => _decryptBytes;
public IMethod DecryptFromRvaInt => _decryptFromRvaInt;
public IMethod DecryptFromRvaLong => _decryptFromRvaLong;
public IMethod DecryptFromRvaFloat => _decryptFromRvaFloat;
public IMethod DecryptFromRvaDouble => _decryptFromRvaDouble;
public IMethod DecryptFromRvaBytes => _decryptFromRvaBytes;
public IMethod DecryptFromRvaString => _decryptFromRvaString;
public EncryptionServiceMetadataImporter(ModuleDef mod, Type encryptionServiceType)
{
_module = mod;
_encryptionServiceType = encryptionServiceType;
_encryptBlock = mod.Import(encryptionServiceType.GetMethod("EncryptBlock", new[] { typeof(byte[]), typeof(int), typeof(int) }));
Assert.IsNotNull(_encryptBlock);
_decryptBlock = mod.Import(encryptionServiceType.GetMethod("DecryptBlock", new[] { typeof(byte[]), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptBlock);
_encryptInt = mod.Import(encryptionServiceType.GetMethod("Encrypt", new[] { typeof(int), typeof(int), typeof(int) }));
Assert.IsNotNull(_encryptInt);
_decryptInt = mod.Import(encryptionServiceType.GetMethod("Decrypt", new[] { typeof(int), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptInt);
_encryptLong = mod.Import(encryptionServiceType.GetMethod("Encrypt", new[] { typeof(long), typeof(int), typeof(int) }));
Assert.IsNotNull(_encryptLong);
_decryptLong = mod.Import(encryptionServiceType.GetMethod("Decrypt", new[] { typeof(long), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptLong);
_encryptFloat = mod.Import(encryptionServiceType.GetMethod("Encrypt", new[] { typeof(float), typeof(int), typeof(int) }));
Assert.IsNotNull(_encryptFloat);
_decryptFloat = mod.Import(encryptionServiceType.GetMethod("Decrypt", new[] { typeof(float), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptFloat);
_encryptDouble = mod.Import(encryptionServiceType.GetMethod("Encrypt", new[] { typeof(double), typeof(int), typeof(int) }));
Assert.IsNotNull(_encryptDouble);
_decryptDouble = mod.Import(encryptionServiceType.GetMethod("Decrypt", new[] { typeof(double), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptDouble);
_encryptString = mod.Import(encryptionServiceType.GetMethod("Encrypt", new[] { typeof(string), typeof(int), typeof(int) }));
Assert.IsNotNull(_encryptString);
_decryptString = mod.Import(encryptionServiceType.GetMethod("DecryptString", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptString);
_encryptBytes = mod.Import(encryptionServiceType.GetMethod("Encrypt", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int), typeof(int) }));
Assert.IsNotNull(_encryptBytes);
_decryptBytes = mod.Import(encryptionServiceType.GetMethod("Decrypt", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptBytes);
_decryptFromRvaInt = mod.Import(encryptionServiceType.GetMethod("DecryptFromRvaInt", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptFromRvaInt);
_decryptFromRvaLong = mod.Import(encryptionServiceType.GetMethod("DecryptFromRvaLong", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptFromRvaLong);
_decryptFromRvaFloat = mod.Import(encryptionServiceType.GetMethod("DecryptFromRvaFloat", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptFromRvaFloat);
_decryptFromRvaDouble = mod.Import(encryptionServiceType.GetMethod("DecryptFromRvaDouble", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptFromRvaDouble);
_decryptFromRvaBytes = mod.Import(encryptionServiceType.GetMethod("DecryptFromRvaBytes", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptFromRvaBytes);
_decryptFromRvaString = mod.Import(encryptionServiceType.GetMethod("DecryptFromRvaString", new[] { typeof(byte[]), typeof(int), typeof(int), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptFromRvaString);
}
}
public class DefaultMetadataImporter : GroupByModuleEntityBase
{
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private EncryptionScopeInfo _encryptionScope;
private EncryptionServiceMetadataImporter _defaultEncryptionServiceMetadataImporter;
private EncryptionServiceMetadataImporter _staticDefaultEncryptionServiceMetadataImporter;
private EncryptionServiceMetadataImporter _dynamicDefaultEncryptionServiceMetadataImporter;
public DefaultMetadataImporter(EncryptionScopeProvider encryptionScopeProvider)
{
_encryptionScopeProvider = encryptionScopeProvider;
}
public override void Init(ModuleDef mod)
{
_module = mod;
_encryptionScope = _encryptionScopeProvider.GetScope(mod);
var constUtilityType = typeof(ConstUtility);
_castIntAsFloat = mod.Import(constUtilityType.GetMethod("CastIntAsFloat"));
Assert.IsNotNull(_castIntAsFloat, "CastIntAsFloat not found");
_castLongAsDouble = mod.Import(constUtilityType.GetMethod("CastLongAsDouble"));
Assert.IsNotNull(_castLongAsDouble, "CastLongAsDouble not found");
_castFloatAsInt = mod.Import(constUtilityType.GetMethod("CastFloatAsInt"));
Assert.IsNotNull(_castFloatAsInt, "CastFloatAsInt not found");
_castDoubleAsLong = mod.Import(constUtilityType.GetMethod("CastDoubleAsLong"));
Assert.IsNotNull(_castDoubleAsLong, "CastDoubleAsLong not found");
_initializeArray = mod.Import(typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("InitializeArray", new[] { typeof(Array), typeof(RuntimeFieldHandle) }));
Assert.IsNotNull(_initializeArray);
_staticDefaultEncryptionServiceMetadataImporter = new EncryptionServiceMetadataImporter(mod, typeof(EncryptionService<DefaultStaticEncryptionScope>));
_dynamicDefaultEncryptionServiceMetadataImporter = new EncryptionServiceMetadataImporter(mod, typeof(EncryptionService<DefaultDynamicEncryptionScope>));
if (_encryptionScopeProvider.IsDynamicSecretAssembly(mod))
{
_defaultEncryptionServiceMetadataImporter = _dynamicDefaultEncryptionServiceMetadataImporter;
}
else
{
_defaultEncryptionServiceMetadataImporter = _staticDefaultEncryptionServiceMetadataImporter;
}
}
public EncryptionServiceMetadataImporter GetEncryptionServiceMetadataImporterOfModule(ModuleDef mod)
{
return _encryptionScopeProvider.IsDynamicSecretAssembly(mod) ? _dynamicDefaultEncryptionServiceMetadataImporter : _staticDefaultEncryptionServiceMetadataImporter;
}
private ModuleDef _module;
private IMethod _castIntAsFloat;
private IMethod _castLongAsDouble;
private IMethod _castFloatAsInt;
private IMethod _castDoubleAsLong;
private IMethod _initializeArray;
public IMethod CastIntAsFloat => _castIntAsFloat;
public IMethod CastLongAsDouble => _castLongAsDouble;
public IMethod CastFloatAsInt => _castFloatAsInt;
public IMethod CastDoubleAsLong => _castDoubleAsLong;
public IMethod InitializedArrayMethod => _initializeArray;
public IMethod EncryptBlock => _defaultEncryptionServiceMetadataImporter.EncryptBlock;
public IMethod DecryptBlock => _defaultEncryptionServiceMetadataImporter.DecryptBlock;
public IMethod EncryptInt => _defaultEncryptionServiceMetadataImporter.EncryptInt;
public IMethod DecryptInt => _defaultEncryptionServiceMetadataImporter.DecryptInt;
public IMethod EncryptLong => _defaultEncryptionServiceMetadataImporter.EncryptLong;
public IMethod DecryptLong => _defaultEncryptionServiceMetadataImporter.DecryptLong;
public IMethod EncryptFloat => _defaultEncryptionServiceMetadataImporter.EncryptFloat;
public IMethod DecryptFloat => _defaultEncryptionServiceMetadataImporter.DecryptFloat;
public IMethod EncryptDouble => _defaultEncryptionServiceMetadataImporter.EncryptDouble;
public IMethod DecryptDouble => _defaultEncryptionServiceMetadataImporter.DecryptDouble;
public IMethod EncryptString => _defaultEncryptionServiceMetadataImporter.EncryptString;
public IMethod DecryptString => _defaultEncryptionServiceMetadataImporter.DecryptString;
public IMethod EncryptBytes => _defaultEncryptionServiceMetadataImporter.EncryptBytes;
public IMethod DecryptBytes => _defaultEncryptionServiceMetadataImporter.DecryptBytes;
public IMethod DecryptFromRvaInt => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaInt;
public IMethod DecryptFromRvaLong => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaLong;
public IMethod DecryptFromRvaFloat => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaFloat;
public IMethod DecryptFromRvaDouble => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaDouble;
public IMethod DecryptFromRvaBytes => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaBytes;
public IMethod DecryptFromRvaString => _defaultEncryptionServiceMetadataImporter.DecryptFromRvaString;
}
}

View File

@ -0,0 +1,66 @@
using dnlib.DotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.Emit
{
public interface IGroupByModuleEntity
{
void Init(ModuleDef mod);
}
public abstract class GroupByModuleEntityBase : IGroupByModuleEntity
{
public abstract void Init(ModuleDef mod);
}
public class GroupByModuleEntityManager
{
private readonly Dictionary<(ModuleDef, Type), IGroupByModuleEntity> _moduleEntityManagers = new Dictionary<(ModuleDef, Type), IGroupByModuleEntity>();
public T GetEntity<T>(ModuleDef mod, Func<T> creator = null) where T : IGroupByModuleEntity
{
var key = (mod, typeof(T));
if (_moduleEntityManagers.TryGetValue(key, out var emitManager))
{
return (T)emitManager;
}
else
{
T newEmitManager;
if (creator != null)
{
newEmitManager = creator();
}
else
{
newEmitManager = (T)Activator.CreateInstance(typeof(T));
}
newEmitManager.Init(mod);
_moduleEntityManagers[key] = newEmitManager;
return newEmitManager;
}
}
public List<T> GetEntities<T>() where T: IGroupByModuleEntity
{
var managers = new List<T>();
foreach (var kv in _moduleEntityManagers)
{
if (kv.Key.Item2 == typeof(T))
{
managers.Add((T)kv.Value);
}
}
return managers;
}
public DefaultMetadataImporter GetDefaultModuleMetadataImporter(ModuleDef module, EncryptionScopeProvider encryptionScopeProvider)
{
return GetEntity<DefaultMetadataImporter>(module, () => new DefaultMetadataImporter(encryptionScopeProvider));
}
}
}

View File

@ -0,0 +1,25 @@
namespace Obfuz.EncryptionVM
{
public class EncryptionInstructionWithOpCode
{
public readonly ushort code;
public readonly IEncryptionInstruction function;
public EncryptionInstructionWithOpCode(ushort code, IEncryptionInstruction function)
{
this.code = code;
this.function = function;
}
public int Encrypt(int value, int[] secretKey, int salt)
{
return function.Encrypt(value, secretKey, salt);
}
public int Decrypt(int value, int[] secretKey, int salt)
{
return function.Decrypt(value, secretKey, salt);
}
}
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace Obfuz.EncryptionVM
{
public interface IEncryptionInstruction
{
int Encrypt(int value, int[] secretKey, int salt);
int Decrypt(int value, int[] secretKey, int salt);
void GenerateEncryptCode(List<string> lines, string indent);
void GenerateDecryptCode(List<string> lines, string indent);
}
public abstract class EncryptionInstructionBase : IEncryptionInstruction
{
public abstract int Encrypt(int value, int[] secretKey, int salt);
public abstract int Decrypt(int value, int[] secretKey, int salt);
public abstract void GenerateEncryptCode(List<string> lines, string indent);
public abstract void GenerateDecryptCode(List<string> lines, string indent);
}
}

View File

@ -0,0 +1,35 @@
using System.Collections.Generic;
namespace Obfuz.EncryptionVM.Instructions
{
public class AddInstruction : EncryptionInstructionBase
{
private readonly int _addValue;
private readonly int _opKeyIndex;
public AddInstruction(int addValue, int opKeyIndex)
{
_addValue = addValue;
_opKeyIndex = opKeyIndex;
}
public override int Encrypt(int value, int[] secretKey, int salt)
{
return ((value + secretKey[_opKeyIndex]) ^ salt) + _addValue;
}
public override int Decrypt(int value, int[] secretKey, int salt)
{
return ((value - _addValue) ^ salt) - secretKey[_opKeyIndex];
}
public override void GenerateEncryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value = ((value + _secretKey[{_opKeyIndex}]) ^ salt) + {_addValue};");
}
public override void GenerateDecryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value = ((value - {_addValue}) ^ salt) - _secretKey[{_opKeyIndex}];");
}
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.EncryptionVM.Instructions
{
public class AddRotateXorInstruction : EncryptionInstructionBase
{
// x = x + p1 + secretKey[index1];
// x = Rotate(x, p2)
// x = x ^ p3 ^ salt;
private readonly int _addValue;
private readonly int _index1;
private readonly int _rotateBitNum;
private readonly int _xorValue;
public AddRotateXorInstruction(int addValue, int index1, int rotateBitNum, int xorValue)
{
_addValue = addValue;
_index1 = index1;
_rotateBitNum = rotateBitNum;
_xorValue = xorValue;
}
public override int Encrypt(int value, int[] secretKey, int salt)
{
value += _addValue + secretKey[_index1];
uint part1 = (uint)value << _rotateBitNum;
uint part2 = (uint)value >> (32 - _rotateBitNum);
value = (int)(part1 | part2);
value ^= _xorValue ^ salt;
return value;
}
public override int Decrypt(int value, int[] secretKey, int salt)
{
value ^= _xorValue ^ salt;
uint value2 = (uint)value >> _rotateBitNum;
uint part1 = (uint)value << (32 - _rotateBitNum);
value = (int)(value2 | part1);
value -= _addValue + secretKey[_index1];
return value;
}
public override void GenerateEncryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value += {_addValue} + _secretKey[{_index1}];");
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(part1 | part2);");
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
}
public override void GenerateDecryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
lines.Add(indent + $"uint value2 = (uint)value >> {_rotateBitNum};");
lines.Add(indent + $"uint part1 = (uint)value << (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(value2 | part1);");
lines.Add(indent + $"value -= {_addValue} + _secretKey[{_index1}];");
}
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.EncryptionVM.Instructions
{
public class AddXorRotateInstruction : EncryptionInstructionBase
{
// x = x + p1 + secretKey[index1];
// x = x ^ p3 ^ salt;
// x = Rotate(x, p2)
private readonly int _addValue;
private readonly int _index1;
private readonly int _rotateBitNum;
private readonly int _xorValue;
public AddXorRotateInstruction(int addValue, int index1, int xorValue, int rotateBitNum)
{
_addValue = addValue;
_index1 = index1;
_rotateBitNum = rotateBitNum;
_xorValue = xorValue;
}
public override int Encrypt(int value, int[] secretKey, int salt)
{
value += _addValue + secretKey[_index1];
value ^= _xorValue ^ salt;
uint part1 = (uint)value << _rotateBitNum;
uint part2 = (uint)value >> (32 - _rotateBitNum);
value = (int)(part1 | part2);
return value;
}
public override int Decrypt(int value, int[] secretKey, int salt)
{
uint value2 = (uint)value >> _rotateBitNum;
uint part1 = (uint)value << (32 - _rotateBitNum);
value = (int)(value2 | part1);
value ^= _xorValue ^ salt;
value -= _addValue + secretKey[_index1];
return value;
}
public override void GenerateEncryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value += {_addValue} + _secretKey[{_index1}];");
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(part1 | part2);");
}
public override void GenerateDecryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"uint part1 = (uint)value >> {_rotateBitNum};");
lines.Add(indent + $"uint part2 = (uint)value << (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(part1 | part2);");
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
lines.Add(indent + $"value -= {_addValue} + _secretKey[{_index1}];");
}
}
}

View File

@ -0,0 +1,46 @@
using System.Collections.Generic;
namespace Obfuz.EncryptionVM.Instructions
{
public class BitRotateInstruction : EncryptionInstructionBase
{
private readonly int _rotateBitNum;
private readonly int _opKeyIndex;
public BitRotateInstruction(int rotateBitNum, int opKeyIndex)
{
_rotateBitNum = rotateBitNum;
_opKeyIndex = opKeyIndex;
}
public override int Encrypt(int value, int[] secretKey, int salt)
{
uint part1 = (uint)value << _rotateBitNum;
uint part2 = (uint)value >> (32 - _rotateBitNum);
return ((int)(part1 | part2) ^ secretKey[_opKeyIndex]) + salt;
}
public override int Decrypt(int value, int[] secretKey, int salt)
{
uint value2 = (uint)((value - salt) ^ secretKey[_opKeyIndex]);
uint part1 = value2 >> _rotateBitNum;
uint part2 = value2 << (32 - _rotateBitNum);
return (int)(part1 | part2);
}
public override void GenerateEncryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
lines.Add(indent + $"value = ((int)(part1 | part2) ^ _secretKey[{_opKeyIndex}]) + salt;");
}
public override void GenerateDecryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"uint value2 = (uint)((value - salt) ^ _secretKey[{_opKeyIndex}]);");
lines.Add(indent + $"uint part1 = value2 >> {_rotateBitNum};");
lines.Add(indent + $"uint part2 = value2 << (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(part1 | part2);");
}
}
}

View File

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.EncryptionVM.Instructions
{
public class EncryptFunction : EncryptionInstructionBase
{
private readonly IEncryptionInstruction[] _instructions;
public EncryptFunction(IEncryptionInstruction[] instructions)
{
_instructions = instructions;
}
public override int Encrypt(int value, int[] secretKey, int salt)
{
foreach (var instruction in _instructions)
{
value = instruction.Encrypt(value, secretKey, salt);
}
return value;
}
public override int Decrypt(int value, int[] secretKey, int salt)
{
for (int i = _instructions.Length - 1; i >= 0; i--)
{
value = _instructions[i].Decrypt(value, secretKey, salt);
}
return value;
}
public override void GenerateEncryptCode(List<string> lines, string indent)
{
throw new NotImplementedException();
}
public override void GenerateDecryptCode(List<string> lines, string indent)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,64 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
namespace Obfuz.EncryptionVM.Instructions
{
public class MultipleInstruction : EncryptionInstructionBase
{
private readonly int _multiValue;
private readonly int _revertMultiValue;
private readonly int _opKeyIndex;
public MultipleInstruction(int addValue, int opKeyIndex)
{
_multiValue = addValue;
_opKeyIndex = opKeyIndex;
_revertMultiValue = (int)ModInverseOdd((uint)addValue);
Verify();
}
private void Verify()
{
int a = 1122334;
Assert.AreEqual(a, a * _multiValue * _revertMultiValue);
}
public static uint ModInverseOdd(uint a)
{
if (a % 2 == 0)
throw new ArgumentException("Input must be an odd number.", nameof(a));
uint x = 1; // 初始解x₀ = 1 (mod 2)
for (int i = 0; i < 5; i++) // 迭代5次2^1 → 2^32
{
int shift = 2 << i; // 当前模数为 2^(2^(i+1))
ulong mod = 1UL << shift; // 使用 ulong 避免溢出
ulong ax = (ulong)a * x; // 计算 a*x64位避免截断
ulong term = (2 - ax) % mod;
x = (uint)((x * term) % mod); // 更新 x结果截断为 uint
}
return x; // 最终解为 x₅ mod 2^32
}
public override int Encrypt(int value, int[] secretKey, int salt)
{
return value * _multiValue + secretKey[_opKeyIndex] + salt;
}
public override int Decrypt(int value, int[] secretKey, int salt)
{
return (value - secretKey[_opKeyIndex] - salt) * _revertMultiValue;
}
public override void GenerateEncryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value = value * {_multiValue} + _secretKey[{_opKeyIndex}] + salt;");
}
public override void GenerateDecryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value = (value - _secretKey[{_opKeyIndex}] - salt) * {_revertMultiValue};");
}
}
}

View File

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.EncryptionVM.Instructions
{
public class MultipleRotateXorInstruction : EncryptionInstructionBase
{
// x = x * p1 + secretKey[index1];
// x = Rotate(x, p2)
// x = x ^ p3 ^ salt;
private readonly int _multipleValue;
private readonly int _revertMultipleValue;
private readonly int _index1;
private readonly int _rotateBitNum;
private readonly int _xorValue;
public MultipleRotateXorInstruction(int multipleValue, int index1, int rotateBitNum, int xorValue)
{
_multipleValue = multipleValue;
_revertMultipleValue = (int)MultipleInstruction.ModInverseOdd((uint)multipleValue);
_index1 = index1;
_rotateBitNum = rotateBitNum;
_xorValue = xorValue;
}
public override int Encrypt(int value, int[] secretKey, int salt)
{
value = value * _multipleValue + secretKey[_index1];
uint part1 = (uint)value << _rotateBitNum;
uint part2 = (uint)value >> (32 - _rotateBitNum);
value = (int)(part1 | part2);
value ^= _xorValue ^ salt;
return value;
}
public override int Decrypt(int value, int[] secretKey, int salt)
{
value ^= _xorValue ^ salt;
uint value2 = (uint)value >> _rotateBitNum;
uint part1 = (uint)value << (32 - _rotateBitNum);
value = (int)(value2 | part1);
value = (value - secretKey[_index1]) * _revertMultipleValue;
return value;
}
public override void GenerateEncryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value = value * {_multipleValue} + _secretKey[{_index1}];");
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(part1 | part2);");
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
}
public override void GenerateDecryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
lines.Add(indent + $"uint value2 = (uint)value >> {_rotateBitNum};");
lines.Add(indent + $"uint part1 = (uint)value << (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(value2 | part1);");
lines.Add(indent + $"value = (value - _secretKey[{_index1}]) * {_revertMultipleValue};");
}
}
}

View File

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.EncryptionVM.Instructions
{
public class MultipleXorRotateInstruction : EncryptionInstructionBase
{
// x = x * p1 + secretKey[index1];
// x = x ^ p3 ^ salt;
// x = Rotate(x, p2)
private readonly int _multipleValue;
private readonly int _revertMultipleValue;
private readonly int _index1;
private readonly int _rotateBitNum;
private readonly int _xorValue;
public MultipleXorRotateInstruction(int multipleValue, int index1, int xorValue, int rotateBitNum)
{
_multipleValue = multipleValue;
_revertMultipleValue = (int)MultipleInstruction.ModInverseOdd((uint)multipleValue);
_index1 = index1;
_rotateBitNum = rotateBitNum;
_xorValue = xorValue;
}
public override int Encrypt(int value, int[] secretKey, int salt)
{
value = value * _multipleValue + secretKey[_index1];
value ^= _xorValue ^ salt;
uint part1 = (uint)value << _rotateBitNum;
uint part2 = (uint)value >> (32 - _rotateBitNum);
value = (int)(part1 | part2);
return value;
}
public override int Decrypt(int value, int[] secretKey, int salt)
{
uint value2 = (uint)value >> _rotateBitNum;
uint part1 = (uint)value << (32 - _rotateBitNum);
value = (int)(value2 | part1);
value ^= _xorValue ^ salt;
value = (value - secretKey[_index1]) * _revertMultipleValue;
return value;
}
public override void GenerateEncryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value = value * {_multipleValue} + _secretKey[{_index1}];");
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(part1 | part2);");
}
public override void GenerateDecryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"uint value2 = (uint)value >> {_rotateBitNum};");
lines.Add(indent + $"uint part1 = (uint)value << (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(value2 | part1);");
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
lines.Add(indent + $"value = (value - _secretKey[{_index1}]) * {_revertMultipleValue};");
}
}
}

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.EncryptionVM.Instructions
{
public class XorAddRotateInstruction : EncryptionInstructionBase
{
// x = x ^ p3 ^ salt;
// x = x + p1 + secretKey[index1];
// x = Rotate(x, p2)
private readonly int _addValue;
private readonly int _index1;
private readonly int _rotateBitNum;
private readonly int _xorValue;
public XorAddRotateInstruction(int xorValue, int addValue, int index1, int rotateBitNum)
{
_addValue = addValue;
_index1 = index1;
_rotateBitNum = rotateBitNum;
_xorValue = xorValue;
}
public override int Encrypt(int value, int[] secretKey, int salt)
{
value ^= _xorValue ^ salt;
value += _addValue + secretKey[_index1];
uint part1 = (uint)value << _rotateBitNum;
uint part2 = (uint)value >> (32 - _rotateBitNum);
value = (int)(part1 | part2);
return value;
}
public override int Decrypt(int value, int[] secretKey, int salt)
{
uint value2 = (uint)value >> _rotateBitNum;
uint part1 = (uint)value << (32 - _rotateBitNum);
value = (int)(value2 | part1);
value -= _addValue + secretKey[_index1];
value ^= _xorValue ^ salt;
return value;
}
public override void GenerateEncryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
lines.Add(indent + $"value += {_addValue} + _secretKey[{_index1}];");
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(part1 | part2);");
}
public override void GenerateDecryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"uint value2 = (uint)value >> {_rotateBitNum};");
lines.Add(indent + $"uint part1 = (uint)value << (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(value2 | part1);");
lines.Add(indent + $"value -= {_addValue} + _secretKey[{_index1}];");
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
}
}
}

View File

@ -0,0 +1,36 @@
using System.Collections.Generic;
namespace Obfuz.EncryptionVM.Instructions
{
public class XorInstruction : EncryptionInstructionBase
{
private readonly int _xorValue;
private readonly int _opKeyIndex;
public XorInstruction(int xorValue, int opKeyIndex)
{
_xorValue = xorValue;
_opKeyIndex = opKeyIndex;
}
public override int Encrypt(int value, int[] secretKey, int salt)
{
return ((value ^ secretKey[_opKeyIndex]) + salt) ^ _xorValue;
}
public override int Decrypt(int value, int[] secretKey, int salt)
{
return ((value ^ _xorValue) - salt) ^ secretKey[_opKeyIndex];
}
public override void GenerateEncryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value = ((value ^ _secretKey[{_opKeyIndex}]) + salt) ^ {_xorValue};");
}
public override void GenerateDecryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value = ((value ^ {_xorValue}) - salt) ^ _secretKey[{_opKeyIndex}];");
}
}
}

View File

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.EncryptionVM.Instructions
{
public class XorMultipleRotateInstruction : EncryptionInstructionBase
{
// x = x ^ p3 ^ salt;
// x = x * p1 + secretKey[index1];
// x = Rotate(x, p2)
private readonly int _multipleValue;
private readonly int _revertMultipleValue;
private readonly int _index1;
private readonly int _rotateBitNum;
private readonly int _xorValue;
public XorMultipleRotateInstruction(int xorValue, int multipleValue, int index1, int rotateBitNum)
{
_multipleValue = multipleValue;
_revertMultipleValue = (int)MultipleInstruction.ModInverseOdd((uint)multipleValue);
_index1 = index1;
_rotateBitNum = rotateBitNum;
_xorValue = xorValue;
}
public override int Encrypt(int value, int[] secretKey, int salt)
{
value ^= _xorValue ^ salt;
value = value * _multipleValue + secretKey[_index1];
uint part1 = (uint)value << _rotateBitNum;
uint part2 = (uint)value >> (32 - _rotateBitNum);
value = (int)(part1 | part2);
return value;
}
public override int Decrypt(int value, int[] secretKey, int salt)
{
uint value2 = (uint)value >> _rotateBitNum;
uint part1 = (uint)value << (32 - _rotateBitNum);
value = (int)(value2 | part1);
value = (value - secretKey[_index1]) * _revertMultipleValue;
value ^= _xorValue ^ salt;
return value;
}
public override void GenerateEncryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
lines.Add(indent + $"value = value * {_multipleValue} + _secretKey[{_index1}];");
lines.Add(indent + $"uint part1 = (uint)value << {_rotateBitNum};");
lines.Add(indent + $"uint part2 = (uint)value >> (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(part1 | part2);");
}
public override void GenerateDecryptCode(List<string> lines, string indent)
{
lines.Add(indent + $"uint value2 = (uint)value >> {_rotateBitNum};");
lines.Add(indent + $"uint part1 = (uint)value << (32 - {_rotateBitNum});");
lines.Add(indent + $"value = (int)(value2 | part1);");
lines.Add(indent + $"value = (value - _secretKey[{_index1}]) * {_revertMultipleValue};");
lines.Add(indent + $"value ^= {_xorValue} ^ salt;");
}
}
}

View File

@ -0,0 +1,17 @@
namespace Obfuz.EncryptionVM
{
public class VirtualMachine
{
public const int SecretKeyLength = 1024;
public readonly int version;
public readonly string codeGenerationSecretKey;
public readonly EncryptionInstructionWithOpCode[] opCodes;
public VirtualMachine(int version, string codeGenerationSecretKey, EncryptionInstructionWithOpCode[] opCodes)
{
this.codeGenerationSecretKey = codeGenerationSecretKey;
this.opCodes = opCodes;
}
}
}

View File

@ -0,0 +1,207 @@
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using UnityEngine;
namespace Obfuz.EncryptionVM
{
public class VirtualMachineCodeGenerator
{
private readonly int _opCodeCount;
private readonly int _opCodeBits;
private readonly VirtualMachine _vm;
public VirtualMachineCodeGenerator(string vmCodeGenerateSecretKey, int opCodeCount)
{
_opCodeCount = opCodeCount;
_opCodeBits = EncryptionUtil.GetBitCount(opCodeCount - 1);
_vm = new VirtualMachineCreator(vmCodeGenerateSecretKey).CreateVirtualMachine(opCodeCount);
}
public VirtualMachineCodeGenerator(VirtualMachine vm)
{
_opCodeCount = vm.opCodes.Length;
_opCodeBits = EncryptionUtil.GetBitCount(_opCodeCount - 1);
_vm = vm;
}
public bool ValidateMatch(string outputFile)
{
if (!File.Exists(outputFile))
{
return false;
}
string oldCode = NormalizeText(File.ReadAllText(outputFile, Encoding.UTF8));
string newCode = NormalizeText(GenerateCode());
return oldCode == newCode;
}
private static string NormalizeText(string input)
{
return Regex.Replace(input, @"\s+", string.Empty);
}
public void Generate(string outputFile)
{
FileUtil.CreateParentDir(outputFile);
string code = GenerateCode();
File.WriteAllText(outputFile, code, Encoding.UTF8);
Debug.Log($"Generate EncryptionVM code to {outputFile}");
UnityEditor.AssetDatabase.Refresh();
}
private string GenerateCode()
{
var lines = new List<string>();
AppendHeader(lines);
AppendEncryptCodes(lines);
AppendDecryptCodes(lines);
AppendTailer(lines);
return string.Join("\n", lines);
}
private void AppendEncryptCodes(List<string> lines)
{
lines.Add(@"
private int ExecuteEncrypt(int value, int opCode, int salt)
{
switch (opCode)
{");
foreach (var opCode in _vm.opCodes)
{
lines.Add($@" case {opCode.code}:
{{
// {opCode.function.GetType().Name}");
AppendEncryptCode(lines, opCode.function);
lines.Add(@" return value;
}");
}
lines.Add(@"
default:
throw new System.Exception($""Invalid opCode:{opCode}"");
}
}");
}
private void AppendDecryptCodes(List<string> lines)
{
lines.Add(@"
private int ExecuteDecrypt(int value, int opCode, int salt)
{
switch (opCode)
{");
foreach (var opCode in _vm.opCodes)
{
lines.Add($@" case {opCode.code}:
{{
// {opCode.function.GetType().Name}");
AppendDecryptCode(lines, opCode.function);
lines.Add(@" return value;
}");
}
lines.Add(@"
default:
throw new System.Exception($""Invalid opCode:{opCode}"");
}
}");
}
private void AppendHeader(List<string> lines)
{
lines.Add($"/// This file is auto-generated by Obfuz. Do not modify it.");
lines.Add($"///");
//lines.Add($"/// Created Time: {DateTime.Now}");
lines.Add($"/// Version: {_vm.version}");
lines.Add($"/// SecretKey: {_vm.codeGenerationSecretKey}");
lines.Add($"/// OpCodeCount: {_vm.opCodes.Length}");
lines.Add(@"
namespace Obfuz.EncryptionVM
{
public class GeneratedEncryptionVirtualMachine : Obfuz.EncryptorBase
{");
lines.Add($@"
private const int kOpCodeBits = {_opCodeBits};
private const int kOpCodeCount = {_opCodeCount};
private const int kOpCodeMask = {_opCodeCount - 1};
");
lines.Add(@"
private readonly int[] _secretKey;
public GeneratedEncryptionVirtualMachine(byte[] secretKey)
{
this._secretKey = ConvertToIntKey(secretKey);
}
public override int OpCodeCount => kOpCodeCount;
public override int Encrypt(int value, int opts, int salt)
{
uint uopts = (uint)opts;
uint revertOps = 0;
while (uopts != 0)
{
uint opCode = uopts & kOpCodeMask;
revertOps <<= kOpCodeBits;
revertOps |= opCode;
uopts >>= kOpCodeBits;
}
while (revertOps != 0)
{
uint opCode = revertOps & kOpCodeMask;
value = ExecuteEncrypt(value, (int)opCode, salt);
revertOps >>= kOpCodeBits;
}
return value;
}
public override int Decrypt(int value, int opts, int salt)
{
uint uopts = (uint)opts;
while (uopts != 0)
{
uint opCode = uopts & kOpCodeMask;
value = ExecuteDecrypt(value, (int)opCode, salt);
uopts >>= kOpCodeBits;
}
return value;
}
");
}
private void AppendTailer(List<string> lines)
{
lines.Add(@"
}
}
");
}
private void AppendEncryptCode(List<string> lines, IEncryptionInstruction instruction)
{
instruction.GenerateEncryptCode(lines, " ");
}
private void AppendDecryptCode(List<string> lines, IEncryptionInstruction instruction)
{
instruction.GenerateDecryptCode(lines, " ");
}
}
}

View File

@ -0,0 +1,71 @@
using NUnit.Framework;
using Obfuz.EncryptionVM.Instructions;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using UnityEngine.Assertions;
using UnityEngine.UIElements;
namespace Obfuz.EncryptionVM
{
public class VirtualMachineCreator
{
private readonly string _vmGenerationSecretKey;
private readonly IRandom _random;
public const int CodeGenerationSecretKeyLength = 1024;
public const int VirtualMachineVersion = 1;
public VirtualMachineCreator(string vmGenerationSecretKey)
{
_vmGenerationSecretKey = vmGenerationSecretKey;
byte[] byteGenerationSecretKey = KeyGenerator.GenerateKey(vmGenerationSecretKey, CodeGenerationSecretKeyLength);
int[] intGenerationSecretKey = KeyGenerator.ConvertToIntKey(byteGenerationSecretKey);
_random = new RandomWithKey(intGenerationSecretKey, 0);
}
private readonly List<Func<IRandom, int, EncryptionInstructionBase>> _instructionCreators = new List<Func<IRandom, int, EncryptionInstructionBase>>
{
(r, len) => new AddInstruction(r.NextInt(), r.NextInt(len)),
(r, len) => new XorInstruction(r.NextInt(), r.NextInt(len)),
(r, len) => new BitRotateInstruction(r.NextInt(32), r.NextInt(len)),
(r, len) => new MultipleInstruction(r.NextInt() | 0x1, r.NextInt(len)),
(r, len) => new AddRotateXorInstruction(r.NextInt(), r.NextInt(len), r.NextInt(32), r.NextInt()),
(r, len) => new AddXorRotateInstruction(r.NextInt(), r.NextInt(len), r.NextInt(), r.NextInt(32)),
(r, len) => new XorAddRotateInstruction(r.NextInt(), r.NextInt(), r.NextInt(len), r.NextInt(32)),
(r, len) => new MultipleRotateXorInstruction(r.NextInt() | 0x1, r.NextInt(len), r.NextInt(32), r.NextInt()),
(r, len) => new MultipleXorRotateInstruction(r.NextInt() | 0x1, r.NextInt(len), r.NextInt(), r.NextInt(32)),
(r, len) => new XorMultipleRotateInstruction(r.NextInt(), r.NextInt() | 0x1, r.NextInt(len), r.NextInt(32)),
};
private IEncryptionInstruction CreateRandomInstruction(int intSecretKeyLength)
{
return _instructionCreators[_random.NextInt(_instructionCreators.Count)](_random, intSecretKeyLength);
}
private EncryptionInstructionWithOpCode CreateEncryptOpCode(ushort code)
{
IEncryptionInstruction inst = CreateRandomInstruction(VirtualMachine.SecretKeyLength / sizeof(int));
return new EncryptionInstructionWithOpCode(code, inst);
}
public VirtualMachine CreateVirtualMachine(int opCodeCount)
{
if (opCodeCount < 64)
{
throw new System.Exception("OpCode count should be >= 64");
}
if ((opCodeCount & (opCodeCount - 1)) != 0)
{
throw new System.Exception("OpCode count should be power of 2");
}
var opCodes = new EncryptionInstructionWithOpCode[opCodeCount];
for (int i = 0; i < opCodes.Length; i++)
{
opCodes[i] = CreateEncryptOpCode((ushort)i);
}
return new VirtualMachine(VirtualMachineVersion, _vmGenerationSecretKey, opCodes);
}
}
}

View File

@ -0,0 +1,96 @@
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Text;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UIElements;
namespace Obfuz.EncryptionVM
{
public class VirtualMachineSimulator : EncryptorBase
{
private readonly EncryptionInstructionWithOpCode[] _opCodes;
private readonly int[] _secretKey;
public override int OpCodeCount => _opCodes.Length;
public VirtualMachineSimulator(VirtualMachine vm, byte[] byteSecretKey)
{
_opCodes = vm.opCodes;
_secretKey = KeyGenerator.ConvertToIntKey(byteSecretKey);
VerifyInstructions();
}
private void VerifyInstructions()
{
int value = 0x11223344;
for (int i = 0; i < _opCodes.Length; i++)
{
int encryptedValue = _opCodes[i].Encrypt(value, _secretKey, i);
int decryptedValue = _opCodes[i].Decrypt(encryptedValue, _secretKey, i);
//Debug.Log($"instruction type:{_opCodes[i].function.GetType()}");
Assert.AreEqual(value, decryptedValue);
}
int ops = 11223344;
int salt = 789;
Assert.AreEqual(1, Decrypt(Encrypt(1, ops, salt), ops, salt));
Assert.AreEqual(1L, Decrypt(Encrypt(1L, ops, salt), ops, salt));
Assert.AreEqual(1.0f, Decrypt(Encrypt(1.0f, ops, salt), ops, salt));
Assert.AreEqual(1.0, Decrypt(Encrypt(1.0, ops, salt), ops, salt));
byte[] strBytes = Encrypt("abcdef", ops, salt);
Assert.AreEqual("abcdef", DecryptString(strBytes, 0, strBytes.Length, ops, salt));
var arr = new byte[100];
for (int i = 0; i < arr.Length ; i++)
{
arr[i] = (byte)i;
}
EncryptBlock(arr, ops, salt);
DecryptBlock(arr, ops, salt);
for (int i = 0; i < arr.Length; i++)
{
Assert.AreEqual(i, arr[i]);
}
}
private List<uint> DecodeOps(uint ops)
{
var codes = new List<uint>();
while (ops != 0)
{
uint code = ops % (uint)_opCodes.Length;
codes.Add(code);
ops /= (uint)_opCodes.Length;
}
return codes;
}
public override int Encrypt(int value, int ops, int salt)
{
var codes = DecodeOps((uint)ops);
for (int i = codes.Count - 1; i >= 0; i--)
{
var opCode = _opCodes[codes[i]];
value = opCode.Encrypt(value, _secretKey, salt);
}
return value;
}
public override int Decrypt(int value, int ops, int salt)
{
var codes = DecodeOps((uint)ops);
foreach (var code in codes)
{
var opCode = _opCodes[code];
value = opCode.Decrypt(value, _secretKey, salt);
}
return value;
}
}
}

View File

@ -0,0 +1,20 @@
using Obfuz.ObfusPasses;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz
{
public interface IObfuscationPass
{
ObfuscationPassType Type { get; }
void Start();
void Stop();
void Process();
}
}

View File

@ -0,0 +1,87 @@
using dnlib.DotNet.Emit;
using dnlib.DotNet;
using System.Collections.Generic;
using System.Linq;
using Obfuz.Emit;
namespace Obfuz.ObfusPasses
{
public abstract class BasicBlockObfuscationPassBase : ObfuscationPassBase
{
protected abstract bool NeedObfuscateMethod(MethodDef method);
public override void Process()
{
var ctx = ObfuscationPassContext.Current;
ObfuscationMethodWhitelist whiteList = ctx.whiteList;
ConfigurablePassPolicy passPolicy = ctx.passPolicy;
foreach (ModuleDef mod in ctx.modulesToObfuscate)
{
if (whiteList.IsInWhiteList(mod) || !Support(passPolicy.GetAssemblyObfuscationPasses(mod)))
{
continue;
}
// ToArray to avoid modify list exception
foreach (TypeDef type in mod.GetTypes().ToArray())
{
if (whiteList.IsInWhiteList(type) || !Support(passPolicy.GetTypeObfuscationPasses(type)))
{
continue;
}
// ToArray to avoid modify list exception
foreach (MethodDef method in type.Methods.ToArray())
{
if (!method.HasBody || ctx.whiteList.IsInWhiteList(method) || !Support(passPolicy.GetMethodObfuscationPasses(method)) || !NeedObfuscateMethod(method))
{
continue;
}
// TODO if isGeneratedBy Obfuscator, continue
ObfuscateData(method);
}
}
}
}
protected abstract bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, BasicBlock block, int instructionIndex,
IList<Instruction> globalInstructions, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions);
private void ObfuscateData(MethodDef method)
{
BasicBlockCollection bbc = new BasicBlockCollection(method);
IList<Instruction> instructions = method.Body.Instructions;
var outputInstructions = new List<Instruction>();
var totalFinalInstructions = new List<Instruction>();
for (int i = 0; i < instructions.Count; i++)
{
Instruction inst = instructions[i];
BasicBlock block = bbc.GetBasicBlockByInstruction(inst);
outputInstructions.Clear();
if (TryObfuscateInstruction(method, inst, block, i, instructions, outputInstructions, totalFinalInstructions))
{
// current instruction may be the target of control flow instruction, so we can't remove it directly.
// we replace it with nop now, then remove it in CleanUpInstructionPass
inst.OpCode = outputInstructions[0].OpCode;
inst.Operand = outputInstructions[0].Operand;
totalFinalInstructions.Add(inst);
for (int k = 1; k < outputInstructions.Count; k++)
{
totalFinalInstructions.Add(outputInstructions[k]);
}
}
else
{
totalFinalInstructions.Add(inst);
}
}
instructions.Clear();
foreach (var obInst in totalFinalInstructions)
{
instructions.Add(obInst);
}
}
}
}

View File

@ -0,0 +1,92 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Utils;
using Obfuz.Emit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.Assertions;
using Obfuz.Settings;
namespace Obfuz.ObfusPasses.CallObfus
{
public class CallObfusPass : BasicBlockObfuscationPassBase
{
private readonly List<string> _configFiles;
private readonly int _obfuscationLevel;
private IObfuscator _dynamicProxyObfuscator;
private IObfuscationPolicy _dynamicProxyPolicy;
public override ObfuscationPassType Type => ObfuscationPassType.CallObfus;
public CallObfusPass(CallObfuscationSettings settings)
{
_configFiles = settings.ruleFiles.ToList();
_obfuscationLevel = settings.obfuscationLevel;
}
public override void Stop()
{
_dynamicProxyObfuscator.Done();
}
public override void Start()
{
var ctx = ObfuscationPassContext.Current;
_dynamicProxyObfuscator = new DefaultCallProxyObfuscator(ctx.encryptionScopeProvider, ctx.constFieldAllocator, ctx.moduleEntityManager, _obfuscationLevel);
_dynamicProxyPolicy = new ConfigurableObfuscationPolicy(ctx.assembliesToObfuscate, _configFiles);
}
protected override bool NeedObfuscateMethod(MethodDef method)
{
return _dynamicProxyPolicy.NeedObfuscateCallInMethod(method);
}
protected override bool TryObfuscateInstruction(MethodDef callerMethod, Instruction inst, BasicBlock block,
int instructionIndex, IList<Instruction> globalInstructions, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
{
IMethod calledMethod = inst.Operand as IMethod;
if (calledMethod == null || !calledMethod.IsMethod)
{
return false;
}
if (MetaUtil.ContainsContainsGenericParameter(calledMethod))
{
return false;
}
bool callVir;
switch (inst.OpCode.Code)
{
case Code.Call:
{
callVir = false;
break;
}
case Code.Callvirt:
{
if (instructionIndex > 0 && globalInstructions[instructionIndex - 1].OpCode.Code == Code.Constrained)
{
return false;
}
callVir = true;
break;
}
default: return false;
}
if (!_dynamicProxyPolicy.NeedObfuscateCalledMethod(callerMethod, calledMethod, callVir, block.inLoop))
{
return false;
}
ObfuscationCachePolicy cachePolicy = _dynamicProxyPolicy.GetMethodObfuscationCachePolicy(callerMethod);
bool cachedCallIndex = block.inLoop ? cachePolicy.cacheInLoop : cachePolicy.cacheNotInLoop;
_dynamicProxyObfuscator.Obfuscate(callerMethod, calledMethod, callVir, cachedCallIndex, outputInstructions);
return true;
}
}
}

View File

@ -0,0 +1,293 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Editor;
using Obfuz.Emit;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MethodImplAttributes = dnlib.DotNet.MethodImplAttributes;
using TypeAttributes = dnlib.DotNet.TypeAttributes;
namespace Obfuz.ObfusPasses.CallObfus
{
public struct ProxyCallMethodData
{
public readonly MethodDef proxyMethod;
public readonly int encryptOps;
public readonly int salt;
public readonly int encryptedIndex;
public readonly int index;
public ProxyCallMethodData(MethodDef proxyMethod, int encryptOps, int salt, int encryptedIndex, int index)
{
this.proxyMethod = proxyMethod;
this.encryptOps = encryptOps;
this.salt = salt;
this.encryptedIndex = encryptedIndex;
this.index = index;
}
}
class ModuleCallProxyAllocator : IGroupByModuleEntity
{
private ModuleDef _module;
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private readonly int _encryptionLevel;
private EncryptionScopeInfo _encryptionScope;
private bool _done;
class MethodKey : IEquatable<MethodKey>
{
public readonly IMethod _method;
public readonly bool _callVir;
private readonly int _hashCode;
public MethodKey(IMethod method, bool callVir)
{
_method = method;
_callVir = callVir;
_hashCode = HashUtil.CombineHash(MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method), callVir ? 1 : 0);
}
public override int GetHashCode()
{
return _hashCode;
}
public bool Equals(MethodKey other)
{
return MethodEqualityComparer.CompareDeclaringTypes.Equals(_method, other._method) && _callVir == other._callVir;
}
}
class MethodProxyInfo
{
public MethodDef proxyMethod;
public int index;
public int encryptedOps;
public int salt;
public int encryptedIndex;
}
private readonly Dictionary<MethodKey, MethodProxyInfo> _methodProxys = new Dictionary<MethodKey, MethodProxyInfo>();
const int maxProxyMethodPerDispatchMethod = 1000;
class CallInfo
{
public IMethod method;
public bool callVir;
}
class DispatchMethodInfo
{
public MethodDef methodDef;
public List<CallInfo> methods = new List<CallInfo>();
}
private readonly Dictionary<MethodSig, List<DispatchMethodInfo>> _dispatchMethods = new Dictionary<MethodSig, List<DispatchMethodInfo>>(SignatureEqualityComparer.Instance);
private TypeDef _proxyTypeDef;
public ModuleCallProxyAllocator(EncryptionScopeProvider encryptionScopeProvider, int encryptionLevel)
{
_encryptionScopeProvider = encryptionScopeProvider;
_encryptionLevel = encryptionLevel;
}
public void Init(ModuleDef mod)
{
_module = mod;
_encryptionScope = _encryptionScopeProvider.GetScope(mod);
}
private TypeDef CreateProxyTypeDef()
{
var typeDef = new TypeDefUser($"{ConstValues.ObfuzInternalSymbolNamePrefix}ProxyCall", _module.CorLibTypes.Object.ToTypeDefOrRef());
typeDef.Attributes = TypeAttributes.NotPublic | TypeAttributes.Sealed;
_module.EnableTypeDefFindCache = false;
_module.Types.Add(typeDef);
_module.EnableTypeDefFindCache = true;
return typeDef;
}
private MethodDef CreateDispatchMethodInfo(MethodSig methodSig)
{
if (_proxyTypeDef == null)
{
_proxyTypeDef = CreateProxyTypeDef();
}
MethodDef methodDef = new MethodDefUser($"{ConstValues.ObfuzInternalSymbolNamePrefix}ProxyCall$Dispatch${_proxyTypeDef.Methods.Count}", methodSig,
MethodImplAttributes.IL | MethodImplAttributes.Managed,
MethodAttributes.Static | MethodAttributes.Private);
methodDef.DeclaringType = _proxyTypeDef;
return methodDef;
}
private MethodSig CreateDispatchMethodSig(IMethod method)
{
MethodSig methodSig = MetaUtil.ToSharedMethodSig(_module.CorLibTypes, MetaUtil.GetInflatedMethodSig(method));
//MethodSig methodSig = MetaUtil.GetInflatedMethodSig(method).Clone();
//methodSig.Params
switch (MetaUtil.GetThisArgType(method))
{
case ThisArgType.Class:
{
methodSig.Params.Insert(0, _module.CorLibTypes.Object);
break;
}
case ThisArgType.ValueType:
{
methodSig.Params.Insert(0, _module.CorLibTypes.IntPtr);
break;
}
}
// extra param for index
methodSig.Params.Add(_module.CorLibTypes.Int32);
return MethodSig.CreateStatic(methodSig.RetType, methodSig.Params.ToArray());
}
private int GenerateSalt(IRandom random)
{
return random.NextInt();
}
private int GenerateEncryptOps(IRandom random)
{
return EncryptionUtil.GenerateEncryptionOpCodes(random, _encryptionScope.encryptor, _encryptionLevel);
}
private DispatchMethodInfo GetDispatchMethod(IMethod method)
{
MethodSig methodSig = CreateDispatchMethodSig(method);
if (!_dispatchMethods.TryGetValue(methodSig, out var dispatchMethods))
{
dispatchMethods = new List<DispatchMethodInfo>();
_dispatchMethods.Add(methodSig, dispatchMethods);
}
if (dispatchMethods.Count == 0 || dispatchMethods.Last().methods.Count >= maxProxyMethodPerDispatchMethod)
{
var newDispatchMethodInfo = new DispatchMethodInfo
{
methodDef = CreateDispatchMethodInfo(methodSig),
};
dispatchMethods.Add(newDispatchMethodInfo);
}
return dispatchMethods.Last();
}
private IRandom CreateRandomForMethod(IMethod method, bool callVir)
{
int seed = MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method);
return _encryptionScope.localRandomCreator(seed);
}
public ProxyCallMethodData Allocate(IMethod method, bool callVir)
{
if (_done)
{
throw new Exception("can't Allocate after done");
}
var key = new MethodKey(method, callVir);
if (!_methodProxys.TryGetValue(key, out var proxyInfo))
{
var methodDispatcher = GetDispatchMethod(method);
int index = methodDispatcher.methods.Count;
IRandom localRandom = CreateRandomForMethod(method, callVir);
int encryptOps = GenerateEncryptOps(localRandom);
int salt = GenerateSalt(localRandom);
int encryptedIndex = _encryptionScope.encryptor.Encrypt(index, encryptOps, salt);
proxyInfo = new MethodProxyInfo()
{
proxyMethod = methodDispatcher.methodDef,
index = index,
encryptedOps = encryptOps,
salt = salt,
encryptedIndex = encryptedIndex,
};
methodDispatcher.methods.Add(new CallInfo { method = method, callVir = callVir});
_methodProxys.Add(key, proxyInfo);
}
return new ProxyCallMethodData(proxyInfo.proxyMethod, proxyInfo.encryptedOps, proxyInfo.salt, proxyInfo.encryptedIndex, proxyInfo.index);
}
public void Done()
{
if (_done)
{
throw new Exception("Already done");
}
_done = true;
foreach (DispatchMethodInfo dispatchMethod in _dispatchMethods.Values.SelectMany(ms => ms))
{
var methodDef = dispatchMethod.methodDef;
var methodSig = methodDef.MethodSig;
var body = new CilBody();
methodDef.Body = body;
var ins = body.Instructions;
foreach (Parameter param in methodDef.Parameters)
{
ins.Add(Instruction.Create(OpCodes.Ldarg, param));
}
var switchCases = new List<Instruction>();
var switchInst = Instruction.Create(OpCodes.Switch, switchCases);
ins.Add(switchInst);
var ret = Instruction.Create(OpCodes.Ret);
foreach (CallInfo ci in dispatchMethod.methods)
{
var callTargetMethod = Instruction.Create(ci.callVir ? OpCodes.Callvirt : OpCodes.Call, ci.method);
switchCases.Add(callTargetMethod);
ins.Add(callTargetMethod);
ins.Add(Instruction.Create(OpCodes.Br, ret));
}
ins.Add(ret);
}
}
}
public class CallProxyAllocator
{
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private GroupByModuleEntityManager _moduleEntityManager;
private readonly int _encryptionLevel;
public CallProxyAllocator(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager, int encryptionLevel)
{
_encryptionScopeProvider = encryptionScopeProvider;
_moduleEntityManager = moduleEntityManager;
_encryptionLevel = encryptionLevel;
}
private ModuleCallProxyAllocator GetModuleAllocator(ModuleDef mod)
{
return _moduleEntityManager.GetEntity<ModuleCallProxyAllocator>(mod, () => new ModuleCallProxyAllocator(_encryptionScopeProvider, _encryptionLevel));
}
public ProxyCallMethodData Allocate(ModuleDef mod, IMethod method, bool callVir)
{
ModuleCallProxyAllocator allocator = GetModuleAllocator(mod);
return allocator.Allocate(method, callVir);
}
public void Done()
{
foreach (var allocator in _moduleEntityManager.GetEntities<ModuleCallProxyAllocator>())
{
allocator.Done();
}
}
}
}

View File

@ -0,0 +1,372 @@
using dnlib.DotNet;
using Obfuz.Conf;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using UnityEngine;
namespace Obfuz.ObfusPasses.CallObfus
{
public class ConfigurableObfuscationPolicy : ObfuscationPolicyBase
{
class WhiteListAssembly
{
public string name;
public NameMatcher nameMatcher;
public bool? obfuscateNone;
public List<WhiteListType> types = new List<WhiteListType>();
}
class WhiteListType
{
public string name;
public NameMatcher nameMatcher;
public bool? obfuscateNone;
public List<WhiteListMethod> methods = new List<WhiteListMethod>();
}
class WhiteListMethod
{
public string name;
public NameMatcher nameMatcher;
}
class ObfuscationRule : IRule<ObfuscationRule>
{
public bool? disableObfuscation;
public bool? obfuscateCallInLoop;
public bool? cacheCallIndexInLoop;
public bool? cacheCallIndexNotLoop;
public void InheritParent(ObfuscationRule parentRule)
{
if (disableObfuscation == null)
{
disableObfuscation = parentRule.disableObfuscation;
}
if (obfuscateCallInLoop == null)
{
obfuscateCallInLoop = parentRule.obfuscateCallInLoop;
}
if (cacheCallIndexInLoop == null)
{
cacheCallIndexInLoop = parentRule.cacheCallIndexInLoop;
}
if (cacheCallIndexNotLoop == null)
{
cacheCallIndexNotLoop = parentRule.cacheCallIndexNotLoop;
}
}
}
class AssemblySpec : AssemblyRuleBase<TypeSpec, MethodSpec, ObfuscationRule>
{
}
class TypeSpec : TypeRuleBase<MethodSpec, ObfuscationRule>
{
}
class MethodSpec : MethodRuleBase<ObfuscationRule>
{
}
private static readonly ObfuscationRule s_default = new ObfuscationRule()
{
disableObfuscation = false,
obfuscateCallInLoop = true,
cacheCallIndexInLoop = true,
cacheCallIndexNotLoop = false,
};
private readonly XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule> _configParser;
private ObfuscationRule _global;
private readonly List<WhiteListAssembly> _whiteListAssemblies = new List<WhiteListAssembly>();
private readonly Dictionary<IMethod, bool> _whiteListMethodCache = new Dictionary<IMethod, bool>(MethodEqualityComparer.CompareDeclaringTypes);
private readonly Dictionary<MethodDef, ObfuscationRule> _methodRuleCache = new Dictionary<MethodDef, ObfuscationRule>();
public ConfigurableObfuscationPolicy(List<string> toObfuscatedAssemblyNames, List<string> xmlConfigFiles)
{
_configParser = new XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule>(toObfuscatedAssemblyNames,
ParseObfuscationRule, ParseGlobalElement);
LoadConfigs(xmlConfigFiles);
}
private void LoadConfigs(List<string> configFiles)
{
_configParser.LoadConfigs(configFiles);
if (_global == null)
{
_global = s_default;
}
else
{
_global.InheritParent(s_default);
}
_configParser.InheritParentRules(_global);
}
private void ParseGlobalElement(string configFile, XmlElement ele)
{
switch (ele.Name)
{
case "global": _global = ParseObfuscationRule(configFile, ele); break;
case "whitelist": ParseWhitelist(ele); break;
default: throw new Exception($"Invalid xml file {configFile}, unknown node {ele.Name}");
}
}
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
{
var rule = new ObfuscationRule();
if (ele.HasAttribute("disableObfuscation"))
{
rule.disableObfuscation = ConfigUtil.ParseBool(ele.GetAttribute("disableObfuscation"));
}
if (ele.HasAttribute("obfuscateCallInLoop"))
{
rule.obfuscateCallInLoop = ConfigUtil.ParseBool(ele.GetAttribute("obfuscateCallInLoop"));
}
if (ele.HasAttribute("cacheCallIndexInLoop"))
{
rule.cacheCallIndexInLoop = ConfigUtil.ParseBool(ele.GetAttribute("cacheCallIndexInLoop"));
}
if (ele.HasAttribute("cacheCallIndexNotLoop"))
{
rule.cacheCallIndexNotLoop = ConfigUtil.ParseBool(ele.GetAttribute("cacheCallIndexNotLoop"));
}
return rule;
}
private void ParseWhitelist(XmlElement ruleEle)
{
foreach (XmlNode xmlNode in ruleEle.ChildNodes)
{
if (!(xmlNode is XmlElement childEle))
{
continue;
}
switch (childEle.Name)
{
case "assembly":
{
var ass = ParseWhiteListAssembly(childEle);
_whiteListAssemblies.Add(ass);
break;
}
default: throw new Exception($"Invalid xml file, unknown node {childEle.Name}");
}
}
}
private WhiteListAssembly ParseWhiteListAssembly(XmlElement element)
{
var ass = new WhiteListAssembly();
ass.name = element.GetAttribute("name");
ass.nameMatcher = new NameMatcher(ass.name);
if (element.HasAttribute("obfuscateNone"))
{
ass.obfuscateNone = ConfigUtil.ParseBool(element.GetAttribute("obfuscateNone"));
}
foreach (XmlNode node in element.ChildNodes)
{
if (!(node is XmlElement ele))
{
continue;
}
switch (ele.Name)
{
case "type":
ass.types.Add(ParseWhiteListType(ele));
break;
default:
throw new Exception($"Invalid xml file, unknown node {ele.Name}");
}
}
return ass;
}
private WhiteListType ParseWhiteListType(XmlElement element)
{
var type = new WhiteListType();
type.name = element.GetAttribute("name");
type.nameMatcher = new NameMatcher(type.name);
if (element.HasAttribute("obfuscateNone"))
{
type.obfuscateNone = ConfigUtil.ParseBool(element.GetAttribute("obfuscateNone"));
}
foreach (XmlNode node in element.ChildNodes)
{
if (!(node is XmlElement ele))
{
continue;
}
switch (ele.Name)
{
case "method":
{
type.methods.Add(ParseWhiteListMethod(ele));
break;
}
default: throw new Exception($"Invalid xml file, unknown node {ele.Name}");
}
}
return type;
}
private WhiteListMethod ParseWhiteListMethod(XmlElement element)
{
var method = new WhiteListMethod();
method.name = element.GetAttribute("name");
method.nameMatcher = new NameMatcher(method.name);
return method;
}
private ObfuscationRule GetMethodObfuscationRule(MethodDef method)
{
if (!_methodRuleCache.TryGetValue(method, out var rule))
{
rule = _configParser.GetMethodRule(method, s_default);
_methodRuleCache[method] = rule;
}
return rule;
}
public override bool NeedObfuscateCallInMethod(MethodDef method)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
return rule.disableObfuscation != true;
}
public override ObfuscationCachePolicy GetMethodObfuscationCachePolicy(MethodDef method)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
return new ObfuscationCachePolicy()
{
cacheInLoop = rule.cacheCallIndexInLoop.Value,
cacheNotInLoop = rule.cacheCallIndexNotLoop.Value,
};
}
private bool IsSpecialNotObfuscatedMethod(TypeDef typeDef, IMethod method)
{
if (typeDef.IsDelegate || typeDef.IsEnum)
return true;
string methodName = method.Name;
// doesn't proxy call if the method is a constructor
if (methodName == ".ctor")
{
return true;
}
// special handle
// don't proxy call for List<T>.Enumerator GetEnumerator()
if (methodName == "GetEnumerator")
{
return true;
}
return false;
}
private bool ComputeIsInWhiteList(IMethod calledMethod)
{
ITypeDefOrRef declaringType = calledMethod.DeclaringType;
TypeSig declaringTypeSig = calledMethod.DeclaringType.ToTypeSig();
declaringTypeSig = declaringTypeSig.RemovePinnedAndModifiers();
switch (declaringTypeSig.ElementType)
{
case ElementType.ValueType:
case ElementType.Class:
{
break;
}
case ElementType.GenericInst:
{
if (MetaUtil.ContainsContainsGenericParameter(calledMethod))
{
return true;
}
break;
}
default: return true;
}
TypeDef typeDef = declaringType.ResolveTypeDef();
if (IsSpecialNotObfuscatedMethod(typeDef, calledMethod))
{
return true;
}
string assName = typeDef.Module.Assembly.Name;
string typeFullName = typeDef.FullName;
string methodName = calledMethod.Name;
foreach (var ass in _whiteListAssemblies)
{
if (!ass.nameMatcher.IsMatch(assName))
{
continue;
}
if (ass.obfuscateNone == true)
{
return true;
}
foreach (var type in ass.types)
{
if (!type.nameMatcher.IsMatch(typeFullName))
{
continue;
}
if (type.obfuscateNone == true)
{
return true;
}
foreach (var method in type.methods)
{
if (method.nameMatcher.IsMatch(methodName))
{
return true;
}
}
}
}
return false;
}
private bool IsInWhiteList(IMethod method)
{
if (!_whiteListMethodCache.TryGetValue(method, out var isWhiteList))
{
isWhiteList = ComputeIsInWhiteList(method);
_whiteListMethodCache.Add(method, isWhiteList);
}
return isWhiteList;
}
public override bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool currentInLoop)
{
if (IsInWhiteList(calledMethod))
{
return false;
}
ObfuscationRule rule = GetMethodObfuscationRule(callerMethod);
if (currentInLoop && rule.obfuscateCallInLoop == false)
{
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,53 @@
using dnlib.DotNet.Emit;
using dnlib.DotNet;
using System.Collections.Generic;
using Obfuz.Utils;
using Obfuz.Emit;
using Obfuz.Data;
using UnityEngine;
namespace Obfuz.ObfusPasses.CallObfus
{
public class DefaultCallProxyObfuscator : ObfuscatorBase
{
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private readonly ConstFieldAllocator _constFieldAllocator;
private readonly CallProxyAllocator _proxyCallAllocator;
private readonly GroupByModuleEntityManager _moduleEntityManager;
public DefaultCallProxyObfuscator(EncryptionScopeProvider encryptionScopeProvider, ConstFieldAllocator constFieldAllocator, GroupByModuleEntityManager moduleEntityManager, int encryptionLevel)
{
_encryptionScopeProvider = encryptionScopeProvider;
_constFieldAllocator = constFieldAllocator;
_moduleEntityManager = moduleEntityManager;
_proxyCallAllocator = new CallProxyAllocator(encryptionScopeProvider, moduleEntityManager, encryptionLevel);
}
public override void Done()
{
_proxyCallAllocator.Done();
}
public override void Obfuscate(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool needCacheCall, List<Instruction> obfuscatedInstructions)
{
MethodSig sharedMethodSig = MetaUtil.ToSharedMethodSig(calledMethod.Module.CorLibTypes, MetaUtil.GetInflatedMethodSig(calledMethod));
ProxyCallMethodData proxyCallMethodData = _proxyCallAllocator.Allocate(callerMethod.Module, calledMethod, callVir);
DefaultMetadataImporter importer = _moduleEntityManager.GetDefaultModuleMetadataImporter(callerMethod.Module, _encryptionScopeProvider);
if (needCacheCall)
{
FieldDef cacheField = _constFieldAllocator.Allocate(callerMethod.Module, proxyCallMethodData.index);
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
}
else
{
obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.encryptedIndex));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.encryptOps));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(proxyCallMethodData.salt));
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptInt));
}
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, proxyCallMethodData.proxyMethod));
}
}
}

View File

@ -0,0 +1,34 @@
using dnlib.DotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.CallObfus
{
public struct ObfuscationCachePolicy
{
public bool cacheInLoop;
public bool cacheNotInLoop;
}
public interface IObfuscationPolicy
{
bool NeedObfuscateCallInMethod(MethodDef method);
ObfuscationCachePolicy GetMethodObfuscationCachePolicy(MethodDef method);
bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool currentInLoop);
}
public abstract class ObfuscationPolicyBase : IObfuscationPolicy
{
public abstract bool NeedObfuscateCallInMethod(MethodDef method);
public abstract ObfuscationCachePolicy GetMethodObfuscationCachePolicy(MethodDef method);
public abstract bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool currentInLoop);
}
}

View File

@ -0,0 +1,23 @@
using dnlib.DotNet.Emit;
using dnlib.DotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.CallObfus
{
public interface IObfuscator
{
void Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, bool needCacheCall, List<Instruction> obfuscatedInstructions);
void Done();
}
public abstract class ObfuscatorBase : IObfuscator
{
public abstract void Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, bool needCacheCall, List<Instruction> obfuscatedInstructions);
public abstract void Done();
}
}

View File

@ -0,0 +1,46 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.CleanUp
{
public class CleanUpInstructionPass : ObfuscationPassBase
{
public override ObfuscationPassType Type => ObfuscationPassType.None;
public override void Start()
{
}
public override void Stop()
{
}
public override void Process()
{
var ctx = ObfuscationPassContext.Current;
foreach (ModuleDef mod in ctx.modulesToObfuscate)
{
foreach (TypeDef type in mod.GetTypes())
{
foreach (MethodDef method in type.Methods)
{
if (method.HasBody)
{
CilBody body = method.Body;
body.SimplifyBranches();
body.OptimizeMacros();
body.OptimizeBranches();
// TODO remove dup
}
}
}
}
}
}
}

View File

@ -0,0 +1,70 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.CleanUp
{
public class RemoveObfuzAttributesPass : ObfuscationPassBase
{
public override ObfuscationPassType Type => ObfuscationPassType.None;
public override void Start()
{
}
public override void Stop()
{
}
private void RemoveObfuzAttributes(IHasCustomAttribute provider)
{
CustomAttributeCollection customAttributes = provider.CustomAttributes;
if (customAttributes.Count == 0)
return;
var toRemove = new List<CustomAttribute>();
customAttributes.RemoveAll("Obfuz.ObfuzIgnoreAttribute");
}
public override void Process()
{
var ctx = ObfuscationPassContext.Current;
foreach (ModuleDef mod in ctx.modulesToObfuscate)
{
RemoveObfuzAttributes(mod);
foreach (TypeDef type in mod.GetTypes())
{
RemoveObfuzAttributes(type);
foreach (FieldDef field in type.Fields)
{
RemoveObfuzAttributes(field);
}
foreach (MethodDef method in type.Methods)
{
RemoveObfuzAttributes(method);
foreach (Parameter param in method.Parameters)
{
if (param.ParamDef != null)
{
RemoveObfuzAttributes(param.ParamDef);
}
}
}
foreach (PropertyDef property in type.Properties)
{
RemoveObfuzAttributes(property);
}
foreach (EventDef eventDef in type.Events)
{
RemoveObfuzAttributes(eventDef);
}
}
}
}
}
}

View File

@ -0,0 +1,489 @@
using dnlib.DotNet;
using Obfuz.Conf;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using UnityEngine;
namespace Obfuz.ObfusPasses.ConstEncrypt
{
public class ConfigurableEncryptPolicy : EncryptPolicyBase
{
class ObfuscationRule : IRule<ObfuscationRule>
{
public bool? disableEncrypt;
public bool? encryptInt;
public bool? encryptLong;
public bool? encryptFloat;
public bool? encryptDouble;
public bool? encryptArray;
public bool? encryptString;
public bool? encryptConstInLoop;
public bool? encryptStringInLoop;
public bool? cacheConstInLoop;
public bool? cacheConstNotInLoop;
public bool? cacheStringInLoop;
public bool? cacheStringNotInLoop;
public void InheritParent(ObfuscationRule parentRule)
{
if (disableEncrypt == null)
disableEncrypt = parentRule.disableEncrypt;
if (encryptInt == null)
encryptInt = parentRule.encryptInt;
if (encryptLong == null)
encryptLong = parentRule.encryptLong;
if (encryptFloat == null)
encryptFloat = parentRule.encryptFloat;
if (encryptDouble == null)
encryptDouble = parentRule.encryptDouble;
if (encryptArray == null)
encryptArray = parentRule.encryptArray;
if (encryptString == null)
encryptString = parentRule.encryptString;
if (encryptConstInLoop == null)
encryptConstInLoop = parentRule.encryptConstInLoop;
if (encryptStringInLoop == null)
encryptStringInLoop = parentRule.encryptStringInLoop;
if (cacheConstInLoop == null)
cacheConstInLoop = parentRule.cacheConstInLoop;
if (cacheConstNotInLoop == null)
cacheConstNotInLoop = parentRule.cacheConstNotInLoop;
if (cacheStringInLoop == null)
cacheStringInLoop = parentRule.cacheStringInLoop;
if (cacheStringNotInLoop == null)
cacheStringNotInLoop = parentRule.cacheStringNotInLoop;
}
}
class MethodSpec : MethodRuleBase<ObfuscationRule>
{
}
class TypeSpec : TypeRuleBase<MethodSpec, ObfuscationRule>
{
}
class AssemblySpec : AssemblyRuleBase<TypeSpec, MethodSpec, ObfuscationRule>
{
}
private static readonly ObfuscationRule s_default = new ObfuscationRule()
{
disableEncrypt = false,
encryptInt = true,
encryptLong = true,
encryptFloat = true,
encryptDouble = true,
encryptArray = true,
encryptString = true,
encryptConstInLoop = true,
encryptStringInLoop = true,
cacheConstInLoop = true,
cacheConstNotInLoop = false,
cacheStringInLoop = true,
cacheStringNotInLoop = true,
};
private ObfuscationRule _global;
public HashSet<int> notEncryptInts = new HashSet<int>();
public HashSet<long> notEncryptLongs = new HashSet<long>();
public HashSet<string> notEncryptStrings = new HashSet<string>();
public List<NumberRange<int>> notEncryptIntRanges = new List<NumberRange<int>>();
public List<NumberRange<long>> notEncryptLongRanges = new List<NumberRange<long>>();
public List<NumberRange<float>> notEncryptFloatRanges = new List<NumberRange<float>>();
public List<NumberRange<double>> notEncryptDoubleRanges = new List<NumberRange<double>>();
public List<NumberRange<int>> notEncryptArrayLengthRanges = new List<NumberRange<int>>();
public List<NumberRange<int>> notEncryptStringLengthRanges = new List<NumberRange<int>>();
private readonly XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule> _xmlParser;
private readonly Dictionary<string, AssemblySpec> _assemblySpecs = new Dictionary<string, AssemblySpec>();
private readonly Dictionary<MethodDef, ObfuscationRule> _methodRuleCache = new Dictionary<MethodDef, ObfuscationRule>();
public ConfigurableEncryptPolicy(List<string> toObfuscatedAssemblyNames, List<string> xmlConfigFiles)
{
_xmlParser = new XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule>(
toObfuscatedAssemblyNames, ParseObfuscationRule, ParseGlobalElement);
LoadConfigs(xmlConfigFiles);
}
private void LoadConfigs(List<string> configFiles)
{
_xmlParser.LoadConfigs(configFiles);
if (_global == null)
{
_global = s_default;
}
else
{
_global.InheritParent(s_default);
}
_xmlParser.InheritParentRules(_global);
}
private void ParseGlobalElement(string configFile, XmlElement ele)
{
switch (ele.Name)
{
case "global": _global = ParseObfuscationRule(configFile, ele); break;
case "whitelist": ParseWhitelist(configFile, ele); break;
default: throw new Exception($"Invalid xml file {configFile}, unknown node {ele.Name}");
}
}
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
{
var rule = new ObfuscationRule();
if (ele.HasAttribute("disableEncrypt"))
{
rule.disableEncrypt = ConfigUtil.ParseBool(ele.GetAttribute("disableEncrypt"));
}
if (ele.HasAttribute("encryptInt"))
{
rule.encryptInt = ConfigUtil.ParseBool(ele.GetAttribute("encryptInt"));
}
if (ele.HasAttribute("encryptLong"))
{
rule.encryptLong = ConfigUtil.ParseBool(ele.GetAttribute("encryptLong"));
}
if (ele.HasAttribute("encryptFloat"))
{
rule.encryptFloat = ConfigUtil.ParseBool(ele.GetAttribute("encryptFloat"));
}
if (ele.HasAttribute("encryptDouble"))
{
rule.encryptDouble = ConfigUtil.ParseBool(ele.GetAttribute("encryptDouble"));
}
if (ele.HasAttribute("encryptBytes"))
{
rule.encryptArray = ConfigUtil.ParseBool(ele.GetAttribute("encryptArray"));
}
if (ele.HasAttribute("encryptString"))
{
rule.encryptString = ConfigUtil.ParseBool(ele.GetAttribute("encryptString"));
}
if (ele.HasAttribute("encryptConstInLoop"))
{
rule.encryptConstInLoop = ConfigUtil.ParseBool(ele.GetAttribute("encryptConstInLoop"));
}
if (ele.HasAttribute("encryptStringInLoop"))
{
rule.encryptStringInLoop = ConfigUtil.ParseBool(ele.GetAttribute("encryptStringInLoop"));
}
if (ele.HasAttribute("cacheConstInLoop"))
{
rule.cacheConstInLoop = ConfigUtil.ParseBool(ele.GetAttribute("cacheConstInLoop"));
}
if (ele.HasAttribute("cacheConstNotInLoop"))
{
rule.cacheConstNotInLoop = ConfigUtil.ParseBool(ele.GetAttribute("cacheConstNotInLoop"));
}
if (ele.HasAttribute("cacheStringInLoop"))
{
rule.cacheStringInLoop = ConfigUtil.ParseBool(ele.GetAttribute("cacheStringInLoop"));
}
if (ele.HasAttribute("cacheStringNotInLoop"))
{
rule.cacheStringNotInLoop = ConfigUtil.ParseBool(ele.GetAttribute("cacheStringNotInLoop"));
}
return rule;
}
private void ParseWhitelist(string configFile, XmlElement childEle)
{
string type = childEle.GetAttribute("type");
if (string.IsNullOrEmpty(type))
{
throw new Exception($"Invalid xml file, whitelist type is empty");
}
string value = childEle.InnerText;
switch (type)
{
case "int":
{
notEncryptInts.AddRange(value.Split(',').Select(s => int.Parse(s.Trim())));
break;
}
case "long":
{
notEncryptLongs.AddRange(value.Split(',').Select(s => long.Parse(s.Trim())));
break;
}
case "string":
{
notEncryptStrings.AddRange(value.Split(',').Select(s => s.Trim()));
break;
}
case "int-range":
{
var parts = value.Split(',');
if (parts.Length != 2)
{
throw new Exception($"Invalid xml file, int-range {value} is invalid");
}
notEncryptIntRanges.Add(new NumberRange<int>(ConfigUtil.ParseNullableInt(parts[0]), ConfigUtil.ParseNullableInt(parts[1])));
break;
}
case "long-range":
{
var parts = value.Split(',');
if (parts.Length != 2)
{
throw new Exception($"Invalid xml file, long-range {value} is invalid");
}
notEncryptLongRanges.Add(new NumberRange<long>(ConfigUtil.ParseNullableLong(parts[0]), ConfigUtil.ParseNullableLong(parts[1])));
break;
}
case "float-range":
{
var parts = value.Split(',');
if (parts.Length != 2)
{
throw new Exception($"Invalid xml file, float-range {value} is invalid");
}
notEncryptFloatRanges.Add(new NumberRange<float>(ConfigUtil.ParseNullableFloat(parts[0]), ConfigUtil.ParseNullableFloat(parts[1])));
break;
}
case "double-range":
{
var parts = value.Split(',');
if (parts.Length != 2)
{
throw new Exception($"Invalid xml file, double-range {value} is invalid");
}
notEncryptDoubleRanges.Add(new NumberRange<double>(ConfigUtil.ParseNullableDouble(parts[0]), ConfigUtil.ParseNullableDouble(parts[1])));
break;
}
case "string-length-range":
{
var parts = value.Split(',');
if (parts.Length != 2)
{
throw new Exception($"Invalid xml file, string-length-range {value} is invalid");
}
notEncryptStringLengthRanges.Add(new NumberRange<int>(ConfigUtil.ParseNullableInt(parts[0]), ConfigUtil.ParseNullableInt(parts[1])));
break;
}
case "array-length-range":
{
var parts = value.Split(',');
if (parts.Length != 2)
{
throw new Exception($"Invalid xml file, array-length-range {value} is invalid");
}
notEncryptArrayLengthRanges.Add(new NumberRange<int>(ConfigUtil.ParseNullableInt(parts[0]), ConfigUtil.ParseNullableInt(parts[1])));
break;
}
default: throw new Exception($"Invalid xml file, unknown whitelist type {type} in {childEle.Name} node");
}
}
private ObfuscationRule GetMethodObfuscationRule(MethodDef method)
{
if (!_methodRuleCache.TryGetValue(method, out var rule))
{
rule = _xmlParser.GetMethodRule(method, _global);
_methodRuleCache[method] = rule;
}
return rule;
}
public override bool NeedObfuscateMethod(MethodDef method)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
return rule.disableEncrypt != true;
}
public override ConstCachePolicy GetMethodConstCachePolicy(MethodDef method)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
return new ConstCachePolicy
{
cacheConstInLoop = rule.cacheConstInLoop.Value,
cacheConstNotInLoop = rule.cacheConstNotInLoop.Value,
cacheStringInLoop = rule.cacheStringInLoop.Value,
cacheStringNotInLoop = rule.cacheStringNotInLoop.Value,
};
}
public override bool NeedObfuscateInt(MethodDef method, bool currentInLoop, int value)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
if (rule.encryptInt == false)
{
return false;
}
if (currentInLoop && rule.encryptConstInLoop == false)
{
return false;
}
if (notEncryptInts.Contains(value))
{
return false;
}
foreach (var range in notEncryptIntRanges)
{
if (range.min != null && value < range.min)
{
continue;
}
if (range.max != null && value > range.max)
{
continue;
}
return false;
}
return true;
}
public override bool NeedObfuscateLong(MethodDef method, bool currentInLoop, long value)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
if (rule.encryptLong == false)
{
return false;
}
if (currentInLoop && rule.encryptConstInLoop == false)
{
return false;
}
if (notEncryptLongs.Contains(value))
{
return false;
}
foreach (var range in notEncryptLongRanges)
{
if (range.min != null && value < range.min)
{
continue;
}
if (range.max != null && value > range.max)
{
continue;
}
return false;
}
return true;
}
public override bool NeedObfuscateFloat(MethodDef method, bool currentInLoop, float value)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
if (rule.encryptFloat == false)
{
return false;
}
if (currentInLoop && rule.encryptConstInLoop == false)
{
return false;
}
foreach (var range in notEncryptFloatRanges)
{
if (range.min != null && value < range.min)
{
continue;
}
if (range.max != null && value > range.max)
{
continue;
}
return false;
}
return true;
}
public override bool NeedObfuscateDouble(MethodDef method, bool currentInLoop, double value)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
if (rule.encryptDouble == false)
{
return false;
}
if (currentInLoop && rule.encryptConstInLoop == false)
{
return false;
}
foreach (var range in notEncryptDoubleRanges)
{
if (range.min != null && value < range.min)
{
continue;
}
if (range.max != null && value > range.max)
{
continue;
}
return false;
}
return true;
}
public override bool NeedObfuscateString(MethodDef method, bool currentInLoop, string value)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
if (rule.encryptString == false)
{
return false;
}
if (currentInLoop && rule.encryptConstInLoop == false)
{
return false;
}
if (notEncryptStrings.Contains(value))
{
return false;
}
foreach (var range in notEncryptStringLengthRanges)
{
if (range.min != null && value.Length < range.min)
{
continue;
}
if (range.max != null && value.Length > range.max)
{
continue;
}
return false;
}
return true;
}
public override bool NeedObfuscateArray(MethodDef method, bool currentInLoop, byte[] array)
{
ObfuscationRule rule = GetMethodObfuscationRule(method);
if (rule.encryptArray == false)
{
return false;
}
if (currentInLoop && rule.encryptConstInLoop == false)
{
return false;
}
foreach (var range in notEncryptArrayLengthRanges)
{
if (range.min != null && array.Length < range.min)
{
continue;
}
if (range.max != null && array.Length > range.max)
{
continue;
}
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,166 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Emit;
using Obfuz.Settings;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.Assertions;
namespace Obfuz.ObfusPasses.ConstEncrypt
{
public class ConstEncryptPass : BasicBlockObfuscationPassBase
{
private readonly List<string> _configFiles;
private readonly int _encryptionLevel;
private IEncryptPolicy _dataObfuscatorPolicy;
private IConstEncryptor _dataObfuscator;
public override ObfuscationPassType Type => ObfuscationPassType.ConstEncrypt;
public ConstEncryptPass(ConstEncryptionSettings settings)
{
_configFiles = settings.ruleFiles.ToList();
_encryptionLevel = settings.encryptionLevel;
}
public override void Start()
{
var ctx = ObfuscationPassContext.Current;
_dataObfuscatorPolicy = new ConfigurableEncryptPolicy(ctx.assembliesToObfuscate, _configFiles);
_dataObfuscator = new DefaultConstEncryptor(ctx.encryptionScopeProvider, ctx.rvaDataAllocator, ctx.constFieldAllocator, ctx.moduleEntityManager, _encryptionLevel);
}
public override void Stop()
{
}
protected override bool NeedObfuscateMethod(MethodDef method)
{
return _dataObfuscatorPolicy.NeedObfuscateMethod(method);
}
protected override bool TryObfuscateInstruction(MethodDef method, Instruction inst, BasicBlock block, int instructionIndex, IList<Instruction> globalInstructions,
List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
{
bool currentInLoop = block.inLoop;
ConstCachePolicy constCachePolicy = _dataObfuscatorPolicy.GetMethodConstCachePolicy(method);
switch (inst.OpCode.OperandType)
{
case OperandType.InlineI:
case OperandType.InlineI8:
case OperandType.ShortInlineI:
case OperandType.ShortInlineR:
case OperandType.InlineR:
{
bool needCache = currentInLoop ? constCachePolicy.cacheConstInLoop : constCachePolicy.cacheConstNotInLoop;
object operand = inst.Operand;
if (operand is int)
{
int value = (int)operand;
if (_dataObfuscatorPolicy.NeedObfuscateInt(method, currentInLoop, value))
{
_dataObfuscator.ObfuscateInt(method, needCache, value, outputInstructions);
return true;
}
}
else if (operand is sbyte)
{
int value = (sbyte)operand;
if (_dataObfuscatorPolicy.NeedObfuscateInt(method, currentInLoop, value))
{
_dataObfuscator.ObfuscateInt(method, needCache, value, outputInstructions);
return true;
}
}
else if (operand is byte)
{
int value = (byte)operand;
if (_dataObfuscatorPolicy.NeedObfuscateInt(method, currentInLoop, value))
{
_dataObfuscator.ObfuscateInt(method, needCache, value, outputInstructions);
return true;
}
}
else if (operand is long)
{
long value = (long)operand;
if (_dataObfuscatorPolicy.NeedObfuscateLong(method, currentInLoop, value))
{
_dataObfuscator.ObfuscateLong(method, needCache, value, outputInstructions);
return true;
}
}
else if (operand is float)
{
float value = (float)operand;
if (_dataObfuscatorPolicy.NeedObfuscateFloat(method, currentInLoop, value))
{
_dataObfuscator.ObfuscateFloat(method, needCache, value, outputInstructions);
return true;
}
}
else if (operand is double)
{
double value = (double)operand;
if (_dataObfuscatorPolicy.NeedObfuscateDouble(method, currentInLoop, value))
{
_dataObfuscator.ObfuscateDouble(method, needCache, value, outputInstructions);
return true;
}
}
return false;
}
case OperandType.InlineString:
{
//RuntimeHelpers.InitializeArray
string value = (string)inst.Operand;
if (_dataObfuscatorPolicy.NeedObfuscateString(method, currentInLoop, value))
{
bool needCache = currentInLoop ? constCachePolicy.cacheStringInLoop : constCachePolicy.cacheStringNotInLoop;
_dataObfuscator.ObfuscateString(method, needCache, value, outputInstructions);
return true;
}
return false;
}
case OperandType.InlineMethod:
{
//if (((IMethod)inst.Operand).FullName == "System.Void System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(System.Array,System.RuntimeFieldHandle)")
//{
// Instruction prevInst = globalInstructions[instructionIndex - 1];
// if (prevInst.OpCode.Code == Code.Ldtoken)
// {
// IField rvaField = (IField)prevInst.Operand;
// FieldDef ravFieldDef = rvaField.ResolveFieldDefThrow();
// byte[] data = ravFieldDef.InitialValue;
// if (data != null && _dataObfuscatorPolicy.NeedObfuscateArray(method, currentInLoop, data))
// {
// if (_encryptedRvaFields.Add(ravFieldDef))
// {
// }
// // remove prev ldtoken instruction
// Assert.AreEqual(Code.Ldtoken, totalFinalInstructions.Last().OpCode.Code);
// //totalFinalInstructions.RemoveAt(totalFinalInstructions.Count - 1);
// // dup arr argument for decryption operation
// totalFinalInstructions.Insert(totalFinalInstructions.Count - 1, Instruction.Create(OpCodes.Dup));
// totalFinalInstructions.Add(inst.Clone());
// //bool needCache = currentInLoop ? constCachePolicy.cacheStringInLoop : constCachePolicy.cacheStringNotInLoop;
// bool needCache = false;
// _dataObfuscator.ObfuscateBytes(method, needCache, data, outputInstructions);
// return true;
// }
// }
//}
return false;
}
default: return false;
}
}
}
}

View File

@ -0,0 +1,204 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Emit;
using Obfuz.Data;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using NUnit.Framework;
using System.Text;
namespace Obfuz.ObfusPasses.ConstEncrypt
{
public class DefaultConstEncryptor : IConstEncryptor
{
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private readonly RvaDataAllocator _rvaDataAllocator;
private readonly ConstFieldAllocator _constFieldAllocator;
private readonly GroupByModuleEntityManager _moduleEntityManager;
private readonly int _encryptionLevel;
public DefaultConstEncryptor(EncryptionScopeProvider encryptionScopeProvider, RvaDataAllocator rvaDataAllocator, ConstFieldAllocator constFieldAllocator, GroupByModuleEntityManager moduleEntityManager, int encryptionLevel)
{
_encryptionScopeProvider = encryptionScopeProvider;
_rvaDataAllocator = rvaDataAllocator;
_constFieldAllocator = constFieldAllocator;
_moduleEntityManager = moduleEntityManager;
_encryptionLevel = encryptionLevel;
}
private IRandom CreateRandomForValue(EncryptionScopeInfo encryptionScope, int value)
{
return encryptionScope.localRandomCreator(value);
}
private int GenerateEncryptionOperations(EncryptionScopeInfo encryptionScope, IRandom random)
{
return EncryptionUtil.GenerateEncryptionOpCodes(random, encryptionScope.encryptor, _encryptionLevel);
}
public int GenerateSalt(IRandom random)
{
return random.NextInt();
}
private DefaultMetadataImporter GetModuleMetadataImporter(MethodDef method)
{
return _moduleEntityManager.GetDefaultModuleMetadataImporter(method.Module, _encryptionScopeProvider);
}
public void ObfuscateInt(MethodDef method, bool needCacheValue, int value, List<Instruction> obfuscatedInstructions)
{
if (needCacheValue)
{
FieldDef cacheField = _constFieldAllocator.Allocate(method.Module, value);
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
return;
}
EncryptionScopeInfo encryptionScope = _encryptionScopeProvider.GetScope(method.Module);
IRandom random = CreateRandomForValue(encryptionScope, value.GetHashCode());
int ops = GenerateEncryptionOperations(encryptionScope, random);
int salt = GenerateSalt(random);
int encryptedValue = encryptionScope.encryptor.Encrypt(value, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(method.Module, encryptedValue);
DefaultMetadataImporter importer = GetModuleMetadataImporter(method);
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(rvaData.offset));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(ops));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(salt));
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaInt));
}
public void ObfuscateLong(MethodDef method, bool needCacheValue, long value, List<Instruction> obfuscatedInstructions)
{
if (needCacheValue)
{
FieldDef cacheField = _constFieldAllocator.Allocate(method.Module, value);
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
return;
}
EncryptionScopeInfo encryptionScope = _encryptionScopeProvider.GetScope(method.Module);
IRandom random = CreateRandomForValue(encryptionScope, value.GetHashCode());
int ops = GenerateEncryptionOperations(encryptionScope, random);
int salt = GenerateSalt(random);
long encryptedValue = encryptionScope.encryptor.Encrypt(value, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(method.Module, encryptedValue);
DefaultMetadataImporter importer = GetModuleMetadataImporter(method);
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(rvaData.offset));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(ops));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(salt));
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaLong));
}
public void ObfuscateFloat(MethodDef method, bool needCacheValue, float value, List<Instruction> obfuscatedInstructions)
{
if (needCacheValue)
{
FieldDef cacheField = _constFieldAllocator.Allocate(method.Module, value);
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
return;
}
EncryptionScopeInfo encryptionScope = _encryptionScopeProvider.GetScope(method.Module);
IRandom random = CreateRandomForValue(encryptionScope, value.GetHashCode());
int ops = GenerateEncryptionOperations(encryptionScope, random);
int salt = GenerateSalt(random);
float encryptedValue = encryptionScope.encryptor.Encrypt(value, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(method.Module, encryptedValue);
DefaultMetadataImporter importer = GetModuleMetadataImporter(method);
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(rvaData.offset));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(ops));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(salt));
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaFloat));
}
public void ObfuscateDouble(MethodDef method, bool needCacheValue, double value, List<Instruction> obfuscatedInstructions)
{
if (needCacheValue)
{
FieldDef cacheField = _constFieldAllocator.Allocate(method.Module, value);
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
return;
}
EncryptionScopeInfo encryptionScope = _encryptionScopeProvider.GetScope(method.Module);
IRandom random = CreateRandomForValue(encryptionScope, value.GetHashCode());
int ops = GenerateEncryptionOperations(encryptionScope, random);
int salt = GenerateSalt(random);
double encryptedValue = encryptionScope.encryptor.Encrypt(value, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(method.Module, encryptedValue);
DefaultMetadataImporter importer = GetModuleMetadataImporter(method);
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(rvaData.offset));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(ops));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(salt));
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaDouble));
}
public void ObfuscateBytes(MethodDef method, bool needCacheValue, byte[] value, List<Instruction> obfuscatedInstructions)
{
throw new NotSupportedException("ObfuscateBytes is not supported yet.");
//if (needCacheValue)
//{
// FieldDef cacheField = _constFieldAllocator.Allocate(method.Module, value);
// obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
// return;
//}
//int ops = GenerateEncryptionOperations();
//int salt = GenerateSalt();
//byte[] encryptedValue = _encryptor.Encrypt(value, 0, value.Length, ops, salt);
//Assert.IsTrue(encryptedValue.Length % 4 == 0);
//RvaData rvaData = _rvaDataAllocator.Allocate(method.Module, encryptedValue);
//DefaultMetadataImporter importer = GetModuleMetadataImporter(method);
//obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
//obfuscatedInstructions.Add(Instruction.CreateLdcI4(rvaData.offset));
//// should use value.Length, can't use rvaData.size, because rvaData.size is align to 4, it's not the actual length.
//obfuscatedInstructions.Add(Instruction.CreateLdcI4(value.Length));
//obfuscatedInstructions.Add(Instruction.CreateLdcI4(ops));
//obfuscatedInstructions.Add(Instruction.CreateLdcI4(salt));
//obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaBytes));
}
public void ObfuscateString(MethodDef method, bool needCacheValue, string value, List<Instruction> obfuscatedInstructions)
{
if (needCacheValue)
{
FieldDef cacheField = _constFieldAllocator.Allocate(method.Module, value);
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, cacheField));
return;
}
EncryptionScopeInfo encryptionScope = _encryptionScopeProvider.GetScope(method.Module);
IRandom random = CreateRandomForValue(encryptionScope, value.GetHashCode());
int ops = GenerateEncryptionOperations(encryptionScope, random);
int salt = GenerateSalt(random);
int stringByteLength = Encoding.UTF8.GetByteCount(value);
byte[] encryptedValue = encryptionScope.encryptor.Encrypt(value, ops, salt);
Assert.IsTrue(encryptedValue.Length % 4 == 0);
RvaData rvaData = _rvaDataAllocator.Allocate(method.Module, encryptedValue);
DefaultMetadataImporter importer = GetModuleMetadataImporter(method);
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(rvaData.offset));
// should use stringByteLength, can't use rvaData.size, because rvaData.size is align to 4, it's not the actual length.
obfuscatedInstructions.Add(Instruction.CreateLdcI4(stringByteLength));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(ops));
obfuscatedInstructions.Add(Instruction.CreateLdcI4(salt));
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Call, importer.DecryptFromRvaString));
}
public void Done()
{
}
}
}

View File

@ -0,0 +1,32 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using System;
using System.Collections.Generic;
namespace Obfuz.ObfusPasses.ConstEncrypt
{
public interface IConstEncryptor
{
void ObfuscateInt(MethodDef method, bool needCacheValue, int value, List<Instruction> obfuscatedInstructions);
void ObfuscateLong(MethodDef method, bool needCacheValue, long value, List<Instruction> obfuscatedInstructions);
void ObfuscateFloat(MethodDef method, bool needCacheValue, float value, List<Instruction> obfuscatedInstructions);
void ObfuscateDouble(MethodDef method, bool needCacheValue, double value, List<Instruction> obfuscatedInstructions);
void ObfuscateString(MethodDef method, bool needCacheValue, string value, List<Instruction> obfuscatedInstructions);
void ObfuscateBytes(MethodDef method, bool needCacheValue, byte[] value, List<Instruction> obfuscatedInstructions);
}
public abstract class ConstEncryptorBase : IConstEncryptor
{
public abstract void ObfuscateBytes(MethodDef method, bool needCacheValue, byte[] value, List<Instruction> obfuscatedInstructions);
public abstract void ObfuscateDouble(MethodDef method, bool needCacheValue, double value, List<Instruction> obfuscatedInstructions);
public abstract void ObfuscateFloat(MethodDef method, bool needCacheValue, float value, List<Instruction> obfuscatedInstructions);
public abstract void ObfuscateInt(MethodDef method, bool needCacheValue, int value, List<Instruction> obfuscatedInstructions);
public abstract void ObfuscateLong(MethodDef method, bool needCacheValue, long value, List<Instruction> obfuscatedInstructions);
public abstract void ObfuscateString(MethodDef method, bool needCacheValue, string value, List<Instruction> obfuscatedInstructions);
}
}

View File

@ -0,0 +1,48 @@
using dnlib.DotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.ConstEncrypt
{
public struct ConstCachePolicy
{
public bool cacheConstInLoop;
public bool cacheConstNotInLoop;
public bool cacheStringInLoop;
public bool cacheStringNotInLoop;
}
public interface IEncryptPolicy
{
bool NeedObfuscateMethod(MethodDef method);
ConstCachePolicy GetMethodConstCachePolicy(MethodDef method);
bool NeedObfuscateInt(MethodDef method, bool currentInLoop, int value);
bool NeedObfuscateLong(MethodDef method, bool currentInLoop, long value);
bool NeedObfuscateFloat(MethodDef method, bool currentInLoop, float value);
bool NeedObfuscateDouble(MethodDef method, bool currentInLoop, double value);
bool NeedObfuscateString(MethodDef method, bool currentInLoop, string value);
bool NeedObfuscateArray(MethodDef method, bool currentInLoop, byte[] array);
}
public abstract class EncryptPolicyBase : IEncryptPolicy
{
public abstract bool NeedObfuscateMethod(MethodDef method);
public abstract ConstCachePolicy GetMethodConstCachePolicy(MethodDef method);
public abstract bool NeedObfuscateDouble(MethodDef method, bool currentInLoop, double value);
public abstract bool NeedObfuscateFloat(MethodDef method, bool currentInLoop, float value);
public abstract bool NeedObfuscateInt(MethodDef method, bool currentInLoop, int value);
public abstract bool NeedObfuscateLong(MethodDef method, bool currentInLoop, long value);
public abstract bool NeedObfuscateString(MethodDef method, bool currentInLoop, string value);
public abstract bool NeedObfuscateArray(MethodDef method, bool currentInLoop, byte[] array);
}
}

View File

@ -0,0 +1,35 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.ExprObfus
{
public class ExprObfusPass : InstructionObfuscationPassBase
{
public override ObfuscationPassType Type => ObfuscationPassType.ExprObfus;
public override void Start()
{
}
public override void Stop()
{
}
protected override bool NeedObfuscateMethod(MethodDef method)
{
return false;
}
protected override bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, IList<Instruction> instructions, int instructionIndex, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
{
return false;
}
}
}

View File

@ -0,0 +1,36 @@
using dnlib.DotNet;
using Obfuz.Conf;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Xml;
namespace Obfuz.ObfusPasses.FieldEncrypt
{
public class ConfigurableEncryptPolicy : EncryptPolicyBase
{
class ObfuscationRule
{
}
private readonly XmlFieldRuleParser<ObfuscationRule> _configParser;
public ConfigurableEncryptPolicy(List<string> toObfuscatedAssemblyNames, List<string> configFiles)
{
_configParser = new XmlFieldRuleParser<ObfuscationRule>(toObfuscatedAssemblyNames, ParseRule, null);
_configParser.LoadConfigs(configFiles);
}
private ObfuscationRule ParseRule(string configFile, XmlElement ele)
{
return new ObfuscationRule();
}
public override bool NeedEncrypt(FieldDef field)
{
var rule = _configParser.GetFieldRule(field);
return rule != null;
}
}
}

View File

@ -0,0 +1,182 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Emit;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using UnityEngine.Assertions;
namespace Obfuz.ObfusPasses.FieldEncrypt
{
public class DefaultFieldEncryptor : FieldEncryptorBase
{
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private readonly GroupByModuleEntityManager _moduleEntityManager;
private readonly int _encryptionLevel;
public DefaultFieldEncryptor(EncryptionScopeProvider encryptionScopeProvider, GroupByModuleEntityManager moduleEntityManager, int encryptionLevel)
{
_encryptionScopeProvider = encryptionScopeProvider;
_moduleEntityManager = moduleEntityManager;
_encryptionLevel = encryptionLevel;
}
private DefaultMetadataImporter GetMetadataImporter(MethodDef method)
{
return _moduleEntityManager.GetDefaultModuleMetadataImporter(method.Module, _encryptionScopeProvider);
}
class FieldEncryptInfo
{
public int encryptOps;
public int salt;
public ElementType fieldType;
public long xorValueForZero;
}
private readonly Dictionary<FieldDef, FieldEncryptInfo> _fieldEncryptInfoCache = new Dictionary<FieldDef, FieldEncryptInfo>();
private long CalcXorValueForZero(IEncryptor encryptor, ElementType type, int encryptOps, int salt)
{
switch (type)
{
case ElementType.I4:
case ElementType.U4:
case ElementType.R4:
return encryptor.Encrypt(0, encryptOps, salt);
case ElementType.I8:
case ElementType.U8:
case ElementType.R8:
return encryptor.Encrypt(0L, encryptOps, salt);
default:
throw new NotSupportedException($"Unsupported field type: {type} for encryption");
}
}
private IRandom CreateRandomForField(RandomCreator randomCreator, FieldDef field)
{
return randomCreator(FieldEqualityComparer.CompareDeclaringTypes.GetHashCode(field));
}
private int GenerateEncryptionOperations(IRandom random, IEncryptor encryptor)
{
return EncryptionUtil.GenerateEncryptionOpCodes(random, encryptor, _encryptionLevel);
}
public int GenerateSalt(IRandom random)
{
return random.NextInt();
}
private FieldEncryptInfo GetFieldEncryptInfo(FieldDef field)
{
if (_fieldEncryptInfoCache.TryGetValue(field, out var info))
{
return info;
}
EncryptionScopeInfo encryptionScope = _encryptionScopeProvider.GetScope(field.Module);
IRandom random = CreateRandomForField(encryptionScope.localRandomCreator, field);
IEncryptor encryptor = encryptionScope.encryptor;
int encryptOps = GenerateEncryptionOperations(random, encryptor);
int salt = GenerateSalt(random);
ElementType fieldType = field.FieldSig.Type.RemovePinnedAndModifiers().ElementType;
long xorValueForZero = CalcXorValueForZero(encryptor, fieldType, encryptOps, salt);
info = new FieldEncryptInfo
{
encryptOps = encryptOps,
salt = salt,
fieldType = fieldType,
xorValueForZero = xorValueForZero,
};
_fieldEncryptInfoCache[field] = info;
return info;
}
public override void Encrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction)
{
DefaultMetadataImporter importer = GetMetadataImporter(method);
EncryptionServiceMetadataImporter encryptionServiceMetadataImporter = importer.GetEncryptionServiceMetadataImporterOfModule(field.Module);
FieldEncryptInfo fei = GetFieldEncryptInfo(field);
if (fei.fieldType == ElementType.I4 || fei.fieldType == ElementType.U4 || fei.fieldType == ElementType.R4)
{
// value has been put on stack
// encrypt
outputInstructions.Add(Instruction.CreateLdcI4(fei.encryptOps));
outputInstructions.Add(Instruction.CreateLdcI4(fei.salt));
outputInstructions.Add(Instruction.Create(OpCodes.Call, encryptionServiceMetadataImporter.EncryptInt));
// xor
outputInstructions.Add(Instruction.CreateLdcI4((int)fei.xorValueForZero));
outputInstructions.Add(Instruction.Create(OpCodes.Xor));
}
else if (fei.fieldType == ElementType.I8 || fei.fieldType == ElementType.U8 || fei.fieldType == ElementType.R8)
{
// value has been put on stack
// encrypt
outputInstructions.Add(Instruction.CreateLdcI4(fei.encryptOps));
outputInstructions.Add(Instruction.CreateLdcI4(fei.salt));
outputInstructions.Add(Instruction.Create(OpCodes.Call, encryptionServiceMetadataImporter.EncryptLong));
// xor
outputInstructions.Add(Instruction.Create(OpCodes.Ldc_I8, fei.xorValueForZero));
outputInstructions.Add(Instruction.Create(OpCodes.Xor));
}
else
{
Assert.IsTrue(false, $"Unsupported field type: {fei.fieldType} for encryption");
}
outputInstructions.Add(currentInstruction.Clone());
}
public override void Decrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction)
{
outputInstructions.Add(currentInstruction.Clone());
DefaultMetadataImporter importer = GetMetadataImporter(method);
EncryptionServiceMetadataImporter encryptionServiceMetadataImporter = importer.GetEncryptionServiceMetadataImporterOfModule(field.Module);
FieldEncryptInfo fei = GetFieldEncryptInfo(field);
if (fei.fieldType == ElementType.I4 || fei.fieldType == ElementType.U4 || fei.fieldType == ElementType.R4)
{
// value has been put on stack
// xor
if (fei.fieldType == ElementType.R4)
{
outputInstructions.Add(Instruction.Create(OpCodes.Call, importer.CastFloatAsInt));
}
outputInstructions.Add(Instruction.CreateLdcI4((int)fei.xorValueForZero));
outputInstructions.Add(Instruction.Create(OpCodes.Xor));
// decrypt
outputInstructions.Add(Instruction.CreateLdcI4(fei.encryptOps));
outputInstructions.Add(Instruction.CreateLdcI4(fei.salt));
outputInstructions.Add(Instruction.Create(OpCodes.Call, encryptionServiceMetadataImporter.DecryptInt));
}
else if (fei.fieldType == ElementType.I8 || fei.fieldType == ElementType.U8 || fei.fieldType == ElementType.R8)
{
// value has been put on stack
// xor
if (fei.fieldType == ElementType.R8)
{
outputInstructions.Add(Instruction.Create(OpCodes.Call, importer.CastDoubleAsLong));
}
outputInstructions.Add(Instruction.Create(OpCodes.Ldc_I8, fei.xorValueForZero));
outputInstructions.Add(Instruction.Create(OpCodes.Xor));
// decrypt
outputInstructions.Add(Instruction.CreateLdcI4(fei.encryptOps));
outputInstructions.Add(Instruction.CreateLdcI4(fei.salt));
outputInstructions.Add(Instruction.Create(OpCodes.Call, encryptionServiceMetadataImporter.DecryptLong));
}
else
{
Assert.IsTrue(false, $"Unsupported field type: {fei.fieldType} for decryption");
}
}
}
}

View File

@ -0,0 +1,108 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz;
using Obfuz.Settings;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Obfuz.ObfusPasses.FieldEncrypt
{
public class FieldEncryptPass : InstructionObfuscationPassBase
{
private readonly List<string> _configFiles;
private readonly int _encryptionLevel;
private IEncryptPolicy _encryptionPolicy;
private IFieldEncryptor _memoryEncryptor;
public override ObfuscationPassType Type => ObfuscationPassType.FieldEncrypt;
public FieldEncryptPass(FieldEncryptionSettings settings)
{
_configFiles = settings.ruleFiles.ToList();
_encryptionLevel = settings.encryptionLevel;
}
protected override bool ForceProcessAllAssembliesAndIgnoreAllPolicy => true;
public override void Start()
{
var ctx = ObfuscationPassContext.Current;
_memoryEncryptor = new DefaultFieldEncryptor(ctx.encryptionScopeProvider, ctx.moduleEntityManager, _encryptionLevel);
_encryptionPolicy = new ConfigurableEncryptPolicy(ctx.assembliesToObfuscate, _configFiles);
}
public override void Stop()
{
}
protected override bool NeedObfuscateMethod(MethodDef method)
{
return true;
}
private bool IsSupportedFieldType(TypeSig type)
{
type = type.RemovePinnedAndModifiers();
switch (type.ElementType)
{
case ElementType.I4:
case ElementType.I8:
case ElementType.U4:
case ElementType.U8:
case ElementType.R4:
case ElementType.R8:
return true;
default: return false;
}
}
protected override bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, IList<Instruction> instructions, int instructionIndex, List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions)
{
Code code = inst.OpCode.Code;
if (!(inst.Operand is IField field) || !field.IsField)
{
return false;
}
FieldDef fieldDef = field.ResolveFieldDefThrow();
if (!IsSupportedFieldType(fieldDef.FieldSig.Type) || !_encryptionPolicy.NeedEncrypt(fieldDef))
{
return false;
}
switch (code)
{
case Code.Ldfld:
{
_memoryEncryptor.Decrypt(callingMethod, fieldDef, outputInstructions, inst);
break;
}
case Code.Stfld:
{
_memoryEncryptor.Encrypt(callingMethod, fieldDef, outputInstructions, inst);
break;
}
case Code.Ldsfld:
{
_memoryEncryptor.Decrypt(callingMethod, fieldDef, outputInstructions, inst);
break;
}
case Code.Stsfld:
{
_memoryEncryptor.Encrypt(callingMethod, fieldDef, outputInstructions, inst);
break;
}
case Code.Ldflda:
case Code.Ldsflda:
{
throw new System.Exception($"You shouldn't get reference to memory encryption field: {field}");
}
default: return false;
}
//Debug.Log($"memory encrypt field: {field}");
return true;
}
}
}

View File

@ -0,0 +1,19 @@
using dnlib.DotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.FieldEncrypt
{
public interface IEncryptPolicy
{
bool NeedEncrypt(FieldDef field);
}
public abstract class EncryptPolicyBase : IEncryptPolicy
{
public abstract bool NeedEncrypt(FieldDef field);
}
}

View File

@ -0,0 +1,29 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.FieldEncrypt
{
public class MemoryEncryptionContext
{
public ModuleDef module;
public Instruction currentInstruction;
}
public interface IFieldEncryptor
{
void Encrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction);
void Decrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction);
}
public abstract class FieldEncryptorBase : IFieldEncryptor
{
public abstract void Encrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction);
public abstract void Decrypt(MethodDef method, FieldDef field, List<Instruction> outputInstructions, Instruction currentInstruction);
}
}

View File

@ -0,0 +1,85 @@
using dnlib.DotNet.Emit;
using dnlib.DotNet;
using System.Collections.Generic;
using System.Linq;
namespace Obfuz.ObfusPasses
{
public abstract class InstructionObfuscationPassBase : ObfuscationPassBase
{
protected virtual bool ForceProcessAllAssembliesAndIgnoreAllPolicy => false;
protected abstract bool NeedObfuscateMethod(MethodDef method);
public override void Process()
{
var ctx = ObfuscationPassContext.Current;
var modules = ForceProcessAllAssembliesAndIgnoreAllPolicy ? ctx.allObfuscationRelativeModules : ctx.modulesToObfuscate;
ObfuscationMethodWhitelist whiteList = ctx.whiteList;
ConfigurablePassPolicy passPolicy = ctx.passPolicy;
foreach (ModuleDef mod in modules)
{
if (!ForceProcessAllAssembliesAndIgnoreAllPolicy && (whiteList.IsInWhiteList(mod) || !Support(passPolicy.GetAssemblyObfuscationPasses(mod))))
{
continue;
}
// ToArray to avoid modify list exception
foreach (TypeDef type in mod.GetTypes().ToArray())
{
if (!ForceProcessAllAssembliesAndIgnoreAllPolicy && (whiteList.IsInWhiteList(type) || !Support(passPolicy.GetTypeObfuscationPasses(type))))
{
continue;
}
// ToArray to avoid modify list exception
foreach (MethodDef method in type.Methods.ToArray())
{
if (!method.HasBody || (!ForceProcessAllAssembliesAndIgnoreAllPolicy && (ctx.whiteList.IsInWhiteList(method) || !Support(passPolicy.GetMethodObfuscationPasses(method)) || !NeedObfuscateMethod(method))))
{
continue;
}
// TODO if isGeneratedBy Obfuscator, continue
ObfuscateData(method);
}
}
}
}
protected abstract bool TryObfuscateInstruction(MethodDef callingMethod, Instruction inst, IList<Instruction> instructions, int instructionIndex,
List<Instruction> outputInstructions, List<Instruction> totalFinalInstructions);
private void ObfuscateData(MethodDef method)
{
IList<Instruction> instructions = method.Body.Instructions;
var outputInstructions = new List<Instruction>();
var totalFinalInstructions = new List<Instruction>();
for (int i = 0; i < instructions.Count; i++)
{
Instruction inst = instructions[i];
outputInstructions.Clear();
if (TryObfuscateInstruction(method, inst, instructions, i, outputInstructions, totalFinalInstructions))
{
// current instruction may be the target of control flow instruction, so we can't remove it directly.
// we replace it with nop now, then remove it in CleanUpInstructionPass
inst.OpCode = outputInstructions[0].OpCode;
inst.Operand = outputInstructions[0].Operand;
totalFinalInstructions.Add(inst);
for (int k = 1; k < outputInstructions.Count; k++)
{
totalFinalInstructions.Add(outputInstructions[k]);
}
}
else
{
totalFinalInstructions.Add(inst);
}
}
instructions.Clear();
foreach (var obInst in totalFinalInstructions)
{
instructions.Add(obInst);
}
}
}
}

View File

@ -0,0 +1,20 @@
using Obfuz.Emit;
namespace Obfuz.ObfusPasses
{
public abstract class ObfuscationPassBase : IObfuscationPass
{
public abstract ObfuscationPassType Type { get; }
public bool Support(ObfuscationPassType passType)
{
return passType.HasFlag(Type);
}
public abstract void Start();
public abstract void Stop();
public abstract void Process();
}
}

View File

@ -0,0 +1,25 @@
using System;
namespace Obfuz.ObfusPasses
{
[Flags]
public enum ObfuscationPassType
{
None = 0,
ConstEncrypt = 0x1,
FieldEncrypt = 0x2,
SymbolObfus = 0x100,
CallObfus = 0x200,
ExprObfus = 0x400,
ControlFlowObfus = 0x800,
AllObfus = SymbolObfus | CallObfus | ExprObfus | ControlFlowObfus,
AllEncrypt = ConstEncrypt | FieldEncrypt,
MethodBodyObfusOrEncrypt = ConstEncrypt | CallObfus | ExprObfus | ControlFlowObfus,
All = ~0,
}
}

View File

@ -0,0 +1,35 @@
using dnlib.DotNet;
namespace Obfuz.ObfusPasses.SymbolObfus
{
public interface INameMaker
{
void AddPreservedName(TypeDef typeDef, string name);
void AddPreservedNamespace(TypeDef typeDef, string name);
void AddPreservedName(MethodDef methodDef, string name);
void AddPreservedName(ParamDef paramDef, string name);
void AddPreservedName(FieldDef fieldDef, string name);
void AddPreservedName(PropertyDef propertyDef, string name);
void AddPreservedName(EventDef eventDef, string name);
string GetNewName(TypeDef typeDef, string originalName);
string GetNewNamespace(TypeDef typeDef, string originalNamespace, bool reuse);
string GetNewName(MethodDef methodDef, string originalName);
string GetNewName(ParamDef param, string originalName);
string GetNewName(FieldDef fieldDef, string originalName);
string GetNewName(PropertyDef propertyDef, string originalName);
string GetNewName(EventDef eventDef, string originalName);
}
}

View File

@ -0,0 +1,19 @@
using dnlib.DotNet;
namespace Obfuz.ObfusPasses.SymbolObfus
{
public interface IObfuscationPolicy
{
bool NeedRename(TypeDef typeDef);
bool NeedRename(MethodDef methodDef);
bool NeedRename(FieldDef fieldDef);
bool NeedRename(PropertyDef propertyDef);
bool NeedRename(EventDef eventDef);
bool NeedRename(ParamDef paramDef);
}
}

View File

@ -0,0 +1,29 @@
using dnlib.DotNet;
using System.Text;
namespace Obfuz.ObfusPasses.SymbolObfus.NameMakers
{
public class DebugNameMaker : NameMakerBase
{
private class TestNameScope : NameScopeBase
{
private int _nextIndex;
protected override void BuildNewName(StringBuilder nameBuilder, string originalName, string lastName)
{
if (string.IsNullOrEmpty(lastName))
{
nameBuilder.Append($"${originalName}");
}
else
{
nameBuilder.Append($"${originalName}{_nextIndex++}");
}
}
}
protected override INameScope CreateNameScope()
{
return new TestNameScope();
}
}
}

View File

@ -0,0 +1,9 @@
namespace Obfuz.ObfusPasses.SymbolObfus.NameMakers
{
public interface INameScope
{
void AddPreservedName(string name);
string GetNewName(string originalName, bool reuse);
}
}

View File

@ -0,0 +1,108 @@
using dnlib.DotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.SymbolObfus.NameMakers
{
public abstract class NameMakerBase : INameMaker
{
private readonly Dictionary<object, INameScope> _nameScopes = new Dictionary<object, INameScope>();
private readonly object _namespaceScope = new object();
protected abstract INameScope CreateNameScope();
protected INameScope GetNameScope(object key)
{
if (!_nameScopes.TryGetValue(key, out var nameScope))
{
nameScope = CreateNameScope();
_nameScopes[key] = nameScope;
}
return nameScope;
}
public void AddPreservedName(TypeDef typeDef, string name)
{
GetNameScope(typeDef.Module).AddPreservedName(name);
}
public void AddPreservedName(MethodDef methodDef, string name)
{
GetNameScope(methodDef.DeclaringType).AddPreservedName(name);
}
public void AddPreservedName(ParamDef paramDef, string name)
{
}
public void AddPreservedName(FieldDef fieldDef, string name)
{
GetNameScope(fieldDef.DeclaringType).AddPreservedName(name);
}
public void AddPreservedName(PropertyDef propertyDef, string name)
{
GetNameScope(propertyDef.DeclaringType).AddPreservedName(name);
}
public void AddPreservedName(EventDef eventDef, string name)
{
GetNameScope(eventDef.DeclaringType).AddPreservedName(name);
}
public void AddPreservedNamespace(TypeDef typeDef, string name)
{
GetNameScope(_namespaceScope).AddPreservedName(name);
}
private string GetDefaultNewName(object scope, string originName)
{
return GetNameScope(scope).GetNewName(originName, false);
}
public string GetNewNamespace(TypeDef typeDef, string originalNamespace, bool reuse)
{
if (string.IsNullOrEmpty(originalNamespace))
{
return string.Empty;
}
return GetNameScope(_namespaceScope).GetNewName(originalNamespace, reuse);
}
public string GetNewName(TypeDef typeDef, string originalName)
{
return GetDefaultNewName(typeDef.Module, originalName);
}
public string GetNewName(MethodDef methodDef, string originalName)
{
return GetDefaultNewName(methodDef.DeclaringType, originalName);
}
public virtual string GetNewName(ParamDef param, string originalName)
{
return "1";
}
public string GetNewName(FieldDef fieldDef, string originalName)
{
return GetDefaultNewName(fieldDef.DeclaringType, originalName);
}
public string GetNewName(PropertyDef propertyDef, string originalName)
{
return GetDefaultNewName(propertyDef.DeclaringType, originalName);
}
public string GetNewName(EventDef eventDef, string originalName)
{
return GetDefaultNewName(eventDef.DeclaringType, originalName);
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.SymbolObfus.NameMakers
{
public static class NameMakerFactory
{
public static INameMaker CreateDebugNameMaker()
{
return new DebugNameMaker();
}
public static INameMaker CreateNameMakerBaseASCIICharSet(string namePrefix)
{
var words = new List<string>();
for (int i = 0; i < 26; i++)
{
words.Add(((char)('a' + i)).ToString());
words.Add(((char)('A' + i)).ToString());
}
return new WordSetNameMaker(namePrefix, words);
}
}
}

View File

@ -0,0 +1,35 @@
using Microsoft.SqlServer.Server;
using System.Collections.Generic;
using System.Text;
namespace Obfuz.ObfusPasses.SymbolObfus.NameMakers
{
public class NameScope : NameScopeBase
{
private readonly string _namePrefix;
private readonly List<string> _wordSet;
private int _nextIndex;
public NameScope(string namePrefix, List<string> wordSet)
{
_namePrefix = namePrefix;
_wordSet = wordSet;
_nextIndex = 0;
}
protected override void BuildNewName(StringBuilder nameBuilder, string originalName, string lastName)
{
nameBuilder.Append(_namePrefix);
for (int i = _nextIndex++; ;)
{
nameBuilder.Append(_wordSet[i % _wordSet.Count]);
i = i / _wordSet.Count;
if (i == 0)
{
break;
}
}
}
}
}

View File

@ -0,0 +1,57 @@
using System.Collections.Generic;
using System.Text;
namespace Obfuz.ObfusPasses.SymbolObfus.NameMakers
{
public abstract class NameScopeBase : INameScope
{
private readonly Dictionary<string, string> _nameMap = new Dictionary<string, string>();
private readonly HashSet<string> _preservedNames = new HashSet<string>();
public void AddPreservedName(string name)
{
if (!string.IsNullOrEmpty(name))
{
_preservedNames.Add(name);
}
}
protected abstract void BuildNewName(StringBuilder nameBuilder, string originalName, string lastName);
private string CreateNewName(string originalName)
{
var nameBuilder = new StringBuilder();
string lastName = null;
while (true)
{
nameBuilder.Clear();
BuildNewName(nameBuilder, originalName, lastName);
string newName = nameBuilder.ToString();
lastName = newName;
if (_preservedNames.Add(newName))
{
return newName;
}
}
}
public string GetNewName(string originalName, bool reuse)
{
if (!reuse)
{
return CreateNewName(originalName);
}
if (_nameMap.TryGetValue(originalName, out var newName))
{
return newName;
}
newName = CreateNewName(originalName);
_nameMap[originalName] = newName;
return newName;
}
}
}

View File

@ -0,0 +1,26 @@
using dnlib.DotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.SymbolObfus.NameMakers
{
public class WordSetNameMaker : NameMakerBase
{
private readonly string _namePrefix;
private readonly List<string> _wordSet;
public WordSetNameMaker(string namePrefix, List<string> wordSet)
{
_namePrefix = namePrefix;
_wordSet = wordSet;
}
protected override INameScope CreateNameScope()
{
return new NameScope(_namePrefix, _wordSet);
}
}
}

View File

@ -0,0 +1,78 @@
using dnlib.DotNet;
using System.Collections.Generic;
using System.Linq;
namespace Obfuz.ObfusPasses.SymbolObfus.Policies
{
public class CacheRenamePolicy : ObfuscationPolicyBase
{
private readonly IObfuscationPolicy _underlyingPolicy;
private readonly Dictionary<object, bool> _computeCache = new Dictionary<object, bool>();
public CacheRenamePolicy(IObfuscationPolicy underlyingPolicy)
{
_underlyingPolicy = underlyingPolicy;
}
public override bool NeedRename(TypeDef typeDef)
{
if (!_computeCache.TryGetValue(typeDef, out var value))
{
value = _underlyingPolicy.NeedRename(typeDef);
_computeCache[typeDef] = value;
}
return value;
}
public override bool NeedRename(MethodDef methodDef)
{
if (!_computeCache.TryGetValue(methodDef, out var value))
{
value = _underlyingPolicy.NeedRename(methodDef);
_computeCache[methodDef] = value;
}
return value;
}
public override bool NeedRename(FieldDef fieldDef)
{
if (!_computeCache.TryGetValue(fieldDef, out var value))
{
value = _underlyingPolicy.NeedRename(fieldDef);
_computeCache[fieldDef] = value;
}
return value;
}
public override bool NeedRename(PropertyDef propertyDef)
{
if (!_computeCache.TryGetValue(propertyDef, out var value))
{
value = _underlyingPolicy.NeedRename(propertyDef);
_computeCache[propertyDef] = value;
}
return value;
}
public override bool NeedRename(EventDef eventDef)
{
if (!_computeCache.TryGetValue(eventDef, out var value))
{
value = _underlyingPolicy.NeedRename(eventDef);
_computeCache[eventDef] = value;
}
return value;
}
public override bool NeedRename(ParamDef paramDef)
{
if (!_computeCache.TryGetValue(paramDef, out var value))
{
value = _underlyingPolicy.NeedRename(paramDef);
_computeCache[paramDef] = value;
}
return value;
}
}
}

View File

@ -0,0 +1,45 @@
using dnlib.DotNet;
using System.Linq;
namespace Obfuz.ObfusPasses.SymbolObfus.Policies
{
public class CombineRenamePolicy : IObfuscationPolicy
{
private readonly IObfuscationPolicy[] _policies;
public CombineRenamePolicy(params IObfuscationPolicy[] policies)
{
_policies = policies;
}
public bool NeedRename(TypeDef typeDef)
{
return _policies.All(policy => policy.NeedRename(typeDef));
}
public bool NeedRename(MethodDef methodDef)
{
return _policies.All(policy => policy.NeedRename(methodDef));
}
public bool NeedRename(FieldDef fieldDef)
{
return _policies.All(policy => policy.NeedRename(fieldDef));
}
public bool NeedRename(PropertyDef propertyDef)
{
return _policies.All(policy => policy.NeedRename(propertyDef));
}
public bool NeedRename(EventDef eventDef)
{
return _policies.All(policy => policy.NeedRename(eventDef));
}
public bool NeedRename(ParamDef paramDef)
{
return _policies.All(policy => policy.NeedRename(paramDef));
}
}
}

View File

@ -0,0 +1,895 @@
using dnlib.DotNet;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using UnityEngine;
namespace Obfuz.ObfusPasses.SymbolObfus.Policies
{
public class ConfigurableRenamePolicy : ObfuscationPolicyBase
{
enum ObfuscationType
{
Name = 1,
Namespace = 2,
NestType = 3,
Method = 4,
Field = 5,
Property = 6,
Event = 7,
Param = 8,
MethodBody = 9,
Getter = 10,
Setter = 11,
Add = 12,
Remove = 13,
Fire = 14,
}
enum RuleType
{
Assembly = 1,
Type = 2,
Method = 3,
Field = 4,
Property = 5,
Event = 6,
}
enum ModifierType
{
Private = 0,
Protected = 1,
Public = 2,
}
interface IRule
{
}
class MethodRuleSpec
{
public NameMatcher nameMatcher;
public ModifierType modifierType;
public MethodRule rule;
}
class MethodRule : IRule
{
public string ruleName;
public bool obfuscateName;
public bool obfuscateParam;
public bool obfuscateBody;
}
class FieldRuleSpec
{
public NameMatcher nameMatcher;
public ModifierType modifierType;
public FieldRule rule;
}
class FieldRule : IRule
{
public string ruleName;
public bool obfuscateName;
}
class PropertyRuleSpec
{
public NameMatcher nameMatcher;
public PropertyRule rule;
}
class PropertyRule : IRule
{
public string ruleName;
public bool obfuscateName;
public MethodRuleSpec getter;
public MethodRuleSpec setter;
}
class EventRuleSpec
{
public NameMatcher nameMatcher;
public EventRule rule;
}
class EventRule : IRule
{
public string ruleName;
public bool obfuscateName;
public MethodRuleSpec add;
public MethodRuleSpec remove;
public MethodRuleSpec fire;
}
class TypeRuleSpec
{
public NameMatcher nameMatcher;
public ModifierType modifierType;
public TypeRule rule;
}
class TypeRule : IRule
{
public string ruleName;
public bool obfuscateName;
public bool obfuscateNamespace;
//public List<TypeRuleSpec> nestTypeRuleSpecs;
public List<FieldRuleSpec> fieldRuleSpecs;
public List<MethodRuleSpec> methodRuleSpecs;
public List<PropertyRuleSpec> propertyRuleSpecs;
public List<EventRuleSpec> eventRuleSpecs;
}
class AssemblyRule : IRule
{
public string ruleName;
public bool obfuscateName;
public List<TypeRuleSpec> typeRuleSpecs;
}
class AssemblyRuleSpec
{
public string assemblyName;
public AssemblyRule rule;
}
//class DefaultRule : IRule
//{
//}
//private readonly static IRule _defaultRule = new DefaultRule();
//private readonly static IRule _noneRule = new DefaultRule();
private readonly Dictionary<(string, RuleType), IRule> _rules = new Dictionary<(string, RuleType), IRule>();
private readonly Dictionary<(string, RuleType), XmlElement> _rawRuleElements = new Dictionary<(string, RuleType), XmlElement>();
private readonly Dictionary<string, AssemblyRuleSpec> _assemblyRuleSpecs = new Dictionary<string, AssemblyRuleSpec>();
private static readonly MethodRule s_noneMethodRule = new MethodRule
{
ruleName = "none",
obfuscateName = false,
obfuscateParam = false,
obfuscateBody = false,
};
private static readonly EventRule s_noneEventRule = new EventRule
{
ruleName = "none",
obfuscateName = false,
add = new MethodRuleSpec { rule = s_noneMethodRule},
remove = new MethodRuleSpec { rule = s_noneMethodRule },
fire = new MethodRuleSpec { rule = s_noneMethodRule },
};
private static readonly PropertyRule s_nonePropertyRule = new PropertyRule
{
ruleName = "none",
obfuscateName = false,
getter = new MethodRuleSpec { rule = s_noneMethodRule },
setter = new MethodRuleSpec { rule = s_noneMethodRule },
};
private static readonly FieldRule s_noneFieldRule = new FieldRule
{
ruleName = "none",
obfuscateName = false,
};
private static readonly TypeRule s_noneTypeRule = new TypeRule
{
ruleName = "none",
obfuscateName = false,
obfuscateNamespace = false,
//nestTypeRuleSpecs = new List<TypeRuleSpec>(),
fieldRuleSpecs = new List<FieldRuleSpec> { new FieldRuleSpec { rule = s_noneFieldRule} },
methodRuleSpecs = new List<MethodRuleSpec> { new MethodRuleSpec { rule = s_noneMethodRule} },
propertyRuleSpecs = new List<PropertyRuleSpec> { new PropertyRuleSpec { rule = s_nonePropertyRule} },
eventRuleSpecs = new List<EventRuleSpec> { new EventRuleSpec { rule = s_noneEventRule} },
};
private static readonly AssemblyRule s_noneAssemblyRule = new AssemblyRule
{
ruleName = "none",
obfuscateName = false,
typeRuleSpecs = new List<TypeRuleSpec> { new TypeRuleSpec { rule = s_noneTypeRule } },
};
//static ObfuscateRuleConfig()
//{
// s_noneTypeRule.nestTypeRuleSpecs.Add(new TypeRuleSpec
// {
// rule = s_noneTypeRule,
// });
//}
private IRule GetOrParseRule(string ruleName, RuleType ruleType, XmlElement ele)
{
IRule rule = null;
XmlElement element;
if (!string.IsNullOrEmpty(ruleName))
{
if (ruleName == "none")
{
switch (ruleType)
{
case RuleType.Assembly: return s_noneAssemblyRule;
case RuleType.Type: return s_noneTypeRule;
case RuleType.Method: return s_noneMethodRule;
case RuleType.Field: return s_noneFieldRule;
case RuleType.Property: return s_nonePropertyRule;
case RuleType.Event: return s_noneEventRule;
default: throw new Exception($"Invalid rule type {ruleType}");
}
}
if (_rules.TryGetValue((ruleName, ruleType), out rule))
{
return rule;
}
if (!_rawRuleElements.TryGetValue((ruleName, ruleType), out element))
{
throw new Exception($"Invalid xml file, rule {ruleName} type {ruleType} not found");
}
}
else
{
element = ele;
}
switch (ruleType)
{
case RuleType.Assembly:
rule = ParseAssemblyRule(ruleName, element);
break;
case RuleType.Type:
rule = ParseTypeRule(ruleName, element);
break;
case RuleType.Method:
rule = ParseMethodRule(ruleName, element);
break;
case RuleType.Field:
rule = ParseFieldRule(ruleName, element);
break;
case RuleType.Property:
rule = ParsePropertyRule(ruleName, element);
break;
case RuleType.Event:
rule = ParseEventRule(ruleName, element);
break;
default:
throw new Exception($"Invalid rule type {ruleType}");
}
if (!string.IsNullOrEmpty(ruleName))
{
_rules.Add((ruleName, ruleType), rule);
}
return rule;
}
private static bool ParseBoolNoneOrFalse(string str)
{
if (string.IsNullOrEmpty(str))
{
return true;
}
switch (str.ToLowerInvariant())
{
case "1":
case "true": throw new Exception($"Invalid bool value '{str}', only accept '0' or 'false' because default is true"); // true is default
case "0":
case "false": return false;
default: throw new Exception($"Invalid bool value {str}");
}
}
private AssemblyRule ParseAssemblyRule(string ruleName, XmlElement element)
{
var rule = new AssemblyRule();
rule.ruleName = ruleName;
rule.obfuscateName = ParseBoolNoneOrFalse(element.GetAttribute("ob-name"));
rule.typeRuleSpecs = new List<TypeRuleSpec>();
foreach (XmlNode node in element.ChildNodes)
{
if (!(node is XmlElement childElement))
{
continue;
}
if (childElement.Name != "type")
{
throw new Exception($"Invalid xml file, unknown node {childElement.Name}");
}
var typeRuleSpec = new TypeRuleSpec();
typeRuleSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
typeRuleSpec.modifierType = ParseModifierType(childElement.GetAttribute("modifier"));
typeRuleSpec.rule = (TypeRule)GetOrParseRule(childElement.GetAttribute("rule"), RuleType.Type, childElement);
rule.typeRuleSpecs.Add(typeRuleSpec);
}
return rule;
}
private TypeRule ParseTypeRule(string ruleName, XmlElement element)
{
var rule = new TypeRule();
rule.ruleName = ruleName;
rule.obfuscateName = ParseBoolNoneOrFalse(element.GetAttribute("ob-name"));
rule.obfuscateNamespace = ParseBoolNoneOrFalse(element.GetAttribute("ob-namespace"));
//rule.nestTypeRuleSpecs = new List<TypeRuleSpec>();
rule.fieldRuleSpecs = new List<FieldRuleSpec>();
rule.methodRuleSpecs = new List<MethodRuleSpec>();
rule.propertyRuleSpecs = new List<PropertyRuleSpec>();
rule.eventRuleSpecs = new List<EventRuleSpec>();
foreach (XmlNode node in element.ChildNodes)
{
if (!(node is XmlElement childElement))
{
continue;
}
switch (childElement.Name)
{
//case "nesttype":
//{
// var typeRuleSpec = new TypeRuleSpec();
// typeRuleSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
// typeRuleSpec.modifierType = ParseModifierType(childElement.GetAttribute("modifier"));
// typeRuleSpec.rule = (TypeRule)GetOrParseRule(childElement.GetAttribute("rule"), RuleType.Type, childElement);
// rule.nestTypeRuleSpecs.Add(typeRuleSpec);
// break;
//}
case "field":
{
var fieldRuleSpec = new FieldRuleSpec();
fieldRuleSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
fieldRuleSpec.modifierType = ParseModifierType(childElement.GetAttribute("modifier"));
fieldRuleSpec.rule = (FieldRule)GetOrParseRule(childElement.GetAttribute("rule"), RuleType.Field, childElement);
rule.fieldRuleSpecs.Add(fieldRuleSpec);
break;
}
case "method":
{
rule.methodRuleSpecs.Add(ParseMethodRuleSpec(childElement));
break;
}
case "property":
{
var propertyRulerSpec = new PropertyRuleSpec();
propertyRulerSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
propertyRulerSpec.rule = (PropertyRule)GetOrParseRule(childElement.GetAttribute("rule"), RuleType.Property, childElement);
rule.propertyRuleSpecs.Add(propertyRulerSpec);
break;
}
case "event":
{
var eventRuleSpec = new EventRuleSpec();
eventRuleSpec.nameMatcher = new NameMatcher(childElement.GetAttribute("name"));
eventRuleSpec.rule = (EventRule)GetOrParseRule(childElement.GetAttribute("rule"), RuleType.Event, childElement);
rule.eventRuleSpecs.Add(eventRuleSpec);
break;
}
default: throw new Exception($"Invalid xml file, unknown node {childElement.Name} in type rule {ruleName}");
}
}
return rule;
}
private MethodRuleSpec ParseMethodRuleSpec(XmlElement el)
{
var methodRuleSpec = new MethodRuleSpec();
methodRuleSpec.nameMatcher = new NameMatcher(el.GetAttribute("name"));
methodRuleSpec.modifierType = ParseModifierType(el.GetAttribute("modifier"));
methodRuleSpec.rule = (MethodRule)GetOrParseRule(el.GetAttribute("rule"), RuleType.Method, el);
return methodRuleSpec;
}
private MethodRule ParseMethodRule(string ruleName, XmlElement element)
{
var rule = new MethodRule();
rule.ruleName = ruleName;
rule.obfuscateName = ParseBoolNoneOrFalse(element.GetAttribute("ob-name"));
rule.obfuscateParam = ParseBoolNoneOrFalse(element.GetAttribute("ob-param"));
rule.obfuscateBody = ParseBoolNoneOrFalse(element.GetAttribute("ob-body"));
return rule;
}
private FieldRule ParseFieldRule(string ruleName, XmlElement element)
{
var rule = new FieldRule();
rule.ruleName = ruleName;
rule.obfuscateName = ParseBoolNoneOrFalse(element.GetAttribute("ob-name"));
return rule;
}
private PropertyRule ParsePropertyRule(string ruleName, XmlElement element)
{
var rule = new PropertyRule();
rule.ruleName = ruleName;
rule.obfuscateName = ParseBoolNoneOrFalse(element.GetAttribute("ob-name"));
foreach (XmlNode node in element.ChildNodes)
{
if (!(node is XmlElement childElement))
{
continue;
}
switch (node.Name)
{
case "getter":
{
if (rule.getter != null)
{
throw new Exception($"Invalid xml file, duplicate getter rule in property rule {ruleName}");
}
rule.getter = ParseMethodRuleSpec(childElement);
break;
}
case "setter":
{
if (rule.setter != null)
{
throw new Exception($"Invalid xml file, duplicate setter rule in property rule {ruleName}");
}
rule.setter = ParseMethodRuleSpec(childElement);
break;
}
default: throw new Exception($"Invalid xml file, unknown node {childElement.Name} in property rule {ruleName}");
}
}
return rule;
}
private EventRule ParseEventRule(string ruleName, XmlElement element)
{
var rule = new EventRule();
rule.ruleName = ruleName;
rule.obfuscateName = ParseBoolNoneOrFalse(element.GetAttribute("ob-name"));
foreach (XmlNode node in element.ChildNodes)
{
if (!(node is XmlElement childElement))
{
continue;
}
switch (node.Name)
{
case "add":
{
if (rule.add != null)
{
throw new Exception($"Invalid xml file, duplicate getter rule in property rule {ruleName}");
}
rule.add = ParseMethodRuleSpec(childElement);
break;
}
case "remove":
{
if (rule.remove != null)
{
throw new Exception($"Invalid xml file, duplicate setter rule in property rule {ruleName}");
}
rule.remove = ParseMethodRuleSpec(childElement);
break;
}
case "fire":
{
if (rule.fire != null)
{
throw new Exception($"Invalid xml file, duplicate fire rule in property rule {ruleName}");
}
rule.fire = ParseMethodRuleSpec(childElement);
break;
}
default: throw new Exception($"Invalid xml file, unknown node {childElement.Name} in event rule {ruleName}");
}
}
return rule;
}
private void LoadXmls(List<string> xmlFiles)
{
var rawAssemblySpecElements = new List<XmlElement>();
foreach (string file in xmlFiles)
{
LoadRawXml(file, rawAssemblySpecElements);
}
ResolveAssemblySpecs(rawAssemblySpecElements);
}
private void ResolveAssemblySpecs(List<XmlElement> rawAssemblySpecElements)
{
foreach (XmlElement ele in rawAssemblySpecElements)
{
string assemblyName = ele.GetAttribute("name");
if (string.IsNullOrEmpty(assemblyName))
{
throw new Exception($"Invalid xml file, assembly name is empty");
}
if (!_obfuscationAssemblyNames.Contains(assemblyName))
{
throw new Exception($"unknown assembly name:{assemblyName}, not in ObfuzSettings.obfuscationAssemblyNames");
}
if (_assemblyRuleSpecs.ContainsKey(assemblyName))
{
throw new Exception($"Invalid xml file, duplicate assembly name {assemblyName}");
}
var assemblyRule = new AssemblyRuleSpec()
{
assemblyName = assemblyName,
rule = (AssemblyRule)GetOrParseRule(ele.GetAttribute("rule"), RuleType.Assembly, ele),
};
_assemblyRuleSpecs.Add(assemblyName, assemblyRule);
}
}
private RuleType ParseRuleType(string ruleType)
{
switch (ruleType)
{
case "assembly": return RuleType.Assembly;
case "type": return RuleType.Type;
case "method": return RuleType.Method;
case "field": return RuleType.Field;
case "property": return RuleType.Property;
case "event": return RuleType.Event;
default: throw new Exception($"Invalid rule type {ruleType}");
}
}
private ModifierType ParseModifierType(string modifierType)
{
if (string.IsNullOrEmpty(modifierType))
{
return ModifierType.Private;
}
switch (modifierType)
{
case "public": return ModifierType.Public;
case "protected": return ModifierType.Protected;
case "private": return ModifierType.Private;
default: throw new Exception($"Invalid modifier type {modifierType}");
}
}
private void LoadRawXml(string xmlFile, List<XmlElement> rawAssemblyElements)
{
Debug.Log($"ObfuscateRule::LoadXml {xmlFile}");
var doc = new XmlDocument();
//var nsManager = new XmlNamespaceManager(doc.NameTable);
//nsManager.AddNamespace("ob", "https://github.com/focus-creative-games/Obfuz"); // 绑定前缀到 URI
doc.Load(xmlFile);
var root = doc.DocumentElement;
if (root.Name != "obfuz")
{
throw new Exception($"Invalid xml file {xmlFile}, root name should be 'obfuz'");
}
foreach (XmlNode node in root.ChildNodes)
{
if (!(node is XmlElement element))
{
continue;
}
switch (element.Name)
{
case "rules":
{
ParseRules(xmlFile, element);
break;
}
case "assembly":
{
rawAssemblyElements.Add(element);
break;
}
default:
{
throw new Exception($"Invalid xml file {xmlFile}, unknown node {element.Name}");
}
}
}
}
void ParseRules(string xmlFile, XmlElement rulesNode)
{
foreach (XmlNode node in rulesNode)
{
if (!(node is XmlElement ruleEle))
{
continue;
}
string ruleName = ruleEle.GetAttribute("name");
string ruleTypeName = ruleEle.Name;
RuleType ruleType = ParseRuleType(ruleTypeName);
var key = (ruleName, ruleType);
if (_rawRuleElements.ContainsKey(key))
{
throw new Exception($"Invalid xml file {xmlFile}, duplicate rule name:{ruleName} type:{ruleType}");
}
_rawRuleElements.Add(key, ruleEle);
}
}
private ModifierType ComputeModifierType(TypeAttributes visibility)
{
if (visibility == TypeAttributes.NotPublic || visibility == TypeAttributes.NestedPrivate)
{
return ModifierType.Private;
}
if (visibility == TypeAttributes.Public || visibility == TypeAttributes.NestedPublic)
{
return ModifierType.Public;
}
return ModifierType.Protected;
}
private ModifierType ComputeModifierType(FieldAttributes access)
{
if (access == FieldAttributes.Private || access == FieldAttributes.PrivateScope)
{
return ModifierType.Private;
}
if (access == FieldAttributes.Public)
{
return ModifierType.Public;
}
return ModifierType.Protected;
}
//private ModifierType ComputeModifierType(MethodAttributes access)
//{
// if (access == MethodAttributes.Private || access == MethodAttributes.PrivateScope)
// {
// return ModifierType.Private;
// }
// if (access == MethodAttributes.Public)
// {
// return ModifierType.Public;
// }
// return ModifierType.Protected;
//}
private bool MatchModifier(ModifierType modifierType, TypeDef typeDef)
{
return modifierType <= ComputeModifierType(typeDef.Visibility);
}
private bool MatchModifier(ModifierType modifierType, FieldDef fieldDef)
{
return modifierType <= ComputeModifierType(fieldDef.Access);
}
private bool MatchModifier(ModifierType modifierType, MethodDef methodDef)
{
return modifierType <= ComputeModifierType((FieldAttributes)methodDef.Access);
}
//private bool MatchModifier(ModifierType modifierType, PropertyDef propertyDef)
//{
// return modifierType <= ComputeModifierType((FieldAttributes)propertyDef.Attributes);
//}
private class MethodComputeCache
{
public bool obfuscateName = true;
public bool obfuscateParam = true;
public bool obfuscateBody = true;
}
private class TypeDefComputeCache
{
public bool obfuscateName = true;
public bool obfuscateNamespace = true;
public readonly Dictionary<MethodDef, MethodComputeCache> methods = new Dictionary<MethodDef, MethodComputeCache>();
public readonly HashSet<FieldDef> notObfuscatedFields = new HashSet<FieldDef>();
public readonly HashSet<PropertyDef> notObfuscatedProperties = new HashSet<PropertyDef>();
public readonly HashSet<EventDef> notObfuscatedEvents = new HashSet<EventDef>();
}
private readonly Dictionary<TypeDef, TypeDefComputeCache> _typeRenameCache = new Dictionary<TypeDef, TypeDefComputeCache>();
private readonly HashSet<string> _obfuscationAssemblyNames;
public ConfigurableRenamePolicy(List<string> obfuscationAssemblyNames, List<string> xmlFiles)
{
_obfuscationAssemblyNames = new HashSet<string>(obfuscationAssemblyNames);
LoadXmls(xmlFiles);
}
private TypeDefComputeCache GetOrCreateTypeDefRenameComputeCache(TypeDef typeDef)
{
if (_typeRenameCache.TryGetValue(typeDef, out var cache))
{
return cache;
}
cache = new TypeDefComputeCache();
_typeRenameCache.Add(typeDef, cache);
if (!_assemblyRuleSpecs.TryGetValue(typeDef.Module.Assembly.Name, out var assemblyRuleSpec) || assemblyRuleSpec.rule == null)
{
return cache;
}
string typeName = typeDef.FullName;
var totalMethodSpecs = new List<MethodRuleSpec>();
var totalMethodSpecFromPropertyAndEvents = new List<(MethodDef, MethodRuleSpec)>();
foreach (var typeRule in assemblyRuleSpec.rule.typeRuleSpecs)
{
if ((typeRule.nameMatcher != null && !typeRule.nameMatcher.IsMatch(typeName)) || !MatchModifier(typeRule.modifierType, typeDef))
{
continue;
}
cache.obfuscateNamespace &= typeRule.rule.obfuscateNamespace;
cache.obfuscateName &= typeRule.rule.obfuscateName;
totalMethodSpecs.AddRange(typeRule.rule.methodRuleSpecs);
foreach (var eventDef in typeDef.Events)
{
foreach (var eventSpec in typeRule.rule.eventRuleSpecs)
{
if (eventSpec.nameMatcher != null && !eventSpec.nameMatcher.IsMatch(eventDef.Name))
{
continue;
}
EventRule eventRule = eventSpec.rule;
if (!eventRule.obfuscateName)
{
cache.notObfuscatedEvents.Add(eventDef);
}
if (eventDef.AddMethod != null && eventRule.add != null && MatchModifier(eventRule.add.modifierType, eventDef.AddMethod))
{
totalMethodSpecFromPropertyAndEvents.Add((eventDef.AddMethod,eventRule.add));
}
if (eventDef.RemoveMethod != null && eventRule.remove != null && MatchModifier(eventRule.remove.modifierType, eventDef.RemoveMethod))
{
totalMethodSpecFromPropertyAndEvents.Add((eventDef.RemoveMethod, eventRule.remove));
}
if (eventDef.InvokeMethod != null && eventRule.fire != null && MatchModifier(eventRule.fire.modifierType, eventDef.InvokeMethod))
{
totalMethodSpecFromPropertyAndEvents.Add((eventDef.InvokeMethod, eventRule.fire));
}
}
}
foreach (var propertyDef in typeDef.Properties)
{
foreach (var propertySpec in typeRule.rule.propertyRuleSpecs)
{
if (propertySpec.nameMatcher != null && !propertySpec.nameMatcher.IsMatch(propertyDef.Name))
{
continue;
}
PropertyRule propertyRule = propertySpec.rule;
if (!propertyRule.obfuscateName)
{
cache.notObfuscatedProperties.Add(propertyDef);
}
if (propertyDef.GetMethod != null && propertyRule.getter != null && MatchModifier(propertyRule.getter.modifierType, propertyDef.GetMethod))
{
totalMethodSpecFromPropertyAndEvents.Add((propertyDef.GetMethod, propertyRule.getter));
}
if (propertyDef.SetMethod != null && propertyRule.setter != null && MatchModifier(propertyRule.setter.modifierType, propertyDef.SetMethod))
{
totalMethodSpecFromPropertyAndEvents.Add((propertyDef.SetMethod, propertyRule.setter));
}
}
}
foreach (var fieldDef in typeDef.Fields)
{
foreach (var fieldRule in typeRule.rule.fieldRuleSpecs)
{
if ((fieldRule.nameMatcher == null || fieldRule.nameMatcher.IsMatch(fieldDef.Name)) && MatchModifier(fieldRule.modifierType, fieldDef) && !fieldRule.rule.obfuscateName)
{
cache.notObfuscatedFields.Add(fieldDef);
}
}
}
foreach (MethodDef methodDef in typeDef.Methods)
{
foreach (var e in totalMethodSpecFromPropertyAndEvents)
{
if (e.Item1 != methodDef)
{
continue;
}
if (!cache.methods.TryGetValue(methodDef, out var methodCache))
{
methodCache = new MethodComputeCache();
cache.methods.Add(methodDef, methodCache);
}
MethodRule methodRule = e.Item2.rule;
methodCache.obfuscateName &= methodRule.obfuscateName;
methodCache.obfuscateParam &= methodRule.obfuscateParam;
methodCache.obfuscateBody &= methodRule.obfuscateBody;
}
foreach (MethodRuleSpec methodSpec in totalMethodSpecs)
{
if ((methodSpec.nameMatcher != null && !methodSpec.nameMatcher.IsMatch(methodDef.Name)) || !MatchModifier(methodSpec.modifierType, methodDef))
{
continue;
}
if (!cache.methods.TryGetValue(methodDef, out var methodCache))
{
methodCache = new MethodComputeCache();
cache.methods.Add(methodDef, methodCache);
}
MethodRule methodRule = methodSpec.rule;
methodCache.obfuscateName &= methodRule.obfuscateName;
methodCache.obfuscateParam &= methodRule.obfuscateParam;
methodCache.obfuscateBody &= methodRule.obfuscateBody;
}
}
}
return cache;
}
public override bool NeedRename(TypeDef typeDef)
{
TypeDefComputeCache cache = GetOrCreateTypeDefRenameComputeCache(typeDef);
return cache.obfuscateName;
}
public override bool NeedRename(MethodDef methodDef)
{
TypeDef typeDef = methodDef.DeclaringType;
TypeDefComputeCache cache = GetOrCreateTypeDefRenameComputeCache(typeDef);
if (!cache.methods.TryGetValue(methodDef, out var methodCache))
{
return true;
}
return methodCache.obfuscateName;
}
public override bool NeedRename(FieldDef fieldDef)
{
TypeDef typeDef = fieldDef.DeclaringType;
TypeDefComputeCache cache = GetOrCreateTypeDefRenameComputeCache(typeDef);
return !cache.notObfuscatedFields.Contains(fieldDef);
}
public override bool NeedRename(PropertyDef propertyDef)
{
TypeDef typeDef = propertyDef.DeclaringType;
TypeDefComputeCache cache = GetOrCreateTypeDefRenameComputeCache(typeDef);
return !cache.notObfuscatedProperties.Contains(propertyDef);
}
public override bool NeedRename(EventDef eventDef)
{
TypeDef typeDef = eventDef.DeclaringType;
TypeDefComputeCache cache = GetOrCreateTypeDefRenameComputeCache(typeDef);
return !cache.notObfuscatedEvents.Contains(eventDef);
}
public override bool NeedRename(ParamDef paramDef)
{
MethodDef methodDef = paramDef.DeclaringMethod;
TypeDef typeDef = methodDef.DeclaringType;
TypeDefComputeCache cache = GetOrCreateTypeDefRenameComputeCache(typeDef);
if (!cache.methods.TryGetValue(methodDef, out var methodCache))
{
return true;
}
return methodCache.obfuscateParam;
}
}
}

View File

@ -0,0 +1,38 @@
using dnlib.DotNet;
namespace Obfuz.ObfusPasses.SymbolObfus.Policies
{
public abstract class ObfuscationPolicyBase : IObfuscationPolicy
{
public virtual bool NeedRename(TypeDef typeDef)
{
return true;
}
public virtual bool NeedRename(MethodDef methodDef)
{
return true;
}
public virtual bool NeedRename(FieldDef fieldDef)
{
return true;
}
public virtual bool NeedRename(PropertyDef propertyDef)
{
return true;
}
public virtual bool NeedRename(EventDef eventDef)
{
return true;
}
public virtual bool NeedRename(ParamDef paramDef)
{
return true;
}
}
}

View File

@ -0,0 +1,50 @@
using dnlib.DotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.SymbolObfus.Policies
{
internal class SupportPassPolicy : ObfuscationPolicyBase
{
private readonly ConfigurablePassPolicy _policy;
private bool Support(ObfuscationPassType passType)
{
return passType.HasFlag(ObfuscationPassType.SymbolObfus);
}
public SupportPassPolicy(ConfigurablePassPolicy policy)
{
_policy = policy;
}
public override bool NeedRename(TypeDef typeDef)
{
return Support(_policy.GetTypeObfuscationPasses(typeDef));
}
public override bool NeedRename(MethodDef methodDef)
{
return Support(_policy.GetMethodObfuscationPasses(methodDef));
}
public override bool NeedRename(FieldDef fieldDef)
{
return Support(_policy.GetFieldObfuscationPasses(fieldDef));
}
public override bool NeedRename(PropertyDef propertyDef)
{
return Support(_policy.GetPropertyObfuscationPasses(propertyDef));
}
public override bool NeedRename(EventDef eventDef)
{
return Support(_policy.GetEventObfuscationPasses(eventDef));
}
}
}

View File

@ -0,0 +1,67 @@
using dnlib.DotNet;
using Obfuz.Utils;
namespace Obfuz.ObfusPasses.SymbolObfus.Policies
{
public class SystemRenamePolicy : ObfuscationPolicyBase
{
public override bool NeedRename(TypeDef typeDef)
{
string name = typeDef.Name;
if (name == "<Module>" || name == "ObfuzIgnoreAttribute")
{
return false;
}
if (MetaUtil.HasObfuzIgnoreAttribute(typeDef))
{
return false;
}
return true;
}
public override bool NeedRename(MethodDef methodDef)
{
if (methodDef.Name == ".ctor" || methodDef.Name == ".cctor")
{
return false;
}
if (MetaUtil.HasObfuzIgnoreAttribute(methodDef) || MetaUtil.HasObfuzIgnoreAttribute(methodDef.DeclaringType))
{
return false;
}
return true;
}
public override bool NeedRename(FieldDef fieldDef)
{
if (MetaUtil.HasObfuzIgnoreAttribute(fieldDef) || MetaUtil.HasObfuzIgnoreAttribute(fieldDef.DeclaringType))
{
return false;
}
if (fieldDef.DeclaringType.IsEnum && fieldDef.Name == "value__")
{
return false;
}
return true;
}
public override bool NeedRename(PropertyDef propertyDef)
{
if (MetaUtil.HasObfuzIgnoreAttribute(propertyDef) || MetaUtil.HasObfuzIgnoreAttribute(propertyDef.DeclaringType))
{
return false;
}
return true;
}
public override bool NeedRename(EventDef eventDef)
{
if (MetaUtil.HasObfuzIgnoreAttribute(eventDef) || MetaUtil.HasObfuzIgnoreAttribute(eventDef.DeclaringType))
{
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,97 @@
using dnlib.DotNet;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.SymbolObfus.Policies
{
public class UnityRenamePolicy : ObfuscationPolicyBase
{
private static HashSet<string> s_monoBehaviourEvents = new HashSet<string> {
"Awake",
"OnEnable",
"Start",
"FixedUpdate",
"Update",
"LateUpdate",
"OnDisable",
"OnDestroy",
"OnApplicationQuit",
"OnTriggerEnter",
"OnTriggerExit",
"OnTriggerStay",
"OnCollisionEnter",
"OnCollisionExit",
"OnCollisionStay",
"OnMouseDown",
"OnMouseUp",
"OnMouseEnter",
"OnMouseExit",
"OnMouseOver",
"OnMouseDrag",
"OnBecameVisible",
"OnBecameInvisible",
"OnGUI",
"OnPreRender",
"OnPostRender",
"OnRenderObject",
"OnDrawGizmos",
"OnDrawGizmosSelected",
"OnValidate",
"OnAnimatorIK",
"OnAnimatorMove",
"OnApplicationFocus",
"OnApplicationPause",
"OnAudioFilterRead",
"OnJointBreak",
"OnParticleCollision",
"OnTransformChildrenChanged",
"OnTransformParentChanged",
"OnRectTransformDimensionsChange",
"OnWillRenderObject"
};
public override bool NeedRename(TypeDef typeDef)
{
if (MetaUtil.IsScriptOrSerializableType(typeDef))
{
return false;
}
if (typeDef.FullName.StartsWith("UnitySourceGeneratedAssemblyMonoScriptTypes_"))
{
return false;
}
return true;
}
public override bool NeedRename(MethodDef methodDef)
{
if (MetaUtil.IsInheritFromUnityObject(methodDef.DeclaringType))
{
return !s_monoBehaviourEvents.Contains(methodDef.Name);
}
if (methodDef.DeclaringType.FullName.StartsWith("UnitySourceGeneratedAssemblyMonoScriptTypes_"))
{
return false;
}
return true;
}
public override bool NeedRename(FieldDef fieldDef)
{
TypeDef typeDef = fieldDef.DeclaringType;
if (MetaUtil.IsScriptOrSerializableType(typeDef))
{
return !MetaUtil.IsSerializableField(fieldDef);
}
if (fieldDef.DeclaringType.FullName.StartsWith("UnitySourceGeneratedAssemblyMonoScriptTypes_"))
{
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,739 @@
using dnlib.DotNet;
using Obfuz.Utils;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using UnityEngine;
namespace Obfuz.ObfusPasses.SymbolObfus
{
public class RenameRecordMap
{
private enum RenameStatus
{
NotRenamed,
Renamed,
}
private class RenameRecord
{
public RenameStatus status;
public string signature;
public string oldName;
public string newName;
public string oldStackTraceSignature; // only for MethodDef
public object renameMappingData;
}
private class RenameMappingField
{
public RenameStatus status;
public string signature;
public string newName;
}
private class RenameMappingMethod
{
public RenameStatus status;
public string signature;
public string newName;
public List<RenameMappingMethodParam> parameters = new List<RenameMappingMethodParam>();
}
private class RenameMappingMethodParam
{
public RenameStatus status;
public int index;
public string newName;
}
private class RenameMappingProperty
{
public RenameStatus status;
public string signature;
public string newName;
}
private class RenameMappingEvent
{
public RenameStatus status;
public string signature;
public string newName;
}
private class RenameMappingType
{
public RenameStatus status;
public string oldFullName;
public string newFullName;
public Dictionary<string, RenameMappingField> fields = new Dictionary<string, RenameMappingField>();
public Dictionary<string, RenameMappingMethod> methods = new Dictionary<string, RenameMappingMethod>();
public Dictionary<string, RenameMappingProperty> properties = new Dictionary<string, RenameMappingProperty>();
public Dictionary<string, RenameMappingEvent> events = new Dictionary<string, RenameMappingEvent>();
}
private class RenameMappingAssembly
{
public string assName;
public Dictionary<string, RenameMappingType> types = new Dictionary<string, RenameMappingType>();
}
private readonly string _mappingFile;
private readonly Dictionary<string, RenameMappingAssembly> _assemblies = new Dictionary<string, RenameMappingAssembly>();
private readonly Dictionary<ModuleDef, RenameRecord> _modRenames = new Dictionary<ModuleDef, RenameRecord>();
private readonly Dictionary<TypeDef, RenameRecord> _typeRenames = new Dictionary<TypeDef, RenameRecord>();
private readonly Dictionary<MethodDef, RenameRecord> _methodRenames = new Dictionary<MethodDef, RenameRecord>();
private readonly Dictionary<ParamDef, RenameRecord> _paramRenames = new Dictionary<ParamDef, RenameRecord>();
private readonly Dictionary<FieldDef, RenameRecord> _fieldRenames = new Dictionary<FieldDef, RenameRecord>();
private readonly Dictionary<PropertyDef, RenameRecord> _propertyRenames = new Dictionary<PropertyDef, RenameRecord>();
private readonly Dictionary<EventDef, RenameRecord> _eventRenames = new Dictionary<EventDef, RenameRecord>();
private readonly Dictionary<VirtualMethodGroup, RenameRecord> _virtualMethodGroups = new Dictionary<VirtualMethodGroup, RenameRecord>();
public RenameRecordMap(string mappingFile)
{
_mappingFile = mappingFile;
}
public void Init(List<ModuleDef> assemblies, INameMaker nameMaker)
{
LoadXmlMappingFile(_mappingFile);
foreach (ModuleDef mod in assemblies)
{
string name = mod.Assembly.Name;
RenameMappingAssembly rma = _assemblies.GetValueOrDefault(name);
_modRenames.Add(mod, new RenameRecord
{
status = RenameStatus.NotRenamed,
signature = name,
oldName = name,
newName = null,
renameMappingData = rma,
});
foreach (TypeDef type in mod.GetTypes())
{
nameMaker.AddPreservedName(type, name);
nameMaker.AddPreservedNamespace(type, type.Namespace);
string fullTypeName = type.FullName;
RenameMappingType rmt = rma?.types.GetValueOrDefault(fullTypeName);
if (rmt != null)
{
var (newNamespace, newName) = MetaUtil.SplitNamespaceAndName(rmt.newFullName);
nameMaker.AddPreservedNamespace(type, newNamespace);
nameMaker.AddPreservedName(type, newName);
}
_typeRenames.Add(type, new RenameRecord
{
status = RenameStatus.NotRenamed,
signature = fullTypeName,
oldName = fullTypeName,
newName = null,
renameMappingData = rmt,
});
foreach (MethodDef method in type.Methods)
{
nameMaker.AddPreservedName(method, method.Name);
string methodSig = TypeSigUtil.ComputeMethodDefSignature(method);
nameMaker.AddPreservedName(method, method.Name);
RenameMappingMethod rmm = rmt?.methods.GetValueOrDefault(methodSig);
if (rmm != null)
{
nameMaker.AddPreservedName(method, rmm.newName);
}
_methodRenames.Add(method, new RenameRecord
{
status = RenameStatus.NotRenamed,
signature = methodSig,
oldName = method.Name,
newName = null,
renameMappingData = rmm,
oldStackTraceSignature = MetaUtil.CreateMethodDefIl2CppStackTraceSignature(method),
});
foreach (Parameter param in method.Parameters)
{
if (param.ParamDef != null)
{
_paramRenames.Add(param.ParamDef, new RenameRecord
{
status = RenameStatus.NotRenamed,
signature = param.Name,
oldName = param.Name,
newName = null,
});
}
}
}
foreach (FieldDef field in type.Fields)
{
nameMaker.AddPreservedName(field, field.Name);
string fieldSig = TypeSigUtil.ComputeFieldDefSignature(field);
RenameMappingField rmf = rmt?.fields.GetValueOrDefault(fieldSig);
if (rmf != null)
{
nameMaker.AddPreservedName(field, rmf.newName);
}
_fieldRenames.Add(field, new RenameRecord
{
status = RenameStatus.NotRenamed,
signature = fieldSig,
oldName = field.Name,
newName = null,
renameMappingData = rmf,
});
}
foreach (PropertyDef property in type.Properties)
{
nameMaker.AddPreservedName(property, property.Name);
string propertySig = TypeSigUtil.ComputePropertyDefSignature(property);
RenameMappingProperty rmp = rmt?.properties.GetValueOrDefault(propertySig);
if (rmp != null)
{
nameMaker.AddPreservedName(property, rmp.newName);
}
_propertyRenames.Add(property, new RenameRecord
{
status = RenameStatus.NotRenamed,
signature = propertySig,
oldName = property.Name,
newName = null,
renameMappingData = rmp,
});
}
foreach (EventDef eventDef in type.Events)
{
nameMaker.AddPreservedName(eventDef, eventDef.Name);
string eventSig = TypeSigUtil.ComputeEventDefSignature(eventDef);
RenameMappingEvent rme = rmt?.events.GetValueOrDefault(eventSig);
if (rme != null)
{
nameMaker.AddPreservedName(eventDef, rme.newName);
}
_eventRenames.Add(eventDef, new RenameRecord
{
status = RenameStatus.NotRenamed,
signature = eventSig,
oldName = eventDef.Name,
newName = null,
renameMappingData = rme,
});
}
}
}
}
private void LoadXmlMappingFile(string mappingFile)
{
if (string.IsNullOrEmpty(mappingFile) || !File.Exists(mappingFile))
{
return;
}
var doc = new XmlDocument();
doc.Load(mappingFile);
var root = doc.DocumentElement;
foreach (XmlNode node in root.ChildNodes)
{
if (!(node is XmlElement element))
{
continue;
}
LoadAssemblyMapping(element);
}
}
private void LoadAssemblyMapping(XmlElement ele)
{
if (ele.Name != "assembly")
{
throw new System.Exception($"Invalid node name: {ele.Name}. Expected 'assembly'.");
}
var assemblyName = ele.Attributes["name"].Value;
var rma = new RenameMappingAssembly
{
assName = assemblyName,
};
foreach (XmlNode node in ele.ChildNodes)
{
if (!(node is XmlElement element))
{
continue;
}
if (element.Name != "type")
{
throw new System.Exception($"Invalid node name: {element.Name}. Expected 'type'.");
}
LoadTypeMapping(element, rma);
}
_assemblies.Add(assemblyName, rma);
}
private void LoadTypeMapping(XmlElement ele, RenameMappingAssembly ass)
{
var typeName = ele.Attributes["fullName"].Value;
var newTypeName = ele.Attributes["newFullName"].Value;
var rmt = new RenameMappingType
{
oldFullName = typeName,
newFullName = newTypeName,
status = (RenameStatus)System.Enum.Parse(typeof(RenameStatus), ele.Attributes["status"].Value),
};
foreach (XmlNode node in ele.ChildNodes)
{
if (!(node is XmlElement c))
{
continue;
}
switch (node.Name)
{
case "field": LoadFieldMapping(c, rmt); break;
case "event": LoadEventMapping(c, rmt); break;
case "property": LoadPropertyMapping(c, rmt); break;
case "method": LoadMethodMapping(c, rmt); break;
default: throw new System.Exception($"Invalid node name:{node.Name}");
}
}
ass.types.Add(typeName, rmt);
}
private void LoadMethodMapping(XmlElement ele, RenameMappingType type)
{
string signature = ele.Attributes["signature"].Value;
string newName = ele.Attributes["newName"].Value;
var rmm = new RenameMappingMethod
{
signature = signature,
newName = newName,
status = RenameStatus.Renamed,
};
foreach (XmlNode node in ele.ChildNodes)
{
if (!(node is XmlElement c))
{
continue;
}
switch (node.Name)
{
case "param": LoadMethodParamMapping(c, rmm); break;
default: throw new System.Exception($"unknown node name:{node.Name}, expect 'param'");
}
}
type.methods.Add(signature, rmm);
}
private void LoadMethodParamMapping(XmlElement ele, RenameMappingMethod method)
{
string index = ele.Attributes["index"].Value;
string newName = ele.Attributes["newName"].Value;
var rmp = new RenameMappingMethodParam
{
index = int.Parse(index),
newName = newName,
status = RenameStatus.Renamed,
};
method.parameters.Add(rmp);
}
private void LoadFieldMapping(XmlElement ele, RenameMappingType type)
{
string signature = ele.Attributes["signature"].Value;
string newName = ele.Attributes["newName"].Value;
var rmf = new RenameMappingField
{
signature = signature,
newName = newName,
status = RenameStatus.Renamed,
};
type.fields.Add(signature, rmf);
}
private void LoadPropertyMapping(XmlElement ele, RenameMappingType type)
{
string signature = ele.Attributes["signature"].Value;
string newName = ele.Attributes["newName"].Value;
var rmp = new RenameMappingProperty
{
signature = signature,
newName = newName,
status = RenameStatus.Renamed,
};
type.properties.Add(signature, rmp);
}
private void LoadEventMapping(XmlElement ele, RenameMappingType type)
{
string signature = ele.Attributes["signature"].Value;
string newName = ele.Attributes["newName"].Value;
var rme = new RenameMappingEvent
{
signature = signature,
newName = newName,
status = RenameStatus.Renamed,
};
type.events.Add(signature, rme);
}
public void WriteXmlMappingFile()
{
if (string.IsNullOrEmpty(_mappingFile))
{
return;
}
var doc = new XmlDocument();
var root = doc.CreateElement("mapping");
doc.AppendChild(root);
foreach (var kvp in _modRenames)
{
ModuleDef mod = kvp.Key;
RenameRecord record = kvp.Value;
var assemblyNode = doc.CreateElement("assembly");
assemblyNode.SetAttribute("name", mod.Assembly.Name);
foreach (TypeDef type in mod.GetTypes())
{
WriteTypeMapping(assemblyNode, type);
}
root.AppendChild(assemblyNode);
}
foreach (RenameMappingAssembly ass in _assemblies.Values)
{
if (_modRenames.Keys.Any(m => m.Assembly.Name == ass.assName))
{
continue;
}
var assemblyNode = doc.CreateElement("assembly");
assemblyNode.SetAttribute("name", ass.assName);
foreach (var e in ass.types)
{
WriteTypeMapping(assemblyNode, e.Key, e.Value);
}
root.AppendChild(assemblyNode);
}
Directory.CreateDirectory(Path.GetDirectoryName(_mappingFile));
doc.Save(_mappingFile);
Debug.Log($"Mapping file saved to {Path.GetFullPath(_mappingFile)}");
}
private void WriteTypeMapping(XmlElement assNode, TypeDef type)
{
_typeRenames.TryGetValue(type, out var record);
var typeNode = assNode.OwnerDocument.CreateElement("type");
typeNode.SetAttribute("fullName", record?.signature ?? type.FullName);
typeNode.SetAttribute("newFullName", record != null && record.status == RenameStatus.Renamed ? record.newName : "");
typeNode.SetAttribute("status", record != null ? record.status.ToString() : RenameStatus.NotRenamed.ToString());
foreach (FieldDef field in type.Fields)
{
WriteFieldMapping(typeNode, field);
}
foreach (PropertyDef property in type.Properties)
{
WritePropertyMapping(typeNode, property);
}
foreach (EventDef eventDef in type.Events)
{
WriteEventMapping(typeNode, eventDef);
}
foreach (MethodDef method in type.Methods)
{
WriteMethodMapping(typeNode, method);
}
if ((record != null && record.status == RenameStatus.Renamed) || typeNode.ChildNodes.Count > 0)
{
assNode.AppendChild(typeNode);
}
}
private void WriteTypeMapping(XmlElement assNode, string fullName, RenameMappingType type)
{
var typeNode = assNode.OwnerDocument.CreateElement("type");
typeNode.SetAttribute("fullName", fullName);
typeNode.SetAttribute("newFullName", type.status == RenameStatus.Renamed ? type.newFullName : "");
typeNode.SetAttribute("status", type.status.ToString());
foreach (var e in type.fields)
{
string signature = e.Key;
RenameMappingField field = e.Value;
WriteFieldMapping(typeNode, e.Key, e.Value);
}
foreach (var e in type.properties)
{
WritePropertyMapping(typeNode, e.Key, e.Value);
}
foreach (var e in type.events)
{
WriteEventMapping(typeNode, e.Key, e.Value);
}
foreach (var e in type.methods)
{
WriteMethodMapping(typeNode, e.Key, e.Value);
}
assNode.AppendChild(typeNode);
}
private void WriteFieldMapping(XmlElement typeEle, FieldDef field)
{
if (!_fieldRenames.TryGetValue(field, out var record) || record.status == RenameStatus.NotRenamed)
{
return;
}
var fieldNode = typeEle.OwnerDocument.CreateElement("field");
fieldNode.SetAttribute("signature", record?.signature);
fieldNode.SetAttribute("newName", record.newName);
//fieldNode.SetAttribute("status", record.status.ToString());
typeEle.AppendChild(fieldNode);
}
private void WriteFieldMapping(XmlElement typeEle, string signature, RenameMappingField field)
{
var fieldNode = typeEle.OwnerDocument.CreateElement("field");
fieldNode.SetAttribute("signature", signature);
fieldNode.SetAttribute("newName", field.newName);
//fieldNode.SetAttribute("status", record.status.ToString());
typeEle.AppendChild(fieldNode);
}
private void WritePropertyMapping(XmlElement typeEle, PropertyDef property)
{
if (!_propertyRenames.TryGetValue(property, out var record) || record.status == RenameStatus.NotRenamed)
{
return;
}
var propertyNode = typeEle.OwnerDocument.CreateElement("property");
propertyNode.SetAttribute("signature", record.signature);
propertyNode.SetAttribute("newName", record.newName);
//propertyNode.SetAttribute("status", record.status.ToString());
typeEle.AppendChild(propertyNode);
}
private void WritePropertyMapping(XmlElement typeEle, string signature, RenameMappingProperty property)
{
var propertyNode = typeEle.OwnerDocument.CreateElement("property");
propertyNode.SetAttribute("signature", signature);
propertyNode.SetAttribute("newName", property.newName);
//propertyNode.SetAttribute("status", record.status.ToString());
typeEle.AppendChild(propertyNode);
}
private void WriteEventMapping(XmlElement typeEle, EventDef eventDef)
{
if (!_eventRenames.TryGetValue(eventDef, out var record) || record.status == RenameStatus.NotRenamed)
{
return;
}
var eventNode = typeEle.OwnerDocument.CreateElement("event");
eventNode.SetAttribute("signature", record.signature);
eventNode.SetAttribute("newName", record.newName);
typeEle.AppendChild(eventNode);
}
private void WriteEventMapping(XmlElement typeEle, string signature, RenameMappingEvent eventDef)
{
var eventNode = typeEle.OwnerDocument.CreateElement("event");
eventNode.SetAttribute("signature", signature);
eventNode.SetAttribute("newName", eventDef.newName);
typeEle.AppendChild(eventNode);
}
private void WriteMethodMapping(XmlElement typeEle, MethodDef method)
{
if (!_methodRenames.TryGetValue(method, out var record) || record.status == RenameStatus.NotRenamed)
{
return;
}
var methodNode = typeEle.OwnerDocument.CreateElement("method");
methodNode.SetAttribute("signature", record.signature);
methodNode.SetAttribute("newName", record.newName);
methodNode.SetAttribute("oldStackTraceSignature", record.oldStackTraceSignature);
methodNode.SetAttribute("newStackTraceSignature", MetaUtil.CreateMethodDefIl2CppStackTraceSignature(method));
//methodNode.SetAttribute("status", record != null ? record.status.ToString() : RenameStatus.NotRenamed.ToString());
foreach (Parameter param in method.Parameters)
{
if (param.ParamDef != null)
{
WriteMethodParamMapping(methodNode, param.ParamDef);
}
}
typeEle.AppendChild(methodNode);
}
private void WriteMethodMapping(XmlElement typeEle, string signature, RenameMappingMethod method)
{
var methodNode = typeEle.OwnerDocument.CreateElement("method");
methodNode.SetAttribute("signature", signature);
methodNode.SetAttribute("newName", method.newName);
//methodNode.SetAttribute("status", record != null ? record.status.ToString() : RenameStatus.NotRenamed.ToString());
foreach (RenameMappingMethodParam param in method.parameters)
{
WriteMethodParamMapping(methodNode, param);
}
typeEle.AppendChild(methodNode);
}
private void WriteMethodParamMapping(XmlElement methodEle, ParamDef param)
{
if (!_paramRenames.TryGetValue(param, out var record) || record.status == RenameStatus.NotRenamed)
{
return;
}
var paramNode = methodEle.OwnerDocument.CreateElement("param");
paramNode.SetAttribute("index", param.Sequence.ToString());
paramNode.SetAttribute("newName", record.newName);
//paramNode.SetAttribute("status", record.status.ToString());
methodEle.AppendChild(paramNode);
}
private void WriteMethodParamMapping(XmlElement methodEle, RenameMappingMethodParam param)
{
var paramNode = methodEle.OwnerDocument.CreateElement("param");
paramNode.SetAttribute("index", param.index.ToString());
paramNode.SetAttribute("newName", param.newName);
//paramNode.SetAttribute("status", record.status.ToString());
methodEle.AppendChild(paramNode);
}
public void AddRename(ModuleDef mod, string newName)
{
RenameRecord record = _modRenames[mod];
record.status = RenameStatus.Renamed;
record.newName = newName;
}
public void AddRename(TypeDef type, string newName)
{
RenameRecord record = _typeRenames[type];
record.status = RenameStatus.Renamed;
record.newName = newName;
}
public void AddRename(MethodDef method, string newName)
{
RenameRecord record = _methodRenames[method];
record.status = RenameStatus.Renamed;
record.newName = newName;
}
public void AddRename(ParamDef paramDef, string newName)
{
RenameRecord record = _paramRenames[paramDef];
record.status = RenameStatus.Renamed;
record.newName = newName;
}
public void InitAndAddRename(VirtualMethodGroup methodGroup, string newName)
{
RenameRecord methodRecord = _methodRenames[methodGroup.methods[0]];
_virtualMethodGroups.Add(methodGroup, new RenameRecord
{
status = RenameStatus.Renamed,
signature = methodRecord.signature,
oldName = methodRecord.oldName,
newName = newName,
});
}
public void AddRename(FieldDef field, string newName)
{
RenameRecord record = _fieldRenames[field];
record.status = RenameStatus.Renamed;
record.newName = newName;
}
public void AddRename(PropertyDef property, string newName)
{
RenameRecord record = _propertyRenames[property];
record.status = RenameStatus.Renamed;
record.newName = newName;
}
public void AddRename(EventDef eventDef, string newName)
{
RenameRecord record = _eventRenames[eventDef];
record.status = RenameStatus.Renamed;
record.newName = newName;
}
public bool TryGetExistRenameMapping(TypeDef type, out string newNamespace, out string newName)
{
if (_typeRenames.TryGetValue(type, out var record) && record.renameMappingData != null)
{
var rmt = (RenameMappingType)record.renameMappingData;
(newNamespace, newName) = MetaUtil.SplitNamespaceAndName(rmt.newFullName);
return true;
}
newNamespace = null;
newName = null;
return false;
}
public bool TryGetExistRenameMapping(MethodDef method, out string newName)
{
if (_methodRenames.TryGetValue(method, out var record) && record.renameMappingData != null)
{
newName = ((RenameMappingMethod)record.renameMappingData).newName;
return true;
}
newName = null;
return false;
}
public bool TryGetExistRenameMapping(FieldDef field, out string newName)
{
if (_fieldRenames.TryGetValue(field, out var record) && record.renameMappingData != null)
{
newName = ((RenameMappingField)record.renameMappingData).newName;
return true;
}
newName = null;
return false;
}
public bool TryGetExistRenameMapping(PropertyDef property, out string newName)
{
if (_propertyRenames.TryGetValue(property, out var record) && record.renameMappingData != null)
{
newName = ((RenameMappingProperty)record.renameMappingData).newName;
return true;
}
newName = null;
return false;
}
public bool TryGetExistRenameMapping(EventDef eventDef, out string newName)
{
if (_eventRenames.TryGetValue(eventDef, out var record) && record.renameMappingData != null)
{
newName = ((RenameMappingEvent)record.renameMappingData).newName;
return true;
}
newName = null;
return false;
}
public bool TryGetRename(VirtualMethodGroup group, out string newName)
{
if (_virtualMethodGroups.TryGetValue(group, out var record))
{
newName = record.newName;
return true;
}
newName = null;
return false;
}
}
}

View File

@ -0,0 +1,36 @@
using Obfuz.Settings;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.SymbolObfus
{
public class SymbolObfusPass : ObfuscationPassBase
{
private SymbolRename _symbolRename;
public override ObfuscationPassType Type => ObfuscationPassType.SymbolObfus;
public SymbolObfusPass(SymbolObfuscationSettings settings)
{
_symbolRename = new SymbolRename(settings);
}
public override void Start()
{
_symbolRename.Init();
}
public override void Stop()
{
_symbolRename.Save();
}
public override void Process()
{
_symbolRename.Process();
}
}
}

View File

@ -0,0 +1,774 @@
using dnlib.DotNet;
using Obfuz.ObfusPasses.SymbolObfus;
using Obfuz.ObfusPasses.SymbolObfus.NameMakers;
using Obfuz.ObfusPasses.SymbolObfus.Policies;
using Obfuz.Settings;
using Obfuz.Utils;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Assertions;
namespace Obfuz.ObfusPasses.SymbolObfus
{
public class SymbolRename
{
class AssemblyReferenceInfo
{
public string name;
public bool needObfuscated;
public ModuleDef module;
public List<AssemblyReferenceInfo> referenceMeAssemblies;
}
private readonly bool _useConsistentNamespaceObfuscation;
private readonly List<string> _obfuscationRuleFiles;
private readonly string _mappingXmlPath;
private AssemblyCache _assemblyCache;
private List<ModuleDef> _toObfuscatedModules;
private List<ModuleDef> _obfuscatedAndNotObfuscatedModules;
private HashSet<ModuleDef> _toObfuscatedModuleSet;
private IObfuscationPolicy _renamePolicy;
private INameMaker _nameMaker;
private readonly Dictionary<ModuleDef, List<CustomAttributeInfo>> _customAttributeArgumentsWithTypeByMods = new Dictionary<ModuleDef, List<CustomAttributeInfo>>();
private readonly RenameRecordMap _renameRecordMap;
private readonly VirtualMethodGroupCalculator _virtualMethodGroupCalculator;
class CustomAttributeInfo
{
public CustomAttributeCollection customAttributes;
public int index;
public List<CAArgument> arguments;
public List<CANamedArgument> namedArguments;
}
public SymbolRename(SymbolObfuscationSettings settings)
{
_useConsistentNamespaceObfuscation = settings.useConsistentNamespaceObfuscation;
_mappingXmlPath = settings.symbolMappingFile;
_obfuscationRuleFiles = settings.ruleFiles.ToList();
_renameRecordMap = new RenameRecordMap(settings.debug ? null : settings.symbolMappingFile);
_virtualMethodGroupCalculator = new VirtualMethodGroupCalculator();
_nameMaker = settings.debug ? NameMakerFactory.CreateDebugNameMaker() : NameMakerFactory.CreateNameMakerBaseASCIICharSet(settings.obfuscatedNamePrefix);
}
public void Init()
{
var ctx = ObfuscationPassContext.Current;
_assemblyCache = ctx.assemblyCache;
_toObfuscatedModules = ctx.modulesToObfuscate;
_obfuscatedAndNotObfuscatedModules = ctx.allObfuscationRelativeModules;
_toObfuscatedModuleSet = new HashSet<ModuleDef>(ctx.modulesToObfuscate);
var obfuscateRuleConfig = new ConfigurableRenamePolicy(ctx.assembliesToObfuscate, _obfuscationRuleFiles);
_renamePolicy = new CacheRenamePolicy(new CombineRenamePolicy(new SupportPassPolicy(ctx.passPolicy), new SystemRenamePolicy(), new UnityRenamePolicy(), obfuscateRuleConfig));
BuildCustomAttributeArguments();
}
private void CollectCArgumentWithTypeOf(IHasCustomAttribute meta, List<CustomAttributeInfo> customAttributes)
{
int index = 0;
foreach (CustomAttribute ca in meta.CustomAttributes)
{
List<CAArgument> arguments = null;
if (ca.ConstructorArguments.Any(a => MetaUtil.MayRenameCustomDataType(a.Type.ElementType)))
{
arguments = ca.ConstructorArguments.ToList();
}
List<CANamedArgument> namedArguments = null;
if (ca.NamedArguments.Any(a => MetaUtil.MayRenameCustomDataType(a.Type.ElementType)))
{
namedArguments = ca.NamedArguments.ToList();
}
if (arguments != null | namedArguments != null)
{
customAttributes.Add(new CustomAttributeInfo
{
customAttributes = meta.CustomAttributes,
index = index,
arguments = arguments,
namedArguments = namedArguments
});
}
++index;
}
}
private void BuildCustomAttributeArguments()
{
foreach (ModuleDef mod in _obfuscatedAndNotObfuscatedModules)
{
var customAttributes = new List<CustomAttributeInfo>();
CollectCArgumentWithTypeOf(mod, customAttributes);
foreach (TypeDef type in mod.GetTypes())
{
CollectCArgumentWithTypeOf(type, customAttributes);
foreach (FieldDef field in type.Fields)
{
CollectCArgumentWithTypeOf(field, customAttributes);
}
foreach (MethodDef method in type.Methods)
{
CollectCArgumentWithTypeOf(method, customAttributes);
foreach (Parameter param in method.Parameters)
{
if (param.ParamDef != null)
{
CollectCArgumentWithTypeOf(param.ParamDef, customAttributes);
}
}
}
foreach (PropertyDef property in type.Properties)
{
CollectCArgumentWithTypeOf(property, customAttributes);
}
foreach (EventDef eventDef in type.Events)
{
CollectCArgumentWithTypeOf (eventDef, customAttributes);
}
}
_customAttributeArgumentsWithTypeByMods.Add(mod, customAttributes);
}
}
public void Process()
{
_renameRecordMap.Init(_toObfuscatedModules, _nameMaker);
RenameTypes();
RenameFields();
RenameMethods();
RenameProperties();
RenameEvents();
}
class RefTypeDefMetas
{
public readonly List<TypeRef> typeRefs = new List<TypeRef>();
public readonly List<CustomAttribute> customAttributes = new List<CustomAttribute>();
}
private void BuildRefTypeDefMetasMap(Dictionary<TypeDef, RefTypeDefMetas> refTypeDefMetasMap)
{
foreach (ModuleDef mod in _obfuscatedAndNotObfuscatedModules)
{
foreach (TypeRef typeRef in mod.GetTypeRefs())
{
if (typeRef.DefinitionAssembly.IsCorLib())
{
continue;
}
TypeDef typeDef = typeRef.ResolveThrow();
if (!refTypeDefMetasMap.TryGetValue(typeDef, out var typeDefMetas))
{
typeDefMetas = new RefTypeDefMetas();
refTypeDefMetasMap.Add(typeDef, typeDefMetas);
}
typeDefMetas.typeRefs.Add(typeRef);
}
}
foreach (CustomAttributeInfo cai in _customAttributeArgumentsWithTypeByMods.Values.SelectMany(cas => cas))
{
CustomAttribute ca = cai.customAttributes[cai.index];
TypeDef typeDef = MetaUtil.GetTypeDefOrGenericTypeBaseThrowException(ca.Constructor.DeclaringType);
if (!refTypeDefMetasMap.TryGetValue(typeDef, out var typeDefMetas))
{
typeDefMetas = new RefTypeDefMetas();
refTypeDefMetasMap.Add(typeDef, typeDefMetas);
}
typeDefMetas.customAttributes.Add(ca);
}
}
private void RetargetTypeRefInCustomAttributes()
{
foreach (CustomAttributeInfo cai in _customAttributeArgumentsWithTypeByMods.Values.SelectMany(cas => cas))
{
CustomAttribute ca = cai.customAttributes[cai.index];
bool anyChange = false;
if (cai.arguments != null)
{
for (int i = 0; i < cai.arguments.Count; i++)
{
CAArgument oldArg = cai.arguments[i];
if (MetaUtil.TryRetargetTypeRefInArgument(oldArg, out CAArgument newArg))
{
anyChange = true;
cai.arguments[i] = newArg;
}
}
}
if (cai.namedArguments != null)
{
for (int i = 0; i < cai.namedArguments.Count; i++)
{
if (MetaUtil.TryRetargetTypeRefInNamedArgument(cai.namedArguments[i]))
{
anyChange = true;
}
}
}
if (anyChange)
{
cai.customAttributes[cai.index] = new CustomAttribute(ca.Constructor,
cai.arguments != null ? cai.arguments : ca.ConstructorArguments,
cai.namedArguments != null ? cai.namedArguments : ca.NamedArguments);
}
}
}
private readonly Dictionary<TypeDef, RefTypeDefMetas> _refTypeRefMetasMap = new Dictionary<TypeDef, RefTypeDefMetas>();
private void RenameTypes()
{
Debug.Log("RenameTypes begin");
RetargetTypeRefInCustomAttributes();
BuildRefTypeDefMetasMap(_refTypeRefMetasMap);
_assemblyCache.EnableTypeDefCache = false;
foreach (ModuleDef mod in _toObfuscatedModules)
{
foreach (TypeDef type in mod.GetTypes())
{
if (_renamePolicy.NeedRename(type))
{
Rename(type, _refTypeRefMetasMap.GetValueOrDefault(type));
}
}
}
// clean cache
_assemblyCache.EnableTypeDefCache = true;
Debug.Log("Rename Types end");
}
class RefFieldMetas
{
public readonly List<MemberRef> fieldRefs = new List<MemberRef>();
public readonly List<CustomAttribute> customAttributes = new List<CustomAttribute>();
}
private void BuildHierarchyFields(TypeDef type, List<FieldDef> fields)
{
while (type != null)
{
fields.AddRange(type.Fields);
type = MetaUtil.GetBaseTypeDef(type);
}
}
private IEnumerable<MemberRef> WalkMemberRefs(ModuleDef mod)
{
foreach (TypeDef type in mod.GetTypes())
{
foreach (MethodDef method in type.Methods)
{
if (!method.HasBody)
{
continue;
}
foreach (var instr in method.Body.Instructions)
{
if (instr.Operand is MemberRef memberRef)
{
yield return memberRef;
}
}
}
}
}
private void BuildRefFieldMetasMap(Dictionary<FieldDef, RefFieldMetas> refFieldMetasMap)
{
foreach (ModuleDef mod in _obfuscatedAndNotObfuscatedModules)
{
foreach (MemberRef memberRef in WalkMemberRefs(mod))
{
if (!memberRef.IsFieldRef)
{
continue;
}
IMemberRefParent parent = memberRef.Class;
TypeDef parentTypeDef = MetaUtil.GetMemberRefTypeDefParentOrNull(parent);
if (parentTypeDef == null)
{
continue;
}
foreach (FieldDef field in parentTypeDef.Fields)
{
if (field.Name == memberRef.Name && TypeEqualityComparer.Instance.Equals(field.FieldSig.Type, memberRef.FieldSig.Type))
{
if (!refFieldMetasMap.TryGetValue(field, out var fieldMetas))
{
fieldMetas = new RefFieldMetas();
refFieldMetasMap.Add(field, fieldMetas);
}
fieldMetas.fieldRefs.Add(memberRef);
break;
}
}
}
}
foreach (var e in _refTypeRefMetasMap)
{
TypeDef typeDef = e.Key;
var hierarchyFields = new List<FieldDef>();
BuildHierarchyFields(typeDef, hierarchyFields);
RefTypeDefMetas typeDefMetas = e.Value;
foreach (CustomAttribute ca in typeDefMetas.customAttributes)
{
foreach (var arg in ca.NamedArguments)
{
if (arg.IsProperty)
{
continue;
}
foreach (FieldDef field in hierarchyFields)
{
// FIXME. field of Generic Base Type may not be same
if (field.Name == arg.Name && TypeEqualityComparer.Instance.Equals(field.FieldType, arg.Type))
{
if (!refFieldMetasMap.TryGetValue(field, out var fieldMetas))
{
fieldMetas = new RefFieldMetas();
refFieldMetasMap.Add(field, fieldMetas);
}
fieldMetas.customAttributes.Add(ca);
break;
}
}
}
}
}
}
private void RenameFields()
{
Debug.Log("Rename fields begin");
var refFieldMetasMap = new Dictionary<FieldDef, RefFieldMetas>();
BuildRefFieldMetasMap(refFieldMetasMap);
foreach (ModuleDef mod in _toObfuscatedModules)
{
foreach (TypeDef type in mod.GetTypes())
{
foreach (FieldDef field in type.Fields)
{
if (_renamePolicy.NeedRename(field))
{
Rename(field, refFieldMetasMap.GetValueOrDefault(field));
}
}
}
}
Debug.Log("Rename fields end");
}
class RefMethodMetas
{
public readonly List<MemberRef> memberRefs = new List<MemberRef>();
}
private void BuildRefMethodMetasMap(Dictionary<MethodDef, RefMethodMetas> refMethodMetasMap)
{
foreach (ModuleDef mod in _obfuscatedAndNotObfuscatedModules)
{
foreach (MemberRef memberRef in WalkMemberRefs(mod))
{
if (!memberRef.IsMethodRef)
{
continue;
}
IMemberRefParent parent = memberRef.Class;
TypeDef parentTypeDef = MetaUtil.GetMemberRefTypeDefParentOrNull(parent);
if (parentTypeDef == null)
{
continue;
}
foreach (MethodDef method in parentTypeDef.Methods)
{
if (method.Name == memberRef.Name && new SigComparer(default).Equals(method.MethodSig, memberRef.MethodSig))
{
if (!refMethodMetasMap.TryGetValue(method, out var refMethodMetas))
{
refMethodMetas = new RefMethodMetas();
refMethodMetasMap.Add(method, refMethodMetas);
}
refMethodMetas.memberRefs.Add(memberRef);
break;
}
}
}
}
}
private void RenameMethods()
{
Debug.Log("Rename methods begin");
Debug.Log("Rename not virtual methods begin");
var virtualMethods = new List<MethodDef>();
var refMethodMetasMap = new Dictionary<MethodDef, RefMethodMetas>();
BuildRefMethodMetasMap(refMethodMetasMap);
foreach (ModuleDef mod in _toObfuscatedModules)
{
foreach (TypeDef type in mod.GetTypes())
{
foreach (MethodDef method in type.Methods)
{
if (method.IsVirtual)
{
continue;
}
if (_renamePolicy.NeedRename(method))
{
Rename(method, refMethodMetasMap.GetValueOrDefault(method));
}
}
}
}
foreach (ModuleDef mod in _obfuscatedAndNotObfuscatedModules)
{
foreach (TypeDef type in mod.GetTypes())
{
_virtualMethodGroupCalculator.CalculateType(type);
foreach (MethodDef method in type.Methods)
{
if (method.IsVirtual)
{
virtualMethods.Add(method);
}
}
}
}
Debug.Log("Rename not virtual methods end");
Debug.Log("Rename virtual methods begin");
var visitedVirtualMethods = new HashSet<MethodDef>();
var groupNeedRenames = new Dictionary<VirtualMethodGroup, bool>();
foreach (var method in virtualMethods)
{
if (!visitedVirtualMethods.Add(method))
{
continue;
}
VirtualMethodGroup group = _virtualMethodGroupCalculator.GetMethodGroup(method);
if (!groupNeedRenames.TryGetValue(group, out var needRename))
{
needRename = group.methods.All(m => _toObfuscatedModuleSet.Contains(m.DeclaringType.Module) && _renamePolicy.NeedRename(m));
groupNeedRenames.Add(group, needRename);
if (needRename)
{
_renameRecordMap.InitAndAddRename(group, _renameRecordMap.TryGetExistRenameMapping(method, out var nn) ? nn : _nameMaker.GetNewName(method, method.Name));
}
}
if (!needRename)
{
continue;
}
if (_renameRecordMap.TryGetRename(group, out var newName))
{
Rename(method, refMethodMetasMap.GetValueOrDefault(method), newName);
}
else
{
throw new Exception($"group:{group} method:{method} not found in rename record map");
}
}
Debug.Log("Rename virtual methods end");
Debug.Log("Rename methods end");
}
class RefPropertyMetas
{
public List<CustomAttribute> customAttributes = new List<CustomAttribute>();
}
private void BuildHierarchyProperties(TypeDef type, List<PropertyDef> properties)
{
while (type != null)
{
properties.AddRange(type.Properties);
type = MetaUtil.GetBaseTypeDef(type);
}
}
private void BuildRefPropertyMetasMap(Dictionary<PropertyDef, RefPropertyMetas> refPropertyMetasMap)
{
foreach (var e in _refTypeRefMetasMap)
{
TypeDef typeDef = e.Key;
var hierarchyProperties = new List<PropertyDef>();
BuildHierarchyProperties(typeDef, hierarchyProperties);
RefTypeDefMetas typeDefMetas = e.Value;
foreach (CustomAttribute ca in typeDefMetas.customAttributes)
{
foreach (var arg in ca.NamedArguments)
{
if (arg.IsField)
{
continue;
}
foreach (PropertyDef field in hierarchyProperties)
{
// FIXME. field of Generic Base Type may not be same
if (field.Name == arg.Name && TypeEqualityComparer.Instance.Equals(arg.Type, field.PropertySig.RetType))
{
if (!refPropertyMetasMap.TryGetValue(field, out var fieldMetas))
{
fieldMetas = new RefPropertyMetas();
refPropertyMetasMap.Add(field, fieldMetas);
}
fieldMetas.customAttributes.Add(ca);
break;
}
}
}
}
}
}
private void RenameProperties()
{
Debug.Log("Rename properties begin");
var refPropertyMetasMap = new Dictionary<PropertyDef, RefPropertyMetas>();
BuildRefPropertyMetasMap(refPropertyMetasMap);
foreach (ModuleDef mod in _toObfuscatedModules)
{
foreach (TypeDef type in mod.GetTypes())
{
foreach (PropertyDef property in type.Properties)
{
if (_renamePolicy.NeedRename(property))
{
Rename(property, refPropertyMetasMap.GetValueOrDefault(property));
}
}
}
}
Debug.Log("Rename properties end");
}
private void RenameEvents()
{
Debug.Log("Rename events begin");
foreach (ModuleDef mod in _toObfuscatedModules)
{
foreach (TypeDef type in mod.GetTypes())
{
foreach (EventDef eventDef in type.Events)
{
if (_renamePolicy.NeedRename(eventDef))
{
Rename(eventDef);
}
}
}
}
Debug.Log("Rename events begin");
}
//private void Rename(ModuleDef mod)
//{
// string oldName = mod.Assembly.Name;
// string newName = _renameRecordMap.TryGetExistRenameMapping(mod, out var n) ? n : _nameMaker.GetNewName(mod, oldName);
// _renameRecordMap.AddRename(mod, newName);
// mod.Assembly.Name = newName;
// mod.Name = $"{newName}.dll";
// //Debug.Log($"rename module. oldName:{oldName} newName:{newName}");
// foreach (AssemblyReferenceInfo ass in GetReferenceMeAssemblies(mod))
// {
// foreach (AssemblyRef assRef in ass.module.GetAssemblyRefs())
// {
// if (assRef.Name == oldName)
// {
// _renameRecordMap.AddRename(mod, newName);
// assRef.Name = newName;
// // Debug.Log($"rename assembly:{ass.name} ref oldName:{oldName} newName:{newName}");
// }
// }
// }
//}
private void Rename(TypeDef type, RefTypeDefMetas refTypeDefMeta)
{
string moduleName = MetaUtil.GetModuleNameWithoutExt(type.Module.Name);
string oldFullName = type.FullName;
string oldNamespace = type.Namespace;
string oldName = type.Name;
string newNamespace;
string newName;
if (_renameRecordMap.TryGetExistRenameMapping(type, out var nns, out var nn))
{
newNamespace = nns;
newName = nn;
}
else
{
newNamespace = _nameMaker.GetNewNamespace(type, oldNamespace, _useConsistentNamespaceObfuscation);
newName = _nameMaker.GetNewName(type, oldName);
}
if (refTypeDefMeta != null)
{
foreach (TypeRef typeRef in refTypeDefMeta.typeRefs)
{
Assert.AreEqual(typeRef.FullName, oldFullName);
Assert.IsTrue(typeRef.DefinitionAssembly.Name == moduleName);
if (!string.IsNullOrEmpty(oldNamespace))
{
typeRef.Namespace = newNamespace;
}
typeRef.Name = newName;
//Debug.Log($"rename assembly:{typeRef.Module.Name} reference {oldFullName} => {typeRef.FullName}");
}
}
type.Name = newName;
type.Namespace = newNamespace;
string newFullName = type.FullName;
_renameRecordMap.AddRename(type, newFullName);
//Debug.Log($"rename typedef. assembly:{type.Module.Name} oldName:{oldFullName} => newName:{newFullName}");
}
private void Rename(FieldDef field, RefFieldMetas fieldMetas)
{
string oldName = field.Name;
string newName = _renameRecordMap.TryGetExistRenameMapping(field, out var nn) ? nn : _nameMaker.GetNewName(field, oldName);
if (fieldMetas != null)
{
foreach (var memberRef in fieldMetas.fieldRefs)
{
memberRef.Name = newName;
//Debug.Log($"rename assembly:{memberRef.Module.Name} reference {field.FullName} => {memberRef.FullName}");
}
foreach (var ca in fieldMetas.customAttributes)
{
foreach (var arg in ca.NamedArguments)
{
if (arg.Name == oldName)
{
arg.Name = newName;
}
}
}
}
//Debug.Log($"rename field. {field} => {newName}");
_renameRecordMap.AddRename(field, newName);
field.Name = newName;
}
private void Rename(MethodDef method, RefMethodMetas refMethodMetas)
{
string oldName = method.Name;
string newName = _renameRecordMap.TryGetExistRenameMapping(method, out var nn) ? nn : _nameMaker.GetNewName(method, oldName);
Rename(method, refMethodMetas, newName);
}
private void Rename(MethodDef method, RefMethodMetas refMethodMetas, string newName)
{
ModuleDefMD mod = (ModuleDefMD)method.DeclaringType.Module;
RenameMethodParams(method);
RenameMethodBody(method);
if (refMethodMetas != null)
{
foreach (MemberRef memberRef in refMethodMetas.memberRefs)
{
string oldMethodFullName = memberRef.ToString();
memberRef.Name = newName;
//Debug.Log($"rename assembly:{memberRef.Module.Name} method:{oldMethodFullName} => {memberRef}");
}
}
_renameRecordMap.AddRename(method, newName);
method.Name = newName;
}
private void RenameMethodBody(MethodDef method)
{
if (method.Body == null)
{
return;
}
}
private void RenameMethodParams(MethodDef method)
{
foreach (Parameter param in method.Parameters)
{
if (param.ParamDef != null)
{
Rename(param.ParamDef);
}
}
}
private void Rename(ParamDef param)
{
if (_renamePolicy.NeedRename(param))
{
string newName = _nameMaker.GetNewName(param, param.Name);
_renameRecordMap.AddRename(param, newName);
param.Name = newName;
}
}
private void Rename(EventDef eventDef)
{
string oldName = eventDef.Name;
string newName = _renameRecordMap.TryGetExistRenameMapping(eventDef, out var nn) ? nn : _nameMaker.GetNewName(eventDef, eventDef.Name);
_renameRecordMap.AddRename(eventDef, newName);
eventDef.Name = newName;
}
private void Rename(PropertyDef property, RefPropertyMetas refPropertyMetas)
{
string oldName = property.Name;
string newName = _renameRecordMap.TryGetExistRenameMapping(property, out var nn) ? nn : _nameMaker.GetNewName(property, oldName);
if (refPropertyMetas != null)
{
foreach (var ca in refPropertyMetas.customAttributes)
{
foreach (var arg in ca.NamedArguments)
{
if (arg.Name == oldName)
{
arg.Name = newName;
}
}
}
}
_renameRecordMap.AddRename(property, newName);
property.Name = newName;
}
public void Save()
{
Directory.CreateDirectory(Path.GetDirectoryName(_mappingXmlPath));
_renameRecordMap.WriteXmlMappingFile();
}
}
}

View File

@ -0,0 +1,100 @@
using dnlib.DotNet;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.ObfusPasses.SymbolObfus
{
public class VirtualMethodGroup
{
public List<MethodDef> methods;
}
public class VirtualMethodGroupCalculator
{
private class TypeFlatMethods
{
public HashSet<MethodDef> flatMethods = new HashSet<MethodDef>();
public bool TryFindMatchVirtualMethod(MethodDef method, out MethodDef matchMethodDef)
{
foreach (var parentOrInterfaceMethod in flatMethods)
{
if (parentOrInterfaceMethod.Name == method.Name && parentOrInterfaceMethod.GetParamCount() == method.GetParamCount())
{
matchMethodDef = parentOrInterfaceMethod;
return true;
}
}
matchMethodDef = null;
return false;
}
}
private readonly Dictionary<MethodDef, VirtualMethodGroup> _methodGroups = new Dictionary<MethodDef, VirtualMethodGroup>();
private readonly Dictionary<TypeDef, TypeFlatMethods> _visitedTypes = new Dictionary<TypeDef, TypeFlatMethods>();
public VirtualMethodGroup GetMethodGroup(MethodDef methodDef)
{
if (_methodGroups.TryGetValue(methodDef, out var group))
{
return group;
}
return null;
}
public void CalculateType(TypeDef typeDef)
{
if (_visitedTypes.ContainsKey(typeDef))
{
return;
}
var typeMethods = new TypeFlatMethods();
if (typeDef.BaseType != null)
{
TypeDef baseTypeDef = MetaUtil.GetTypeDefOrGenericTypeBaseThrowException(typeDef.BaseType);
CalculateType(baseTypeDef);
typeMethods.flatMethods.AddRange(_visitedTypes[baseTypeDef].flatMethods);
foreach (var intfType in typeDef.Interfaces)
{
TypeDef intfTypeDef = MetaUtil.GetTypeDefOrGenericTypeBaseThrowException(intfType.Interface);
CalculateType(intfTypeDef);
typeMethods.flatMethods.AddRange(_visitedTypes[intfTypeDef].flatMethods);
}
}
foreach (var method in typeDef.Methods)
{
if (!method.IsVirtual)
{
continue;
}
if (typeMethods.TryFindMatchVirtualMethod(method, out var matchMethodDef))
{
var group = _methodGroups[matchMethodDef];
group.methods.Add(method);
_methodGroups.Add(method, group);
}
else
{
_methodGroups.Add(method, new VirtualMethodGroup() { methods = new List<MethodDef> { method } });
}
if (method.IsNewSlot)
{
typeMethods.flatMethods.Add(method);
}
}
_visitedTypes.Add(typeDef, typeMethods);
}
}
}

View File

@ -0,0 +1,71 @@
using dnlib.DotNet;
using Obfuz.Editor;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz
{
public class ObfuscationMethodWhitelist
{
private bool ShouldBeIgnoredByCustomAttribute(IHasCustomAttribute obj)
{
return MetaUtil.HasObfuzIgnoreAttribute(obj) || MetaUtil.HasCompilerGeneratedAttribute(obj);
}
public bool IsInWhiteList(ModuleDef module)
{
string modName = module.Assembly.Name;
if (modName == "Obfuz.Runtime")
{
return true;
}
if (ShouldBeIgnoredByCustomAttribute(module))
{
return true;
}
return false;
}
public bool IsInWhiteList(MethodDef method)
{
if (IsInWhiteList(method.DeclaringType))
{
return true;
}
if (method.Name.StartsWith(ConstValues.ObfuzInternalSymbolNamePrefix))
{
return true;
}
if (ShouldBeIgnoredByCustomAttribute(method))
{
return true;
}
return false;
}
public bool IsInWhiteList(TypeDef type)
{
if (type.Name.StartsWith(ConstValues.ObfuzInternalSymbolNamePrefix))
{
return true;
}
if (IsInWhiteList(type.Module))
{
return true;
}
if (ShouldBeIgnoredByCustomAttribute(type))
{
return true;
}
if (type.FullName == "Obfuz.EncryptionVM.GeneratedEncryptionVirtualMachine")
{
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,83 @@
using dnlib.DotNet;
using Obfuz.Data;
using Obfuz.Emit;
using Obfuz.ObfusPasses;
using Obfuz.ObfusPasses.SymbolObfus;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz
{
public delegate IRandom RandomCreator(int seed);
public class EncryptionScopeInfo
{
public readonly IEncryptor encryptor;
public readonly RandomCreator localRandomCreator;
public EncryptionScopeInfo(IEncryptor encryptor, RandomCreator localRandomCreator)
{
this.encryptor = encryptor;
this.localRandomCreator = localRandomCreator;
}
}
public class EncryptionScopeProvider
{
private readonly EncryptionScopeInfo _defaultStaticScope;
private readonly EncryptionScopeInfo _defaultDynamicScope;
private readonly HashSet<string> _dynamicSecretAssemblyNames;
public EncryptionScopeProvider(EncryptionScopeInfo defaultStaticScope, EncryptionScopeInfo defaultDynamicScope, HashSet<string> dynamicSecretAssemblyNames)
{
_defaultStaticScope = defaultStaticScope;
_defaultDynamicScope = defaultDynamicScope;
_dynamicSecretAssemblyNames = dynamicSecretAssemblyNames;
}
public EncryptionScopeInfo GetScope(ModuleDef module)
{
if (_dynamicSecretAssemblyNames.Contains(module.Assembly.Name))
{
return _defaultDynamicScope;
}
else
{
return _defaultStaticScope;
}
}
public bool IsDynamicSecretAssembly(ModuleDef module)
{
return _dynamicSecretAssemblyNames.Contains(module.Assembly.Name);
}
}
public class ObfuscationPassContext
{
public static ObfuscationPassContext Current { get; set; }
public GroupByModuleEntityManager moduleEntityManager;
public AssemblyCache assemblyCache;
public List<ModuleDef> modulesToObfuscate;
public List<ModuleDef> allObfuscationRelativeModules;
public List<string> assembliesToObfuscate;
public List<string> nonObfuscatedButReferencingObfuscatedAssemblies;
public string obfuscatedAssemblyOutputPath;
public EncryptionScopeProvider encryptionScopeProvider;
public ConstFieldAllocator constFieldAllocator;
public RvaDataAllocator rvaDataAllocator;
public ObfuscationMethodWhitelist whiteList;
public ConfigurablePassPolicy passPolicy;
}
}

View File

@ -0,0 +1,337 @@
using dnlib.DotNet;
using Obfuz.Data;
using Obfuz.Emit;
using Obfuz.EncryptionVM;
using Obfuz.ObfusPasses;
using Obfuz.ObfusPasses.CleanUp;
using Obfuz.ObfusPasses.SymbolObfus;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Obfuz
{
public class Obfuscator
{
private readonly string _obfuscatedAssemblyOutputPath;
private readonly List<string> _assembliesToObfuscate;
private readonly List<string> _nonObfuscatedButReferencingObfuscatedAssemblies;
private readonly List<string> _assemblySearchPaths;
private readonly ConfigurablePassPolicy _passPolicy;
private readonly Pipeline _pipeline1 = new Pipeline();
private readonly Pipeline _pipeline2 = new Pipeline();
private readonly byte[] _defaultStaticByteSecretKey;
private readonly byte[] _defaultDynamicByteSecret;
private readonly HashSet<string> _assembliesUsingDynamicSecretKeys;
private readonly int _randomSeed;
private readonly string _encryptionVmGenerationSecretKey;
private readonly int _encryptionVmOpCodeCount;
private readonly string _encryptionVmCodeFile;
private ObfuscationPassContext _ctx;
public Obfuscator(ObfuscatorBuilder builder)
{
_defaultStaticByteSecretKey = KeyGenerator.GenerateKey(builder.DefaultStaticSecretKey, VirtualMachine.SecretKeyLength);
_defaultDynamicByteSecret = KeyGenerator.GenerateKey(builder.DefaultDynamicSecretKey, VirtualMachine.SecretKeyLength);
_assembliesUsingDynamicSecretKeys = new HashSet<string>(builder.AssembliesUsingDynamicSecretKeys);
_randomSeed = builder.RandomSeed;
_encryptionVmGenerationSecretKey = builder.EncryptionVmGenerationSecretKey;
_encryptionVmOpCodeCount = builder.EncryptionVmOpCodeCount;
_encryptionVmCodeFile = builder.EncryptionVmCodeFile;
_assembliesToObfuscate = builder.AssembliesToObfuscate;
_nonObfuscatedButReferencingObfuscatedAssemblies = builder.NonObfuscatedButReferencingObfuscatedAssemblies;
_obfuscatedAssemblyOutputPath = builder.ObfuscatedAssemblyOutputPath;
_assemblySearchPaths = builder.AssemblySearchPaths;
_passPolicy = new ConfigurablePassPolicy(_assembliesToObfuscate, builder.EnableObfuscationPasses, builder.ObfuscationPassRuleConfigFiles);
foreach (var pass in builder.ObfuscationPasses)
{
if (pass is SymbolObfusPass symbolObfusPass)
{
_pipeline2.AddPass(pass);
}
else
{
_pipeline1.AddPass(pass);
}
}
_pipeline1.AddPass(new CleanUpInstructionPass());
_pipeline2.AddPass(new RemoveObfuzAttributesPass());
}
public void Run()
{
FileUtil.RecreateDir(_obfuscatedAssemblyOutputPath);
RunPipeline(_pipeline1);
_assemblySearchPaths.Insert(0, _obfuscatedAssemblyOutputPath);
RunPipeline(_pipeline2);
}
private void RunPipeline(Pipeline pipeline)
{
if (pipeline.Empty)
{
return;
}
OnPreObfuscation(pipeline);
DoObfuscation(pipeline);
OnPostObfuscation(pipeline);
}
private IEncryptor CreateEncryptionVirtualMachine(byte[] secretKey)
{
var vmCreator = new VirtualMachineCreator(_encryptionVmGenerationSecretKey);
var vm = vmCreator.CreateVirtualMachine(_encryptionVmOpCodeCount);
var vmGenerator = new VirtualMachineCodeGenerator(vm);
if (!File.Exists(_encryptionVmCodeFile))
{
throw new Exception($"EncryptionVm CodeFile:`{_encryptionVmCodeFile}` not exists! Please run `Obfuz/GenerateVm` to generate it!");
}
if (!vmGenerator.ValidateMatch(_encryptionVmCodeFile))
{
throw new Exception($"EncryptionVm CodeFile:`{_encryptionVmCodeFile}` not match with encryptionVM settings! Please run `Obfuz/GenerateVm` to update it!");
}
var vms = new VirtualMachineSimulator(vm, secretKey);
var generatedVmTypes = AppDomain.CurrentDomain.GetAssemblies()
.Select(assembly => assembly.GetType("Obfuz.EncryptionVM.GeneratedEncryptionVirtualMachine"))
.Where(type => type != null)
.ToList();
if (generatedVmTypes.Count == 0)
{
throw new Exception($"class Obfuz.EncryptionVM.GeneratedEncryptionVirtualMachine not found in any assembly! Please run `Obfuz/GenerateVm` to generate it!");
}
if (generatedVmTypes.Count > 1)
{
throw new Exception($"class Obfuz.EncryptionVM.GeneratedEncryptionVirtualMachine found in multiple assemblies! Please retain only one!");
}
var gvmInstance = (IEncryptor)Activator.CreateInstance(generatedVmTypes[0], new object[] { secretKey } );
VerifyVm(vm, vms, gvmInstance);
return vms;
}
private void VerifyVm(VirtualMachine vm, VirtualMachineSimulator vms, IEncryptor gvm)
{
int testInt = 11223344;
long testLong = 1122334455667788L;
float testFloat = 1234f;
double testDouble = 1122334455.0;
string testString = "hello,world";
for (int i = 0; i < vm.opCodes.Length; i++)
{
int ops = i * vm.opCodes.Length + i;
//int salt = i;
//int ops = -1135538782;
int salt = -879409147;
{
int encryptedIntOfVms = vms.Encrypt(testInt, ops, salt);
int decryptedIntOfVms = vms.Decrypt(encryptedIntOfVms, ops, salt);
if (decryptedIntOfVms != testInt)
{
throw new Exception($"VirtualMachineSimulator decrypt failed! opCode:{i}, originalValue:{testInt} decryptedValue:{decryptedIntOfVms}");
}
int encryptedValueOfGvm = gvm.Encrypt(testInt, ops, salt);
int decryptedValueOfGvm = gvm.Decrypt(encryptedValueOfGvm, ops, salt);
if (encryptedValueOfGvm != encryptedIntOfVms)
{
throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testInt} encryptedValue VirtualMachineSimulator:{encryptedIntOfVms} GeneratedEncryptionVirtualMachine:{encryptedValueOfGvm}");
}
if (decryptedValueOfGvm != testInt)
{
throw new Exception($"GeneratedEncryptionVirtualMachine decrypt failed! opCode:{i}, originalValue:{testInt} decryptedValue:{decryptedValueOfGvm}");
}
}
{
long encryptedLongOfVms = vms.Encrypt(testLong, ops, salt);
long decryptedLongOfVms = vms.Decrypt(encryptedLongOfVms, ops, salt);
if (decryptedLongOfVms != testLong)
{
throw new Exception($"VirtualMachineSimulator decrypt long failed! opCode:{i}, originalValue:{testLong} decryptedValue:{decryptedLongOfVms}");
}
long encryptedValueOfGvm = gvm.Encrypt(testLong, ops, salt);
long decryptedValueOfGvm = gvm.Decrypt(encryptedValueOfGvm, ops, salt);
if (encryptedValueOfGvm != encryptedLongOfVms)
{
throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testLong} encryptedValue VirtualMachineSimulator:{encryptedLongOfVms} GeneratedEncryptionVirtualMachine:{encryptedValueOfGvm}");
}
if (decryptedValueOfGvm != testLong)
{
throw new Exception($"GeneratedEncryptionVirtualMachine decrypt long failed! opCode:{i}, originalValue:{testLong} decryptedValue:{decryptedValueOfGvm}");
}
}
{
float encryptedFloatOfVms = vms.Encrypt(testFloat, ops, salt);
float decryptedFloatOfVms = vms.Decrypt(encryptedFloatOfVms, ops, salt);
if (decryptedFloatOfVms != testFloat)
{
throw new Exception("encryptedFloat not match");
}
float encryptedValueOfGvm = gvm.Encrypt(testFloat, ops, salt);
float decryptedValueOfGvm = gvm.Decrypt(encryptedFloatOfVms, ops, salt);
if (encryptedFloatOfVms != encryptedValueOfGvm)
{
throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testFloat} encryptedValue");
}
if (decryptedValueOfGvm != testFloat)
{
throw new Exception($"GeneratedEncryptionVirtualMachine decrypt float failed! opCode:{i}, originalValue:{testFloat}");
}
}
{
double encryptedFloatOfVms = vms.Encrypt(testDouble, ops, salt);
double decryptedFloatOfVms = vms.Decrypt(encryptedFloatOfVms, ops, salt);
if (decryptedFloatOfVms != testDouble)
{
throw new Exception("encryptedFloat not match");
}
double encryptedValueOfGvm = gvm.Encrypt(testDouble, ops, salt);
double decryptedValueOfGvm = gvm.Decrypt(encryptedFloatOfVms, ops, salt);
if (encryptedFloatOfVms != encryptedValueOfGvm)
{
throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testDouble} encryptedValue");
}
if (decryptedValueOfGvm != testDouble)
{
throw new Exception($"GeneratedEncryptionVirtualMachine decrypt float failed! opCode:{i}, originalValue:{testDouble}");
}
}
{
byte[] encryptedStrOfVms = vms.Encrypt(testString, ops, salt);
string decryptedStrOfVms = vms.DecryptString(encryptedStrOfVms, 0, encryptedStrOfVms.Length, ops, salt);
if (decryptedStrOfVms != testString)
{
throw new Exception($"VirtualMachineSimulator decrypt string failed! opCode:{i}, originalValue:{testString} decryptedValue:{decryptedStrOfVms}");
}
byte[] encryptedStrOfGvm = gvm.Encrypt(testString, ops, salt);
string decryptedStrOfGvm = gvm.DecryptString(encryptedStrOfGvm, 0, encryptedStrOfGvm.Length, ops, salt);
if (!encryptedStrOfGvm.SequenceEqual(encryptedStrOfVms))
{
throw new Exception($"encryptedValue not match! opCode:{i}, originalValue:{testString} encryptedValue VirtualMachineSimulator:{encryptedStrOfVms} GeneratedEncryptionVirtualMachine:{encryptedStrOfGvm}");
}
if (decryptedStrOfGvm != testString)
{
throw new Exception($"GeneratedEncryptionVirtualMachine decrypt string failed! opCode:{i}, originalValue:{testString} decryptedValue:{decryptedStrOfGvm}");
}
}
}
}
private EncryptionScopeInfo CreateEncryptionScope(byte[] byteSecret)
{
int[] intSecretKey = KeyGenerator.ConvertToIntKey(byteSecret);
IEncryptor encryption = CreateEncryptionVirtualMachine(byteSecret);
RandomCreator localRandomCreator = (seed) => new RandomWithKey(intSecretKey, _randomSeed ^ seed);
return new EncryptionScopeInfo(encryption, localRandomCreator);
}
private EncryptionScopeProvider CreateEncryptionScopeProvider()
{
var defaultStaticScope = CreateEncryptionScope(_defaultStaticByteSecretKey);
var defaultDynamicScope = CreateEncryptionScope(_defaultDynamicByteSecret);
foreach (string dynamicAssName in _assembliesUsingDynamicSecretKeys)
{
if (!_assembliesToObfuscate.Contains(dynamicAssName))
{
throw new Exception($"Dynamic secret assembly `{dynamicAssName}` should be in the toObfuscatedAssemblyNames list!");
}
}
return new EncryptionScopeProvider(defaultStaticScope, defaultDynamicScope, _assembliesUsingDynamicSecretKeys);
}
private void OnPreObfuscation(Pipeline pipeline)
{
AssemblyCache assemblyCache = new AssemblyCache(new PathAssemblyResolver(_assemblySearchPaths.ToArray()));
List<ModuleDef> modulesToObfuscate = new List<ModuleDef>();
List<ModuleDef> allObfuscationRelativeModules = new List<ModuleDef>();
LoadAssemblies(assemblyCache, modulesToObfuscate, allObfuscationRelativeModules);
EncryptionScopeProvider encryptionScopeProvider = CreateEncryptionScopeProvider();
var moduleEntityManager = new GroupByModuleEntityManager();
var rvaDataAllocator = new RvaDataAllocator(encryptionScopeProvider, moduleEntityManager);
var constFieldAllocator = new ConstFieldAllocator(encryptionScopeProvider, rvaDataAllocator, moduleEntityManager);
_ctx = new ObfuscationPassContext
{
assemblyCache = assemblyCache,
modulesToObfuscate = modulesToObfuscate,
allObfuscationRelativeModules = allObfuscationRelativeModules,
assembliesToObfuscate = _assembliesToObfuscate,
nonObfuscatedButReferencingObfuscatedAssemblies = _nonObfuscatedButReferencingObfuscatedAssemblies,
obfuscatedAssemblyOutputPath = _obfuscatedAssemblyOutputPath,
moduleEntityManager = moduleEntityManager,
encryptionScopeProvider = encryptionScopeProvider,
rvaDataAllocator = rvaDataAllocator,
constFieldAllocator = constFieldAllocator,
whiteList = new ObfuscationMethodWhitelist(),
passPolicy = _passPolicy,
};
ObfuscationPassContext.Current = _ctx;
pipeline.Start();
}
private void LoadAssemblies(AssemblyCache assemblyCache, List<ModuleDef> modulesToObfuscate, List<ModuleDef> allObfuscationRelativeModules)
{
foreach (string assName in _assembliesToObfuscate.Concat(_nonObfuscatedButReferencingObfuscatedAssemblies))
{
ModuleDefMD mod = assemblyCache.TryLoadModule(assName);
if (mod == null)
{
Debug.Log($"assembly: {assName} not found! ignore.");
continue;
}
if (_assembliesToObfuscate.Contains(assName))
{
modulesToObfuscate.Add(mod);
}
allObfuscationRelativeModules.Add(mod);
}
}
private void WriteAssemblies()
{
foreach (ModuleDef mod in _ctx.allObfuscationRelativeModules)
{
string assNameWithExt = mod.Name;
string outputFile = $"{_obfuscatedAssemblyOutputPath}/{assNameWithExt}";
mod.Write(outputFile);
Debug.Log($"save module. name:{mod.Assembly.Name} output:{outputFile}");
}
}
private void DoObfuscation(Pipeline pipeline)
{
pipeline.Run();
}
private void OnPostObfuscation(Pipeline pipeline)
{
pipeline.Stop();
_ctx.constFieldAllocator.Done();
_ctx.rvaDataAllocator.Done();
WriteAssemblies();
}
}
}

View File

@ -0,0 +1,216 @@
using Obfuz.EncryptionVM;
using Obfuz.ObfusPasses;
using Obfuz.ObfusPasses.CallObfus;
using Obfuz.ObfusPasses.ConstEncrypt;
using Obfuz.ObfusPasses.ExprObfus;
using Obfuz.ObfusPasses.FieldEncrypt;
using Obfuz.ObfusPasses.SymbolObfus;
using Obfuz.Settings;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
namespace Obfuz
{
public class ObfuscatorBuilder
{
private string _defaultStaticSecretKey;
private string _defaultStaticSecretKeyOutputPath;
private string _defaultDynamicSecretKey;
private string _defaultDynamicSecretKeyOutputPath;
private List<string> _assembliesUsingDynamicSecretKeys = new List<string>();
private int _randomSeed;
private string _encryptionVmGenerationSecretKey;
private int _encryptionVmOpCodeCount;
private string _encryptionVmCodeFile;
private List<string> _assembliesToObfuscate = new List<string>();
private List<string> _nonObfuscatedButReferencingObfuscatedAssemblies = new List<string>();
private List<string> _assemblySearchPaths = new List<string>();
private string _obfuscatedAssemblyOutputPath;
private List<string> _obfuscationPassRuleConfigFiles;
private ObfuscationPassType _enabledObfuscationPasses;
private List<IObfuscationPass> _obfuscationPasses = new List<IObfuscationPass>();
public string DefaultStaticSecretKey
{
get => _defaultStaticSecretKey;
set => _defaultStaticSecretKey = value;
}
public string DefaultStaticSecretKeyOutputPath
{
get => _defaultStaticSecretKeyOutputPath;
set => _defaultStaticSecretKeyOutputPath = value;
}
public string DefaultDynamicSecretKey
{
get => _defaultDynamicSecretKey;
set => _defaultDynamicSecretKey = value;
}
public string DefaultDynamicSecretKeyOutputPath
{
get => _defaultDynamicSecretKeyOutputPath;
set => _defaultDynamicSecretKeyOutputPath = value;
}
public List<string> AssembliesUsingDynamicSecretKeys
{
get => _assembliesUsingDynamicSecretKeys;
set => _assembliesUsingDynamicSecretKeys = value;
}
public int RandomSeed
{
get => _randomSeed;
set => _randomSeed = value;
}
public string EncryptionVmGenerationSecretKey
{
get => _encryptionVmGenerationSecretKey;
set => _encryptionVmGenerationSecretKey = value;
}
public int EncryptionVmOpCodeCount
{
get => _encryptionVmOpCodeCount;
set => _encryptionVmOpCodeCount = value;
}
public string EncryptionVmCodeFile
{
get => _encryptionVmCodeFile;
set => _encryptionVmCodeFile = value;
}
public List<string> AssembliesToObfuscate
{
get => _assembliesToObfuscate;
set => _assembliesToObfuscate = value;
}
public List<string> NonObfuscatedButReferencingObfuscatedAssemblies
{
get => _nonObfuscatedButReferencingObfuscatedAssemblies;
set => _nonObfuscatedButReferencingObfuscatedAssemblies = value;
}
public List<string> AssemblySearchPaths
{
get => _assemblySearchPaths;
set => _assemblySearchPaths = value;
}
public string ObfuscatedAssemblyOutputPath
{
get => _obfuscatedAssemblyOutputPath;
set => _obfuscatedAssemblyOutputPath = value;
}
public ObfuscationPassType EnableObfuscationPasses
{
get => _enabledObfuscationPasses;
set => _enabledObfuscationPasses = value;
}
public List<string> ObfuscationPassRuleConfigFiles
{
get => _obfuscationPassRuleConfigFiles;
set => _obfuscationPassRuleConfigFiles = value;
}
public List<IObfuscationPass> ObfuscationPasses
{
get => _obfuscationPasses;
set => _obfuscationPasses = value;
}
public void InsertTopPriorityAssemblySearchPaths(List<string> assemblySearchPaths)
{
_assemblySearchPaths.InsertRange(0, assemblySearchPaths);
}
public ObfuscatorBuilder AddPass(IObfuscationPass pass)
{
_obfuscationPasses.Add(pass);
return this;
}
public Obfuscator Build()
{
return new Obfuscator(this);
}
public static List<string> BuildUnityAssemblySearchPaths()
{
string applicationContentsPath = EditorApplication.applicationContentsPath;
return new List<string>
{
#if UNITY_2021_1_OR_NEWER
Path.Combine(applicationContentsPath, "UnityReferenceAssemblies/unity-4.8-api/Facades"),
Path.Combine(applicationContentsPath, "UnityReferenceAssemblies/unity-4.8-api"),
#elif UNITY_2020 || UNITY_2019
Path.Combine(applicationContentsPath, "MonoBleedingEdge/lib/mono/4.7.1-api/Facades"),
Path.Combine(applicationContentsPath, "MonoBleedingEdge/lib/mono/4.7.1-api"),
#else
#error "Unsupported Unity version"
#endif
Path.Combine(applicationContentsPath, "Managed/UnityEngine"),
};
}
public static ObfuscatorBuilder FromObfuzSettings(ObfuzSettings settings, BuildTarget target, bool searchPathIncludeUnityEditorInstallLocation)
{
List<string> searchPaths = searchPathIncludeUnityEditorInstallLocation ?
BuildUnityAssemblySearchPaths().Concat(settings.assemblySettings.additionalAssemblySearchPaths).ToList()
: settings.assemblySettings.additionalAssemblySearchPaths.ToList();
var builder = new ObfuscatorBuilder
{
_defaultStaticSecretKey = settings.secretSettings.defaultStaticSecretKey,
_defaultStaticSecretKeyOutputPath = settings.secretSettings.DefaultStaticSecretKeyOutputPath,
_defaultDynamicSecretKey = settings.secretSettings.defaultDynamicSecretKey,
_defaultDynamicSecretKeyOutputPath = settings.secretSettings.DefaultDynamicSecretKeyOutputPath,
_assembliesUsingDynamicSecretKeys = settings.secretSettings.assembliesUsingDynamicSecretKeys.ToList(),
_randomSeed = settings.secretSettings.randomSeed,
_encryptionVmGenerationSecretKey = settings.encryptionVMSettings.codeGenerationSecretKey,
_encryptionVmOpCodeCount = settings.encryptionVMSettings.encryptionOpCodeCount,
_encryptionVmCodeFile = settings.encryptionVMSettings.codeOutputPath,
_assembliesToObfuscate = settings.assemblySettings.assembliesToObfuscate.ToList(),
_nonObfuscatedButReferencingObfuscatedAssemblies = settings.assemblySettings.nonObfuscatedButReferencingObfuscatedAssemblies.ToList(),
_assemblySearchPaths = searchPaths,
_obfuscatedAssemblyOutputPath = settings.GetObfuscatedAssemblyOutputPath(target),
_enabledObfuscationPasses = settings.obfuscationPassSettings.enabledPasses,
_obfuscationPassRuleConfigFiles = settings.obfuscationPassSettings.ruleFiles.ToList(),
};
ObfuscationPassType obfuscationPasses = settings.obfuscationPassSettings.enabledPasses;
if (obfuscationPasses.HasFlag(ObfuscationPassType.ConstEncrypt))
{
builder.AddPass(new ConstEncryptPass(settings.constEncryptSettings));
}
if (obfuscationPasses.HasFlag(ObfuscationPassType.FieldEncrypt))
{
builder.AddPass(new FieldEncryptPass(settings.fieldEncryptSettings));
}
if (obfuscationPasses.HasFlag(ObfuscationPassType.CallObfus))
{
builder.AddPass(new CallObfusPass(settings.callObfusSettings));
}
if (obfuscationPasses.HasFlag(ObfuscationPassType.ExprObfus))
{
builder.AddPass(new ExprObfusPass());
}
if (obfuscationPasses.HasFlag(ObfuscationPassType.SymbolObfus))
{
builder.AddPass(new SymbolObfusPass(settings.symbolObfusSettings));
}
return builder;
}
}
}

View File

@ -0,0 +1,18 @@
{
"name": "Obfuz.Editor",
"rootNamespace": "",
"references": [
"GUID:4140bd2e2764f1f47ab93125ecb61942"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,43 @@
using Obfuz.ObfusPasses;
using System.Collections.Generic;
namespace Obfuz
{
public class Pipeline
{
private readonly List<IObfuscationPass> _passes = new List<IObfuscationPass>();
public bool Empty => _passes.Count == 0;
public Pipeline AddPass(IObfuscationPass pass)
{
_passes.Add(pass);
return this;
}
public void Start()
{
foreach (var pass in _passes)
{
pass.Start();
}
}
public void Stop()
{
foreach (var pass in _passes)
{
pass.Stop();
}
}
public void Run()
{
foreach (var pass in _passes)
{
pass.Process();
}
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Linq;
using UnityEngine;
namespace Obfuz.Settings
{
[Serializable]
public class AssemblySettings
{
[Tooltip("name of assemblies to obfuscate")]
public string[] assembliesToObfuscate;
[Tooltip("name of assemblies not obfuscated but reference assemblies to obfuscated ")]
public string[] nonObfuscatedButReferencingObfuscatedAssemblies;
[Tooltip("additional assembly search paths")]
public string[] additionalAssemblySearchPaths;
public string[] GetObfuscationRelativeAssemblyNames()
{
return assembliesToObfuscate
.Concat(nonObfuscatedButReferencingObfuscatedAssemblies)
.ToArray();
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Obfuz.Settings
{
[Serializable]
public class CallObfuscationSettings
{
[Tooltip("The obfuscation level for the obfuscation. Higher levels provide more security but may impact performance.")]
[Range(1, 4)]
public int obfuscationLevel = 1;
[Tooltip("rule config xml files")]
public string[] ruleFiles;
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Obfuz.Settings
{
[Serializable]
public class ConstEncryptionSettings
{
[Tooltip("The encryption level for the obfuscation. Higher levels provide more security but may impact performance.")]
[Range(1, 4)]
public int encryptionLevel = 1;
[Tooltip("config xml files")]
public string[] ruleFiles;
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Obfuz.Settings
{
[Serializable]
public class EncryptionVMSettings
{
[Tooltip("secret key for generating encryption virtual machine source code")]
public string codeGenerationSecretKey = "Obfuz";
[Tooltip("encryption OpCode count, should be power of 2 and >= 64")]
public int encryptionOpCodeCount = 256;
[Tooltip("encryption virtual machine source code output path")]
public string codeOutputPath = "Assets/Obfuz/GeneratedEncryptionVirtualMachine.cs";
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Obfuz.Settings
{
[Serializable]
public class FieldEncryptionSettings
{
[Tooltip("The encryption level for the obfuscation. Higher levels provide more security but may impact performance.")]
[Range(1, 4)]
public int encryptionLevel = 1;
[Tooltip("rule config xml files")]
public string[] ruleFiles;
}
}

View File

@ -0,0 +1,16 @@
using Obfuz.ObfusPasses;
using System;
using UnityEngine;
namespace Obfuz.Settings
{
[Serializable]
public class ObfuscationPassSettings
{
[Tooltip("enable obfuscation pass")]
public ObfuscationPassType enabledPasses = ObfuscationPassType.All;
[Tooltip("rule config xml files")]
public string[] ruleFiles;
}
}

View File

@ -0,0 +1,94 @@
using System.Collections.Generic;
using System.IO;
using System.Runtime.Remoting.Messaging;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace Obfuz.Settings
{
public class ObfuzSettings : ScriptableObject
{
[Tooltip("enable Obfuz")]
public bool enable = true;
[Tooltip("assembly settings")]
public AssemblySettings assemblySettings;
[Tooltip("obfuscation pass settings")]
public ObfuscationPassSettings obfuscationPassSettings;
[Tooltip("secret settings")]
public SecretSettings secretSettings;
[Tooltip("encryption virtual machine settings")]
public EncryptionVMSettings encryptionVMSettings;
[Tooltip("symbol obfuscation settings")]
public SymbolObfuscationSettings symbolObfusSettings;
[Tooltip("const encryption settings")]
public ConstEncryptionSettings constEncryptSettings;
[Tooltip("field encryption settings")]
public FieldEncryptionSettings fieldEncryptSettings;
[Tooltip("call obfuscation settings")]
public CallObfuscationSettings callObfusSettings;
public string ObfuzRootDir => $"Library/Obfuz";
public string GetObfuscatedAssemblyOutputPath(BuildTarget target)
{
return $"{ObfuzRootDir}/{target}/ObfuscatedAssemblies";
}
public string GetOriginalAssemblyBackupDir(BuildTarget target)
{
return $"{ObfuzRootDir}/{target}/OriginalAssemblies";
}
private static ObfuzSettings s_Instance;
public static ObfuzSettings Instance
{
get
{
if (!s_Instance)
{
LoadOrCreate();
}
return s_Instance;
}
}
protected static string SettingsPath => "ProjectSettings/Obfuz.asset";
private static ObfuzSettings LoadOrCreate()
{
string filePath = SettingsPath;
var arr = InternalEditorUtility.LoadSerializedFileAndForget(filePath);
//Debug.Log($"typeof arr:{arr?.GetType()} arr[0]:{(arr != null && arr.Length > 0 ? arr[0].GetType(): null)}");
s_Instance = arr != null && arr.Length > 0 ? (ObfuzSettings)arr[0] : (s_Instance ?? CreateInstance<ObfuzSettings>());
return s_Instance;
}
public static void Save()
{
if (!s_Instance)
{
Debug.LogError("Cannot save ScriptableSingleton: no instance!");
return;
}
string filePath = SettingsPath;
string directoryName = Path.GetDirectoryName(filePath);
Directory.CreateDirectory(directoryName);
UnityEngine.Object[] obj = new ObfuzSettings[1] { s_Instance };
InternalEditorUtility.SaveToSerializedFileAndForget(obj, filePath, true);
}
}
}

View File

@ -0,0 +1,104 @@
using System;
using System.Reflection;
using UnityEditor;
using UnityEditor.Presets;
using UnityEngine;
using UnityEngine.UIElements;
namespace Obfuz.Settings
{
public class ObfuzSettingsProvider : SettingsProvider
{
private static ObfuzSettingsProvider s_provider;
[SettingsProvider]
public static SettingsProvider CreateMyCustomSettingsProvider()
{
if (s_provider == null)
{
s_provider = new ObfuzSettingsProvider();
using (var so = new SerializedObject(ObfuzSettings.Instance))
{
s_provider.keywords = GetSearchKeywordsFromSerializedObject(so);
}
}
return s_provider;
}
private SerializedObject _serializedObject;
private SerializedProperty _enable;
private SerializedProperty _assemblySettings;
private SerializedProperty _obfuscationPassSettings;
private SerializedProperty _secretSettings;
private SerializedProperty _encryptionVMSettings;
private SerializedProperty _symbolObfusSettings;
private SerializedProperty _constEncryptSettings;
private SerializedProperty _fieldEncryptSettings;
private SerializedProperty _callObfusSettings;
public ObfuzSettingsProvider() : base("Project/Obfuz", SettingsScope.Project)
{
}
public override void OnActivate(string searchContext, VisualElement rootElement)
{
InitGUI();
}
public override void OnDeactivate()
{
base.OnDeactivate();
ObfuzSettings.Save();
}
private void InitGUI()
{
var setting = ObfuzSettings.Instance;
_serializedObject?.Dispose();
_serializedObject = new SerializedObject(setting);
_enable = _serializedObject.FindProperty("enable");
_assemblySettings = _serializedObject.FindProperty("assemblySettings");
_obfuscationPassSettings = _serializedObject.FindProperty("obfuscationPassSettings");
_secretSettings = _serializedObject.FindProperty("secretSettings");
_encryptionVMSettings = _serializedObject.FindProperty("encryptionVMSettings");
_symbolObfusSettings = _serializedObject.FindProperty("symbolObfusSettings");
_constEncryptSettings = _serializedObject.FindProperty("constEncryptSettings");
_fieldEncryptSettings = _serializedObject.FindProperty("fieldEncryptSettings");
_callObfusSettings = _serializedObject.FindProperty("callObfusSettings");
}
public override void OnGUI(string searchContext)
{
if (_serializedObject == null||!_serializedObject.targetObject)
{
InitGUI();
}
_serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(_enable);
EditorGUILayout.PropertyField(_assemblySettings);
EditorGUILayout.PropertyField(_obfuscationPassSettings);
EditorGUILayout.PropertyField(_secretSettings);
EditorGUILayout.PropertyField(_encryptionVMSettings);
EditorGUILayout.PropertyField(_symbolObfusSettings);
EditorGUILayout.PropertyField(_constEncryptSettings);
EditorGUILayout.PropertyField(_fieldEncryptSettings);
EditorGUILayout.PropertyField(_callObfusSettings);
if (EditorGUI.EndChangeCheck())
{
_serializedObject.ApplyModifiedProperties();
ObfuzSettings.Save();
}
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.IO;
using UnityEngine;
namespace Obfuz.Settings
{
[Serializable]
public class SecretSettings
{
[Tooltip("default static secret key")]
public string defaultStaticSecretKey = "Code Philosophy-Static";
[Tooltip("default dynamic secret key")]
public string defaultDynamicSecretKey = "Code Philosophy-Dynamic";
[Tooltip("secret key output path")]
public string secretKeyOutputPath = $"Assets/Resources/Obfuz";
[Tooltip("random seed")]
public int randomSeed = 0;
[Tooltip("name of assemblies those use dynamic secret key")]
public string[] assembliesUsingDynamicSecretKeys;
public string DefaultStaticSecretKeyOutputPath => Path.Combine(secretKeyOutputPath, "defaultStaticSecret.bytes");
public string DefaultDynamicSecretKeyOutputPath => Path.Combine(secretKeyOutputPath, "defaultDynamicSecret.bytes");
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Obfuz.Settings
{
[Serializable]
public class SymbolObfuscationSettings
{
public bool debug;
[Tooltip("prefix for obfuscated name to avoid name confliction with original name")]
public string obfuscatedNamePrefix = "$";
[Tooltip("obfuscate same namespace to one name")]
public bool useConsistentNamespaceObfuscation = true;
[Tooltip("symbol mapping file path")]
public string symbolMappingFile = "Assets/Obfuz/SymbolObfus/symbol-mapping.xml";
[Tooltip("rule files")]
public string[] ruleFiles;
}
}

View File

@ -0,0 +1,8 @@
namespace Obfuz.Unity
{
public class ObfuscationBeginEventArgs
{
public string scriptAssembliesPath;
public string obfuscatedScriptAssembliesPath;
}
}

View File

@ -0,0 +1,9 @@
namespace Obfuz.Unity
{
public class ObfuscationEndEventArgs
{
public bool success;
public string originalScriptAssembliesPath;
public string obfuscatedScriptAssembliesPath;
}
}

View File

@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEditor.Build;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;
using UnityEditor.Compilation;
using Obfuz.Utils;
using FileUtil = Obfuz.Utils.FileUtil;
using Obfuz.Settings;
namespace Obfuz.Unity
{
#if UNITY_2019_1_OR_NEWER
public class ObfuscationProcess : IPostBuildPlayerScriptDLLs
{
public int callbackOrder => 10000;
public static event Action<ObfuscationBeginEventArgs> OnObfuscationBegin;
public static event Action<ObfuscationEndEventArgs> OnObfuscationEnd;
public void OnPostBuildPlayerScriptDLLs(BuildReport report)
{
#if !UNITY_2022_1_OR_NEWER
RunObfuscate(report.files);
#else
RunObfuscate(report.GetFiles());
#endif
}
private static void BackupOriginalDlls(string srcDir, string dstDir, HashSet<string> dllNames)
{
FileUtil.RecreateDir(dstDir);
foreach (string dllName in dllNames)
{
string srcFile = Path.Combine(srcDir, dllName);
string dstFile = Path.Combine(dstDir, dllName);
if (File.Exists(srcFile))
{
File.Copy(srcFile, dstFile, true);
Debug.Log($"BackupOriginalDll {srcFile} -> {dstFile}");
}
}
}
private static void RunObfuscate(BuildFile[] files)
{
ObfuzSettings settings = ObfuzSettings.Instance;
if (!settings.enable)
{
Debug.Log("Obfuscation is disabled.");
return;
}
Debug.Log("Obfuscation begin...");
var buildTarget = EditorUserBuildSettings.activeBuildTarget;
var obfuscationRelativeAssemblyNames = new HashSet<string>(settings.assemblySettings.GetObfuscationRelativeAssemblyNames());
string stagingAreaTempManagedDllDir = Path.GetDirectoryName(files.First(file => file.path.EndsWith(".dll")).path);
string backupPlayerScriptAssembliesPath = settings.GetOriginalAssemblyBackupDir(buildTarget);
BackupOriginalDlls(stagingAreaTempManagedDllDir, backupPlayerScriptAssembliesPath, obfuscationRelativeAssemblyNames);
string applicationContentsPath = EditorApplication.applicationContentsPath;
var obfuscatorBuilder = ObfuscatorBuilder.FromObfuzSettings(settings, buildTarget, false);
var assemblySearchDirs = new List<string>
{
stagingAreaTempManagedDllDir,
};
obfuscatorBuilder.InsertTopPriorityAssemblySearchPaths(assemblySearchDirs);
OnObfuscationBegin?.Invoke(new ObfuscationBeginEventArgs
{
scriptAssembliesPath = stagingAreaTempManagedDllDir,
obfuscatedScriptAssembliesPath = obfuscatorBuilder.ObfuscatedAssemblyOutputPath,
});
bool succ = false;
try
{
Obfuscator obfuz = obfuscatorBuilder.Build();
obfuz.Run();
foreach (var dllName in obfuscationRelativeAssemblyNames)
{
string src = $"{obfuscatorBuilder.ObfuscatedAssemblyOutputPath}/{dllName}.dll";
string dst = $"{stagingAreaTempManagedDllDir}/{dllName}.dll";
if (!File.Exists(src))
{
Debug.LogWarning($"obfuscation assembly not found! skip copy. path:{src}");
continue;
}
File.Copy(src, dst, true);
Debug.Log($"obfuscate dll:{dst}");
}
succ = true;
}
catch (Exception e)
{
succ = false;
Debug.LogException(e);
Debug.LogError($"Obfuscation failed.");
}
OnObfuscationEnd?.Invoke(new ObfuscationEndEventArgs
{
success = succ,
originalScriptAssembliesPath = backupPlayerScriptAssembliesPath,
obfuscatedScriptAssembliesPath = stagingAreaTempManagedDllDir,
});
Debug.Log("Obfuscation end.");
}
}
#endif
}

View File

@ -0,0 +1,63 @@
using Obfuz.EncryptionVM;
using Obfuz.Settings;
using Obfuz.Utils;
using System.IO;
using UnityEditor;
using UnityEngine;
using FileUtil = Obfuz.Utils.FileUtil;
namespace Obfuz.Unity
{
public static class ObfuzMenu
{
[MenuItem("Obfuz/Settings...", priority = 1)]
public static void OpenSettings() => SettingsService.OpenProjectSettings("Project/Obfuz");
[MenuItem("Obfuz/GenerateVM", priority = 62)]
public static void GenerateEncryptionVM()
{
EncryptionVMSettings settings = ObfuzSettings.Instance.encryptionVMSettings;
var generator = new VirtualMachineCodeGenerator(settings.codeGenerationSecretKey, settings.encryptionOpCodeCount);
generator.Generate(settings.codeOutputPath);
}
[MenuItem("Obfuz/SaveSecretFile", priority = 63)]
public static void SaveSecretFile()
{
SecretSettings settings = ObfuzSettings.Instance.secretSettings;
var staticSecretBytes = KeyGenerator.GenerateKey(settings.defaultStaticSecretKey, VirtualMachine.SecretKeyLength);
SaveKey(staticSecretBytes, settings.DefaultStaticSecretKeyOutputPath);
Debug.Log($"Save static secret key to {settings.DefaultStaticSecretKeyOutputPath}");
var dynamicSecretBytes = KeyGenerator.GenerateKey(settings.defaultDynamicSecretKey, VirtualMachine.SecretKeyLength);
SaveKey(dynamicSecretBytes, settings.DefaultDynamicSecretKeyOutputPath);
Debug.Log($"Save dynamic secret key to {settings.DefaultDynamicSecretKeyOutputPath}");
}
private static void SaveKey(byte[] secret, string secretOutputPath)
{
FileUtil.CreateParentDir(secretOutputPath);
File.WriteAllBytes(secretOutputPath, secret);
}
[MenuItem("Obfuz/Documents/Quick Start")]
public static void OpenQuickStart() => Application.OpenURL("https://obfuz.doc.code-philosophy.com/docs/beginner/quickstart");
[MenuItem("Obfuz/Documents/FAQ")]
public static void OpenFAQ() => Application.OpenURL("https://obfuz.doc.code-philosophy.com/docs/help/faq");
[MenuItem("Obfuz/Documents/Common Errors")]
public static void OpenCommonErrors() => Application.OpenURL("https://obfuz.doc.code-philosophy.com/docs/help/commonerrors");
[MenuItem("Obfuz/Documents/Bug Report")]
public static void OpenBugReport() => Application.OpenURL("https://obfuz.doc.code-philosophy.com/docs/help/issue");
[MenuItem("Obfuz/Documents/GitHub")]
public static void OpenGitHub() => Application.OpenURL("https://github.com/focus-creative-games/obfuz");
[MenuItem("Obfuz/Documents/About")]
public static void OpenAbout() => Application.OpenURL("https://obfuz.doc.code-philosophy.com/docs/intro");
}
}

View File

@ -0,0 +1,91 @@
using dnlib.DotNet;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.Utils
{
public class AssemblyCache
{
private readonly IAssemblyResolver _assemblyPathResolver;
private readonly ModuleContext _modCtx;
private readonly AssemblyResolver _asmResolver;
private bool _enableTypeDefCache;
public ModuleContext ModCtx => _modCtx;
public Dictionary<string, ModuleDefMD> LoadedModules { get; } = new Dictionary<string, ModuleDefMD>();
public AssemblyCache(IAssemblyResolver assemblyResolver)
{
_enableTypeDefCache = true;
_assemblyPathResolver = assemblyResolver;
_modCtx = ModuleDef.CreateModuleContext();
_asmResolver = (AssemblyResolver)_modCtx.AssemblyResolver;
_asmResolver.EnableTypeDefCache = _enableTypeDefCache;
_asmResolver.UseGAC = false;
}
public bool EnableTypeDefCache
{
get => _enableTypeDefCache;
set
{
_enableTypeDefCache = value;
_asmResolver.EnableTypeDefCache = value;
foreach (var mod in LoadedModules.Values)
{
mod.EnableTypeDefFindCache = value;
}
}
}
public ModuleDefMD TryLoadModule(string moduleName)
{
string dllPath = _assemblyPathResolver.ResolveAssembly(moduleName);
if (string.IsNullOrEmpty(dllPath))
{
return null;
}
return LoadModule(moduleName);
}
public ModuleDefMD LoadModule(string moduleName)
{
// Debug.Log($"load module:{moduleName}");
if (LoadedModules.TryGetValue(moduleName, out var mod))
{
return mod;
}
string assemblyPath = _assemblyPathResolver.ResolveAssembly(moduleName);
if (string.IsNullOrEmpty(assemblyPath))
{
throw new FileNotFoundException($"Assembly {moduleName} not found");
}
mod = DoLoadModule(assemblyPath);
LoadedModules.Add(moduleName, mod);
foreach (var refAsm in mod.GetAssemblyRefs())
{
LoadModule(refAsm.Name);
}
return mod;
}
private ModuleDefMD DoLoadModule(string dllPath)
{
//Debug.Log($"do load module:{dllPath}");
ModuleDefMD mod = ModuleDefMD.Load(File.ReadAllBytes(dllPath), _modCtx);
mod.EnableTypeDefFindCache = _enableTypeDefCache;
_asmResolver.AddToCache(mod);
return mod;
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.Utils
{
public abstract class AssemblyResolverBase : IAssemblyResolver
{
public abstract string ResolveAssembly(string assemblyName);
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.Utils
{
public static class CollectionExtensions
{
public static void AddRange<T>(this HashSet<T> values, IEnumerable<T> newValues)
{
foreach (var value in newValues)
{
values.Add(value);
}
}
public static V GetValueOrDefault<K, V>(this Dictionary<K, V> dic, K key)
{
return dic.TryGetValue(key, out V v) ? v : default(V);
}
}
}

Some files were not shown because too many files have changed in this diff Show More