.NET 逆向工程实战:将 Game.Utils 和 Game.Kernel 从 .NET 2.0 升级到 .NET 10

本文记录了一次完整的 .NET 逆向工程实践,包括使用 ILSpy 反编译 DLL、移除 ASP.NET Framework 依赖、迁移到 .NET 10 的全过程。文中涉及的技术方案对老旧项目现代化改造具有参考价值。

一、项目背景

在维护一个游戏 Web 项目时,遇到了两个核心类库 `Game.Utils.dll` 和 `Game.Kernel.dll`,它们:

  • 基于 .NET Framework 2.0 编译

  • 大量依赖 `System.Web` 等已过时的 API

  • 以 DLL 引用方式存在,无源码

为了项目可持续发展,决定逆向并升级到 .NET 10。

二、工具准备

2.1 安装 ILSpy 命令行工具

```bash

dotnet tool install --global ilspycmd

```

2.2 环境信息

| 工具 | 版本 |

|------|------|

| .NET SDK | 10.0.201 |

| ILSpy | 9.1.0 |

| Visual Studio | 2022 |

三、反编译 DLL

3.1 创建输出目录

```bash

mkdir ReverseEngineered

```

3.2 执行反编译

```bash

反编译 Game.Utils.dll

ilspycmd Game.Utils.dll -o ReverseEngineered/Game.Utils -p

反编译 Game.Kernel.dll

ilspycmd Game.Kernel.dll -o ReverseEngineered/Game.Kernel -p

```

**参数说明**:

  • `-o`:输出目录

  • `-p`:生成完整项目文件

3.3 反编译结果

```

ReverseEngineered/

├── Game.Utils/

│ ├── Game.Utils.csproj

│ ├── Game.Utils/ # 37 个核心工具类

│ ├── Game.Utils.Cache/ # 缓存相关

│ ├── Game.Utils.Verify/ # 验证码生成

│ └── Game.Facade.SiteLibrary/

└── Game.Kernel/

├── Game.Kernel.csproj

├── Game.Kernel/ # 15 个数据访问类

└── Properties/

```


四、迁移到 .NET 10

4.1 更新项目文件

**Game.Utils.csproj**

```xml

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>

<TargetFramework>net10.0</TargetFramework>

<LangVersion>14.0</LangVersion>

<AllowUnsafeBlocks>True</AllowUnsafeBlocks>

</PropertyGroup>

<ItemGroup>

<PackageReference Include="System.Drawing.Common" Version="10.0.0" />

<PackageReference Include="System.Configuration.ConfigurationManager" Version="10.0.0" />

</ItemGroup>

</Project>

```

**Game.Kernel.csproj**

```xml

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>

<TargetFramework>net10.0</TargetFramework>

<LangVersion>14.0</LangVersion>

<AllowUnsafeBlocks>True</AllowUnsafeBlocks>

</PropertyGroup>

<ItemGroup>

<ProjectReference Include="..\Game.Utils\Game.Utils.csproj" />

<PackageReference Include="System.Data.SqlClient" Version="4.9.0" />

</ItemGroup>

</Project>

```

4.2 处理 ASP.NET 依赖

.NET Core/.NET 10 中已移除 `System.Web` 命名空间,需要删除或重写以下文件:

| 已删除文件 | 原因 |

|-----------|------|

| AspNetCache.cs | 依赖 HttpRuntime.Cache |

| CookiesCache.cs | 依赖 HttpCookie |

| CtrlHelper.cs | 依赖 WebControls |

| JavaScript.cs | 依赖 Page/ClientScript |

| Template.cs | 依赖 HttpContext |

| Terminator.cs | 依赖 HttpContext |

| SerializationHelper.cs | BinaryFormatter 已废弃 |

4.3 核心类重写示例

4.3.1 Utility.cs - 移除 HttpContext 依赖

**原代码(.NET 2.0)**

```csharp

public static string CurrentPath

{

get

{

if (HttpContext.Current == null)

return string.Empty;

string path = HttpContext.Current.Request.Path;

return path.Substring(0, path.LastIndexOf("/"));

}

}

```

**新代码(.NET 10)**

```csharp

// 改为参数传递或使用 IHttpContextAccessor

public static string GetMapPath(string folderPath)

{

if (folderPath.IndexOf(":\\") > 0)

return AddLast(folderPath, "\\");

if (folderPath.StartsWith("~/"))

return AddLast(Path.Combine(Environment.CurrentDirectory,

folderPath.Substring(2)), "\\");

return AddLast(Path.Combine(Environment.CurrentDirectory, folderPath), "\\");

}

```

4.3.2 GameRequest.cs - 改为纯函数

**原代码**

```csharp

public static HttpRequest Request

{

get

{

HttpContext current = HttpContext.Current;

return current?.Request;

}

}

public static string GetUserIP()

{

return HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

}

```

**新代码**

```csharp

public static string GetUserIP(string remoteIpAddress = null, string xForwardedFor = null)

{

string empty = string.Empty;

if (!string.IsNullOrEmpty(xForwardedFor))

empty = xForwardedFor.Split(',')[0].Trim();

if (string.IsNullOrEmpty(empty) && !string.IsNullOrEmpty(remoteIpAddress))

empty = remoteIpAddress;

return string.IsNullOrEmpty(empty) || !Validate.IsIP(empty)

? "0.0.0.0" : empty;

}

```

4.3.3 SessionCache.cs - 使用 ConcurrentDictionary

**原代码**

```csharp

public void Add(string key, object value)

{

HttpContext.Current.Session[key] = value;

}

```

**新代码**

```csharp

private static readonly ConcurrentDictionary<string, object> _sessionData = new();

public void Add(string key, object value)

{

_sessionData[key] = value;

}

```

4.3.4 随机数生成器升级

