├── .gitignore ├── Cargo.toml ├── README.md ├── build.rs ├── des-lib ├── DES.cs └── des-lib.csproj └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/visualstudio,visualstudiocode,csharp,rust 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=visualstudio,visualstudiocode,csharp,rust 3 | 4 | ### Csharp ### 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | ## 8 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 9 | 10 | # User-specific files 11 | *.rsuser 12 | *.suo 13 | *.user 14 | *.userosscache 15 | *.sln.docstates 16 | 17 | # User-specific files (MonoDevelop/Xamarin Studio) 18 | *.userprefs 19 | 20 | # Mono auto generated files 21 | mono_crash.* 22 | 23 | # Build results 24 | [Dd]ebug/ 25 | [Dd]ebugPublic/ 26 | [Rr]elease/ 27 | [Rr]eleases/ 28 | x64/ 29 | x86/ 30 | [Ww][Ii][Nn]32/ 31 | [Aa][Rr][Mm]/ 32 | [Aa][Rr][Mm]64/ 33 | bld/ 34 | [Bb]in/ 35 | [Oo]bj/ 36 | [Ll]og/ 37 | [Ll]ogs/ 38 | 39 | # Visual Studio 2015/2017 cache/options directory 40 | .vs/ 41 | # Uncomment if you have tasks that create the project's static files in wwwroot 42 | #wwwroot/ 43 | 44 | # Visual Studio 2017 auto generated files 45 | Generated\ Files/ 46 | 47 | # MSTest test Results 48 | [Tt]est[Rr]esult*/ 49 | [Bb]uild[Ll]og.* 50 | 51 | # NUnit 52 | *.VisualState.xml 53 | TestResult.xml 54 | nunit-*.xml 55 | 56 | # Build Results of an ATL Project 57 | [Dd]ebugPS/ 58 | [Rr]eleasePS/ 59 | dlldata.c 60 | 61 | # Benchmark Results 62 | BenchmarkDotNet.Artifacts/ 63 | 64 | # .NET Core 65 | project.lock.json 66 | project.fragment.lock.json 67 | artifacts/ 68 | 69 | # ASP.NET Scaffolding 70 | ScaffoldingReadMe.txt 71 | 72 | # StyleCop 73 | StyleCopReport.xml 74 | 75 | # Files built by Visual Studio 76 | *_i.c 77 | *_p.c 78 | *_h.h 79 | *.ilk 80 | *.meta 81 | *.obj 82 | *.iobj 83 | *.pch 84 | *.pdb 85 | *.ipdb 86 | *.pgc 87 | *.pgd 88 | *.rsp 89 | *.sbr 90 | *.tlb 91 | *.tli 92 | *.tlh 93 | *.tmp 94 | *.tmp_proj 95 | *_wpftmp.csproj 96 | *.log 97 | *.tlog 98 | *.vspscc 99 | *.vssscc 100 | .builds 101 | *.pidb 102 | *.svclog 103 | *.scc 104 | 105 | # Chutzpah Test files 106 | _Chutzpah* 107 | 108 | # Visual C++ cache files 109 | ipch/ 110 | *.aps 111 | *.ncb 112 | *.opendb 113 | *.opensdf 114 | *.sdf 115 | *.cachefile 116 | *.VC.db 117 | *.VC.VC.opendb 118 | 119 | # Visual Studio profiler 120 | *.psess 121 | *.vsp 122 | *.vspx 123 | *.sap 124 | 125 | # Visual Studio Trace Files 126 | *.e2e 127 | 128 | # TFS 2012 Local Workspace 129 | $tf/ 130 | 131 | # Guidance Automation Toolkit 132 | *.gpState 133 | 134 | # ReSharper is a .NET coding add-in 135 | _ReSharper*/ 136 | *.[Rr]e[Ss]harper 137 | *.DotSettings.user 138 | 139 | # TeamCity is a build add-in 140 | _TeamCity* 141 | 142 | # DotCover is a Code Coverage Tool 143 | *.dotCover 144 | 145 | # AxoCover is a Code Coverage Tool 146 | .axoCover/* 147 | !.axoCover/settings.json 148 | 149 | # Coverlet is a free, cross platform Code Coverage Tool 150 | coverage*.json 151 | coverage*.xml 152 | coverage*.info 153 | 154 | # Visual Studio code coverage results 155 | *.coverage 156 | *.coveragexml 157 | 158 | # NCrunch 159 | _NCrunch_* 160 | .*crunch*.local.xml 161 | nCrunchTemp_* 162 | 163 | # MightyMoose 164 | *.mm.* 165 | AutoTest.Net/ 166 | 167 | # Web workbench (sass) 168 | .sass-cache/ 169 | 170 | # Installshield output folder 171 | [Ee]xpress/ 172 | 173 | # DocProject is a documentation generator add-in 174 | DocProject/buildhelp/ 175 | DocProject/Help/*.HxT 176 | DocProject/Help/*.HxC 177 | DocProject/Help/*.hhc 178 | DocProject/Help/*.hhk 179 | DocProject/Help/*.hhp 180 | DocProject/Help/Html2 181 | DocProject/Help/html 182 | 183 | # Click-Once directory 184 | publish/ 185 | 186 | # Publish Web Output 187 | *.[Pp]ublish.xml 188 | *.azurePubxml 189 | # Note: Comment the next line if you want to checkin your web deploy settings, 190 | # but database connection strings (with potential passwords) will be unencrypted 191 | *.pubxml 192 | *.publishproj 193 | 194 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 195 | # checkin your Azure Web App publish settings, but sensitive information contained 196 | # in these scripts will be unencrypted 197 | PublishScripts/ 198 | 199 | # NuGet Packages 200 | *.nupkg 201 | # NuGet Symbol Packages 202 | *.snupkg 203 | # The packages folder can be ignored because of Package Restore 204 | **/[Pp]ackages/* 205 | # except build/, which is used as an MSBuild target. 206 | !**/[Pp]ackages/build/ 207 | # Uncomment if necessary however generally it will be regenerated when needed 208 | #!**/[Pp]ackages/repositories.config 209 | # NuGet v3's project.json files produces more ignorable files 210 | *.nuget.props 211 | *.nuget.targets 212 | 213 | # Microsoft Azure Build Output 214 | csx/ 215 | *.build.csdef 216 | 217 | # Microsoft Azure Emulator 218 | ecf/ 219 | rcf/ 220 | 221 | # Windows Store app package directories and files 222 | AppPackages/ 223 | BundleArtifacts/ 224 | Package.StoreAssociation.xml 225 | _pkginfo.txt 226 | *.appx 227 | *.appxbundle 228 | *.appxupload 229 | 230 | # Visual Studio cache files 231 | # files ending in .cache can be ignored 232 | *.[Cc]ache 233 | # but keep track of directories ending in .cache 234 | !?*.[Cc]ache/ 235 | 236 | # Others 237 | ClientBin/ 238 | ~$* 239 | *~ 240 | *.dbmdl 241 | *.dbproj.schemaview 242 | *.jfm 243 | *.pfx 244 | *.publishsettings 245 | orleans.codegen.cs 246 | 247 | # Including strong name files can present a security risk 248 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 249 | #*.snk 250 | 251 | # Since there are multiple workflows, uncomment next line to ignore bower_components 252 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 253 | #bower_components/ 254 | 255 | # RIA/Silverlight projects 256 | Generated_Code/ 257 | 258 | # Backup & report files from converting an old project file 259 | # to a newer Visual Studio version. Backup files are not needed, 260 | # because we have git ;-) 261 | _UpgradeReport_Files/ 262 | Backup*/ 263 | UpgradeLog*.XML 264 | UpgradeLog*.htm 265 | ServiceFabricBackup/ 266 | *.rptproj.bak 267 | 268 | # SQL Server files 269 | *.mdf 270 | *.ldf 271 | *.ndf 272 | 273 | # Business Intelligence projects 274 | *.rdl.data 275 | *.bim.layout 276 | *.bim_*.settings 277 | *.rptproj.rsuser 278 | *- [Bb]ackup.rdl 279 | *- [Bb]ackup ([0-9]).rdl 280 | *- [Bb]ackup ([0-9][0-9]).rdl 281 | 282 | # Microsoft Fakes 283 | FakesAssemblies/ 284 | 285 | # GhostDoc plugin setting file 286 | *.GhostDoc.xml 287 | 288 | # Node.js Tools for Visual Studio 289 | .ntvs_analysis.dat 290 | node_modules/ 291 | 292 | # Visual Studio 6 build log 293 | *.plg 294 | 295 | # Visual Studio 6 workspace options file 296 | *.opt 297 | 298 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 299 | *.vbw 300 | 301 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 302 | *.vbp 303 | 304 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 305 | *.dsw 306 | *.dsp 307 | 308 | # Visual Studio 6 technical files 309 | 310 | # Visual Studio LightSwitch build output 311 | **/*.HTMLClient/GeneratedArtifacts 312 | **/*.DesktopClient/GeneratedArtifacts 313 | **/*.DesktopClient/ModelManifest.xml 314 | **/*.Server/GeneratedArtifacts 315 | **/*.Server/ModelManifest.xml 316 | _Pvt_Extensions 317 | 318 | # Paket dependency manager 319 | .paket/paket.exe 320 | paket-files/ 321 | 322 | # FAKE - F# Make 323 | .fake/ 324 | 325 | # CodeRush personal settings 326 | .cr/personal 327 | 328 | # Python Tools for Visual Studio (PTVS) 329 | __pycache__/ 330 | *.pyc 331 | 332 | # Cake - Uncomment if you are using it 333 | # tools/** 334 | # !tools/packages.config 335 | 336 | # Tabs Studio 337 | *.tss 338 | 339 | # Telerik's JustMock configuration file 340 | *.jmconfig 341 | 342 | # BizTalk build output 343 | *.btp.cs 344 | *.btm.cs 345 | *.odx.cs 346 | *.xsd.cs 347 | 348 | # OpenCover UI analysis results 349 | OpenCover/ 350 | 351 | # Azure Stream Analytics local run output 352 | ASALocalRun/ 353 | 354 | # MSBuild Binary and Structured Log 355 | *.binlog 356 | 357 | # NVidia Nsight GPU debugger configuration file 358 | *.nvuser 359 | 360 | # MFractors (Xamarin productivity tool) working folder 361 | .mfractor/ 362 | 363 | # Local History for Visual Studio 364 | .localhistory/ 365 | 366 | # Visual Studio History (VSHistory) files 367 | .vshistory/ 368 | 369 | # BeatPulse healthcheck temp database 370 | healthchecksdb 371 | 372 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 373 | MigrationBackup/ 374 | 375 | # Ionide (cross platform F# VS Code tools) working folder 376 | .ionide/ 377 | 378 | # Fody - auto-generated XML schema 379 | FodyWeavers.xsd 380 | 381 | # VS Code files for those working on multiple tools 382 | .vscode/* 383 | !.vscode/settings.json 384 | !.vscode/tasks.json 385 | !.vscode/launch.json 386 | !.vscode/extensions.json 387 | *.code-workspace 388 | 389 | # Local History for Visual Studio Code 390 | .history/ 391 | 392 | # Windows Installer files from build outputs 393 | *.cab 394 | *.msi 395 | *.msix 396 | *.msm 397 | *.msp 398 | 399 | # JetBrains Rider 400 | *.sln.iml 401 | 402 | ### Rust ### 403 | # Generated by Cargo 404 | # will have compiled files and executables 405 | debug/ 406 | target/ 407 | 408 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 409 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 410 | Cargo.lock 411 | 412 | # These are backup files generated by rustfmt 413 | **/*.rs.bk 414 | 415 | # MSVC Windows builds of rustc generate these, which store debugging information 416 | 417 | ### VisualStudioCode ### 418 | !.vscode/*.code-snippets 419 | 420 | # Local History for Visual Studio Code 421 | 422 | # Built Visual Studio Code Extensions 423 | *.vsix 424 | 425 | ### VisualStudioCode Patch ### 426 | # Ignore all local history of files 427 | .history 428 | .ionide 429 | 430 | ### VisualStudio ### 431 | 432 | # User-specific files 433 | 434 | # User-specific files (MonoDevelop/Xamarin Studio) 435 | 436 | # Mono auto generated files 437 | 438 | # Build results 439 | 440 | # Visual Studio 2015/2017 cache/options directory 441 | # Uncomment if you have tasks that create the project's static files in wwwroot 442 | 443 | # Visual Studio 2017 auto generated files 444 | 445 | # MSTest test Results 446 | 447 | # NUnit 448 | 449 | # Build Results of an ATL Project 450 | 451 | # Benchmark Results 452 | 453 | # .NET Core 454 | 455 | # ASP.NET Scaffolding 456 | 457 | # StyleCop 458 | 459 | # Files built by Visual Studio 460 | 461 | # Chutzpah Test files 462 | 463 | # Visual C++ cache files 464 | 465 | # Visual Studio profiler 466 | 467 | # Visual Studio Trace Files 468 | 469 | # TFS 2012 Local Workspace 470 | 471 | # Guidance Automation Toolkit 472 | 473 | # ReSharper is a .NET coding add-in 474 | 475 | # TeamCity is a build add-in 476 | 477 | # DotCover is a Code Coverage Tool 478 | 479 | # AxoCover is a Code Coverage Tool 480 | 481 | # Coverlet is a free, cross platform Code Coverage Tool 482 | 483 | # Visual Studio code coverage results 484 | 485 | # NCrunch 486 | 487 | # MightyMoose 488 | 489 | # Web workbench (sass) 490 | 491 | # Installshield output folder 492 | 493 | # DocProject is a documentation generator add-in 494 | 495 | # Click-Once directory 496 | 497 | # Publish Web Output 498 | # Note: Comment the next line if you want to checkin your web deploy settings, 499 | # but database connection strings (with potential passwords) will be unencrypted 500 | 501 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 502 | # checkin your Azure Web App publish settings, but sensitive information contained 503 | # in these scripts will be unencrypted 504 | 505 | # NuGet Packages 506 | # NuGet Symbol Packages 507 | # The packages folder can be ignored because of Package Restore 508 | # except build/, which is used as an MSBuild target. 509 | # Uncomment if necessary however generally it will be regenerated when needed 510 | # NuGet v3's project.json files produces more ignorable files 511 | 512 | # Microsoft Azure Build Output 513 | 514 | # Microsoft Azure Emulator 515 | 516 | # Windows Store app package directories and files 517 | 518 | # Visual Studio cache files 519 | # files ending in .cache can be ignored 520 | # but keep track of directories ending in .cache 521 | 522 | # Others 523 | 524 | # Including strong name files can present a security risk 525 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 526 | 527 | # Since there are multiple workflows, uncomment next line to ignore bower_components 528 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 529 | 530 | # RIA/Silverlight projects 531 | 532 | # Backup & report files from converting an old project file 533 | # to a newer Visual Studio version. Backup files are not needed, 534 | # because we have git ;-) 535 | 536 | # SQL Server files 537 | 538 | # Business Intelligence projects 539 | 540 | # Microsoft Fakes 541 | 542 | # GhostDoc plugin setting file 543 | 544 | # Node.js Tools for Visual Studio 545 | 546 | # Visual Studio 6 build log 547 | 548 | # Visual Studio 6 workspace options file 549 | 550 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 551 | 552 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 553 | 554 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 555 | 556 | # Visual Studio 6 technical files 557 | 558 | # Visual Studio LightSwitch build output 559 | 560 | # Paket dependency manager 561 | 562 | # FAKE - F# Make 563 | 564 | # CodeRush personal settings 565 | 566 | # Python Tools for Visual Studio (PTVS) 567 | 568 | # Cake - Uncomment if you are using it 569 | # tools/** 570 | # !tools/packages.config 571 | 572 | # Tabs Studio 573 | 574 | # Telerik's JustMock configuration file 575 | 576 | # BizTalk build output 577 | 578 | # OpenCover UI analysis results 579 | 580 | # Azure Stream Analytics local run output 581 | 582 | # MSBuild Binary and Structured Log 583 | 584 | # NVidia Nsight GPU debugger configuration file 585 | 586 | # MFractors (Xamarin productivity tool) working folder 587 | 588 | # Local History for Visual Studio 589 | 590 | # Visual Studio History (VSHistory) files 591 | 592 | # BeatPulse healthcheck temp database 593 | 594 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 595 | 596 | # Ionide (cross platform F# VS Code tools) working folder 597 | 598 | # Fody - auto-generated XML schema 599 | 600 | # VS Code files for those working on multiple tools 601 | 602 | # Local History for Visual Studio Code 603 | 604 | # Windows Installer files from build outputs 605 | 606 | # JetBrains Rider 607 | 608 | ### VisualStudio Patch ### 609 | # Additional files built by Visual Studio 610 | 611 | # End of https://www.toptal.com/developers/gitignore/api/visualstudio,visualstudiocode,csharp,rust -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "call-net-from-rust-statically" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["hamflx "] 6 | 7 | [dependencies] 8 | windows = "0.51.1" 9 | 10 | [build-dependencies] 11 | ignore = "0.4.20" 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 静态链接 C# 到 Rust 2 | 3 | 最近 `.Net 7` 发布之后,因为带了 `AOT` 编译器,又爆发了一波热度,正好我最近有需求需要使用到这个功能,本文就记录下如何实现将 `.Net 7` 库编译成静态库,然后用 `Rust` 链接。 4 | 5 | 本文实现的是将一个非标准的 `DES` 算法编译成静态库,供 `Rust` 调用。该 `DES` 算法的 `C#` 实现在这里可以找到:。 6 | 7 | 本文项目的目录结构为: 8 | 9 | ```plaintext 10 | ./call-net-from-rust-statically 11 | ├── des-lib 12 | │ ├── des-lib.csproj 13 | │ └── DES.cs 14 | ├── Cargo.toml 15 | ├── build.rs 16 | └── src 17 | └── main.rs 18 | ``` 19 | 20 | 先创建好 `call-net-from-rust-statically` 目录: 21 | 22 | ```powershell 23 | mkdir call-net-from-rust-statically 24 | ``` 25 | 26 | ## C# 项目部分 27 | 28 | 首先创建项目: 29 | 30 | ```powershell 31 | cd call-net-from-rust-statically 32 | dotnet new classlib -n des-lib 33 | ``` 34 | 35 | 将 `Class1.cs` 重命名为 `DES.cs`,然后把上面链接中的 `DES` 类复制到 `DES.cs` 中,改下命名空间,再加上导出函数的代码,如下: 36 | 37 | ```csharp 38 | namespace des_lib; 39 | 40 | using System.Runtime.InteropServices; 41 | 42 | public class DES 43 | { 44 | [UnmanagedCallersOnly(EntryPoint = "wtf_des_encrypt")] 45 | public static nint FFI_Encrypt(nint message, nint key) 46 | { 47 | var managedMessage = Marshal.PtrToStringUTF8(message); 48 | var managedKey = Marshal.PtrToStringUTF8(key); 49 | if (managedKey == null || managedMessage == null) 50 | { 51 | return nint.Zero; 52 | } 53 | var cipherText = EncryptDES(managedMessage, managedKey); 54 | return Marshal.StringToHGlobalAnsi(cipherText); 55 | } 56 | 57 | [UnmanagedCallersOnly(EntryPoint = "wtf_des_decrypt")] 58 | public static nint FFI_Decrypt(nint cipherMessage, nint key) 59 | { 60 | var managedCipherMessage = Marshal.PtrToStringUTF8(cipherMessage); 61 | var managedKey = Marshal.PtrToStringUTF8(key); 62 | if (managedKey == null || managedCipherMessage == null) 63 | { 64 | return nint.Zero; 65 | } 66 | var plainText = DecryptDES(managedCipherMessage, managedKey); 67 | return Marshal.StringToHGlobalAnsi(plainText); 68 | } 69 | 70 | [UnmanagedCallersOnly(EntryPoint = "wtf_des_free")] 71 | public static void FFI_FreeMemory(nint buffer) 72 | { 73 | Marshal.FreeHGlobal(buffer); 74 | } 75 | 76 | // 将原有 DES 类的内容放在这里。 77 | } 78 | ``` 79 | 80 | 其中 `wtf_des_encrypt`、`wtf_des_decrypt` 和 `wtf_des_free` 就是导出的加密、解密以及释放内存的方法。 81 | 82 | 配置项目的属性: 83 | 84 | ```xml 85 | 86 | 87 | 88 | net7.0 89 | Static 90 | true 91 | true 92 | true 93 | 94 | 95 | 96 | ``` 97 | 98 | 然后就可以用如下命令编译一下试试看: 99 | 100 | ```powershell 101 | cd des-lib 102 | dotnet publish -r win-x64 -c Release 103 | ``` 104 | 105 | 在构建完毕之后,会在 `bin\Release\net7.0\win-x64\publish` 目录下生成 `des-lib.lib` 文件。 106 | 107 | ## Rust 项目部分 108 | 109 | 在上面的项目构建成功后,将会把 `ilcompiler` 包缓存,并可以在该目录 `%USERPROFILE%/.nuget/packages/runtime.win-x64.microsoft.dotnet.ilcompiler/7.0.1/sdk` 找到链接依赖的一些静态库(注意,版本号可能会变更)。 110 | 111 | 在 `call-net-from-rust-statically` 目录中创建 `Rust` 项目: 112 | 113 | ```powershell 114 | cd call-net-from-rust-statically 115 | cargo init 116 | ``` 117 | 118 | 先添加 `windows` 依赖,这是因为在链接的时候,`.Net` 运行时会依赖 `Win32 API`: 119 | 120 | ```powershell 121 | cargo add windows 122 | ``` 123 | 124 | 添加 `build.rs`,一定要注意修改 `sdk_path` 中的 `ilcompiler` 版本号(本文讲的是实现步骤,最终的代码我会把 `des-lib` 的构建也放在 `build.rs` 中,并从构建的输出中寻找这个版本号,而不需要写死): 125 | 126 | ```rust 127 | use std::path::PathBuf; 128 | 129 | fn main() { 130 | let user_profile: PathBuf = std::env::var("USERPROFILE").unwrap().into(); 131 | let sdk_path: PathBuf = (user_profile) 132 | .join(".nuget\\packages\\runtime.win-x64.microsoft.dotnet.ilcompiler\\7.0.1\\sdk"); 133 | let manifest_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into(); 134 | let des_lib_path = manifest_dir.join("des-lib"); 135 | 136 | println!("cargo:rustc-link-arg=/INCLUDE:NativeAOT_StaticInitialization"); 137 | println!("cargo:rustc-link-search={}", sdk_path.display()); 138 | println!( 139 | "cargo:rustc-link-search={}\\bin\\Release\\net7.0\\win-x64\\publish", 140 | des_lib_path.display() 141 | ); 142 | 143 | // 新版本的 windows crate 已不再提供 windows.lib,此处不再需要。 144 | // println!("cargo:rustc-link-lib=static=windows"); 145 | println!("cargo:rustc-link-lib=static=bootstrapperdll"); 146 | println!("cargo:rustc-link-lib=static=Runtime.WorkstationGC"); 147 | println!("cargo:rustc-link-lib=static=System.Globalization.Native.Aot"); 148 | println!("cargo:rustc-link-lib=static=des-lib"); 149 | } 150 | ``` 151 | 152 | 接下来就是调用了,在 `main.rs` 中添加: 153 | 154 | ```rust 155 | // 链接 windows crate。 156 | extern crate windows; 157 | 158 | extern "C" { 159 | fn wtf_des_encrypt(message: *const u8, key: *const u8) -> *const u8; 160 | fn wtf_des_decrypt(cipher_text: *const u8, key: *const u8) -> *const u8; 161 | fn wtf_des_free(ptr: *const u8); 162 | } 163 | 164 | fn main() { 165 | let key = b"key\0"; 166 | let cipher_text = unsafe { wtf_des_encrypt(b"message\0".as_ptr(), key.as_ptr()) }; 167 | let cipher_text = unsafe { std::ffi::CStr::from_ptr(cipher_text as *const i8) }; 168 | let plain_text = unsafe { wtf_des_decrypt(cipher_text.as_ptr() as _, key.as_ptr()) }; 169 | let plain_text = unsafe { std::ffi::CStr::from_ptr(plain_text as *const i8) }; 170 | println!("cipher_text: {}", cipher_text.to_str().unwrap()); 171 | println!("plain_text: {}", plain_text.to_str().unwrap()); 172 | 173 | unsafe { 174 | wtf_des_free(cipher_text.as_ptr() as _); 175 | wtf_des_free(plain_text.as_ptr() as _); 176 | } 177 | } 178 | ``` 179 | 180 | ## Linux 兼容 181 | 182 | Linux 系统上,需要链接的库有些许不一样,具体参见:。 183 | 184 | 如果你加了额外的功能导致构建失败,可以自行查找所依赖的库,并链接。 185 | 186 | 例如,构建时报错,`undefined reference to ``RhRegisterOSModule'`,我们可以运行如下的代码找到所需的依赖: 187 | 188 | ```bash 189 | # 这个路径按你的需要更改。 190 | cd /home/hamflx/.nuget/packages/runtime.linux-x64.microsoft.dotnet.ilcompiler/7.0.10 191 | 192 | find . -name '*.a' | xargs -I{} nm -o '{}' | grep RhRegisterOSModule 193 | ``` 194 | 195 | 此时,输出结果大致如下: 196 | 197 | ```plaintext 198 | ./framework/libSystem.Native.a:pal_threading.c.o:0000000000000100 T SystemNative_LowLevelMonitor_Release 199 | ``` 200 | 201 | 因此,我们可以链接 `System.Native` 库。 202 | 203 | **注意,有时输出结果可能有多个,我们需要的是具有 `T` 标志的库。** 204 | 205 | **注意,Linux 系统下,库的顺序会影响符号的解析,有时报错 `undefined reference` 可能调整下顺序即可。** 206 | 207 | ## 最终版本 208 | 209 | 仓库地址:,在本文的基础增加了自动构建 `C#` 项目,自动查找 `ilcompiler` 的路径并链接。 210 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use std::{path::PathBuf, process::Command}; 2 | 3 | use ignore::{types::TypesBuilder, WalkBuilder}; 4 | 5 | fn main() { 6 | let manifest_dir = env!("CARGO_MANIFEST_DIR"); 7 | let csharp_project = "des-lib"; 8 | 9 | let (rid, pattern) = if cfg!(windows) { 10 | ("win-x64", "sdk\\System.Private.CoreLib.dll") 11 | } else { 12 | ("linux-x64", "sdk/System.Private.CoreLib.dll") 13 | }; 14 | 15 | // 运行 dotnet publish 命令。 16 | let output = Command::new("dotnet") 17 | .args(["publish", "-v", "d", "-r", rid, "-c", "Release"]) 18 | .current_dir(PathBuf::from(manifest_dir).join(csharp_project)) 19 | .output() 20 | .unwrap(); 21 | let out: String = String::from_utf8_lossy(&output.stdout).into(); 22 | if !output.status.success() { 23 | let err: String = String::from_utf8_lossy(&output.stderr).into(); 24 | panic!("Error: \n{}\n{}\n", out, err); 25 | } 26 | 27 | // 在构建的日志中查找 ilcompiler 的安装位置。 28 | let core_lib_path: PathBuf = out 29 | .find(pattern) 30 | .and_then(|pos| { 31 | let matched = &out[..pos + pattern.len()]; 32 | if cfg!(windows) { 33 | matched.rfind(':').map(|begin| &matched[begin - 1..]) 34 | } else { 35 | matched.rfind("/home").map(|begin| &matched[begin..]) 36 | } 37 | }) 38 | .unwrap_or_else(|| { 39 | std::fs::write("dotnet-output.txt", &out).unwrap(); 40 | panic!("ILCompiler sdk path not found in the dotnet command output: dotnet-output.txt") 41 | }) 42 | .into(); 43 | let ilcompiler_path = core_lib_path 44 | .parent() 45 | .unwrap() 46 | .parent() 47 | .unwrap() 48 | .to_str() 49 | .unwrap(); 50 | 51 | // 找到 C# 项目中的 *.cs 和 *.csproj 文件,如果这些文件发生变化,则应重新构建。 52 | let mut types = TypesBuilder::new(); 53 | types.add("cs", "*.cs").unwrap(); 54 | types.add("csproj", "*.csproj").unwrap(); 55 | types.select("cs").select("csproj"); 56 | let walker = WalkBuilder::new(csharp_project) 57 | .types(types.build().unwrap()) 58 | .build(); 59 | for file in walker { 60 | let file = file.unwrap(); 61 | if file.file_type().map(|t| t.is_file()).unwrap_or_default() { 62 | println!("cargo:rerun-if-changed={}", file.path().display()); 63 | } 64 | } 65 | 66 | println!("cargo:rustc-link-search={ilcompiler_path}/sdk"); 67 | println!("cargo:rustc-link-search={ilcompiler_path}/framework"); 68 | 69 | println!("cargo:rustc-link-lib=static={csharp_project}"); 70 | println!( 71 | "cargo:rustc-link-search={manifest_dir}/{csharp_project}/bin/Release/net7.0/{rid}/publish" 72 | ); 73 | 74 | if cfg!(windows) { 75 | println!("cargo:rustc-link-arg=/INCLUDE:NativeAOT_StaticInitialization"); 76 | println!("cargo:rustc-link-lib=static=System.Globalization.Native.Aot"); 77 | } else { 78 | println!("cargo:rustc-link-arg=-Wl,--require-defined,NativeAOT_StaticInitialization"); 79 | println!("cargo:rustc-link-arg=-lstdc++"); 80 | println!("cargo:rustc-link-lib=static=System.Globalization.Native"); 81 | println!("cargo:rustc-link-lib=static=System.Native"); 82 | } 83 | println!("cargo:rustc-link-lib=static=bootstrapperdll"); 84 | println!("cargo:rustc-link-lib=static=Runtime.WorkstationGC"); 85 | } 86 | -------------------------------------------------------------------------------- /des-lib/DES.cs: -------------------------------------------------------------------------------- 1 | namespace des_lib; 2 | 3 | using System.Runtime.InteropServices; 4 | 5 | public class DES 6 | { 7 | [UnmanagedCallersOnly(EntryPoint = "wtf_des_encrypt")] 8 | public static nint FFI_Encrypt(nint message, nint key) 9 | { 10 | var managedMessage = Marshal.PtrToStringUTF8(message); 11 | var managedKey = Marshal.PtrToStringUTF8(key); 12 | if (managedKey == null || managedMessage == null) 13 | { 14 | return nint.Zero; 15 | } 16 | var cipherText = EncryptDES(managedMessage, managedKey); 17 | return Marshal.StringToHGlobalAnsi(cipherText); 18 | } 19 | 20 | [UnmanagedCallersOnly(EntryPoint = "wtf_des_decrypt")] 21 | public static nint FFI_Decrypt(nint cipherMessage, nint key) 22 | { 23 | var managedCipherMessage = Marshal.PtrToStringUTF8(cipherMessage); 24 | var managedKey = Marshal.PtrToStringUTF8(key); 25 | if (managedKey == null || managedCipherMessage == null) 26 | { 27 | return nint.Zero; 28 | } 29 | var plainText = DecryptDES(managedCipherMessage, managedKey); 30 | return Marshal.StringToHGlobalAnsi(plainText); 31 | } 32 | 33 | [UnmanagedCallersOnly(EntryPoint = "wtf_des_free")] 34 | public static void FFI_FreeMemory(nint buffer) 35 | { 36 | Marshal.FreeHGlobal(buffer); 37 | } 38 | 39 | /// 40 | /// DES 加密 41 | /// 42 | /// 待加密的字符串 43 | /// 密钥 44 | /// 45 | public static string EncryptDES(string beinetstr, string beinetkey) 46 | { 47 | if (string.IsNullOrEmpty(beinetkey)) 48 | return string.Empty; 49 | 50 | return stringToHex(des(beinetkey, beinetstr, true, false, string.Empty)); 51 | } 52 | 53 | /// 54 | /// DES 解密 55 | /// 56 | /// 待解密的字符串 57 | /// 密钥 58 | /// 59 | public static string? DecryptDES(string beinetstr, string beinetkey) 60 | { 61 | if (string.IsNullOrEmpty(beinetkey)) 62 | return null; 63 | string ret = des(beinetkey, HexTostring(beinetstr), false, false, string.Empty); 64 | return ret; 65 | } 66 | 67 | /// 68 | /// 把字符串转换为16进制字符串 69 | /// 如:a变成61(即10进制的97);abc变成616263 70 | /// 71 | /// 72 | /// 73 | public static string stringToHex(string s) 74 | { 75 | string r = ""; 76 | string[] hexes = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; 77 | for (int i = 0; i < (s.Length); i++) 78 | { 79 | r += hexes[RM(s[i], 4)] + hexes[s[i] & 0xf]; 80 | } 81 | return r; 82 | } 83 | 84 | /// 85 | /// 16进制字符串转换为字符串 86 | /// 如:61(即10进制的97)变成a;616263变成abc 87 | /// 88 | /// 89 | /// 90 | public static string HexTostring(string s) 91 | { 92 | string ret = string.Empty; 93 | 94 | for (int i = 0; i < s.Length; i += 2) 95 | { 96 | int sxx = Convert.ToInt32(s.Substring(i, 2), 16); 97 | ret += (char)sxx; 98 | } 99 | return ret; 100 | } 101 | 102 | /// 103 | /// 带符号位右移(类似于js的>>>) 104 | /// 105 | /// 用于右移的操作数 106 | /// 右移位数 107 | /// 108 | public static int RM(int a, int bit) 109 | { 110 | unchecked 111 | { 112 | uint b = (uint)a; 113 | b = b >> bit; 114 | return (int)b; 115 | } 116 | } 117 | 118 | /// 119 | /// 加解密主调方法 120 | /// 121 | /// 密钥 122 | /// 加密时为string,解密时为byte[] 123 | /// true:加密;false:解密 124 | /// true:CBC mode;false:非CBC mode 125 | /// 初始化向量 126 | /// 127 | public static string des(string beinetkey, string message, bool encrypt, bool mode, string iv) 128 | { 129 | //declaring this locally speeds things up a bit 130 | long[] spfunction1 = { 0x1010400, 0, 0x10000, 0x1010404, 0x1010004, 0x10404, 0x4, 0x10000, 0x400, 0x1010400, 0x1010404, 0x400, 0x1000404, 0x1010004, 0x1000000, 0x4, 0x404, 0x1000400, 0x1000400, 0x10400, 0x10400, 0x1010000, 0x1010000, 0x1000404, 0x10004, 0x1000004, 0x1000004, 0x10004, 0, 0x404, 0x10404, 0x1000000, 0x10000, 0x1010404, 0x4, 0x1010000, 0x1010400, 0x1000000, 0x1000000, 0x400, 0x1010004, 0x10000, 0x10400, 0x1000004, 0x400, 0x4, 0x1000404, 0x10404, 0x1010404, 0x10004, 0x1010000, 0x1000404, 0x1000004, 0x404, 0x10404, 0x1010400, 0x404, 0x1000400, 0x1000400, 0, 0x10004, 0x10400, 0, 0x1010004 }; 131 | long[] spfunction2 = { -0x7fef7fe0, -0x7fff8000, 0x8000, 0x108020, 0x100000, 0x20, -0x7fefffe0, -0x7fff7fe0, -0x7fffffe0, -0x7fef7fe0, -0x7fef8000, -0x80000000, -0x7fff8000, 0x100000, 0x20, -0x7fefffe0, 0x108000, 0x100020, -0x7fff7fe0, 0, -0x80000000, 0x8000, 0x108020, -0x7ff00000, 0x100020, -0x7fffffe0, 0, 0x108000, 0x8020, -0x7fef8000, -0x7ff00000, 0x8020, 0, 0x108020, -0x7fefffe0, 0x100000, -0x7fff7fe0, -0x7ff00000, -0x7fef8000, 0x8000, -0x7ff00000, -0x7fff8000, 0x20, -0x7fef7fe0, 0x108020, 0x20, 0x8000, -0x80000000, 0x8020, -0x7fef8000, 0x100000, -0x7fffffe0, 0x100020, -0x7fff7fe0, -0x7fffffe0, 0x100020, 0x108000, 0, -0x7fff8000, 0x8020, -0x80000000, -0x7fefffe0, -0x7fef7fe0, 0x108000 }; 132 | long[] spfunction3 = { 0x208, 0x8020200, 0, 0x8020008, 0x8000200, 0, 0x20208, 0x8000200, 0x20008, 0x8000008, 0x8000008, 0x20000, 0x8020208, 0x20008, 0x8020000, 0x208, 0x8000000, 0x8, 0x8020200, 0x200, 0x20200, 0x8020000, 0x8020008, 0x20208, 0x8000208, 0x20200, 0x20000, 0x8000208, 0x8, 0x8020208, 0x200, 0x8000000, 0x8020200, 0x8000000, 0x20008, 0x208, 0x20000, 0x8020200, 0x8000200, 0, 0x200, 0x20008, 0x8020208, 0x8000200, 0x8000008, 0x200, 0, 0x8020008, 0x8000208, 0x20000, 0x8000000, 0x8020208, 0x8, 0x20208, 0x20200, 0x8000008, 0x8020000, 0x8000208, 0x208, 0x8020000, 0x20208, 0x8, 0x8020008, 0x20200 }; 133 | long[] spfunction4 = { 0x802001, 0x2081, 0x2081, 0x80, 0x802080, 0x800081, 0x800001, 0x2001, 0, 0x802000, 0x802000, 0x802081, 0x81, 0, 0x800080, 0x800001, 0x1, 0x2000, 0x800000, 0x802001, 0x80, 0x800000, 0x2001, 0x2080, 0x800081, 0x1, 0x2080, 0x800080, 0x2000, 0x802080, 0x802081, 0x81, 0x800080, 0x800001, 0x802000, 0x802081, 0x81, 0, 0, 0x802000, 0x2080, 0x800080, 0x800081, 0x1, 0x802001, 0x2081, 0x2081, 0x80, 0x802081, 0x81, 0x1, 0x2000, 0x800001, 0x2001, 0x802080, 0x800081, 0x2001, 0x2080, 0x800000, 0x802001, 0x80, 0x800000, 0x2000, 0x802080 }; 134 | long[] spfunction5 = { 0x100, 0x2080100, 0x2080000, 0x42000100, 0x80000, 0x100, 0x40000000, 0x2080000, 0x40080100, 0x80000, 0x2000100, 0x40080100, 0x42000100, 0x42080000, 0x80100, 0x40000000, 0x2000000, 0x40080000, 0x40080000, 0, 0x40000100, 0x42080100, 0x42080100, 0x2000100, 0x42080000, 0x40000100, 0, 0x42000000, 0x2080100, 0x2000000, 0x42000000, 0x80100, 0x80000, 0x42000100, 0x100, 0x2000000, 0x40000000, 0x2080000, 0x42000100, 0x40080100, 0x2000100, 0x40000000, 0x42080000, 0x2080100, 0x40080100, 0x100, 0x2000000, 0x42080000, 0x42080100, 0x80100, 0x42000000, 0x42080100, 0x2080000, 0, 0x40080000, 0x42000000, 0x80100, 0x2000100, 0x40000100, 0x80000, 0, 0x40080000, 0x2080100, 0x40000100 }; 135 | long[] spfunction6 = { 0x20000010, 0x20400000, 0x4000, 0x20404010, 0x20400000, 0x10, 0x20404010, 0x400000, 0x20004000, 0x404010, 0x400000, 0x20000010, 0x400010, 0x20004000, 0x20000000, 0x4010, 0, 0x400010, 0x20004010, 0x4000, 0x404000, 0x20004010, 0x10, 0x20400010, 0x20400010, 0, 0x404010, 0x20404000, 0x4010, 0x404000, 0x20404000, 0x20000000, 0x20004000, 0x10, 0x20400010, 0x404000, 0x20404010, 0x400000, 0x4010, 0x20000010, 0x400000, 0x20004000, 0x20000000, 0x4010, 0x20000010, 0x20404010, 0x404000, 0x20400000, 0x404010, 0x20404000, 0, 0x20400010, 0x10, 0x4000, 0x20400000, 0x404010, 0x4000, 0x400010, 0x20004010, 0, 0x20404000, 0x20000000, 0x400010, 0x20004010 }; 136 | long[] spfunction7 = { 0x200000, 0x4200002, 0x4000802, 0, 0x800, 0x4000802, 0x200802, 0x4200800, 0x4200802, 0x200000, 0, 0x4000002, 0x2, 0x4000000, 0x4200002, 0x802, 0x4000800, 0x200802, 0x200002, 0x4000800, 0x4000002, 0x4200000, 0x4200800, 0x200002, 0x4200000, 0x800, 0x802, 0x4200802, 0x200800, 0x2, 0x4000000, 0x200800, 0x4000000, 0x200800, 0x200000, 0x4000802, 0x4000802, 0x4200002, 0x4200002, 0x2, 0x200002, 0x4000000, 0x4000800, 0x200000, 0x4200800, 0x802, 0x200802, 0x4200800, 0x802, 0x4000002, 0x4200802, 0x4200000, 0x200800, 0, 0x2, 0x4200802, 0, 0x200802, 0x4200000, 0x800, 0x4000002, 0x4000800, 0x800, 0x200002 }; 137 | long[] spfunction8 = { 0x10001040, 0x1000, 0x40000, 0x10041040, 0x10000000, 0x10001040, 0x40, 0x10000000, 0x40040, 0x10040000, 0x10041040, 0x41000, 0x10041000, 0x41040, 0x1000, 0x40, 0x10040000, 0x10000040, 0x10001000, 0x1040, 0x41000, 0x40040, 0x10040040, 0x10041000, 0x1040, 0, 0, 0x10040040, 0x10000040, 0x10001000, 0x41040, 0x40000, 0x41040, 0x40000, 0x10041000, 0x1000, 0x40, 0x10040040, 0x1000, 0x41040, 0x10001000, 0x40, 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x40000, 0x10001040, 0, 0x10041040, 0x40040, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0, 0x10041040, 0x41000, 0x41000, 0x1040, 0x1040, 0x40040, 0x10000000, 0x10041000 }; 138 | 139 | 140 | //create the 16 or 48 subkeys we will need 141 | int[] keys = des_createKeys(beinetkey); 142 | int m = 0; 143 | int i, j; 144 | int temp, right1, right2, left, right; 145 | int[] looping; 146 | int cbcleft = 0, cbcleft2 = 0, cbcright = 0, cbcright2 = 0; 147 | int endloop; 148 | int loopinc; 149 | int len = message.Length; 150 | int chunk = 0; 151 | //set up the loops for single and triple des 152 | var iterations = keys.Length == 32 ? 3 : 9;//single or triple des 153 | if (iterations == 3) 154 | { 155 | looping = encrypt ? new int[] { 0, 32, 2 } : new int[] { 30, -2, -2 }; 156 | } 157 | else { looping = encrypt ? new int[] { 0, 32, 2, 62, 30, -2, 64, 96, 2 } : new int[] { 94, 62, -2, 32, 64, 2, 30, -2, -2 }; } 158 | 159 | if (encrypt) 160 | { 161 | message += "\0\0\0\0\0\0\0\0";//pad the message out with null bytes 162 | } 163 | //store the result here 164 | //List result = new List(); 165 | //List tempresult = new List(); 166 | string result = string.Empty; 167 | string tempresult = string.Empty; 168 | 169 | if (mode) 170 | {//CBC mode 171 | int[] tmp = { 0, 0, 0, 0, 0, 0, 0, 0 }; 172 | int pos = 24; 173 | int iTmp = 0; 174 | while (m < iv.Length && iTmp < tmp.Length) 175 | { 176 | if (pos < 0) 177 | pos = 24; 178 | tmp[iTmp++] = iv[m++] << pos; 179 | pos -= 8; 180 | } 181 | cbcleft = tmp[0] | tmp[1] | tmp[2] | tmp[3]; 182 | cbcright = tmp[4] | tmp[5] | tmp[6] | tmp[7]; 183 | 184 | //cbcleft = (iv[m++] << 24) | (iv[m++] << 16) | (iv[m++] << 8) | iv[m++]; 185 | //cbcright = (iv[m++] << 24) | (iv[m++] << 16) | (iv[m++] << 8) | iv[m++]; 186 | m = 0; 187 | } 188 | 189 | //loop through each 64 bit chunk of the message 190 | while (m < len) 191 | { 192 | if (encrypt) 193 | {/*加密时按双字节操作*/ 194 | left = (message[m++] << 16) | message[m++]; 195 | right = (message[m++] << 16) | message[m++]; 196 | } 197 | else 198 | { 199 | left = (message[m++] << 24) | (message[m++] << 16) | (message[m++] << 8) | message[m++]; 200 | right = (message[m++] << 24) | (message[m++] << 16) | (message[m++] << 8) | message[m++]; 201 | } 202 | //for Cipher Block Chaining mode,xor the message with the previous result 203 | if (mode) 204 | { 205 | if (encrypt) 206 | { 207 | left ^= cbcleft; right ^= cbcright; 208 | } 209 | else 210 | { 211 | cbcleft2 = cbcleft; cbcright2 = cbcright; cbcleft = left; cbcright = right; 212 | } 213 | } 214 | 215 | //first each 64 but chunk of the message must be permuted according to IP 216 | temp = (RM(left, 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); 217 | temp = (RM(left, 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16); 218 | temp = (RM(right, 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2); 219 | temp = (RM(right, 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); 220 | temp = (RM(left, 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); 221 | 222 | left = ((left << 1) | RM(left, 31)); 223 | right = ((right << 1) | RM(right, 31)); 224 | 225 | //do this either 1 or 3 times for each chunk of the message 226 | for (j = 0; j < iterations; j += 3) 227 | { 228 | endloop = looping[j + 1]; 229 | loopinc = looping[j + 2]; 230 | //now go through and perform the encryption or decryption 231 | for (i = looping[j]; i != endloop; i += loopinc) 232 | {//for efficiency 233 | right1 = right ^ keys[i]; 234 | right2 = (RM(right, 4) | (right << 28)) ^ keys[i + 1]; 235 | //the result is attained by passing these bytes through the S selection functions 236 | temp = left; 237 | left = right; 238 | right = (int)(temp ^ (spfunction2[RM(right1, 24) & 0x3f] | spfunction4[RM(right1, 16) & 0x3f] | spfunction6[RM(right1, 8) & 0x3f] | spfunction8[right1 & 0x3f] | spfunction1[RM(right2, 24) & 0x3f] | spfunction3[RM(right2, 16) & 0x3f] | spfunction5[RM(right2, 8) & 0x3f] | spfunction7[right2 & 0x3f])); 239 | } 240 | temp = left; left = right; right = temp;//unreverse left and right 241 | }//for either 1 or 3 iterations 242 | 243 | //move then each one bit to the right 244 | left = (RM(left, 1) | (left << 31)); 245 | right = (RM(right, 1) | (right << 31)); 246 | 247 | //now perform IP-1,which is IP in the opposite direction 248 | temp = (RM(left, 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); 249 | temp = (RM(right, 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); 250 | temp = (RM(right, 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2); 251 | temp = (RM(left, 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16); 252 | temp = (RM(left, 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); 253 | 254 | //for Cipher Block Chaining mode,xor the message with the previous result 255 | if (mode) 256 | { 257 | if (encrypt) 258 | { 259 | cbcleft = left; cbcright = right; 260 | } 261 | else 262 | { 263 | left ^= cbcleft2; right ^= cbcright2; 264 | } 265 | } 266 | //int[] arrInt; 267 | if (encrypt) 268 | { 269 | //arrInt = new int[]{ RM(left, 24), (RM(left, 16) & 0xff), (RM(left, 8) & 0xff), (left & 0xff), RM(right, 24), (RM(right, 16) & 0xff), (RM(right, 8) & 0xff), (right & 0xff) }; 270 | tempresult += String.Concat((char)RM(left, 24), 271 | (char)(RM(left, 16) & 0xff), 272 | (char)(RM(left, 8) & 0xff), 273 | (char)(left & 0xff), 274 | (char)RM(right, 24), 275 | (char)(RM(right, 16) & 0xff), 276 | (char)(RM(right, 8) & 0xff), 277 | (char)(right & 0xff)); 278 | } 279 | else 280 | { 281 | // 解密时,最后一个字符如果是\0,去除 282 | //arrInt = new int[] { (RM(left, 16) & 0xffff), (left & 0xffff), (RM(right, 16) & 0xffff), (right & 0xffff) }; 283 | int tmpch = (RM(left, 16) & 0xffff); 284 | if (tmpch != 0) 285 | tempresult += (char)tmpch; 286 | tmpch = (left & 0xffff); 287 | if (tmpch != 0) 288 | tempresult += (char)tmpch; 289 | tmpch = (RM(right, 16) & 0xffff); 290 | if (tmpch != 0) 291 | tempresult += (char)tmpch; 292 | tmpch = (right & 0xffff); 293 | if (tmpch != 0) 294 | tempresult += (char)tmpch; 295 | //tempresult += String.Concat((char)(RM(left, 16) & 0xffff), 296 | // (char)(left & 0xffff), 297 | // (char)(RM(right, 16) & 0xffff), 298 | // (char)(right & 0xffff)); 299 | }/*解密时输出双字节*/ 300 | //byte[] arrByte = new byte[arrInt.Length]; 301 | //for (int loop = 0; loop < arrInt.Length; loop++) 302 | //{ 303 | // tempresult.Add(byte.Parse(arrInt[loop].ToString())); 304 | // //arrByte[loop] = byte.Parse(arrInt[loop].ToString()); 305 | //} 306 | //tempresult.Add(arrByte;// System.Text.Encoding.Unicode.GetString(arrByte); 307 | 308 | chunk += encrypt ? 16 : 8; 309 | if (chunk == 512) 310 | { 311 | //result.AddRange(tempresult);tempresult.Clear(); 312 | result += tempresult; tempresult = string.Empty; 313 | chunk = 0; 314 | } 315 | }//for every 8 characters,or 64 bits in the message 316 | 317 | //return the result as an array 318 | 319 | //result.AddRange(tempresult); 320 | //return result.ToArray(); 321 | return result + tempresult; 322 | }//end of des 323 | 324 | /// 325 | /// this takes as input a 64 bit beinetkey(even though only 56 bits are used) 326 | /// as an array of 2 integers,and returns 16 48 bit keys 327 | /// 328 | /// 329 | /// 330 | static int[] des_createKeys(string beinetkey) 331 | { 332 | //declaring this locally speeds things up a bit 333 | int[] pc2bytes0 = { 0, 0x4, 0x20000000, 0x20000004, 0x10000, 0x10004, 0x20010000, 0x20010004, 0x200, 0x204, 0x20000200, 0x20000204, 0x10200, 0x10204, 0x20010200, 0x20010204 }; 334 | int[] pc2bytes1 = { 0, 0x1, 0x100000, 0x100001, 0x4000000, 0x4000001, 0x4100000, 0x4100001, 0x100, 0x101, 0x100100, 0x100101, 0x4000100, 0x4000101, 0x4100100, 0x4100101 }; 335 | int[] pc2bytes2 = { 0, 0x8, 0x800, 0x808, 0x1000000, 0x1000008, 0x1000800, 0x1000808, 0, 0x8, 0x800, 0x808, 0x1000000, 0x1000008, 0x1000800, 0x1000808 }; 336 | int[] pc2bytes3 = { 0, 0x200000, 0x8000000, 0x8200000, 0x2000, 0x202000, 0x8002000, 0x8202000, 0x20000, 0x220000, 0x8020000, 0x8220000, 0x22000, 0x222000, 0x8022000, 0x8222000 }; 337 | int[] pc2bytes4 = { 0, 0x40000, 0x10, 0x40010, 0, 0x40000, 0x10, 0x40010, 0x1000, 0x41000, 0x1010, 0x41010, 0x1000, 0x41000, 0x1010, 0x41010 }; 338 | int[] pc2bytes5 = { 0, 0x400, 0x20, 0x420, 0, 0x400, 0x20, 0x420, 0x2000000, 0x2000400, 0x2000020, 0x2000420, 0x2000000, 0x2000400, 0x2000020, 0x2000420 }; 339 | int[] pc2bytes6 = { 0, 0x10000000, 0x80000, 0x10080000, 0x2, 0x10000002, 0x80002, 0x10080002, 0, 0x10000000, 0x80000, 0x10080000, 0x2, 0x10000002, 0x80002, 0x10080002 }; 340 | int[] pc2bytes7 = { 0, 0x10000, 0x800, 0x10800, 0x20000000, 0x20010000, 0x20000800, 0x20010800, 0x20000, 0x30000, 0x20800, 0x30800, 0x20020000, 0x20030000, 0x20020800, 0x20030800 }; 341 | int[] pc2bytes8 = { 0, 0x40000, 0, 0x40000, 0x2, 0x40002, 0x2, 0x40002, 0x2000000, 0x2040000, 0x2000000, 0x2040000, 0x2000002, 0x2040002, 0x2000002, 0x2040002 }; 342 | int[] pc2bytes9 = { 0, 0x10000000, 0x8, 0x10000008, 0, 0x10000000, 0x8, 0x10000008, 0x400, 0x10000400, 0x408, 0x10000408, 0x400, 0x10000400, 0x408, 0x10000408 }; 343 | int[] pc2bytes10 = { 0, 0x20, 0, 0x20, 0x100000, 0x100020, 0x100000, 0x100020, 0x2000, 0x2020, 0x2000, 0x2020, 0x102000, 0x102020, 0x102000, 0x102020 }; 344 | int[] pc2bytes11 = { 0, 0x1000000, 0x200, 0x1000200, 0x200000, 0x1200000, 0x200200, 0x1200200, 0x4000000, 0x5000000, 0x4000200, 0x5000200, 0x4200000, 0x5200000, 0x4200200, 0x5200200 }; 345 | int[] pc2bytes12 = { 0, 0x1000, 0x8000000, 0x8001000, 0x80000, 0x81000, 0x8080000, 0x8081000, 0x10, 0x1010, 0x8000010, 0x8001010, 0x80010, 0x81010, 0x8080010, 0x8081010 }; 346 | int[] pc2bytes13 = { 0, 0x4, 0x100, 0x104, 0, 0x4, 0x100, 0x104, 0x1, 0x5, 0x101, 0x105, 0x1, 0x5, 0x101, 0x105 }; 347 | 348 | //how many iterations(1 for des,3 for triple des) 349 | int iterations = beinetkey.Length >= 24 ? 3 : 1; 350 | //stores the return keys 351 | int[] keys = new int[32 * iterations]; 352 | //now define the left shifts which need to be done 353 | int[] shifts = { 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0 }; 354 | //other variables 355 | int left, right; 356 | int lefttemp; 357 | int righttemp; 358 | int m = 0, n = 0; 359 | int temp; 360 | 361 | for (int j = 0; j < iterations; j++) 362 | {//either 1 or 3 iterations 363 | int[] tmp = { 0, 0, 0, 0, 0, 0, 0, 0 }; 364 | int pos = 24; 365 | int iTmp = 0; 366 | while (m < beinetkey.Length && iTmp < tmp.Length) 367 | { 368 | if (pos < 0) 369 | pos = 24; 370 | tmp[iTmp++] = beinetkey[m++] << pos; 371 | pos -= 8; 372 | } 373 | left = tmp[0] | tmp[1] | tmp[2] | tmp[3]; 374 | right = tmp[4] | tmp[5] | tmp[6] | tmp[7]; 375 | 376 | //left = (beinetkey[m++] << 24) | (beinetkey[m++] << 16) | (beinetkey[m++] << 8) | beinetkey[m++]; 377 | //right = (beinetkey[m++] << 24) | (beinetkey[m++] << 16) | (beinetkey[m++] << 8) | beinetkey[m++]; 378 | 379 | temp = (RM(left, 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); 380 | temp = (RM(right, -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16); 381 | temp = (RM(left, 2) ^ right) & 0x33333333; right ^= temp; left ^= (temp << 2); 382 | temp = (RM(right, -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16); 383 | temp = (RM(left, 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); 384 | temp = (RM(right, 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); 385 | temp = (RM(left, 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); 386 | 387 | //the right side needs to be shifted and to get the last four bits of the left side 388 | temp = (left << 8) | (RM(right, 20) & 0x000000f0); 389 | //left needs to be put upside down 390 | left = (right << 24) | ((right << 8) & 0xff0000) | (RM(right, 8) & 0xff00) | (RM(right, 24) & 0xf0); 391 | right = temp; 392 | 393 | //now go through and perform these shifts on the left and right keys 394 | for (int i = 0; i < shifts.Length; i++) 395 | { 396 | //shift the keys either one or two bits to the left 397 | if (shifts[i] == 1) 398 | { 399 | left = (left << 2) | RM(left, 26); right = (right << 2) | RM(right, 26); 400 | } 401 | else 402 | { 403 | left = (left << 1) | RM(left, 27); right = (right << 1) | RM(right, 27); 404 | } 405 | left &= -0xf; right &= -0xf; 406 | 407 | //now apply PC-2,in such a way that E is easier when encrypting or decrypting 408 | //this conversion will look like PC-2 except only the last 6 bits of each byte are used 409 | //rather than 48 consecutive bits and the order of lines will be according to 410 | //how the S selection functions will be applied:S2,S4,S6,S8,S1,S3,S5,S7 411 | lefttemp = pc2bytes0[RM(left, 28)] | pc2bytes1[RM(left, 24) & 0xf] 412 | | pc2bytes2[RM(left, 20) & 0xf] | pc2bytes3[RM(left, 16) & 0xf] 413 | | pc2bytes4[RM(left, 12) & 0xf] | pc2bytes5[RM(left, 8) & 0xf] 414 | | pc2bytes6[RM(left, 4) & 0xf]; 415 | righttemp = pc2bytes7[RM(right, 28)] | pc2bytes8[RM(right, 24) & 0xf] 416 | | pc2bytes9[RM(right, 20) & 0xf] | pc2bytes10[RM(right, 16) & 0xf] 417 | | pc2bytes11[RM(right, 12) & 0xf] | pc2bytes12[RM(right, 8) & 0xf] 418 | | pc2bytes13[RM(right, 4) & 0xf]; 419 | temp = (RM(righttemp, 16) ^ lefttemp) & 0x0000ffff; 420 | keys[n++] = lefttemp ^ temp; keys[n++] = righttemp ^ (temp << 16); 421 | } 422 | }//for each iterations 423 | //return the keys we"ve created 424 | return keys; 425 | }//end of des_createKeys 426 | } -------------------------------------------------------------------------------- /des-lib/des-lib.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | des_lib 6 | enable 7 | enable 8 | 9 | Static 10 | true 11 | true 12 | true 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate windows; 2 | 3 | extern "C" { 4 | fn wtf_des_encrypt(message: *const u8, key: *const u8) -> *const u8; 5 | fn wtf_des_decrypt(cipher_text: *const u8, key: *const u8) -> *const u8; 6 | fn wtf_des_free(ptr: *const u8); 7 | } 8 | 9 | fn main() { 10 | let key = b"key\0"; 11 | let cipher_text = unsafe { wtf_des_encrypt(b"message\0".as_ptr(), key.as_ptr()) }; 12 | let cipher_text = unsafe { std::ffi::CStr::from_ptr(cipher_text as *const i8) }; 13 | let plain_text = unsafe { wtf_des_decrypt(cipher_text.as_ptr() as _, key.as_ptr()) }; 14 | let plain_text = unsafe { std::ffi::CStr::from_ptr(plain_text as *const i8) }; 15 | println!("cipher_text: {}", cipher_text.to_str().unwrap()); 16 | println!("plain_text: {}", plain_text.to_str().unwrap()); 17 | 18 | unsafe { 19 | wtf_des_free(cipher_text.as_ptr() as _); 20 | wtf_des_free(plain_text.as_ptr() as _); 21 | } 22 | } 23 | --------------------------------------------------------------------------------