Compare commits

...

246 Commits

Author SHA1 Message Date
walon 403764e7af fix: 修复符号混淆规则文件中modifier条件对property和event没有生效的bug。由于property和event本身并不存在Access Attributes,实际上是由它们所包含的method的Access属性`或`计算而来。 2025-09-10 18:20:29 +08:00
walon e12a0e26dc new: 新增CompatibilitySettings,允许指定生成与mono或il2cpp兼容的混淆代码 2025-09-10 11:15:05 +08:00
walon daff29ea94 fix: 修复当原始dll中包含resource文件(如mscorlib)时,ObfuscateUtil.GeneratePolymorphicDll执行出现异常的bug 2025-09-07 18:32:02 +08:00
walon eeff4a75ca fix: 修复 EvalStackCalculator计算 eval data的reduce type时未import来自其他程序集的共享基类的bug 2025-09-06 17:50:09 +08:00
walon b0699ecf5c 修复EvalStackCalculator计算input eval stack data类型时没有对多个inbound basic block的input计算共享类型的bug 2025-09-06 12:26:02 +08:00
walon 5557b27724 typo: AssetUtility -> AssertUtility 2025-08-29 21:55:24 +08:00
walon 59b1166ff3 fix: 修复 ReflectionCompatibilityDetector处理Unity 6000新增的Enum.TryParse(Type,bool, out object)函数时抛出异常的bug
refactor: 将 ReflectionCompatibilityDetector移到独立的ReflectionCompatibilityDetectionPass
2025-08-27 20:15:20 +08:00
walon 4ad3ed76dc fix: 修复 ConstFieldAllocator和 RvaDataAllocator的encryption level固定取4导致EncryptionVirtualMachine的Opcode个数超过256时打印了大量"OpCode overflow"警告日志的问题 2025-08-26 20:58:09 +08:00
walon 2576236960 暂时对泛型禁用控制流混淆 2025-08-12 14:19:32 +08:00
walon c3c53b2797 修复 EvalDataTypeWithSig计算ldarga等取引用的指令的栈数据类型未包含具体类型的bug 2025-08-12 14:19:11 +08:00
walon 0889f730fd - 修复EvalStackCalculator计算Ref类型栈变量全部归结来IntPtr类型,导致il2cpp生成代码出错的bug
- 修复MethodControlFlowCalculator将box后的值类型变量类型定义为值类型的bug,正确应该是object
2025-08-12 12:46:34 +08:00
walon 87cd3caf44 change: 禁用EvalStackObfus pass,因为它的性价比太低了。如果将来无法优化,则会被移除 2025-08-07 21:32:00 +08:00
walon 8dd6352028 fix: 修复类型上包含[ObufzIgnore(ObfuzScope.TypeName)]导致类型内所有函数体不混淆的bug 2025-08-07 18:55:17 +08:00
walon 739fcb24c5 WatermarkPass不对DOTS函数注入水印指令 2025-08-06 18:27:15 +08:00
walon 246a49f8ba fix: 修复WatermarkPass某些情况下导致栈不平衡的bug 2025-08-06 15:21:23 +08:00
walon 1ec6e2f426 将ObfuscationTypeMapper::RegisterType函数由internal改为public 2025-08-06 10:57:01 +08:00
walon b68571de7e fix: 修复在Unity 2019上的编译错误 2025-08-05 14:20:46 +08:00
walon 50fe1e1179 更新英文README 2025-08-04 11:32:10 +08:00
walon d52a9e7016 Code Clean Up 2025-08-03 12:37:44 +08:00
walon f5c2fe94ea WatermarkPass不打印注入日志 2025-08-02 23:22:26 +08:00
walon 54bcae5178 代码水印注入特征IL指令 2025-08-02 23:15:17 +08:00
walon cfb544426b 更新README中关于代码水印的说明 2025-08-02 21:53:37 +08:00
walon 1e027e6299 feature: 支持代码水印 2025-08-02 21:52:35 +08:00
walon 4db68f707b MethodControlFlowCalculator对InputArgs为0的BasicBlock随机化打乱顺序 2025-08-01 11:32:33 +08:00
walon c596b58d3e 优化 MethodControlFlowCalculator生成的指令有可能违反IL控制流规则的问题 2025-08-01 10:57:53 +08:00
walon ddb144eac8 feature: 新增 RemoveConstField pass 2025-08-01 09:19:24 +08:00
walon 1a240c47ac 修复ModifierType::Public枚举项值的错误 2025-07-31 20:06:04 +08:00
walon 2e48164ae9 修复EvalStackCalculator计算isinst和castclass指令的返回值的类型未转换为token所引用的类型的bug 2025-07-31 12:14:18 +08:00
walon 9b6e6375f6 更新README 2025-07-29 10:14:58 +08:00
walon 38ad0de746 禁用obfuz时LinkXmlProcess::GenerateAdditionalLinkXmlFile返回null 2025-07-28 10:46:17 +08:00
walon 6ec1a74d57 README中添加多态DLL说明 2025-07-26 13:40:16 +08:00
walon d6d9cde741 升级版本到v3.0.0-beta 2025-07-26 10:32:53 +08:00
walon 87f086e310 支持多态dll文件结构 2025-07-25 20:47:38 +08:00
walon 69b91575db 发布正式版本v2.0.0 2025-07-24 11:10:23 +08:00
walon 0985b3d06e 修复$$Obfuz$RVA$的所有rva字段累计长度超出16k引发的在hybridclr下运行出错的问题。此问题是hybridclr的bug,因为ldslfd之类指令要求offset小于16k。 2025-07-23 20:56:38 +08:00
walon d4133f1e8a 移除不必要的对NUnit.Framework的引用 2025-07-23 19:34:42 +08:00
walon 3bcf204f69 ExprObfus的BasicObfuscator支持混淆参数类型为IntPtr的表达式 2025-07-21 11:37:29 +08:00
walon 905351789e fix: 修复静态与成员函数在包含this参数后如果参数完全相同则计算出MethodDefSignature相同导致RenameRecordMap加载symbol-mapping文件时signature冲突的bug 2025-07-21 09:35:40 +08:00
walon 3bcb093467 由于EvalStackObfus会显著增加混淆后的程序集大小(当obfuscationPercentage=1.0时大约增加了5倍),将默认obfuscationPercentage改为0.05,并且在global obfuscationPercentage超过0.1时打印警告 2025-07-14 18:12:54 +08:00
walon 49194ca1af 升级版本到v2.0.0-rc.1 2025-07-14 11:49:07 +08:00
walon 6a4f84a9b0 不混淆被BurstCompile函数直接或者间接调用的函数的代码,但仍然混淆函数名。 2025-07-14 11:45:15 +08:00
walon 083ddd3035 对于标记`[BurstCompile]`的类型,除了类型名和函数名以外的仍然混淆 2025-07-11 19:08:25 +08:00
walon 2887231df7 禁止混淆带`[BurstCompile]`的函数 2025-07-11 18:03:25 +08:00
walon c3238c54a9 更新README 2025-07-03 21:23:34 +08:00
walon f908b648c1 修复LinkXmlProcess::GenerateAdditionalLinkXmlFile返回的link.xml为相对路径,没有生效的bug 2025-07-03 12:41:06 +08:00
walon 29debc44bf 升级版本到v2.0.0-beta.1 2025-07-03 12:10:05 +08:00
walon 9cbb105405 LinkXmlProcess中保留整个Obfuz.Runtime 2025-07-03 12:09:56 +08:00
walon e3d9d7a08e 重构GroupByModuleEntityManager代码 2025-07-02 18:57:53 +08:00
walon 4b0c5d7521 CallObufs新增Delegate Proxy支持 2025-07-01 18:46:09 +08:00
walon 9b9eb6d12d 重构CallObfusPass代码 2025-06-30 20:02:49 +08:00
walon 52d9ee1349 修复dnlib插件在非Editor模式下也能被引用的问题 2025-06-30 18:25:53 +08:00
walon bf3f6e4534 ObfuscatorBuilder::FromObfuzSettings新增searchPathIncludeUnityEditorDll参数,允许将UnityEditor相关dll也加入到搜索路径 2025-06-28 20:51:54 +08:00
walon 655c2fe07f `ObfuzSettings.enable`字段移到`BuildPipelineSettings`中 2025-06-28 20:38:27 +08:00
walon 02ed0608e4 添加BuildPipelineSettings,支持自定义 LinkXmlProcess和 ObfuscationProcess的 callbackOrder。 2025-06-28 20:20:08 +08:00
walon 59db0dfaab 更新说明 2025-06-28 20:08:00 +08:00
walon 2b8e51b12d 调整一些类型名 2025-06-28 19:01:57 +08:00
walon df181ed5c1 更新版本号为v2.0.0-alpha 2025-06-28 13:51:45 +08:00
walon 1f29b5530e 支持UI垃圾代码生成 2025-06-28 13:43:32 +08:00
walon af8477f4b3 添加ConfigGarbageCodeGenerator 2025-06-28 13:10:37 +08:00
walon 9d46b5438a 垃圾代码生成 2025-06-28 12:12:31 +08:00
walon a833cf26e1 修复MonoBehavoiur和ScriptableObject的属性没有混淆的bug 2025-06-28 10:49:30 +08:00
walon 7a7ef72728 支持控制流混淆 2025-06-28 10:10:50 +08:00
walon ac9c96b4b9 删除CallObfusPass中无用代码 2025-06-26 17:28:30 +08:00
walon 95b789deb2 升级版本为 v2.0.0-preview.3 2025-06-26 11:01:33 +08:00
walon f3bde846ea CallObfuscationSettings新增设置obfuscateCallToMethodInMscorlib 2025-06-26 11:01:00 +08:00
walon 905da05afc 重构:将CallObfus的特殊白名单函数计算逻辑从 ConfigurableObfuscationPolicy移到 CallObfusPass 2025-06-26 10:49:14 +08:00
walon 62deffa10d 1. 修复CallObfus混淆了Enum.HasFlag和GetHashCode函数后,Unity 2021的il2cpp生成cpp代码时发生内部异常的问题
2. 修复CallObfus混淆了MethodBase.GetCurrentMethod导致返回了错误函数的严重bug
2025-06-26 10:09:01 +08:00
walon 72d0b292c5 修复 EvalStackCalculator::SimulateRunAllBlocks 计算返回值包含`creq`的函数(如`int XXX{ init;}`的setter函数)的进出栈参数时由于没有对method.ReturnType RemovePinnedAndModifiers,导致错误地判定为methodHasReturnValue,导致进出栈错误的bug 2025-06-26 08:13:24 +08:00
walon 2572841e59 修复 EvalStackCalculator计算泛型类字段类型未inflate的bug 2025-06-22 18:01:47 +08:00
walon 38ebe11d7d - 修复 EvalStackCalculator不支持Conv_R_Un指令的bug
- 修复未不支持float与double进行二元计算的bug
2025-06-22 12:03:34 +08:00
walon 73915db7ca EvalStackCalculator计算EvalStack变量时如果为ValueType则包含类型信息 2025-06-22 11:45:23 +08:00
walon 832a955167 修复DefaultObfuscator抛出异常的问题 2025-06-22 10:48:55 +08:00
walon 5cdc4c9f92 修复符号混淆开启Debug时由于没有读取mapping文件导致增量混淆结果不一样的bug 2025-06-22 10:48:08 +08:00
walon 0508421850 添加ControlFlowObfus基础代码 2025-06-22 10:39:57 +08:00
walon 0ad96daa32 代码清理 2025-06-22 10:39:31 +08:00
walon cfe9dcdd08 删除错误的断言,运行ldsfld指令时栈有可能为空 2025-06-21 18:02:35 +08:00
walon 1afc36339d 更新版本号为2.0.0-preview.2 2025-06-21 11:43:35 +08:00
walon 1f9aac59ee 调整表达式混淆顺序,改到EvalStackObfus之前 2025-06-21 11:43:04 +08:00
walon 338d2acf05 ExprObfus默认混淆级别的None改为Basic 2025-06-21 11:42:43 +08:00
walon d8fb8bc630 CallObfus ConfigurableObfuscationPolicy默认对所有调用都开启cacheIndex 2025-06-21 11:21:24 +08:00
walon 67990f841a 支持EvalStackObfus 2025-06-21 10:59:39 +08:00
walon cbf9ab7d68 ConstFieldAllocator生成的字段不再是initOnly,这显然是给破解者提示 2025-06-21 09:26:07 +08:00
walon f8571ada9e const加密支持多种混淆方式 2025-06-21 09:20:36 +08:00
walon b1731a8c38 Settings中的array参数加上检查 2025-06-21 08:27:45 +08:00
walon 8314ed4327 支持 MostAdvancedObfuscator 2025-06-20 18:58:54 +08:00
walon f247719bc1 实现 AdvancedObfuscator 2025-06-20 18:23:38 +08:00
walon 9c445213b5 支持配置 obfuscationLevel和 obfuscationPercentage,支持advancedObfuscation::neg 2025-06-20 17:34:25 +08:00
walon 111d3a7dc1 支持部分 AdvancedObfuscator 2025-06-20 16:56:14 +08:00
walon 4bc6cf923f 修复 EvalStackCalculator在Unity 2019的编译错误 2025-06-20 12:10:32 +08:00
walon 8288042e94 实现 ExprObfus,暂时只支持BasicObfuscator 2025-06-20 12:06:01 +08:00
walon b84d158fac 修复 ReflectionCompatibilityDetector处理Enum.TryParse<T>,并且T包含泛型参数时抛出异常的bug 2025-06-16 17:02:31 +08:00
walon 0fe0a91793 修复 ReflectionCompatibilityDetector错误地对未混淆程序集中Enum打印错误日志的bug 2025-06-16 11:41:18 +08:00
walon d60d0e02dd BasicBlockCollection新增参数决定是否计算inLoop 2025-06-15 10:55:09 +08:00
walon 52fcf17161 更新版本号到v2.0.0-preview 2025-06-14 09:29:54 +08:00
walon 7ff5c97140 更新版本号到1.0.0-rc 2025-06-14 09:19:13 +08:00
walon df53a0bd1b 修复 ReflectionCompatibilityDetector检查Enum.Parse TryParse GetName GetNames时的判定条件为枚举类型名被混淆的错误,应该是枚举项被混淆 2025-06-14 09:16:06 +08:00
walon 174140c5da 修复 ReflectionCompatibilityDetector检查Enum.ToString时的判定条件为枚举类型名被混淆的错误,应该是枚举项被混淆 2025-06-13 21:31:05 +08:00
walon 3f00d5ca91 新增 ReflectionCompatibilityDetector 2025-06-13 21:00:58 +08:00
walon 28b841562c 修复LinkXmlProcess在Unity 2019-2022执行OnBeforeRun和OnAfterRun接口函数抛出NotImplementedException的bug 2025-06-12 15:27:16 +08:00
walon 6226c3a867 - 修复 RvaDataAllocator::GetDataHolderType 创建TypeDefUser未disableTypeDefFindCache的bug
- 使用 DisableTypeDefFindCacheScope 重构创建TypeDef类型自动disable和enable TypeDefFindCache
2025-06-12 15:07:13 +08:00
walon a61f31e289 不混淆标记了`[Serializable]`特性的枚举类的枚举项名 2025-06-12 14:25:24 +08:00
walon ea5a8e62ce 修复当混淆程序集虚函数override了nonObfuscatedButReferencingObfuscatedModuleSet程序集中虚函数,错误地将nonObfuscatedButReferencingObfuscatedModuleSet中虚函数也混淆的bug 2025-06-12 10:30:42 +08:00
walon 83598d6805 更精确地进行虚函数override匹配,检查返回值和参数的匹配性 2025-06-12 09:49:24 +08:00
walon 114707544a 修复在Unity 2019和2020上的编译错误 2025-06-12 09:42:22 +08:00
walon a109511f9e 新增 ObfuscationTypeMapper及相应的Instinct函数RegisterReflectionType 2025-06-10 13:56:05 +08:00
walon 3867a98d48 修复未Preserve混淆后的名字,导致新增的混淆函数名与旧的混淆函数名冲突的严重bug 2025-06-10 13:55:09 +08:00
walon 025f900f4e 修复 CallObfus生成Dispatch函数对被调用排序造成调用到错误函数的严重bug 2025-06-10 13:53:53 +08:00
walon 9505f2b90f 更新版本号到1.0.0-beta.5 2025-06-10 08:34:01 +08:00
walon 10d450c4e3 生成稳定的call obfus的Dispatch函数名 2025-06-10 08:32:31 +08:00
walon ac6ca08d87 优化InstinctPass 2025-06-10 08:29:47 +08:00
walon 260f8b8cc5 新增InstinctPass,将 ObfuscationInstincts::FullNameOf和NameOf函数转为混淆前的原始类全名和类名 2025-06-09 22:57:40 +08:00
walon 40f6c90494 修复在Unity 2020及更低版本上的编译错误 2025-06-09 17:21:26 +08:00
walon f2409de99a 修复团结引擎1.1.0+版本调整了微信小游戏平台PlaybackEngines路径导致搜索不到引擎dll的bug 2025-06-09 15:10:05 +08:00
walon 5124f993c7 修复微信小游戏平台PlaybackEngines路径错误的bug 2025-06-09 14:36:55 +08:00
walon 2ecf363d0b 修复 VirtualMethodGroupCalculator 计算一个类型继承了多个interface,并且这些interface包含相同签名的函数时,未映射为相同虚函数名的bug 2025-06-09 13:28:23 +08:00
walon 75a4e2cdad 符号混淆中method和field名全局唯一,方便还原混淆符号 2025-06-09 13:25:04 +08:00
walon fea2e65075 修复ObfuscatorBuilder::BuildUnityAssemblySearchPaths在微信小游戏平台使用WebGL平台的搜索路径的bug 2025-06-09 12:45:46 +08:00
walon b7d2f69321 生成混淆名全局唯一,而不是本程序集内唯一,否则DeobfuscateStackTrace很有可能因为不同程序集的类型名重名而无法分辨准确类型 2025-06-09 09:16:01 +08:00
walon 7e2162e66d 修复 ObfuscatorBuilder::BuildUnityAssemblySearchPaths 在UNITY_TVOS平台路径分割符为'\'的问题 2025-06-09 08:53:12 +08:00
walon 9db3f0bd05 更新 QQ群和discord频道地址 2025-06-07 10:03:49 +08:00
walon f1c423ed02 SymbolObfuscationSettings新增配置项keepUnknownSymbolInSymbolMappingFile 2025-06-06 22:57:28 +08:00
walon 432eb83c41 更新版本号到1.0.0-beta.4 2025-06-06 22:50:35 +08:00
walon 161ab5a29e symbol mapping文件中不存在的类型相关的记录仍然保留在输出的symbol mapping文件中 2025-06-06 22:47:38 +08:00
walon 8bd2aab9da 修复symbol mapping文件时输出内容顺序不稳定的问题 2025-06-06 22:25:45 +08:00
walon 58ad0fca67 修复虚函数名混淆不稳定的bug 2025-06-06 21:31:56 +08:00
walon b38becf84a 对MonoPInvokeCallbackAttribute、Zlua.LuaInvokeAttribute、Zlua.LuaCallbackAttribute、ZluaLuaMarshalAsAttribute禁用所有符号混淆 2025-06-06 20:28:08 +08:00
walon 1508cdc31d 修复在MacOS系统上 PlaybackEngines目录位置错误的bug 2025-06-06 15:39:56 +08:00
walon b021a8be40 修正ObfuzMenu中的链接地址 2025-06-03 19:57:17 +08:00
walon ebf34b22af 修复symbol mapping中存在不在混淆程序集列表中的程序集时,输出的method信息中没有包含oldStackTraceSignature和newStackTraceSignature字段的bug 2025-06-03 19:56:45 +08:00
walon e023f0aa7d 更新 README.md 2025-06-02 10:43:51 +08:00
walon 1604efbd19 add README.md 2025-06-02 10:34:26 +08:00
walon 998af4ed38 更新版本号到1.0.0-beta.3 2025-06-02 10:02:30 +08:00
walon 39c9925cbc obfuz仓库只保留obfuz,将Samples、obfuz4hybridclr和DeobfuscatedStackTrace拆分为独立的仓库 2025-06-02 09:48:44 +08:00
walon 1f74c8d65d 更新package中的category为Scripting 2025-06-01 17:58:59 +08:00
walon 0c0a6afee4 修复 CustomConfigure/Assets/Obfuz/symbol-obfuscation.xml 配置错误,没有加 ApplyToMembers 2025-05-31 12:24:40 +08:00
walon fa4fb9da09 更新 .gitignore 2025-05-31 08:31:41 +08:00
walon 88c8cbab08 删除一些错误的注释 2025-05-31 08:31:41 +08:00
walon da044b72bc
Update issue templates 2025-05-31 08:29:47 +08:00
walon 51020dfc93 更新 WorkWithHybridCLR项目,与最新 obfuz4hybridclr的代码同步 2025-05-30 19:06:50 +08:00
walon a231de1f62 [obfuz4hybridclr] 修复ObfuscateUtil::Obfuscate没有将混淆后程序集输出到obfuscatedAssemblyOutputPath目录的bug 2025-05-30 19:04:03 +08:00
walon 76cb8fbcbb 修复PrebuildCommandExt.GenerateAll中生成桥接函数时如果混淆程序集是预编译的dll,并不在热更新dll输出目录中,会错误地从搜索目录加载原始插件dll的bug 2025-05-30 18:24:54 +08:00
walon 10e6c0d914 修复 VirtualMachineCodeGenerator生成的加密解密代码缩进不正确的问题 2025-05-30 13:37:21 +08:00
walon cad7fff2d1 clean up codes 2025-05-30 13:32:29 +08:00
walon b9061c567c SymbolRename预先计算NeedRename,将SymbolRename总耗时减少50%,将混淆总耗时减少30% 2025-05-30 10:01:07 +08:00
walon f0c09ad741 打印混淆耗各个pass耗时和总耗时 2025-05-30 09:51:57 +08:00
walon dcd38e288b 修复混淆了编译器生成的有特殊用意的Microsoft.CodeAnalysis.EmbeddedAttribute及声明了EmbeddedAttribute的CustomAttribute的bug 2025-05-30 09:03:03 +08:00
walon 374a297e45 ObfuzIgnore支持ApplyToChildTypes属性 2025-05-30 08:15:49 +08:00
walon b113364214 重构 ObfuzIgnore计算,使用 ObfuzIgnoreScopeComputeCache提升计算性能 2025-05-29 22:20:27 +08:00
walon 84ed5b127a 修复enabled pass计算的bug 2025-05-29 21:23:22 +08:00
walon 2312291040 修复计算assembly的obfuscation pass时总是使用默认值的bug 2025-05-29 20:59:12 +08:00
walon c3ed85fb3f - 不混淆`[Serializable]`类型的类型名
- 不混淆从MonoBehaviour和ScriptableObject继承或带`[Serializable]`的类型的public非静态成员字段和property
2025-05-29 16:16:32 +08:00
walon c173efe689 符号混淆开启debug情况下不读取但写入symbol-mapping-debug.xml文件,这样LinkXmlProcess中也能正确映射到混淆后的名字 2025-05-29 11:39:10 +08:00
walon 5ce7b9b5f4 当 symbol mapping文件不存在时跳过LinkXmlProcess 2025-05-29 11:07:29 +08:00
walon febb8c9fd8 修复计算虚函数名冲突域时只考虑到虚函数所定义的类,而未考虑到在整个类继承树内不冲突的bug 2025-05-29 09:20:40 +08:00
walon e818c6ad4d README.md添加常量数组加密的介绍 2025-05-28 17:38:01 +08:00
walon a6863b5089 常量加密支持array(byte[],int[],float[],int[,]等等) 2025-05-28 17:23:24 +08:00
walon 28ac98352d 构建过程中自动转换link.xml中原始的类型名为混淆后的名称,确保混淆后能正确保留类型 2025-05-28 12:00:07 +08:00
walon b85f3f54a0 优化UnityRenamePolicy,缓存计算结果,将整体混淆时间减少了一半左右 2025-05-28 10:11:48 +08:00
walon 620d695880 支持custom rename policy 2025-05-28 09:25:09 +08:00
walon ceb92fba40 符号混淆的规则文件中type规则新增hasCustomAttributes属性 2025-05-28 08:42:57 +08:00
walon cbd4f1ded9 symbol obfus规则支持type inherit过滤条件 2025-05-27 23:55:18 +08:00
walon 468ea6a343 修复 DOTSCompilerGenerated和BurstCompile判定没有用于method的bug 2025-05-27 22:50:05 +08:00
walon 94b9b7ee2f 缓存 UnityRenamePolicy::IsUnitySourceGeneratedAssemblyType的计算结果,优化性能 2025-05-27 22:21:24 +08:00
walon 14a6ddb661 更新 README.md 2025-05-27 20:30:08 +08:00
walon 77c6635eda 不混淆DOTS相关类型 2025-05-27 20:20:49 +08:00
walon 8f0a5bc0f2 修复默认混淆了DOTS生成的Unity.Entities.CodeGeneratedRegistry.AssemblyTypeRegistry类名,导致DOTS类型注册失败的Bug 2025-05-27 20:06:14 +08:00
walon 1b3c1c4958 symbol obfus规则中TypeSpec支持 applyToNestedTypes 属性 2025-05-27 19:02:18 +08:00
walon 22fca877f6 新增 符号混淆applyToMembers测试用例 2025-05-27 17:29:12 +08:00
walon 994e63966f 重构符号混淆的规则文件 2025-05-27 14:45:18 +08:00
walon f134b88c13 删除 ObfuzIgnoreAttribute.ApplyToMembers属性,因为没有必要,正常逻辑来说应该是ApplyToMembers 2025-05-27 10:28:34 +08:00
walon cd99bfe4f3 ObufzScope拆分为独立代码文件 2025-05-27 10:18:13 +08:00
walon 62f4063078 更新 README.md 2025-05-27 09:45:39 +08:00
walon c1600f0f4d - Serializable类型名也会被混淆,但可序列化字段不会被混淆
- Obfuz.EncryptField类不会被混淆
- 必须是MonoBehaviour继承的类型中事件函数名才不会被混淆
2025-05-27 09:33:17 +08:00
walon 4ca7a53a1e 修改SystemRenamePolicy对Enum的value__字段的检查条件,由检查名字改为!field.IsStatic 2025-05-26 20:31:05 +08:00
walon fcbf77bb40 优化:加解密长度为0的字符串和bytes时直接返回长度为0的值 2025-05-26 20:03:09 +08:00
walon b51893d154 ConstEncrypt不加密长度为0的字符串 2025-05-26 19:48:55 +08:00
walon 78699d7959 修复ObfuzIgnoreAttribute实现的bug 2025-05-26 19:22:55 +08:00
walon ad258e9c84 修复 symbol obfuscation rule文件中存在不在混淆列表的程序集时,BuildRuleResultCaches抛出空引用异常的bug 2025-05-26 17:24:23 +08:00
walon 386048b485 更新 README.md 2025-05-26 11:09:30 +08:00
walon 3066fbf3c7 升级obfuz版本号v1.0.0-beta.2 2025-05-25 17:38:01 +08:00
walon cd99a13562 change: ObfuzIgnoreAttribute的InheritByNestedTypes属性名改为ApplyToMembers 2025-05-25 12:42:38 +08:00
walon 9d77cfa269 调整symbol obfus的规则文件的规则定义 2025-05-25 12:28:54 +08:00
walon 702f20c6b0 修复FieldEncryption处理浮点类型时未插入castFloatAsInt指令,在il2cpp生成的代码为直接强转`(int)f`,运行结果错误的bug 2025-05-25 10:06:15 +08:00
walon c84d4bae5b 为泛型生成混淆名时仍然保留`{n}后缀,因为il2cpp会在typeof(G<T>)时出现问题 2025-05-25 08:40:53 +08:00
walon 6e989ccd36 修复当一个Type之前为不需要混淆名字,后面改为需要混淆时,仍然使用mapping文件中的newFullname作为新名字,而它的newFullname为空字符串串,导致il2cpp生成代码出现问题的严重bug。 2025-05-25 08:39:43 +08:00
walon e0da3da2bc RenameMethod时检查CustomAttribute 2025-05-25 08:20:33 +08:00
walon 882047eb6c MetaUtil::RetargetTypeRefInTypeSig 特殊处理来自mscorlib的类型,不再创建新的Class或ValueSig 2025-05-25 08:16:19 +08:00
walon b2ba676ec1 修复在Unity 2019上的编译错误 2025-05-24 12:55:53 +08:00
walon 1cd1b912ec 修复SymbolRename::CollectCArgumentWithTypeOf没考虑到NamedArgument有可能名称发生改变,仅仅判断了类型或者值是否可能包含类型引用,导致跳过收集 `[MyCustom(X=1)]`这样的属性的bug 2025-05-24 09:49:14 +08:00
walon b2d7637438 修复 VirtualMethodGroupCalculator没有考虑到某个类本身没有实现它的接口类的某个函数但它的父类实现了它的接口类的该函数时没有将接口的该函数和父类的该虚函数计算为一个Group的bug 2025-05-24 00:01:33 +08:00
walon 48f1dfe64a 修复RenameMethod未处理MethodImpl的bug 2025-05-23 23:06:18 +08:00
walon 647619943f 修复VirtualMethodGroup中仅包含非混淆程序集中函数时仍然试图改名的bug 2025-05-23 22:32:21 +08:00
walon f1a225cd81 修复 RenameRecordMap::InitAndAddRename初始化signature的bug 2025-05-23 22:11:41 +08:00
walon d64e57b370 修复虚函数分配名字未检查是否与每个虚函数所在类型其他函数冲突的严重bug 2025-05-23 20:40:54 +08:00
walon 5f4083066b 修复给虚函数分配名字时未考虑到不要与每个虚函数所在命名空间不冲突的bug 2025-05-23 19:48:55 +08:00
walon 5de469c4a6 修复 Obfuscator未检查AssembliesToObfuscate和nonObfuscatedButReferencingObfuscatedAssemblies列表存在空或者重复程序集名的问题 2025-05-23 19:47:43 +08:00
walon 28e276eda5 修复 MetaUtil::AppendIl2CppStackTraceNameOfTypeSig 无法处理TypedReference类型的bug 2025-05-23 19:46:44 +08:00
walon 03b0172240 因为重构了Obfuscator传参方式,而ObfuscateUtil引用了相关代码,连带改动 2025-05-23 12:56:09 +08:00
walon 41230aca29 修复BuildUnityAssemblySearchPaths的WebGL平台PlaybackEngine目录错误的bug。WebGL平台跟其他平台的路径规则不一样。 2025-05-23 12:54:08 +08:00
walon fb9ffae1e8 重构Obfuscator和Obfusacation Pass参数,不再直接传递Settings,而是传递SettingsFacade,简化大量参数复制代码 2025-05-23 12:47:57 +08:00
walon b6500147c2 CallObfusSettings新增maxProxyMethodCountPerDispatchMethod字段,设置每个dispatch函数中最多转发的函数个数 2025-05-23 12:01:04 +08:00
walon 3425abceb5 RvaData初始化时校验密钥的正确性,避免程序集混淆时使用的密钥与运行时加载的密钥不匹配的失误! 2025-05-23 11:45:37 +08:00
walon f5b45a0543 fix: 修复非混淆的程序集中虚函数override混淆程序集中函数,当基类虚函数名被混淆后没有同步修改非混淆程序集中override函数名的bug
remove: 删除 RenameRecordMap中维护parameter映射的代码
2025-05-23 11:13:45 +08:00
walon 4d8737f01c change: 如果类型中包含有`[RuntimeInitializeOnLoadMethod]`并且LoadType等于或者早于AfterAssembliesLoaded的函数,则类型的静态构造函数不混淆 2025-05-23 11:11:08 +08:00
walon d06caf5e38 change: SecretSettings.secretKeyOutputPath拆为staticSecretKeyOutputPath和dynamicSecretKeyOutputPath,方便单独指定每个key的输出路径 2025-05-23 11:09:27 +08:00
walon 32d1aac9ba change: ObfuscatorBuilder::BuildUnityAssemblySearchPaths 为各个平台添加对应的PlaybackEngine目录,解决构建webgl之类平台时找不到一些特定dll的bug 2025-05-23 10:18:37 +08:00
walon cb27fadb08 移除所有Sample中`SetUpStaticSecretKey`函数上的`[ObfuzIngore]`特性,因为Obfuz会自动对`[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]`禁用一切混淆 2025-05-23 09:27:31 +08:00
walon 05c3fd24bf change: ObfuscationMethodWhitelist::IsInWhiteList(MethodDef)对于带有`[RuntimeInitializeOnLoadMethod]`的函数,如果LoadType等于或者早于AfterAssembliesLoaded,就不对函数进行混淆 2025-05-23 09:25:44 +08:00
walon 3fe3a6b302 fix: 修复 UnityRenamePolicy::NeedRename(MethodDef)错误地判定当前类型为MonoBehaviour类脚本,如果不是事件函数就直接返回,没有尝试后续检查的bug 2025-05-23 09:24:24 +08:00
walon e19dbe1dc8 SymbolObfus: 不混淆Unity.Behaviour.BlackboardEnum特性标注的枚举类型名及其枚举项名 2025-05-23 08:31:43 +08:00
walon c1beb962f5 SymbolObfus: 不混淆带有`[RuntimeInitializeOnLoadMethod]`的函数及它的父类名(否则Unity无法根据类型和函数名找到此函数) 2025-05-23 08:18:21 +08:00
walon 0efc98fb4d remove comments of GenericArgumentContext 2025-05-22 19:56:35 +08:00
walon bf38f43824 fix: fix bug of GenericArgumentContext that inflate ByRef, SZArray to Ptr. 2025-05-22 19:44:13 +08:00
walon 09fdd3052d change: add ObfuscationProcess.ValidateReferences to check assemblies which reference to obfuscated assemblies must have been added to `ObfuzSettings.AssemblySettings.NonObfuscatedButReferencingObfuscatedAssemblies`. 2025-05-22 11:34:53 +08:00
walon 1e8832ca7e simplify sample DynamicSecretKey. remove hybridclr and obfuz4dhybridclr. 2025-05-22 10:11:02 +08:00
walon 69a4cd686f fix: fix a serious bug that didn't setup _obfuscatedAssemblyTempOutputPath which make all obfuscation passes exclude symbol obfuscation were skipped! 2025-05-22 10:09:02 +08:00
walon 4398ee0875 update package.json for obfuz and obfuz4hybridclr 2025-05-22 09:20:18 +08:00
walon b18a8f70e3 add: add sample project DynamicSecretKey 2025-05-22 09:15:34 +08:00
walon 8cdd69fd45 change: Obfuscator write intermediate obfuscated assembly to temp directory before write to obfuscated assembly directory 2025-05-21 21:29:26 +08:00
walon f265022d0c change: obfuz will try find managed dll in project which is compatible with current build target. 2025-05-21 20:41:01 +08:00
walon 2f7e2be97a fix: fix the bug that TypeSigUtil::ComputePropertyDefSignature didn't compute parameters to final signature. 2025-05-21 19:44:38 +08:00
walon 3d04c15d98 new: AssemblySettings adds a new option: `obfuscateObfuzRuntime` 2025-05-21 18:36:08 +08:00
walon 425db24ee2 fix: fix bug that MetaUtil.GetTypeDefOrGenericTypeBaseOrNull and GetMemberRefTypeDefParentOrNull raise NullException when type is TypeSpec but isn't GenericType 2025-05-21 18:31:10 +08:00
walon 1f29de26cf fix: fix the bug that MetaUtil.AppendIl2CppStackTraceNameOfTypeSig didn't support ElementType.Array 2025-05-21 17:57:31 +08:00
walon b6cc07b54b fix: fix the bug that didn't rename MethodSpec in instruction operand when rename method in Symbol Obfus pass 2025-05-21 17:29:52 +08:00
walon bd370e545a change: add `[Flags]` to ObfuzScope 2025-05-21 16:50:56 +08:00
walon c260cc2379 change: ObfuzIgnoreAttribute support ObfuzScope 2025-05-21 16:24:26 +08:00
walon d258738262 temp 2025-05-21 13:33:03 +08:00
walon 10eef16d78 change: UnityRenamePolicy adds missing MonoBehaviour event methods of Animator, ParticleSystem, UGUI. 2025-05-21 11:49:20 +08:00
walon 2bc310f15e change: add missing Unity MonoBehaviour event methods in UnityRenamePolicy. 2025-05-21 11:39:42 +08:00
walon f332617acc change: SymbolObfus obNamespace inherits value from obName 2025-05-21 11:22:35 +08:00
walon 0a9968f7a1 update: update samples 2025-05-21 10:42:06 +08:00
walon 48779110f6 change: obfuz4hybridclr add menu `HybridCLR/ObfuzExtension/CompileAndObfuscateDll` 2025-05-21 10:31:43 +08:00
walon 0ef162b901 update url of obfuz package 2025-05-21 09:38:33 +08:00
walon 2453acc1d3 remove Obfuz project 2025-05-21 09:26:04 +08:00
walon 8f8422864b move package com.code-philosophy.obfuz and com.code-philosophy.obfuz4hybridclr to root directory 2025-05-21 09:23:29 +08:00
walon acb35e375e rename assembly name ObfuzExtension4HybridCLR to Obfuz4HybridCLR.Editor 2025-05-21 09:19:52 +08:00
walon 4fd71a30c3 add hybridclr package to Obfuz project 2025-05-21 09:11:01 +08:00
walon da47e1283d new: add package com.code-philosophy.obfuz4hybridclr 2025-05-21 09:08:13 +08:00
walon 5766d2f2f4 change : update package.json of obfuz 2025-05-21 09:06:09 +08:00
walon 8f66a0ac5b change: add ignore HybridCLRData to .gitignore 2025-05-20 17:34:18 +08:00
walon 544f8157fc add: add sample WorkWithHybridCLR 2025-05-20 17:33:18 +08:00
walon bed98bd6be add: add sample CustomConfigure 2025-05-20 16:48:06 +08:00
walon aa2ea50ed3 fix: fix the bug that `[ObfuzIgnore]` doesn't take effect in nested child type 2025-05-20 16:37:38 +08:00
walon faeafc3cf8 new: add sample project MultiObfuscatedAssemblies 2025-05-20 15:32:45 +08:00
walon e13adb7efd change : changes for QuickStart Sample 2025-05-20 15:32:02 +08:00
510 changed files with 13286 additions and 23291 deletions

22
.gitignore vendored
View File

@ -1,22 +0,0 @@
## 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
**/Library/
**/Logs/
**/Temp/
**/Build-*/
**/Release-*/
**/Debug-*/
**/.vs/
**/bin/
**/obj/
.vsconfig
**/UserSettings/
*.csproj
*.sln
packages-lock.json

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<Version>1.0.0</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
</ItemGroup>
</Project>

View File

@ -1,24 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35931.197
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeobfuscateStackTrace", "DeobfuscateStackTrace.csproj", "{B7192F39-1EEA-4F31-885B-B606D700FC79}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B7192F39-1EEA-4F31-885B-B606D700FC79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7192F39-1EEA-4F31-885B-B606D700FC79}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7192F39-1EEA-4F31-885B-B606D700FC79}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7192F39-1EEA-4F31-885B-B606D700FC79}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9F39E3ED-EF31-43DE-B085-0F7BF60844E8}
EndGlobalSection
EndGlobal

View File

@ -1,57 +0,0 @@
using CommandLine;
namespace DeobfuscateStackTrace
{
internal class Program
{
private class CommandOptions
{
[Option('m', "mappingFile", Required = true, HelpText = "mapping xml file")]
public string MappingFile { get; set; }
[Option('i', "input", Required = true, HelpText = "input obfuscated log file")]
public string InputFile { get; set; }
[Option('o', "output", Required = true, HelpText = "output deobfuscated log file")]
public string OutputFile { get; set; }
}
static void Main(string[] args)
{
CommandOptions opt = ParseArgs(args);
if (!File.Exists(opt.MappingFile))
{
Console.Error.WriteLine($"Mapping file {opt.MappingFile} not found");
Environment.Exit(1);
}
if (!File.Exists(opt.InputFile))
{
Console.Error.WriteLine($"Input file {opt.InputFile} not found");
Environment.Exit(1);
}
var reader = new SymbolMappingReader(opt.MappingFile);
StackTraceDeObfuscator.Convert(reader, opt.InputFile, opt.OutputFile);
}
private static CommandOptions ParseArgs(string[] args)
{
var helpWriter = new StringWriter();
var parser = new Parser(settings =>
{
settings.AllowMultiInstance = true;
settings.HelpWriter = helpWriter;
});
var result = parser.ParseArguments<CommandOptions>(args);
if (result.Tag == ParserResultType.NotParsed)
{
Console.Error.WriteLine(helpWriter.ToString());
Environment.Exit(1);
}
return ((Parsed<CommandOptions>)result).Value;
}
}
}

View File

@ -1,8 +0,0 @@
{
"profiles": {
"DeobfuscateStackTrace": {
"commandName": "Project",
"commandLineArgs": "-m ../../../mapping.xml -i ../../../obfuscated.log -o ../../../deobfuscated.log"
}
}
}

View File

@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DeobfuscateStackTrace
{
public class StackTraceDeObfuscator
{
public static void Convert(SymbolMappingReader reader, string oldLogFile, string newLogFile)
{
var obfuscatedLines = File.ReadAllLines(oldLogFile, Encoding.UTF8);
var deObfuscatedLines = new List<string>();
bool logLineFound = false;
foreach (string line in obfuscatedLines)
{
if (TryConvertLine(line, reader, ref logLineFound, out var newLine))
{
deObfuscatedLines.Add(newLine);
}
else
{
deObfuscatedLines.Add(line);
}
}
File.WriteAllLines(newLogFile, deObfuscatedLines, Encoding.UTF8);
}
private static bool TryConvertLine(string line, SymbolMappingReader reader, ref bool logLineFound, out string deObfuscatedStackTrace)
{
deObfuscatedStackTrace = line;
if (string.IsNullOrEmpty(line))
{
logLineFound = false;
return false;
}
if (!logLineFound)
{
logLineFound = line.StartsWith("UnityEngine.DebugLogHandler:Internal_Log")
|| line.StartsWith("UnityEngine.DebugLogHandler:LogFormat")
|| line.StartsWith("UnityEngine.Logger:Log");
return false;
}
return reader.TryDeObfuscateStackTrace(line, out deObfuscatedStackTrace);
}
}
}

View File

@ -1,128 +0,0 @@
using System.Xml;
namespace DeobfuscateStackTrace
{
public class SymbolMappingReader
{
private readonly Dictionary<string, List<string>> _fullSignatureMapper = new Dictionary<string, List<string>>();
private readonly Dictionary<string, List<string>> _signatureWithParamsMapper = new Dictionary<string, List<string>>();
public SymbolMappingReader(string mappingFile)
{
LoadXmlMappingFile(mappingFile);
}
private void LoadXmlMappingFile(string mappingFile)
{
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'.");
}
foreach (XmlNode node in ele.ChildNodes)
{
if (!(node is XmlElement element))
{
continue;
}
if (element.Name == "type")
{
LoadTypeMapping(element);
}
}
}
private void LoadTypeMapping(XmlElement ele)
{
foreach (XmlNode node in ele.ChildNodes)
{
if (!(node is XmlElement c))
{
continue;
}
if (node.Name == "method")
{
LoadMethodMapping(c);
}
}
}
private string GetMethodSignatureWithoutParams(string signature)
{
int index = signature.IndexOf('(');
if (index < 0)
{
return signature;
}
return signature.Substring(0, index);
}
private void LoadMethodMapping(XmlElement ele)
{
if (!ele.HasAttribute("oldStackTraceSignature"))
{
throw new System.Exception($"Invalid node name: {ele.Name}. attribute 'oldStackTraceSignature' missing.");
}
if (!ele.HasAttribute("newStackTraceSignature"))
{
throw new System.Exception($"Invalid node name: {ele.Name}. attribute 'newStackTraceSignature' missing.");
}
string oldStackTraceSignature = ele.Attributes["oldStackTraceSignature"].Value;
string newStackTraceSignature = ele.Attributes["newStackTraceSignature"].Value;
if (!_fullSignatureMapper.TryGetValue(newStackTraceSignature, out var oldFullSignatures))
{
oldFullSignatures = new List<string>();
_fullSignatureMapper[newStackTraceSignature] = oldFullSignatures;
}
oldFullSignatures.Add(oldStackTraceSignature);
string oldStackTraceSignatureWithoutParams = GetMethodSignatureWithoutParams(oldStackTraceSignature);
string newStackTraceSignatureWithoutParams = GetMethodSignatureWithoutParams(newStackTraceSignature);
if (!_signatureWithParamsMapper.TryGetValue(newStackTraceSignatureWithoutParams, out var oldSignaturesWithoutParams))
{
oldSignaturesWithoutParams = new List<string>();
_signatureWithParamsMapper[newStackTraceSignatureWithoutParams] = oldSignaturesWithoutParams;
}
oldSignaturesWithoutParams.Add(oldStackTraceSignatureWithoutParams);
}
public bool TryDeObfuscateStackTrace(string obfuscatedStackTraceLog, out string deObfuscatedStackTrace)
{
obfuscatedStackTraceLog = obfuscatedStackTraceLog.Trim();
if (_fullSignatureMapper.TryGetValue(obfuscatedStackTraceLog, out var oldFullSignatures))
{
deObfuscatedStackTrace = string.Join("|", oldFullSignatures);
return true;
}
string obfuscatedStackTraceSignatureWithoutParams = GetMethodSignatureWithoutParams(obfuscatedStackTraceLog);
if (_signatureWithParamsMapper.TryGetValue(obfuscatedStackTraceSignatureWithoutParams, out var oldSignaturesWithoutParams))
{
deObfuscatedStackTrace = obfuscatedStackTraceLog.Replace(obfuscatedStackTraceSignatureWithoutParams, string.Join("|", oldSignaturesWithoutParams));
return true;
}
deObfuscatedStackTrace = null;
return false;
}
}
}

View File

@ -1,17 +0,0 @@
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()

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +0,0 @@
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()

View File

@ -2,11 +2,7 @@
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
@ -17,7 +13,7 @@ namespace Obfuz.Conf
}
public interface IMethodRule<R> where R: IRule<R>
public interface IMethodRule<R> where R : IRule<R>
{
string Name { get; set; }
NameMatcher NameMatcher { get; set; }
@ -33,7 +29,7 @@ namespace Obfuz.Conf
public R Rule { get; set; }
}
public interface ITypeRule<T, R> where T: IMethodRule<R> where R : IRule<R>
public interface ITypeRule<T, R> where T : IMethodRule<R> where R : IRule<R>
{
string Name { get; set; }

View File

@ -2,12 +2,7 @@
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
{

View File

@ -3,9 +3,6 @@ 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;
@ -189,7 +186,7 @@ namespace Obfuz
ObfuscationPassType passType = ObfuscationPassType.None;
foreach (var passName in obfuscationPassTypesStr.Split('|'))
{
if (Enum.TryParse< ObfuscationPassType>(passName, out var pass))
if (Enum.TryParse<ObfuscationPassType>(passName, out var pass))
{
passType |= pass;
}
@ -389,7 +386,7 @@ namespace Obfuz
{
if (ass.nameMatcher.IsMatch(assName))
{
result = (ass, _defaultPassRule);
result = (ass, ass.rule);
break;
}
}

32
Editor/ConstValues.cs Normal file
View File

@ -0,0 +1,32 @@
namespace Obfuz.Editor
{
public static class ConstValues
{
public const string ObfuzInternalSymbolNamePrefix = "$Obfuz$";
public const string ObfuzRuntimeAssemblyName = "Obfuz.Runtime";
public const string ObfuzIgnoreAttributeFullName = "Obfuz.ObfuzIgnoreAttribute";
public const string ObfuzScopeFullName = "Obfuz.ObfuzScope";
public const string EncryptFieldAttributeFullName = "Obfuz.EncryptFieldAttribute";
public const string GeneratedEncryptionVirtualMachineFullName = "Obfuz.EncryptionVM.GeneratedEncryptionVirtualMachine";
public const string EmbeddedAttributeFullName = "Microsoft.CodeAnalysis.EmbeddedAttribute";
public const string MonoPInvokeCallbackAttributeName = "MonoPInvokeCallbackAttribute";
public const string ZluaLuaInvokeAttributeFullName = "Zlua.LuaInvokeAttribute";
public const string ZluaLuaCallbackAttributeFullName = "Zlua.LuaCallbackAttribute";
public const string ZluaLuaMarshalAsAttributeFullName = "Zlua.LuaMarshalAsAttribute";
public const string BurstCompileFullName = "Unity.Burst.BurstCompileAttribute";
public const string DOTSCompilerGeneratedAttributeFullName = "Unity.Jobs.DOTSCompilerGeneratedAttribute";
public const string RuntimeInitializedOnLoadMethodAttributeFullName = "UnityEngine.RuntimeInitializeOnLoadMethodAttribute";
public const string BlackboardEnumAttributeFullName = "Unity.Behavior.BlackboardEnumAttribute";
public const string CompilerGeneratedAttributeFullName = "System.Runtime.CompilerServices.CompilerGeneratedAttribute";
}
}

View File

@ -6,22 +6,12 @@ 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
public class ConstFieldAllocator : GroupByModuleEntityBase
{
private ModuleDef _module;
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private readonly RvaDataAllocator _rvaDataAllocator;
private readonly GroupByModuleEntityManager _moduleEntityManager;
private EncryptionScopeInfo _encryptionScope;
private RandomCreator _randomCreator;
private IEncryptor _encryptor;
@ -62,19 +52,14 @@ namespace Obfuz.Data
private bool _done;
public ModuleConstFieldAllocator(EncryptionScopeProvider encryptionScopeProvider, RvaDataAllocator rvaDataAllocator, GroupByModuleEntityManager moduleEntityManager)
public ConstFieldAllocator()
{
_encryptionScopeProvider = encryptionScopeProvider;
_rvaDataAllocator = rvaDataAllocator;
_moduleEntityManager = moduleEntityManager;
}
public void Init(ModuleDef mod)
public override void Init()
{
_module = mod;
_encryptionScope = _encryptionScopeProvider.GetScope(mod);
_randomCreator = _encryptionScope.localRandomCreator;
_encryptor = _encryptionScope.encryptor;
_randomCreator = EncryptionScope.localRandomCreator;
_encryptor = EncryptionScope.encryptor;
}
const int maxFieldCount = 1000;
@ -82,34 +67,37 @@ namespace Obfuz.Data
private TypeSig GetTypeSigOfValue(object value)
{
ModuleDef mod = Module;
if (value is int)
return _module.CorLibTypes.Int32;
return mod.CorLibTypes.Int32;
if (value is long)
return _module.CorLibTypes.Int64;
return mod.CorLibTypes.Int64;
if (value is float)
return _module.CorLibTypes.Single;
return mod.CorLibTypes.Single;
if (value is double)
return _module.CorLibTypes.Double;
return mod.CorLibTypes.Double;
if (value is string)
return _module.CorLibTypes.String;
return mod.CorLibTypes.String;
if (value is byte[])
return new SZArraySig(_module.CorLibTypes.Byte);
return new SZArraySig(mod.CorLibTypes.Byte);
throw new NotSupportedException($"Unsupported type: {value.GetType()}");
}
private ConstFieldInfo CreateConstFieldInfo(object value)
{
ModuleDef mod = Module;
if (_holderTypeDef == null || _holderTypeDef.Fields.Count >= maxFieldCount)
{
_module.EnableTypeDefFindCache = false;
ITypeDefOrRef objectTypeRef = _module.Import(typeof(object));
using (var scope = new DisableTypeDefFindCacheScope(mod))
{
ITypeDefOrRef objectTypeRef = mod.Import(typeof(object));
_holderTypeDef = new TypeDefUser($"{ConstValues.ObfuzInternalSymbolNamePrefix}ConstFieldHolder${_holderTypeDefs.Count}", objectTypeRef);
_module.Types.Add(_holderTypeDef);
mod.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.Public | FieldAttributes.InitOnly);
var field = new FieldDefUser($"{ConstValues.ObfuzInternalSymbolNamePrefix}RVA_Value{_holderTypeDef.Fields.Count}", new FieldSig(GetTypeSigOfValue(value)), FieldAttributes.Static | FieldAttributes.Public);
field.DeclaringType = _holderTypeDef;
return new ConstFieldInfo
{
@ -163,41 +151,35 @@ namespace Obfuz.Data
return AllocateAny(value);
}
private DefaultMetadataImporter GetModuleMetadataImporter()
{
return _moduleEntityManager.GetDefaultModuleMetadataImporter(_module, _encryptionScopeProvider);
}
private void CreateCCtorOfRvaTypeDef(TypeDef type)
{
ModuleDef mod = Module;
var cctor = new MethodDefUser(".cctor",
MethodSig.CreateStatic(_module.CorLibTypes.Void),
MethodSig.CreateStatic(mod.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();
DefaultMetadataImporter importer = this.GetDefaultModuleMetadataImporter();
RvaDataAllocator rvaDataAllocator = GetEntity<RvaDataAllocator>();
// 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 ops = EncryptionUtil.GenerateEncryptionOpCodes(localRandom, _encryptor, EncryptionScopeInfo.MaxEncryptionLevel, false);
int salt = localRandom.NextInt();
switch (constInfo.value)
{
case int i:
{
int encryptedValue = _encryptor.Encrypt(i, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
ins.Add(Instruction.CreateLdcI4(ops));
@ -208,7 +190,7 @@ namespace Obfuz.Data
case long l:
{
long encryptedValue = _encryptor.Encrypt(l, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
ins.Add(Instruction.CreateLdcI4(ops));
@ -219,7 +201,7 @@ namespace Obfuz.Data
case float f:
{
float encryptedValue = _encryptor.Encrypt(f, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
ins.Add(Instruction.CreateLdcI4(ops));
@ -230,7 +212,7 @@ namespace Obfuz.Data
case double d:
{
double encryptedValue = _encryptor.Encrypt(d, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
ins.Add(Instruction.CreateLdcI4(ops));
@ -241,7 +223,7 @@ namespace Obfuz.Data
case string s:
{
byte[] encryptedValue = _encryptor.Encrypt(s, ops, salt);
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
Assert.AreEqual(encryptedValue.Length, rvaData.size);
@ -255,7 +237,7 @@ namespace Obfuz.Data
{
byte[] encryptedValue = _encryptor.Encrypt(bs, 0, bs.Length, ops, salt);
Assert.AreEqual(encryptedValue.Length, bs.Length);
RvaData rvaData = _rvaDataAllocator.Allocate(_module, encryptedValue);
RvaData rvaData = rvaDataAllocator.Allocate(encryptedValue);
ins.Add(Instruction.Create(OpCodes.Ldsfld, rvaData.field));
ins.Add(Instruction.CreateLdcI4(rvaData.offset));
ins.Add(Instruction.CreateLdcI4(bs.Length));
@ -271,7 +253,7 @@ namespace Obfuz.Data
ins.Add(Instruction.Create(OpCodes.Ret));
}
public void Done()
public override void Done()
{
if (_done)
{
@ -284,61 +266,4 @@ namespace Obfuz.Data
}
}
}
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,323 @@
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 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 RvaDataAllocator : GroupByModuleEntityBase
{
const int maxRvaDataSize = 2 * 1024;
// in HybridCLR version below 8.3.0, the max total static field size of a type is 16KB, so we limit the total size of RVA data to 16KB
const int maxTotalRvaDataFieldSizeInHybridCLR = 16 * 1024;
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 class RvaTypeDefInfo
{
public readonly TypeDef typeDef;
public readonly int index;
public readonly List<RvaField> rvaFields = new List<RvaField>();
public RvaTypeDefInfo(TypeDef typeDef, int index)
{
this.typeDef = typeDef;
this.index = index;
}
}
private RvaField _currentField;
private RvaTypeDefInfo _currentRvaType;
private readonly List<RvaTypeDefInfo> _rvaTypeDefs = new List<RvaTypeDefInfo>();
private readonly Dictionary<int, TypeDef> _dataHolderTypeBySizes = new Dictionary<int, TypeDef>();
private bool _done;
public RvaDataAllocator()
{
}
public override void Init()
{
_random = EncryptionScope.localRandomCreator(HashUtil.ComputeHash(Module.Name));
}
private (FieldDef, FieldDef) CreateDataHolderRvaField(TypeDef dataHolderType)
{
if (_currentRvaType == null || _currentRvaType.rvaFields.Count >= maxTotalRvaDataFieldSizeInHybridCLR / maxRvaDataSize - 1)
{
using (var scope = new DisableTypeDefFindCacheScope(Module))
{
var rvaTypeDef = new TypeDefUser($"$Obfuz$RVA${_rvaTypeDefs.Count}", Module.CorLibTypes.Object.ToTypeDefOrRef());
Module.Types.Add(rvaTypeDef);
_currentRvaType = new RvaTypeDefInfo(rvaTypeDef, _rvaTypeDefs.Count);
_rvaTypeDefs.Add(_currentRvaType);
}
}
var holderField = new FieldDefUser($"$RVA_Data{_currentRvaType.rvaFields.Count}", new FieldSig(dataHolderType.ToTypeSig()), FieldAttributes.InitOnly | FieldAttributes.Static | FieldAttributes.HasFieldRVA);
holderField.DeclaringType = _currentRvaType.typeDef;
var runtimeValueField = new FieldDefUser($"$RVA_Value{_currentRvaType.rvaFields.Count}", new FieldSig(new SZArraySig(Module.CorLibTypes.Byte)), FieldAttributes.Static | FieldAttributes.Public);
runtimeValueField.DeclaringType = _currentRvaType.typeDef;
return (holderField, runtimeValueField);
}
private TypeDef GetDataHolderType(int size)
{
size = (size + 15) & ~15; // align to 16 bytes
if (_dataHolderTypeBySizes.TryGetValue(size, out var type))
return type;
using (var scope = new DisableTypeDefFindCacheScope(Module))
{
var dataHolderType = new TypeDefUser($"$ObfuzRVA$DataHolder{size}", Module.Import(typeof(ValueType)));
dataHolderType.Attributes = TypeAttributes.Public | TypeAttributes.Sealed;
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(),
};
_currentRvaType.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 AddVerifyCodes(IList<Instruction> insts, DefaultMetadataImporter importer)
{
int verifyIntValue = 0x12345678;
EncryptionScopeInfo encryptionScope = this.EncryptionScope;
IRandom verifyRandom = encryptionScope.localRandomCreator(verifyIntValue);
int verifyOps = EncryptionUtil.GenerateEncryptionOpCodes(verifyRandom, encryptionScope.encryptor, EncryptionScopeInfo.MaxEncryptionLevel, false);
int verifySalt = verifyRandom.NextInt();
int encryptedVerifyIntValue = encryptionScope.encryptor.Encrypt(verifyIntValue, verifyOps, verifySalt);
insts.Add(Instruction.Create(OpCodes.Ldc_I4, verifyIntValue));
insts.Add(Instruction.CreateLdcI4(encryptedVerifyIntValue));
insts.Add(Instruction.CreateLdcI4(verifyOps));
insts.Add(Instruction.CreateLdcI4(verifySalt));
insts.Add(Instruction.Create(OpCodes.Call, importer.DecryptInt));
insts.Add(Instruction.Create(OpCodes.Call, importer.VerifySecretKey));
}
private void CreateCCtorOfRvaTypeDef()
{
foreach (RvaTypeDefInfo rvaTypeDef in _rvaTypeDefs)
{
ModuleDef mod = rvaTypeDef.typeDef.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.typeDef;
//_rvaTypeDef.Methods.Add(cctor);
var body = new CilBody();
cctorMethod.Body = body;
var ins = body.Instructions;
DefaultMetadataImporter importer = this.GetDefaultModuleMetadataImporter();
AddVerifyCodes(ins, importer);
foreach (var field in rvaTypeDef.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.InitializedArray));
// 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 _rvaTypeDefs.SelectMany(t => t.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 override void Done()
{
if (_done)
{
throw new Exception("can't call Done twice");
}
_done = true;
SetFieldsRVA();
CreateCCtorOfRvaTypeDef();
}
}
}

View File

@ -3,9 +3,6 @@ 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
{
@ -41,13 +38,20 @@ namespace Obfuz.Emit
public IList<BasicBlock> Blocks => _blocks;
public BasicBlockCollection(MethodDef method)
public BasicBlockCollection(MethodDef method, bool computeInLoop)
{
_method = method;
HashSet<Instruction> splitPoints = BuildSplitPoint(method);
BuildBasicBlocks(method, splitPoints);
BuildInOutGraph(method);
if (computeInLoop)
{
ComputeBlocksInLoop();
}
}
public void ComputeBlocksInLoop()
{
var loopBlocks = FindLoopBlocks(_blocks);
foreach (var block in loopBlocks)
{
@ -100,6 +104,7 @@ namespace Obfuz.Emit
{
splitPoints.Add(nextInst);
}
splitPoints.Add((Instruction)curInst.Operand);
break;
}
case FlowControl.Cond_Branch:
@ -207,11 +212,21 @@ namespace Obfuz.Emit
}
break;
}
case FlowControl.Call:
case FlowControl.Next:
{
if (nextBlock != null)
{
curBlock.AddTargetBasicBlock(nextBlock);
}
break;
}
case FlowControl.Return:
case FlowControl.Throw:
{
break;
}
default: throw new NotSupportedException($"Unsupported flow control: {lastInst.OpCode.FlowControl} in method {method.FullName}");
}
}
}

View File

@ -0,0 +1,423 @@
using dnlib.DotNet;
using System;
using System.Reflection;
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;
private IMethod _decryptInitializeArray;
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 IMethod DecryptInitializeArray => _decryptInitializeArray;
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);
_decryptInitializeArray = mod.Import(encryptionServiceType.GetMethod("DecryptInitializeArray", new[] { typeof(System.Array), typeof(System.RuntimeFieldHandle), typeof(int), typeof(int), typeof(int) }));
Assert.IsNotNull(_decryptInitializeArray);
}
}
public class DefaultMetadataImporter : GroupByModuleEntityBase
{
private EncryptionServiceMetadataImporter _defaultEncryptionServiceMetadataImporter;
private EncryptionServiceMetadataImporter _staticDefaultEncryptionServiceMetadataImporter;
private EncryptionServiceMetadataImporter _dynamicDefaultEncryptionServiceMetadataImporter;
public DefaultMetadataImporter()
{
}
public override void Init()
{
ModuleDef mod = Module;
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);
_verifySecretKey = mod.Import(typeof(AssertUtility).GetMethod("VerifySecretKey", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_verifySecretKey, "VerifySecretKey not found");
_obfuscationTypeMapperRegisterType = mod.Import(typeof(ObfuscationTypeMapper).GetMethod("RegisterType", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null));
Assert.IsNotNull(_obfuscationTypeMapperRegisterType, "ObfuscationTypeMapper.RegisterType not found");
var exprUtilityType = typeof(ExprUtility);
_addInt = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_addInt, "ExprUtility.Add(int, int) not found");
_addLong = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(long), typeof(long) }));
Assert.IsNotNull(_addLong, "ExprUtility.Add(long, long) not found");
_addFloat = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(float), typeof(float) }));
Assert.IsNotNull(_addFloat, "ExprUtility.Add(float, float) not found");
_addDouble = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(double), typeof(double) }));
Assert.IsNotNull(_addDouble, "ExprUtility.Add(double, double) not found");
_addIntPtr = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(IntPtr), typeof(IntPtr) }));
Assert.IsNotNull(_addIntPtr, "ExprUtility.Add(IntPtr, IntPtr) not found");
_addIntPtrInt = mod.Import(exprUtilityType.GetMethod("Add", new[] { typeof(IntPtr), typeof(int) }));
Assert.IsNotNull(_addIntPtrInt, "ExprUtility.Add(IntPtr, int) not found");
_subtractInt = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_subtractInt, "ExprUtility.Subtract(int, int) not found");
_subtractLong = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(long), typeof(long) }));
Assert.IsNotNull(_subtractLong, "ExprUtility.Subtract(long, long) not found");
_subtractFloat = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(float), typeof(float) }));
Assert.IsNotNull(_subtractFloat, "ExprUtility.Subtract(float, float) not found");
_subtractDouble = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(double), typeof(double) }));
Assert.IsNotNull(_subtractDouble, "ExprUtility.Subtract(double, double) not found");
_subtractIntPtr = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(IntPtr), typeof(IntPtr) }));
Assert.IsNotNull(_subtractIntPtr, "ExprUtility.Subtract(IntPtr, IntPtr) not found");
_subtractIntPtrInt = mod.Import(exprUtilityType.GetMethod("Subtract", new[] { typeof(IntPtr), typeof(int) }));
Assert.IsNotNull(_subtractIntPtrInt, "ExprUtility.Subtract(IntPtr, int) not found");
_multiplyInt = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_multiplyInt, "ExprUtility.Multiply(int, int) not found");
_multiplyLong = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(long), typeof(long) }));
Assert.IsNotNull(_multiplyLong, "ExprUtility.Multiply(long, long) not found");
_multiplyFloat = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(float), typeof(float) }));
Assert.IsNotNull(_multiplyFloat, "ExprUtility.Multiply(float, float) not found");
_multiplyDouble = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(double), typeof(double) }));
Assert.IsNotNull(_multiplyDouble, "ExprUtility.Multiply(double, double) not found");
_multiplyIntPtr = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(IntPtr), typeof(IntPtr) }));
Assert.IsNotNull(_multiplyIntPtr, "ExprUtility.Multiply(IntPtr, IntPtr) not found");
_multiplyIntPtrInt = mod.Import(exprUtilityType.GetMethod("Multiply", new[] { typeof(IntPtr), typeof(int) }));
Assert.IsNotNull(_multiplyIntPtrInt, "ExprUtility.Multiply(IntPtr, int) not found");
_divideInt = mod.Import(exprUtilityType.GetMethod("Divide", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_divideInt, "ExprUtility.Divide(int, int) not found");
_divideLong = mod.Import(exprUtilityType.GetMethod("Divide", new[] { typeof(long), typeof(long) }));
Assert.IsNotNull(_divideLong);
_divideFloat = mod.Import(exprUtilityType.GetMethod("Divide", new[] { typeof(float), typeof(float) }));
Assert.IsNotNull(_divideFloat, "ExprUtility.Divide(float, float) not found");
_divideDouble = mod.Import(exprUtilityType.GetMethod("Divide", new[] { typeof(double), typeof(double) }));
Assert.IsNotNull(_divideDouble, "ExprUtility.Divide(double, double) not found");
_divideUnInt = mod.Import(exprUtilityType.GetMethod("DivideUn", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_divideUnInt, "ExprUtility.DivideUn(int, int) not found");
_divideUnLong = mod.Import(exprUtilityType.GetMethod("DivideUn", new[] { typeof(long), typeof(long) }));
Assert.IsNotNull(_divideUnLong, "ExprUtility.DivideUn(long, long) not found");
_remInt = mod.Import(exprUtilityType.GetMethod("Rem", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_remInt, "ExprUtility.Rem(int, int) not found");
_remLong = mod.Import(exprUtilityType.GetMethod("Rem", new[] { typeof(long), typeof(long) }));
Assert.IsNotNull(_remLong, "ExprUtility.Rem(long, long) not found");
_remFloat = mod.Import(exprUtilityType.GetMethod("Rem", new[] { typeof(float), typeof(float) }));
Assert.IsNotNull(_remFloat, "ExprUtility.Rem(float, float) not found");
_remDouble = mod.Import(exprUtilityType.GetMethod("Rem", new[] { typeof(double), typeof(double) }));
Assert.IsNotNull(_remDouble, "ExprUtility.Rem(double, double) not found");
_remUnInt = mod.Import(exprUtilityType.GetMethod("RemUn", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_remUnInt, "ExprUtility.RemUn(int, int) not found");
_remUnLong = mod.Import(exprUtilityType.GetMethod("RemUn", new[] { typeof(long), typeof(long) }));
Assert.IsNotNull(_remUnLong, "ExprUtility.RemUn(long, long) not found");
_negInt = mod.Import(exprUtilityType.GetMethod("Negate", new[] { typeof(int) }));
Assert.IsNotNull(_negInt, "ExprUtility.Negate(int) not found");
_negLong = mod.Import(exprUtilityType.GetMethod("Negate", new[] { typeof(long) }));
Assert.IsNotNull(_negLong, "ExprUtility.Negate(long) not found");
_negFloat = mod.Import(exprUtilityType.GetMethod("Negate", new[] { typeof(float) }));
Assert.IsNotNull(_negFloat, "ExprUtility.Negate(float) not found");
_negDouble = mod.Import(exprUtilityType.GetMethod("Negate", new[] { typeof(double) }));
Assert.IsNotNull(_negDouble, "ExprUtility.Negate(double) not found");
_andInt = mod.Import(exprUtilityType.GetMethod("And", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_andInt, "ExprUtility.And(int, int) not found");
_andLong = mod.Import(exprUtilityType.GetMethod("And", new[] { typeof(long), typeof(long) }));
Assert.IsNotNull(_andLong, "ExprUtility.And(long, long) not found");
_orInt = mod.Import(exprUtilityType.GetMethod("Or", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_orInt, "ExprUtility.Or(int, int) not found");
_orLong = mod.Import(exprUtilityType.GetMethod("Or", new[] { typeof(long), typeof(long) }));
Assert.IsNotNull(_orLong, "ExprUtility.Or(long, long) not found");
_xorInt = mod.Import(exprUtilityType.GetMethod("Xor", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_xorInt, "ExprUtility.Xor(int, int) not found");
_xorLong = mod.Import(exprUtilityType.GetMethod("Xor", new[] { typeof(long), typeof(long) }));
Assert.IsNotNull(_xorLong, "ExprUtility.Xor(long, long) not found");
_notInt = mod.Import(exprUtilityType.GetMethod("Not", new[] { typeof(int) }));
Assert.IsNotNull(_notInt, "ExprUtility.Not(int) not found");
_notLong = mod.Import(exprUtilityType.GetMethod("Not", new[] { typeof(long) }));
Assert.IsNotNull(_notLong, "ExprUtility.Not(long) not found");
_shlInt = mod.Import(exprUtilityType.GetMethod("ShiftLeft", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_shlInt, "ExprUtility.ShiftLeft(int, int) not found");
_shlLong = mod.Import(exprUtilityType.GetMethod("ShiftLeft", new[] { typeof(long), typeof(int) }));
Assert.IsNotNull(_shlLong, "ExprUtility.ShiftLeft(long, int) not found");
_shrInt = mod.Import(exprUtilityType.GetMethod("ShiftRight", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_shrInt, "ExprUtility.ShiftRight(int, int) not found");
_shrLong = mod.Import(exprUtilityType.GetMethod("ShiftRight", new[] { typeof(long), typeof(int) }));
Assert.IsNotNull(_shrLong, "ExprUtility.ShiftRight(long, int) not found");
_shrUnInt = mod.Import(exprUtilityType.GetMethod("ShiftRightUn", new[] { typeof(int), typeof(int) }));
Assert.IsNotNull(_shrUnInt, "ExprUtility.ShiftRightUn(int, int) not found");
_shrUnLong = mod.Import(exprUtilityType.GetMethod("ShiftRightUn", new[] { typeof(long), typeof(int) }));
Assert.IsNotNull(_shrUnLong, "ExprUtility.ShiftRightUn(long, int) not found");
_staticDefaultEncryptionServiceMetadataImporter = new EncryptionServiceMetadataImporter(mod, typeof(EncryptionService<DefaultStaticEncryptionScope>));
_dynamicDefaultEncryptionServiceMetadataImporter = new EncryptionServiceMetadataImporter(mod, typeof(EncryptionService<DefaultDynamicEncryptionScope>));
if (EncryptionScopeProvider.IsDynamicSecretAssembly(mod))
{
_defaultEncryptionServiceMetadataImporter = _dynamicDefaultEncryptionServiceMetadataImporter;
}
else
{
_defaultEncryptionServiceMetadataImporter = _staticDefaultEncryptionServiceMetadataImporter;
}
}
public override void Done()
{
}
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;
private IMethod _verifySecretKey;
private IMethod _obfuscationTypeMapperRegisterType;
private IMethod _addInt;
private IMethod _addLong;
private IMethod _addFloat;
private IMethod _addDouble;
private IMethod _addIntPtr;
private IMethod _addIntPtrInt;
private IMethod _subtractInt;
private IMethod _subtractLong;
private IMethod _subtractFloat;
private IMethod _subtractDouble;
private IMethod _subtractIntPtr;
private IMethod _subtractIntPtrInt;
private IMethod _multiplyInt;
private IMethod _multiplyLong;
private IMethod _multiplyFloat;
private IMethod _multiplyDouble;
private IMethod _multiplyIntPtr;
private IMethod _multiplyIntPtrInt;
private IMethod _divideInt;
private IMethod _divideLong;
private IMethod _divideFloat;
private IMethod _divideDouble;
private IMethod _divideUnInt;
private IMethod _divideUnLong;
private IMethod _remInt;
private IMethod _remLong;
private IMethod _remFloat;
private IMethod _remDouble;
private IMethod _remUnInt;
private IMethod _remUnLong;
private IMethod _negInt;
private IMethod _negLong;
private IMethod _negFloat;
private IMethod _negDouble;
private IMethod _andInt;
private IMethod _andLong;
private IMethod _orInt;
private IMethod _orLong;
private IMethod _xorInt;
private IMethod _xorLong;
private IMethod _notInt;
private IMethod _notLong;
private IMethod _shlInt;
private IMethod _shlLong;
private IMethod _shrInt;
private IMethod _shrLong;
private IMethod _shrUnInt;
private IMethod _shrUnLong;
public IMethod CastIntAsFloat => _castIntAsFloat;
public IMethod CastLongAsDouble => _castLongAsDouble;
public IMethod CastFloatAsInt => _castFloatAsInt;
public IMethod CastDoubleAsLong => _castDoubleAsLong;
public IMethod InitializedArray => _initializeArray;
public IMethod VerifySecretKey => _verifySecretKey;
public IMethod ObfuscationTypeMapperRegisterType => _obfuscationTypeMapperRegisterType;
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;
public IMethod DecryptInitializeArray => _defaultEncryptionServiceMetadataImporter.DecryptInitializeArray;
public IMethod AddInt => _addInt;
public IMethod AddLong => _addLong;
public IMethod AddFloat => _addFloat;
public IMethod AddDouble => _addDouble;
public IMethod AddIntPtr => _addIntPtr;
public IMethod AddIntPtrInt => _addIntPtrInt;
public IMethod SubtractInt => _subtractInt;
public IMethod SubtractLong => _subtractLong;
public IMethod SubtractFloat => _subtractFloat;
public IMethod SubtractDouble => _subtractDouble;
public IMethod SubtractIntPtr => _subtractIntPtr;
public IMethod SubtractIntPtrInt => _subtractIntPtrInt;
public IMethod MultiplyInt => _multiplyInt;
public IMethod MultiplyLong => _multiplyLong;
public IMethod MultiplyFloat => _multiplyFloat;
public IMethod MultiplyDouble => _multiplyDouble;
public IMethod MultiplyIntPtr => _multiplyIntPtr;
public IMethod MultiplyIntPtrInt => _multiplyIntPtrInt;
public IMethod DivideInt => _divideInt;
public IMethod DivideLong => _divideLong;
public IMethod DivideFloat => _divideFloat;
public IMethod DivideDouble => _divideDouble;
public IMethod DivideUnInt => _divideUnInt;
public IMethod DivideUnLong => _divideUnLong;
public IMethod RemInt => _remInt;
public IMethod RemLong => _remLong;
public IMethod RemFloat => _remFloat;
public IMethod RemDouble => _remDouble;
public IMethod RemUnInt => _remUnInt;
public IMethod RemUnLong => _remUnLong;
public IMethod NegInt => _negInt;
public IMethod NegLong => _negLong;
public IMethod NegFloat => _negFloat;
public IMethod NegDouble => _negDouble;
public IMethod AndInt => _andInt;
public IMethod AndLong => _andLong;
public IMethod OrInt => _orInt;
public IMethod OrLong => _orLong;
public IMethod XorInt => _xorInt;
public IMethod XorLong => _xorLong;
public IMethod NotInt => _notInt;
public IMethod NotLong => _notLong;
public IMethod ShlInt => _shlInt;
public IMethod ShlLong => _shlLong;
public IMethod ShrInt => _shrInt;
public IMethod ShrLong => _shrLong;
public IMethod ShrUnInt => _shrUnInt;
public IMethod ShrUnLong => _shrUnLong;
}
}

View File

@ -0,0 +1,15 @@
namespace Obfuz.Emit
{
public static class EntityExtensions
{
public static T GetEntity<T>(this IGroupByModuleEntity entity) where T : IGroupByModuleEntity, new()
{
return entity.Manager.GetEntity<T>(entity.Module);
}
public static DefaultMetadataImporter GetDefaultModuleMetadataImporter(this IGroupByModuleEntity entity)
{
return entity.GetEntity<DefaultMetadataImporter>();
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 639006a739675484884778c298eebdc4
guid: 6e9557733f180764692756653eb60f88
MonoImporter:
externalObjects: {}
serializedVersion: 2

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 639006a739675484884778c298eebdc4
guid: f25425a3077f6db41873dee4223d0abc
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -0,0 +1,89 @@
using dnlib.DotNet;
using System;
using System.Collections.Generic;
namespace Obfuz.Emit
{
public interface IGroupByModuleEntity
{
GroupByModuleEntityManager Manager { get; set; }
ModuleDef Module { get; set; }
EncryptionScopeProvider EncryptionScopeProvider { get; }
EncryptionScopeInfo EncryptionScope { get; set; }
void Init();
void Done();
}
public abstract class GroupByModuleEntityBase : IGroupByModuleEntity
{
public GroupByModuleEntityManager Manager { get; set; }
public ModuleDef Module { get; set; }
public EncryptionScopeInfo EncryptionScope { get; set; }
public EncryptionScopeProvider EncryptionScopeProvider => Manager.EncryptionScopeProvider;
public T GetEntity<T>() where T : IGroupByModuleEntity, new()
{
return Manager.GetEntity<T>(Module);
}
public abstract void Init();
public abstract void Done();
}
public class GroupByModuleEntityManager
{
private readonly Dictionary<(ModuleDef, Type), IGroupByModuleEntity> _moduleEntityManagers = new Dictionary<(ModuleDef, Type), IGroupByModuleEntity>();
public EncryptionScopeProvider EncryptionScopeProvider { get; set; }
public T GetEntity<T>(ModuleDef mod) where T : IGroupByModuleEntity, new()
{
var key = (mod, typeof(T));
if (_moduleEntityManagers.TryGetValue(key, out var emitManager))
{
return (T)emitManager;
}
else
{
T newEmitManager = new T();
newEmitManager.Manager = this;
newEmitManager.Module = mod;
newEmitManager.EncryptionScope = EncryptionScopeProvider.GetScope(mod);
newEmitManager.Init();
_moduleEntityManagers[key] = newEmitManager;
return newEmitManager;
}
}
public List<T> GetEntities<T>() where T : IGroupByModuleEntity, new()
{
var managers = new List<T>();
foreach (var kv in _moduleEntityManagers)
{
if (kv.Key.Item2 == typeof(T))
{
managers.Add((T)kv.Value);
}
}
return managers;
}
public void Done<T>() where T : IGroupByModuleEntity, new()
{
var managers = GetEntities<T>();
foreach (var manager in managers)
{
manager.Done();
}
}
}
}

View File

@ -0,0 +1,74 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using System;
using System.Collections.Generic;
namespace Obfuz.Emit
{
class ScopeLocalVariables : IDisposable
{
private readonly LocalVariableAllocator _localVariableAllocator;
private readonly List<Local> _allocatedVars = new List<Local>();
public IReadOnlyList<Local> AllocatedLocals => _allocatedVars;
public ScopeLocalVariables(LocalVariableAllocator localVariableAllocator)
{
_localVariableAllocator = localVariableAllocator;
}
public Local AllocateLocal(TypeSig type)
{
var local = _localVariableAllocator.AllocateLocal(type);
_allocatedVars.Add(local);
return local;
}
public void Dispose()
{
foreach (var local in _allocatedVars)
{
_localVariableAllocator.ReturnLocal(local);
}
}
}
class LocalVariableAllocator
{
private readonly MethodDef _method;
private readonly List<Local> _freeLocals = new List<Local>();
public LocalVariableAllocator(MethodDef method)
{
_method = method;
}
public Local AllocateLocal(TypeSig type)
{
foreach (var local in _freeLocals)
{
if (TypeEqualityComparer.Instance.Equals(local.Type, type))
{
_freeLocals.Remove(local);
return local;
}
}
var newLocal = new Local(type);
// _freeLocals.Add(newLocal);
_method.Body.Variables.Add(newLocal);
return newLocal;
}
public void ReturnLocal(Local local)
{
_freeLocals.Add(local);
}
public ScopeLocalVariables CreateScope()
{
return new ScopeLocalVariables(this);
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 406c63e9d464ca544ac337bc8fcce30e
guid: 955da34fbde179641a94108ec53405ce
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Obfuz.EncryptionVM.Instructions
{

View File

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Obfuz.EncryptionVM.Instructions
{

View File

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.EncryptionVM.Instructions
{

View File

@ -1,5 +1,4 @@
using NUnit.Framework;
using System;
using Obfuz.Utils;
using System.Collections.Generic;
namespace Obfuz.EncryptionVM.Instructions
@ -14,31 +13,14 @@ namespace Obfuz.EncryptionVM.Instructions
{
_multiValue = addValue;
_opKeyIndex = opKeyIndex;
_revertMultiValue = (int)ModInverseOdd((uint)addValue);
_revertMultiValue = MathUtil.ModInverse32(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
UnityEngine.Assertions.Assert.AreEqual(a, a * _multiValue * _revertMultiValue);
}
public override int Encrypt(int value, int[] secretKey, int salt)

View File

@ -1,8 +1,5 @@
using System;
using Obfuz.Utils;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.EncryptionVM.Instructions
{
@ -21,7 +18,7 @@ namespace Obfuz.EncryptionVM.Instructions
public MultipleRotateXorInstruction(int multipleValue, int index1, int rotateBitNum, int xorValue)
{
_multipleValue = multipleValue;
_revertMultipleValue = (int)MultipleInstruction.ModInverseOdd((uint)multipleValue);
_revertMultipleValue = MathUtil.ModInverse32(multipleValue);
_index1 = index1;
_rotateBitNum = rotateBitNum;
_xorValue = xorValue;

View File

@ -1,8 +1,5 @@
using System;
using Obfuz.Utils;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.EncryptionVM.Instructions
{
@ -21,7 +18,7 @@ namespace Obfuz.EncryptionVM.Instructions
public MultipleXorRotateInstruction(int multipleValue, int index1, int xorValue, int rotateBitNum)
{
_multipleValue = multipleValue;
_revertMultipleValue = (int)MultipleInstruction.ModInverseOdd((uint)multipleValue);
_revertMultipleValue = MathUtil.ModInverse32(multipleValue);
_index1 = index1;
_rotateBitNum = rotateBitNum;
_xorValue = xorValue;

View File

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace Obfuz.EncryptionVM.Instructions
{

View File

@ -1,8 +1,5 @@
using System;
using Obfuz.Utils;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz.EncryptionVM.Instructions
{
@ -21,7 +18,7 @@ namespace Obfuz.EncryptionVM.Instructions
public XorMultipleRotateInstruction(int xorValue, int multipleValue, int index1, int rotateBitNum)
{
_multipleValue = multipleValue;
_revertMultipleValue = (int)MultipleInstruction.ModInverseOdd((uint)multipleValue);
_revertMultipleValue = MathUtil.ModInverse32(multipleValue);
_index1 = index1;
_rotateBitNum = rotateBitNum;
_xorValue = xorValue;

View File

@ -1,11 +1,8 @@
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
@ -55,7 +52,6 @@ namespace Obfuz.EncryptionVM
File.WriteAllText(outputFile, code, Encoding.UTF8);
Debug.Log($"Generate EncryptionVM code to {outputFile}");
UnityEditor.AssetDatabase.Refresh();
}
private string GenerateCode()

View File

@ -1,10 +1,7 @@
using NUnit.Framework;
using Obfuz.EncryptionVM.Instructions;
using Obfuz.EncryptionVM.Instructions;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using UnityEngine.Assertions;
using UnityEngine.UIElements;
namespace Obfuz.EncryptionVM
{

View File

@ -1,12 +1,6 @@
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
{
@ -47,7 +41,7 @@ namespace Obfuz.EncryptionVM
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++)
for (int i = 0; i < arr.Length; i++)
{
arr[i] = (byte)i;
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 5be12685e3d38a24ab47ccfde4f424a1
guid: f47f2abd9eb7ba8469ba5cb1bb085d33
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@ -0,0 +1,117 @@
using Obfuz.Utils;
using System;
using System.Linq;
using System.Text;
namespace Obfuz.GarbageCodeGeneration
{
public class ConfigGarbageCodeGenerator : SpecificGarbageCodeGeneratorBase
{
private readonly string[] _types = new string[]
{
"bool",
"byte",
"short",
"int",
"long",
"float",
"double",
};
private string CreateRandomType(IRandom random)
{
return _types[random.NextInt(_types.Length)];
}
private string GetReadMethodNameOfType(string type)
{
switch (type)
{
case "bool": return "ReadBoolean";
case "byte": return "ReadByte";
case "short": return "ReadInt16";
case "int": return "ReadInt32";
case "long": return "ReadInt64";
case "float": return "ReadSingle";
case "double": return "ReadDouble";
default: throw new ArgumentException($"Unsupported type: {type}");
}
}
class FieldGenerationInfo
{
public int index;
public string name;
public string type;
}
class MethodGenerationInfo
{
public int index;
public string name;
}
protected override object CreateField(int index, IRandom random, GenerationParameters parameters)
{
return new FieldGenerationInfo
{
index = index,
name = $"x{index}",
type = CreateRandomType(random),
};
}
protected override object CreateMethod(int index, IRandom random, GenerationParameters parameters)
{
return new MethodGenerationInfo
{
index = index,
name = $"Load{index}",
};
}
protected override void GenerateUsings(StringBuilder result, IClassGenerationInfo cgi)
{
}
protected override void GenerateField(StringBuilder result, IClassGenerationInfo cgi, IRandom random, object field, string indent)
{
var fgi = (FieldGenerationInfo)field;
result.AppendLine($"{indent}public {fgi.type} {fgi.name};");
}
protected override void GenerateMethod(StringBuilder result, IClassGenerationInfo cgi, IRandom random, object method, string indent)
{
var mgi = (MethodGenerationInfo)method;
result.AppendLine($"{indent}public void {mgi.name}(BinaryReader reader)");
result.AppendLine($"{indent}{{");
string indent2 = indent + " ";
result.AppendLine($"{indent2}int a = 0;");
result.AppendLine($"{indent2}int b = 0;");
int maxN = 100;
var shuffledFields = cgi.Fields.ToList();
RandomUtil.ShuffleList(shuffledFields, random);
foreach (FieldGenerationInfo fgi in shuffledFields)
{
result.AppendLine($"{indent2}this.{fgi.name} = reader.{GetReadMethodNameOfType(fgi.type)}();");
if (random.NextInPercentage(0.5f))
{
result.AppendLine($"{indent2}a = b * {random.NextInt(maxN)} + reader.ReadInt32();");
result.AppendLine($"{indent2}b = a * reader.ReadInt32() + {random.NextInt(maxN)};");
}
if (random.NextInPercentage(0.5f))
{
result.AppendLine($"{indent2}a += {random.NextInt(0, 10000)};");
}
if (random.NextInPercentage(0.5f))
{
result.AppendLine($"{indent2}b += {random.NextInt(0, 10000)};");
}
}
result.AppendLine($"{indent}}}");
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 406c63e9d464ca544ac337bc8fcce30e
guid: 327cb4a465ff23944a5fea30bf3beeeb
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -0,0 +1,88 @@
using Obfuz.Settings;
using Obfuz.Utils;
using System;
using UnityEngine;
namespace Obfuz.GarbageCodeGeneration
{
public class GarbageCodeGenerator
{
private const int CodeGenerationSecretKeyLength = 1024;
private readonly GarbageCodeGenerationSettings _settings;
private readonly int[] _intGenerationSecretKey;
public GarbageCodeGenerator(GarbageCodeGenerationSettings settings)
{
_settings = settings;
byte[] byteGenerationSecretKey = KeyGenerator.GenerateKey(settings.codeGenerationSecret, CodeGenerationSecretKeyLength);
_intGenerationSecretKey = KeyGenerator.ConvertToIntKey(byteGenerationSecretKey);
}
public void Generate()
{
GenerateTask(_settings.defaultTask);
if (_settings.additionalTasks != null && _settings.additionalTasks.Length > 0)
{
foreach (var task in _settings.additionalTasks)
{
GenerateTask(task);
}
}
}
public void CleanCodes()
{
Debug.Log($"Cleaning generated garbage codes begin.");
if (_settings.defaultTask != null)
{
FileUtil.RemoveDir(_settings.defaultTask.outputPath, true);
}
if (_settings.additionalTasks != null && _settings.additionalTasks.Length > 0)
{
foreach (var task in _settings.additionalTasks)
{
FileUtil.RemoveDir(task.outputPath, true);
}
}
}
private void GenerateTask(GarbageCodeGenerationTask task)
{
Debug.Log($"Generating garbage code with seed: {task.codeGenerationRandomSeed}, class count: {task.classCount}, method count per class: {task.methodCountPerClass}, types: {task.garbageCodeType}, output path: {task.outputPath}");
if (string.IsNullOrWhiteSpace(task.outputPath))
{
throw new Exception("outputPath of GarbageCodeGenerationTask is empty!");
}
var generator = CreateSpecificCodeGenerator(task.garbageCodeType);
var parameters = new GenerationParameters
{
random = new RandomWithKey(_intGenerationSecretKey, task.codeGenerationRandomSeed),
classNamespace = task.classNamespace,
classNamePrefix = task.classNamePrefix,
classCount = task.classCount,
methodCountPerClass = task.methodCountPerClass,
fieldCountPerClass = task.fieldCountPerClass,
outputPath = task.outputPath,
};
generator.Generate(parameters);
Debug.Log($"Generate garbage code end.");
}
private ISpecificGarbageCodeGenerator CreateSpecificCodeGenerator(GarbageCodeType type)
{
switch (type)
{
case GarbageCodeType.Config: return new ConfigGarbageCodeGenerator();
case GarbageCodeType.UI: return new UIGarbageCodeGenerator();
default: throw new NotSupportedException($"Garbage code type {type} is not supported.");
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ff64fd1e6f7b8874db5a5228fab159f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
using Obfuz.Utils;
namespace Obfuz.GarbageCodeGeneration
{
public class GenerationParameters
{
public IRandom random;
public string classNamespace;
public string classNamePrefix;
public int classCount;
public int methodCountPerClass;
public int fieldCountPerClass;
public string outputPath;
}
public interface ISpecificGarbageCodeGenerator
{
void Generate(GenerationParameters parameters);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 74a17802b5aab2e40a3c89e0ddbcec0d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
using Obfuz.Utils;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
namespace Obfuz.GarbageCodeGeneration
{
public abstract class SpecificGarbageCodeGeneratorBase : ISpecificGarbageCodeGenerator
{
protected interface IClassGenerationInfo
{
string Namespace { get; set; }
string Name { get; set; }
IList<object> Fields { get; set; }
IList<object> Methods { get; set; }
}
protected class ClassGenerationInfo : IClassGenerationInfo
{
public string Namespace { get; set; }
public string Name { get; set; }
public IList<object> Fields { get; set; } = new List<object>();
public IList<object> Methods { get; set; } = new List<object>();
}
public virtual void Generate(GenerationParameters parameters)
{
FileUtil.RecreateDir(parameters.outputPath);
for (int i = 0; i < parameters.classCount; i++)
{
Debug.Log($"[{GetType().Name}] Generating class {i}");
var localRandom = new RandomWithKey(((RandomWithKey)parameters.random).Key, parameters.random.NextInt());
string outputFile = $"{parameters.outputPath}/__GeneratedGarbageClass_{i}.cs";
var result = new StringBuilder(64 * 1024);
GenerateClass(i, localRandom, result, parameters);
File.WriteAllText(outputFile, result.ToString(), Encoding.UTF8);
Debug.Log($"[{GetType().Name}] Generated class {i} to {outputFile}");
}
}
protected abstract object CreateField(int index, IRandom random, GenerationParameters parameters);
protected abstract object CreateMethod(int index, IRandom random, GenerationParameters parameters);
protected virtual IClassGenerationInfo CreateClassGenerationInfo(string classNamespace, string className, IRandom random, GenerationParameters parameters)
{
var cgi = new ClassGenerationInfo
{
Namespace = classNamespace,
Name = className,
};
for (int i = 0; i < parameters.fieldCountPerClass; i++)
{
cgi.Fields.Add(CreateField(i, random, parameters));
}
for (int i = 0; i < parameters.methodCountPerClass; i++)
{
cgi.Methods.Add(CreateMethod(i, random, parameters));
}
return cgi;
}
protected virtual void GenerateClass(int classIndex, IRandom random, StringBuilder result, GenerationParameters parameters)
{
IClassGenerationInfo cgi = CreateClassGenerationInfo(parameters.classNamespace, $"{parameters.classNamePrefix}{classIndex}", random, parameters);
result.AppendLine("using System;");
result.AppendLine("using System.Collections.Generic;");
result.AppendLine("using System.Linq;");
result.AppendLine("using System.IO;");
result.AppendLine("using UnityEngine;");
GenerateUsings(result, cgi);
result.AppendLine($"namespace {cgi.Namespace}");
result.AppendLine("{");
result.AppendLine($" public class {cgi.Name}");
result.AppendLine(" {");
string indent = " ";
foreach (object field in cgi.Fields)
{
GenerateField(result, cgi, random, field, indent);
}
foreach (object method in cgi.Methods)
{
GenerateMethod(result, cgi, random, method, indent);
}
result.AppendLine(" }");
result.AppendLine("}");
}
protected abstract void GenerateUsings(StringBuilder result, IClassGenerationInfo cgi);
protected abstract void GenerateField(StringBuilder result, IClassGenerationInfo cgi, IRandom random, object field, string indent);
protected abstract void GenerateMethod(StringBuilder result, IClassGenerationInfo cgi, IRandom random, object method, string indent);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bae18fd49482f00439d37f28a6a78d9b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,157 @@
using Obfuz.Utils;
using System;
using System.Linq;
using System.Text;
namespace Obfuz.GarbageCodeGeneration
{
public class UIGarbageCodeGenerator : SpecificGarbageCodeGeneratorBase
{
/*
*
* public Button b1;
public Image b2;
public RawImage b30;
public Text b3;
public Slider b4;
public ScrollRect b5;
public Scrollbar b6;
public Mask b7;
public RectMask2D b70;
public Canvas b8;
public CanvasGroup b9;
public RectTransform b10;
public Transform b11;
public GameObject b12;
*/
private readonly string[] _types = new string[]
{
"Button",
"Image",
"RawImage",
"Text",
"Slider",
"ScrollRect",
"Scrollbar",
"Mask",
"RectMask2D",
"Canvas",
"CanvasGroup",
"RectTransform",
//"Transform",
//"GameObject",
};
private string CreateRandomType(IRandom random)
{
return _types[random.NextInt(_types.Length)];
}
private string GetReadMethodNameOfType(string type)
{
switch (type)
{
case "bool": return "ReadBoolean";
case "byte": return "ReadByte";
case "short": return "ReadInt16";
case "int": return "ReadInt32";
case "long": return "ReadInt64";
case "float": return "ReadSingle";
case "double": return "ReadDouble";
default: throw new ArgumentException($"Unsupported type: {type}");
}
}
class FieldGenerationInfo
{
public int index;
public string name;
public string type;
}
class MethodGenerationInfo
{
public int index;
public string name;
}
protected override object CreateField(int index, IRandom random, GenerationParameters parameters)
{
return new FieldGenerationInfo
{
index = index,
name = $"x{index}",
type = CreateRandomType(random),
};
}
protected override object CreateMethod(int index, IRandom random, GenerationParameters parameters)
{
return new MethodGenerationInfo
{
index = index,
name = $"Init{index}",
};
}
protected override void GenerateUsings(StringBuilder result, IClassGenerationInfo cgi)
{
result.AppendLine("using UnityEngine.UI;");
}
protected override void GenerateField(StringBuilder result, IClassGenerationInfo cgi, IRandom random, object field, string indent)
{
var fgi = (FieldGenerationInfo)field;
result.AppendLine($"{indent}public {fgi.type} {fgi.name};");
}
protected override void GenerateMethod(StringBuilder result, IClassGenerationInfo cgi, IRandom random, object method, string indent)
{
var mgi = (MethodGenerationInfo)method;
result.AppendLine($"{indent}public void {mgi.name}(GameObject go)");
result.AppendLine($"{indent}{{");
string indent2 = indent + " ";
result.AppendLine($"{indent2}int a = 0;");
result.AppendLine($"{indent2}int b = 0;");
int maxN = 100;
var shuffledFields = cgi.Fields.ToList();
RandomUtil.ShuffleList(shuffledFields, random);
foreach (FieldGenerationInfo fgi in shuffledFields)
{
if (random.NextInPercentage(0.5f))
{
result.AppendLine($"{indent2}this.{fgi.name} = go.transform.Find(\"ui/{fgi.name}\").GetComponent<{fgi.type}>();");
}
else
{
result.AppendLine($"{indent2}this.{fgi.name} = go.GetComponent<{fgi.type}>();");
}
if (random.NextInPercentage(0.5f))
{
result.AppendLine($"{indent2}a = b * {random.NextInt(maxN)} + go.layer;");
result.AppendLine($"{indent2}b = a * go.layer + {random.NextInt(maxN)};");
}
if (random.NextInPercentage(0.5f))
{
result.AppendLine($"{indent2}a *= {random.NextInt(0, 10000)};");
}
if (random.NextInPercentage(0.5f))
{
result.AppendLine($"{indent2}b /= {random.NextInt(0, 10000)};");
}
if (random.NextInPercentage(0.5f))
{
result.AppendLine($"{indent2}a = a * b << {random.NextInt(0, 10000)};");
}
if (random.NextInPercentage(0.5f))
{
result.AppendLine($"{indent2}b = a / b & {random.NextInt(0, 10000)};");
}
}
result.AppendLine($"{indent}}}");
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5071c4b9c7f5aef409f3e7fdb45ecd8d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,9 +1,4 @@
using Obfuz.ObfusPasses;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Obfuz
{

View File

@ -1,54 +1,20 @@
using dnlib.DotNet.Emit;
using dnlib.DotNet;
using System.Collections.Generic;
using System.Linq;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Emit;
using System.Collections.Generic;
namespace Obfuz.ObfusPasses
{
public abstract class BasicBlockObfuscationPassBase : ObfuscationPassBase
public abstract class BasicBlockObfuscationPassBase : ObfuscationMethodPassBase
{
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 virtual bool ComputeBlockInLoop => true;
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)
protected override void ObfuscateData(MethodDef method)
{
BasicBlockCollection bbc = new BasicBlockCollection(method);
BasicBlockCollection bbc = new BasicBlockCollection(method, ComputeBlockInLoop);
IList<Instruction> instructions = method.Body.Instructions;

View File

@ -0,0 +1,166 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Emit;
using Obfuz.Settings;
using Obfuz.Utils;
using System.Collections.Generic;
namespace Obfuz.ObfusPasses.CallObfus
{
class ObfusMethodContext
{
public MethodDef method;
public LocalVariableAllocator localVariableAllocator;
public IRandom localRandom;
public EncryptionScopeInfo encryptionScope;
}
public class CallObfusPass : ObfuscationMethodPassBase
{
public static CallObfuscationSettingsFacade CurrentSettings { get; private set; }
private readonly CallObfuscationSettingsFacade _settings;
private SpecialWhiteListMethodCalculator _specialWhiteListMethodCache;
private IObfuscator _dynamicProxyObfuscator;
private IObfuscationPolicy _dynamicProxyPolicy;
public override ObfuscationPassType Type => ObfuscationPassType.CallObfus;
public CallObfusPass(CallObfuscationSettingsFacade settings)
{
_settings = settings;
CurrentSettings = settings;
}
public override void Stop()
{
_dynamicProxyObfuscator.Done();
}
public override void Start()
{
var ctx = ObfuscationPassContext.Current;
_specialWhiteListMethodCache = new SpecialWhiteListMethodCalculator(ctx.coreSettings.targetRuntime, _settings.obfuscateCallToMethodInMscorlib);
_dynamicProxyObfuscator = CreateObfuscator(ctx, _settings.proxyMode);
_dynamicProxyPolicy = new ConfigurableObfuscationPolicy(ctx.coreSettings.assembliesToObfuscate, _settings.ruleFiles);
}
private IObfuscator CreateObfuscator(ObfuscationPassContext ctx, ProxyMode mode)
{
switch (mode)
{
case ProxyMode.Dispatch:
return new DispatchProxyObfuscator(ctx.moduleEntityManager);
case ProxyMode.Delegate:
return new DelegateProxyObfuscator(ctx.moduleEntityManager);
default:
throw new System.NotSupportedException($"Unsupported proxy mode: {mode}");
}
}
protected override void ObfuscateData(MethodDef method)
{
BasicBlockCollection bbc = new BasicBlockCollection(method, false);
IList<Instruction> instructions = method.Body.Instructions;
var outputInstructions = new List<Instruction>();
var totalFinalInstructions = new List<Instruction>();
ObfuscationPassContext ctx = ObfuscationPassContext.Current;
var encryptionScope = ctx.moduleEntityManager.EncryptionScopeProvider.GetScope(method.Module);
var localRandom = encryptionScope.localRandomCreator(MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method));
var omc = new ObfusMethodContext
{
method = method,
localVariableAllocator = new LocalVariableAllocator(method),
localRandom = localRandom,
encryptionScope = encryptionScope,
};
Instruction lastInst = null;
for (int i = 0; i < instructions.Count; i++)
{
Instruction inst = instructions[i];
BasicBlock block = bbc.GetBasicBlockByInstruction(inst);
outputInstructions.Clear();
if (TryObfuscateInstruction(method, lastInst, inst, outputInstructions, omc))
{
// 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);
}
lastInst = inst;
}
instructions.Clear();
foreach (var obInst in totalFinalInstructions)
{
instructions.Add(obInst);
}
}
protected override bool NeedObfuscateMethod(MethodDef method)
{
return _dynamicProxyPolicy.NeedObfuscateCallInMethod(method);
}
private bool TryObfuscateInstruction(MethodDef callerMethod, Instruction lastInst, Instruction inst, List<Instruction> outputInstructions, ObfusMethodContext ctx)
{
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 (lastInst != null && lastInst.OpCode.Code == Code.Constrained)
{
return false;
}
callVir = true;
break;
}
default: return false;
}
if (_specialWhiteListMethodCache.IsInWhiteList(calledMethod))
{
return false;
}
if (!_dynamicProxyPolicy.NeedObfuscateCalledMethod(callerMethod, calledMethod, callVir))
{
return false;
}
return _dynamicProxyObfuscator.Obfuscate(callerMethod, calledMethod, callVir, outputInstructions);
}
}
}

View File

@ -1,13 +1,10 @@
using dnlib.DotNet;
using Obfuz.Conf;
using Obfuz.Settings;
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
{
@ -38,29 +35,12 @@ namespace Obfuz.ObfusPasses.CallObfus
class ObfuscationRule : IRule<ObfuscationRule>
{
public bool? disableObfuscation;
public bool? obfuscateCallInLoop;
public bool? cacheCallIndexInLoop;
public bool? cacheCallIndexNotLoop;
public ObfuscationLevel? obfuscationLevel;
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;
}
if (obfuscationLevel == null)
obfuscationLevel = parentRule.obfuscationLevel;
}
}
@ -79,10 +59,7 @@ namespace Obfuz.ObfusPasses.CallObfus
private static readonly ObfuscationRule s_default = new ObfuscationRule()
{
disableObfuscation = false,
obfuscateCallInLoop = true,
cacheCallIndexInLoop = true,
cacheCallIndexNotLoop = false,
obfuscationLevel = ObfuscationLevel.Basic,
};
private readonly XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule> _configParser;
@ -90,11 +67,12 @@ namespace Obfuz.ObfusPasses.CallObfus
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 CachedDictionary<IMethod, bool> _whiteListMethodCache;
private readonly Dictionary<MethodDef, ObfuscationRule> _methodRuleCache = new Dictionary<MethodDef, ObfuscationRule>();
public ConfigurableObfuscationPolicy(List<string> toObfuscatedAssemblyNames, List<string> xmlConfigFiles)
{
_whiteListMethodCache = new CachedDictionary<IMethod, bool>(MethodEqualityComparer.CompareDeclaringTypes, this.ComputeIsInWhiteList);
_configParser = new XmlAssemblyTypeMethodRuleParser<AssemblySpec, TypeSpec, MethodSpec, ObfuscationRule>(toObfuscatedAssemblyNames,
ParseObfuscationRule, ParseGlobalElement);
LoadConfigs(xmlConfigFiles);
@ -150,21 +128,9 @@ namespace Obfuz.ObfusPasses.CallObfus
private ObfuscationRule ParseObfuscationRule(string configFile, XmlElement ele)
{
var rule = new ObfuscationRule();
if (ele.HasAttribute("disableObfuscation"))
if (ele.HasAttribute("obfuscationLevel"))
{
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"));
rule.obfuscationLevel = ConfigUtil.ParseObfuscationLevel(ele.GetAttribute("obfuscationLevel"));
}
return rule;
}
@ -256,7 +222,7 @@ namespace Obfuz.ObfusPasses.CallObfus
{
if (!_methodRuleCache.TryGetValue(method, out var rule))
{
rule = _configParser.GetMethodRule(method, s_default);
rule = _configParser.GetMethodRule(method, _global);
_methodRuleCache[method] = rule;
}
return rule;
@ -265,44 +231,7 @@ namespace Obfuz.ObfusPasses.CallObfus
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;
}
if (typeDef.Name == "EncryptionService`1")
{
return true;
}
// special handle
// don't proxy call for List<T>.Enumerator GetEnumerator()
if (methodName == "GetEnumerator")
{
return true;
}
return false;
return rule.obfuscationLevel != null && rule.obfuscationLevel.Value >= ObfuscationLevel.Basic;
}
private bool ComputeIsInWhiteList(IMethod calledMethod)
@ -330,11 +259,6 @@ namespace Obfuz.ObfusPasses.CallObfus
TypeDef typeDef = declaringType.ResolveTypeDef();
if (IsSpecialNotObfuscatedMethod(typeDef, calledMethod))
{
return true;
}
string assName = typeDef.Module.Assembly.Name;
string typeFullName = typeDef.FullName;
string methodName = calledMethod.Name;
@ -364,44 +288,9 @@ namespace Obfuz.ObfusPasses.CallObfus
return false;
}
private bool IsInWhiteList(IMethod method)
public override bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir)
{
if (!_whiteListMethodCache.TryGetValue(method, out var isWhiteList))
{
isWhiteList = ComputeIsInWhiteList(method);
_whiteListMethodCache.Add(method, isWhiteList);
}
return isWhiteList;
}
private bool IsTypeSelfAndParentPublic(TypeDef type)
{
if (type.DeclaringType != null && !IsTypeSelfAndParentPublic(type.DeclaringType))
{
return false;
}
return type.IsPublic;
}
public override bool NeedObfuscateCalledMethod(MethodDef callerMethod, IMethod calledMethod, bool callVir, bool currentInLoop)
{
if (IsInWhiteList(calledMethod))
{
return false;
}
// mono has more strict access control, calls non-public method will raise exception.
if (PlatformUtil.IsMonoBackend())
{
MethodDef calledMethodDef = calledMethod.ResolveMethodDef();
if (calledMethodDef != null && (!calledMethodDef.IsPublic || !IsTypeSelfAndParentPublic(calledMethodDef.DeclaringType)))
{
return false;
}
}
ObfuscationRule rule = GetMethodObfuscationRule(callerMethod);
if (currentInLoop && rule.obfuscateCallInLoop == false)
if (_whiteListMethodCache.GetValue(calledMethod))
{
return false;
}

View File

@ -0,0 +1,265 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Data;
using Obfuz.Emit;
using Obfuz.Settings;
using Obfuz.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Obfuz.ObfusPasses.CallObfus
{
struct DelegateProxyMethodData
{
public readonly FieldDef delegateInstanceField;
public readonly MethodDef delegateInvokeMethod;
public DelegateProxyMethodData(FieldDef delegateInstanceField, MethodDef delegateInvokeMethod)
{
this.delegateInstanceField = delegateInstanceField;
this.delegateInvokeMethod = delegateInvokeMethod;
}
}
class DelegateProxyAllocator : GroupByModuleEntityBase
{
private readonly CachedDictionary<MethodSig, TypeDef> _delegateTypes;
private readonly HashSet<string> _allocatedDelegateNames = new HashSet<string>();
private TypeDef _delegateInstanceHolderType;
private bool _done;
class CallInfo
{
public string key1;
public int key2;
public IMethod method;
public bool callVir;
public int index;
public TypeDef delegateType;
public FieldDef delegateInstanceField;
public MethodDef delegateInvokeMethod;
public MethodDef proxyMethod;
}
private readonly Dictionary<MethodKey, CallInfo> _callMethods = new Dictionary<MethodKey, CallInfo>();
private CallObfuscationSettingsFacade _settings;
public DelegateProxyAllocator()
{
_delegateTypes = new CachedDictionary<MethodSig, TypeDef>(SignatureEqualityComparer.Instance, CreateDelegateForSignature);
}
public override void Init()
{
_delegateInstanceHolderType = CreateDelegateInstanceHolderTypeDef();
_settings = CallObfusPass.CurrentSettings;
}
private string AllocateDelegateTypeName(MethodSig delegateInvokeSig)
{
uint hashCode = (uint)SignatureEqualityComparer.Instance.GetHashCode(delegateInvokeSig);
string typeName = $"$Obfuz$Delegate_{hashCode}";
if (_allocatedDelegateNames.Add(typeName))
{
return typeName;
}
for (int i = 0; ; i++)
{
typeName = $"$Obfuz$Delegate_{hashCode}_{i}";
if (_allocatedDelegateNames.Add(typeName))
{
return typeName;
}
}
}
private TypeDef CreateDelegateForSignature(MethodSig delegateInvokeSig)
{
ModuleDef mod = Module;
using (var scope = new DisableTypeDefFindCacheScope(mod))
{
string typeName = AllocateDelegateTypeName(delegateInvokeSig);
mod.Import(typeof(MulticastDelegate));
TypeDef delegateType = new TypeDefUser("", typeName, mod.CorLibTypes.GetTypeRef("System", "MulticastDelegate"));
delegateType.Attributes = TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Public;
mod.Types.Add(delegateType);
MethodDef ctor = new MethodDefUser(
".ctor",
MethodSig.CreateInstance(mod.CorLibTypes.Void, mod.CorLibTypes.Object, mod.CorLibTypes.IntPtr),
MethodImplAttributes.Runtime,
MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Public
);
ctor.DeclaringType = delegateType;
MethodDef invokeMethod = new MethodDefUser(
"Invoke",
MethodSig.CreateInstance(delegateInvokeSig.RetType, delegateInvokeSig.Params.ToArray()),
MethodImplAttributes.Runtime,
MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.Virtual
);
invokeMethod.DeclaringType = delegateType;
return delegateType;
}
}
private TypeDef CreateDelegateInstanceHolderTypeDef()
{
ModuleDef mod = Module;
using (var scope = new DisableTypeDefFindCacheScope(mod))
{
string typeName = "$Obfuz$DelegateInstanceHolder";
TypeDef holderType = new TypeDefUser("", typeName, mod.CorLibTypes.Object.ToTypeDefOrRef());
holderType.Attributes = TypeAttributes.Class | TypeAttributes.Public;
mod.Types.Add(holderType);
return holderType;
}
}
private string AllocateFieldName(IMethod method, bool callVir)
{
uint hashCode = (uint)MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method);
string typeName = $"$Obfuz$Delegate$Field_{hashCode}_{callVir}";
if (_allocatedDelegateNames.Add(typeName))
{
return typeName;
}
for (int i = 0; ; i++)
{
typeName = $"$Obfuz$Delegate$Field_{hashCode}_{callVir}_{i}";
if (_allocatedDelegateNames.Add(typeName))
{
return typeName;
}
}
}
private MethodDef CreateProxyMethod(string name, IMethod calledMethod, bool callVir, MethodSig delegateInvokeSig)
{
var proxyMethod = new MethodDefUser(name, delegateInvokeSig, MethodImplAttributes.Managed, MethodAttributes.Public | MethodAttributes.Static);
var body = new CilBody();
proxyMethod.Body = body;
var ins = body.Instructions;
foreach (Parameter param in proxyMethod.Parameters)
{
ins.Add(Instruction.Create(OpCodes.Ldarg, param));
}
ins.Add(Instruction.Create(callVir ? OpCodes.Callvirt : OpCodes.Call, calledMethod));
ins.Add(Instruction.Create(OpCodes.Ret));
return proxyMethod;
}
public DelegateProxyMethodData Allocate(IMethod method, bool callVir, MethodSig delegateInvokeSig)
{
var key = new MethodKey(method, callVir);
if (!_callMethods.TryGetValue(key, out var callInfo))
{
TypeDef delegateType = _delegateTypes.GetValue(delegateInvokeSig);
MethodDef delegateInvokeMethod = delegateType.FindMethod("Invoke");
string fieldName = AllocateFieldName(method, callVir);
FieldDef delegateInstanceField = new FieldDefUser(fieldName, new FieldSig(delegateType.ToTypeSig()), FieldAttributes.Public | FieldAttributes.Static | FieldAttributes.InitOnly);
string key1 = $"{method.FullName}_{callVir}";
callInfo = new CallInfo
{
key1 = key1,
key2 = HashUtil.ComputePrimitiveOrStringOrBytesHashCode(key1) * 33445566,
method = method,
callVir = callVir,
delegateType = delegateType,
delegateInstanceField = delegateInstanceField,
delegateInvokeMethod = delegateInvokeMethod,
proxyMethod = CreateProxyMethod($"{fieldName}$Proxy", method, callVir, delegateInvokeSig),
};
_callMethods.Add(key, callInfo);
}
return new DelegateProxyMethodData(callInfo.delegateInstanceField, callInfo.delegateInvokeMethod);
}
public override void Done()
{
if (_done)
{
throw new Exception("Already done");
}
_done = true;
ModuleDef mod = Module;
// for stable order, we sort methods by name
List<CallInfo> callMethodList = _callMethods.Values.ToList();
callMethodList.Sort((a, b) => a.key1.CompareTo(b.key1));
var cctor = new MethodDefUser(".cctor",
MethodSig.CreateStatic(mod.CorLibTypes.Void),
MethodImplAttributes.IL | MethodImplAttributes.Managed,
MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Private);
cctor.DeclaringType = _delegateInstanceHolderType;
//_rvaTypeDef.Methods.Add(cctor);
var body = new CilBody();
cctor.Body = body;
var ins = body.Instructions;
// var arr = new array[];
// var d = new delegate;
// arr[index] = d;
int index = 0;
ins.Add(Instruction.CreateLdcI4(callMethodList.Count));
ins.Add(Instruction.Create(OpCodes.Newarr, mod.CorLibTypes.Object));
foreach (CallInfo ci in callMethodList)
{
ci.index = index;
_delegateInstanceHolderType.Methods.Add(ci.proxyMethod);
ins.Add(Instruction.Create(OpCodes.Dup));
ins.Add(Instruction.CreateLdcI4(index));
ins.Add(Instruction.Create(OpCodes.Ldnull));
ins.Add(Instruction.Create(OpCodes.Ldftn, ci.proxyMethod));
MethodDef ctor = ci.delegateType.FindMethod(".ctor");
UnityEngine.Assertions.Assert.IsNotNull(ctor, $"Delegate type {ci.delegateType.FullName} does not have a constructor.");
ins.Add(Instruction.Create(OpCodes.Newobj, ctor));
ins.Add(Instruction.Create(OpCodes.Stelem_Ref));
++index;
}
List<CallInfo> callMethodList2 = callMethodList.ToList();
callMethodList2.Sort((a, b) => a.key2.CompareTo(b.key2));
EncryptionScopeInfo encryptionScope = EncryptionScope;
DefaultMetadataImporter importer = this.GetDefaultModuleMetadataImporter();
RvaDataAllocator rvaDataAllocator = this.GetEntity<RvaDataAllocator>();
foreach (CallInfo ci in callMethodList2)
{
_delegateInstanceHolderType.Fields.Add(ci.delegateInstanceField);
ins.Add(Instruction.Create(OpCodes.Dup));
IRandom localRandom = encryptionScope.localRandomCreator(HashUtil.ComputePrimitiveOrStringOrBytesHashCode(ci.key1));
int ops = EncryptionUtil.GenerateEncryptionOpCodes(localRandom, encryptionScope.encryptor, _settings.obfuscationLevel);
int salt = localRandom.NextInt();
int encryptedValue = encryptionScope.encryptor.Encrypt(ci.index, ops, salt);
RvaData rvaData = rvaDataAllocator.Allocate(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));
ins.Add(Instruction.Create(OpCodes.Ldelem_Ref));
ins.Add(Instruction.Create(OpCodes.Stsfld, ci.delegateInstanceField));
}
ins.Add(Instruction.Create(OpCodes.Pop));
ins.Add(Instruction.Create(OpCodes.Ret));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 02761bacbed8a8b489ae3e7f49f0f84a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,81 @@
using dnlib.DotNet;
using dnlib.DotNet.Emit;
using Obfuz.Emit;
using Obfuz.Utils;
using System.Collections.Generic;
using System.Linq;
namespace Obfuz.ObfusPasses.CallObfus
{
public class DelegateProxyObfuscator : ObfuscatorBase
{
private readonly GroupByModuleEntityManager _entityManager;
public DelegateProxyObfuscator(GroupByModuleEntityManager moduleEntityManager)
{
_entityManager = moduleEntityManager;
}
public override void Done()
{
_entityManager.Done<DelegateProxyAllocator>();
}
private MethodSig CreateProxyMethodSig(ModuleDef module, IMethod method)
{
MethodSig methodSig = MetaUtil.ToSharedMethodSig(module.CorLibTypes, MetaUtil.GetInflatedMethodSig(method, null));
//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;
}
}
return MethodSig.CreateStatic(methodSig.RetType, methodSig.Params.ToArray());
}
public override bool Obfuscate(MethodDef callingMethod, IMethod calledMethod, bool callVir, List<Instruction> obfuscatedInstructions)
{
DelegateProxyAllocator allocator = _entityManager.GetEntity<DelegateProxyAllocator>(callingMethod.Module);
LocalVariableAllocator localVarAllocator = new LocalVariableAllocator(callingMethod);
MethodSig methodSig = CreateProxyMethodSig(callingMethod.Module, calledMethod);
DelegateProxyMethodData proxyData = allocator.Allocate(calledMethod, callVir, methodSig);
bool isVoidReturn = MetaUtil.IsVoidType(methodSig.RetType);
using (var varScope = localVarAllocator.CreateScope())
{
List<Local> localVars = new List<Local>();
if (!isVoidReturn)
{
varScope.AllocateLocal(methodSig.RetType);
}
foreach (var p in methodSig.Params)
{
localVars.Add(varScope.AllocateLocal(p));
}
// save args
for (int i = localVars.Count - 1; i >= 0; i--)
{
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Stloc, localVars[i]));
}
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldsfld, proxyData.delegateInstanceField));
foreach (var local in localVars)
{
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Ldloc, local));
}
obfuscatedInstructions.Add(Instruction.Create(OpCodes.Callvirt, proxyData.delegateInvokeMethod));
}
return true;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1102cd9f03de27c4b9fde3d6a87277c7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -2,17 +2,17 @@
using dnlib.DotNet.Emit;
using Obfuz.Editor;
using Obfuz.Emit;
using Obfuz.Settings;
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;
@ -31,38 +31,11 @@ namespace Obfuz.ObfusPasses.CallObfus
}
}
class ModuleCallProxyAllocator : IGroupByModuleEntity
class ModuleDispatchProxyAllocator : GroupByModuleEntityBase
{
private ModuleDef _module;
private readonly EncryptionScopeProvider _encryptionScopeProvider;
private readonly int _encryptionLevel;
private EncryptionScopeInfo _encryptionScope;
private bool _done;
private CallObfuscationSettingsFacade _settings;
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
{
@ -76,11 +49,9 @@ namespace Obfuz.ObfusPasses.CallObfus
private readonly Dictionary<MethodKey, MethodProxyInfo> _methodProxys = new Dictionary<MethodKey, MethodProxyInfo>();
const int maxProxyMethodPerDispatchMethod = 1000;
class CallInfo
{
public string id;
public IMethod method;
public bool callVir;
}
@ -96,35 +67,60 @@ namespace Obfuz.ObfusPasses.CallObfus
private TypeDef _proxyTypeDef;
public ModuleCallProxyAllocator(EncryptionScopeProvider encryptionScopeProvider, int encryptionLevel)
public ModuleDispatchProxyAllocator()
{
_encryptionScopeProvider = encryptionScopeProvider;
_encryptionLevel = encryptionLevel;
}
public void Init(ModuleDef mod)
public override void Init()
{
_module = mod;
_encryptionScope = _encryptionScopeProvider.GetScope(mod);
_settings = CallObfusPass.CurrentSettings;
}
private TypeDef CreateProxyTypeDef()
{
var typeDef = new TypeDefUser($"{ConstValues.ObfuzInternalSymbolNamePrefix}ProxyCall", _module.CorLibTypes.Object.ToTypeDefOrRef());
ModuleDef mod = Module;
using (var scope = new DisableTypeDefFindCacheScope(mod))
{
var typeDef = new TypeDefUser($"{ConstValues.ObfuzInternalSymbolNamePrefix}ProxyCall", mod.CorLibTypes.Object.ToTypeDefOrRef());
typeDef.Attributes = TypeAttributes.NotPublic | TypeAttributes.Sealed;
_module.EnableTypeDefFindCache = false;
_module.Types.Add(typeDef);
_module.EnableTypeDefFindCache = true;
mod.Types.Add(typeDef);
return typeDef;
}
}
private MethodDef CreateDispatchMethodInfo(MethodSig methodSig)
private readonly HashSet<string> _uniqueMethodNames = new HashSet<string>();
private string ToUniqueMethodName(string originalName)
{
if (_uniqueMethodNames.Add(originalName))
{
return originalName;
}
for (int index = 1; ; index++)
{
string uniqueName = $"{originalName}${index}";
if (_uniqueMethodNames.Add(uniqueName))
{
return uniqueName;
}
}
}
private string CreateDispatchMethodName(MethodSig methodSig, int index)
{
// use a stable name for the dispatch method, so that we can reuse it across different modules
// this is important for cross-module calls
return ToUniqueMethodName($"{ConstValues.ObfuzInternalSymbolNamePrefix}Dispatch_{HashUtil.ComputeHash(methodSig.Params) & 0xFFFF}_{HashUtil.ComputeHash(methodSig.RetType) & 0xFFFFFF}");
}
private MethodDef CreateDispatchMethodInfo(MethodSig methodSig, int index)
{
if (_proxyTypeDef == null)
{
_proxyTypeDef = CreateProxyTypeDef();
}
MethodDef methodDef = new MethodDefUser($"{ConstValues.ObfuzInternalSymbolNamePrefix}ProxyCall$Dispatch${_proxyTypeDef.Methods.Count}", methodSig,
MethodDef methodDef = new MethodDefUser(CreateDispatchMethodName(methodSig, index), methodSig,
MethodImplAttributes.IL | MethodImplAttributes.Managed,
MethodAttributes.Static | MethodAttributes.Public);
methodDef.DeclaringType = _proxyTypeDef;
@ -133,24 +129,25 @@ namespace Obfuz.ObfusPasses.CallObfus
private MethodSig CreateDispatchMethodSig(IMethod method)
{
MethodSig methodSig = MetaUtil.ToSharedMethodSig(_module.CorLibTypes, MetaUtil.GetInflatedMethodSig(method));
ModuleDef mod = Module;
MethodSig methodSig = MetaUtil.ToSharedMethodSig(mod.CorLibTypes, MetaUtil.GetInflatedMethodSig(method, null));
//MethodSig methodSig = MetaUtil.GetInflatedMethodSig(method).Clone();
//methodSig.Params
switch (MetaUtil.GetThisArgType(method))
{
case ThisArgType.Class:
{
methodSig.Params.Insert(0, _module.CorLibTypes.Object);
methodSig.Params.Insert(0, mod.CorLibTypes.Object);
break;
}
case ThisArgType.ValueType:
{
methodSig.Params.Insert(0, _module.CorLibTypes.IntPtr);
methodSig.Params.Insert(0, mod.CorLibTypes.IntPtr);
break;
}
}
// extra param for index
methodSig.Params.Add(_module.CorLibTypes.Int32);
methodSig.Params.Add(mod.CorLibTypes.Int32);
return MethodSig.CreateStatic(methodSig.RetType, methodSig.Params.ToArray());
}
@ -161,7 +158,7 @@ namespace Obfuz.ObfusPasses.CallObfus
private int GenerateEncryptOps(IRandom random)
{
return EncryptionUtil.GenerateEncryptionOpCodes(random, _encryptionScope.encryptor, _encryptionLevel);
return EncryptionUtil.GenerateEncryptionOpCodes(random, EncryptionScope.encryptor, _settings.obfuscationLevel);
}
private DispatchMethodInfo GetDispatchMethod(IMethod method)
@ -172,11 +169,11 @@ namespace Obfuz.ObfusPasses.CallObfus
dispatchMethods = new List<DispatchMethodInfo>();
_dispatchMethods.Add(methodSig, dispatchMethods);
}
if (dispatchMethods.Count == 0 || dispatchMethods.Last().methods.Count >= maxProxyMethodPerDispatchMethod)
if (dispatchMethods.Count == 0 || dispatchMethods.Last().methods.Count >= _settings.maxProxyMethodCountPerDispatchMethod)
{
var newDispatchMethodInfo = new DispatchMethodInfo
{
methodDef = CreateDispatchMethodInfo(methodSig),
methodDef = CreateDispatchMethodInfo(methodSig, dispatchMethods.Count),
};
dispatchMethods.Add(newDispatchMethodInfo);
}
@ -186,7 +183,7 @@ namespace Obfuz.ObfusPasses.CallObfus
private IRandom CreateRandomForMethod(IMethod method, bool callVir)
{
int seed = MethodEqualityComparer.CompareDeclaringTypes.GetHashCode(method);
return _encryptionScope.localRandomCreator(seed);
return EncryptionScope.localRandomCreator(seed);
}
public ProxyCallMethodData Allocate(IMethod method, bool callVir)
@ -204,7 +201,7 @@ namespace Obfuz.ObfusPasses.CallObfus
IRandom localRandom = CreateRandomForMethod(method, callVir);
int encryptOps = GenerateEncryptOps(localRandom);
int salt = GenerateSalt(localRandom);
int encryptedIndex = _encryptionScope.encryptor.Encrypt(index, encryptOps, salt);
int encryptedIndex = EncryptionScope.encryptor.Encrypt(index, encryptOps, salt);
proxyInfo = new MethodProxyInfo()
{
proxyMethod = methodDispatcher.methodDef,
@ -213,19 +210,32 @@ namespace Obfuz.ObfusPasses.CallObfus
salt = salt,
encryptedIndex = encryptedIndex,
};
methodDispatcher.methods.Add(new CallInfo { method = method, callVir = callVir});
methodDispatcher.methods.Add(new CallInfo { id = $"{method}{(callVir ? "" : "v")}", method = method, callVir = callVir });
_methodProxys.Add(key, proxyInfo);
}
return new ProxyCallMethodData(proxyInfo.proxyMethod, proxyInfo.encryptedOps, proxyInfo.salt, proxyInfo.encryptedIndex, proxyInfo.index);
}
public void Done()
public override void Done()
{
if (_done)
{
throw new Exception("Already done");
}
_done = true;
if (_proxyTypeDef == null)
{
return;
}
// for stable order, we sort methods by name
var methodWithNamePairList = _proxyTypeDef.Methods.Select(m => (m, m.ToString())).ToList();
methodWithNamePairList.Sort((a, b) => a.Item2.CompareTo(b.Item2));
_proxyTypeDef.Methods.Clear();
foreach (var methodPair in methodWithNamePairList)
{
methodPair.Item1.DeclaringType = _proxyTypeDef;
}
foreach (DispatchMethodInfo dispatchMethod in _dispatchMethods.Values.SelectMany(ms => ms))
{
@ -246,6 +256,9 @@ namespace Obfuz.ObfusPasses.CallObfus
var switchInst = Instruction.Create(OpCodes.Switch, switchCases);
ins.Add(switchInst);
var ret = Instruction.Create(OpCodes.Ret);
// sort methods by signature to ensure stable order
//dispatchMethod.methods.Sort((a, b) => a.id.CompareTo(b.id));
foreach (CallInfo ci in dispatchMethod.methods)
{
var callTargetMethod = Instruction.Create(ci.callVir ? OpCodes.Callvirt : OpCodes.Call, ci.method);
@ -257,37 +270,4 @@ namespace Obfuz.ObfusPasses.CallObfus
}
}
}
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();
}
}
}
}

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