**原代码(已过时)**

```csharp

private static RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

public static int GetNewSeed()

{

byte[] array = new byte[4];

rng.GetBytes(array);

return BitConverter.ToInt32(array, 0);

}

```

**新代码**

```csharp

public static int GetNewSeed()

{

byte[] array = new byte[4];

using (var rng = RandomNumberGenerator.Create())

{

rng.GetBytes(array);

}

return BitConverter.ToInt32(array, 0);

}

```


五、常见问题及解决方案

5.1 System.Web 命名空间不存在

**错误信息**

```

error CS0234: 命名空间"System.Web"中不存在类型或命名空间名

```

**解决方案**

  • 添加 `System.Web.HttpUtility` 包(仅部分 API)

  • 使用 `System.Net.WebUtility` 替代

  • 重构代码移除依赖

5.2 BinaryFormatter 已废弃

**错误信息**

```

error SYSLIB0011: "BinaryFormatter"已过时

```

**解决方案**

  • 删除相关代码

  • 使用 `System.Text.Json` 或 `MemoryPack` 替代

5.3 System.Drawing 平台警告

**警告信息**

```

warning CA1416: 可在所有平台上访问此调用站点。仅在 "windows" 6.1 及更高版本上受支持

```

**解决方案**

  • 添加 `<UseWindowsForms>true</UseWindowsForms>`(如需要)

  • 或在非 Windows 平台使用 `ImageSharp` 替代

5.4 SqlClient 类型转发

**错误信息**

```

error CS1069: 未能在命名空间"System.Data.SqlClient"中找到类型名"SqlCommand"

```

**解决方案**

```xml

<PackageReference Include="System.Data.SqlClient" Version="4.9.0" />

<!-- 或使用新版 -->

<PackageReference Include="Microsoft.Data.SqlClient" Version="5.0.0" />

```


六、编译验证

6.1 清理并重建

```bash

dotnet clean Game.Utils.csproj

dotnet clean Game.Kernel.csproj

dotnet build Game.Utils.csproj

dotnet build Game.Kernel.csproj

```

6.2 编译结果

| 项目 | 状态 | 警告数 |

|------|------|--------|

| Game.Utils | ✅ 成功 | 67 |

| Game.Kernel | ✅ 成功 | 12 |

**警告类型分布**:

  • `SYSLIB0022` RijndaelManaged 过时(8 个)

  • `SYSLIB0021` 派生加密类型过时(10 个)

  • `CA1416` 平台兼容性警告(40+ 个)

  • `CS0618` SqlClient 过时(8 个)

七、添加到解决方案

7.1 修改 .sln 文件

在 `GlobalSection(ProjectConfigurationPlatforms)` 中添加:

```

{C8F6D0FD-1405-4D73-A1C1-BC498CA3D2CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU

{C8F6D0FD-1405-4D73-A1C1-BC498CA3D2CC}.Debug|Any CPU.Build.0 = Debug|Any CPU

{138E704E-B0C8-4821-B32C-F6FB6314651A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU

{138E704E-B0C8-4821-B32C-F6FB6314651A}.Debug|Any CPU.Build.0 = Debug|Any CPU

```

7.2 在 NestedProjects 中添加

```

{C8F6D0FD-1405-4D73-A1C1-BC498CA3D2CC} = {D1D390F4-1147-4EDC-A1FC-4EFF9285154D}

{138E704E-B0C8-4821-B32C-F6FB6314651A} = {D1D390F4-1147-4EDC-A1FC-4EFF9285154D}

```


八、经验总结

8.1 迁移难点

  1. **HttpContext 依赖**:大量工具类直接访问 `HttpContext.Current`

  2. **加密 API 过时**:RijndaelManaged、RNGCryptoServiceProvider 等已废弃

  3. **BinaryFormatter**:序列化类完全不可用

  4. **System.Drawing**:跨平台支持有限

8.2 最佳实践

  1. **渐进式迁移**:先编译通过,再逐步消除警告

  2. **单元测试**:迁移后验证核心功能

  3. **API 映射表**:建立新旧 API 对照文档

  4. **条件编译**:如需同时支持多框架

8.3 后续工作

  • \] 将 RijndaelManaged 替换为 Aes

  • \] 将 MD5CryptoServiceProvider 替换为 MD5.Create()

  • \] 添加单元测试覆盖核心功能

九、参考资源

  1. .NET 升级助手\](https://dotnet.microsoft.com/platform/upgrade-assistant)

  2. ILSpy GitHub\](https://github.com/icsharpcode/ILSpy)

**作者**:林宏权

**许可协议**:CC BY-NC-SA 4.0

*如果本文对你有帮助,欢迎点赞、收藏、转发!*

相关推荐
NPE~1 天前
[App逆向]环境搭建上篇——抓取apk https包
android·教程·逆向·android逆向·逆向分析
嫂子的姐夫3 天前
042-spiderbuf第C7题
爬虫·python·逆向
嫂子的姐夫3 天前
041-全扣补环境:同花顺
爬虫·python·js逆向·逆向
嫂子的姐夫3 天前
040-spiderbuf第C8题
javascript·爬虫·python·js逆向·逆向
嫂子的姐夫3 天前
043-spiderbuf第C3题
爬虫·python·js逆向·逆向
嫂子的姐夫3 天前
039-DES:gov招标(解密)
爬虫·python·js逆向·逆向
深念Y3 天前
光猫改桥接模式实战:通过Telnet获取超级管理员密码
ssh·路由器·桥接模式·逆向·光猫·telent·管理员密码
Y5neKO4 天前
某国赛CTF逆向题目Writeup:re1
python·逆向·ctf
阿捏利5 天前
vscode+ida-mcp-server配置及使用
vscode·ida·逆向·mcp