diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..29dbfa6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,22 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + # Enable version updates for npm + - package-ecosystem: 'npm' + # Look for `package.json` and `lock` files in the `root` directory + directory: '/' + # Check the npm registry for updates every day (weekdays) + schedule: + interval: 'weekly' + + # Enable version updates for GitHub Actions + - package-ecosystem: 'github-actions' + # Workflow files stored in the default location of `.github/workflows` + # You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`. + directory: '/' + schedule: + interval: 'weekly' \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..1b2f700 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,49 @@ +name: Build UEAESKeyFinder + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: write + +jobs: + build: + runs-on: windows-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: | + 3.1.x + 6.0.x + 7.0.x + 8.0.x + 9.0.x + 10.0.x + - name: Restore dependencies + run: dotnet restore UEAESKeyFinder.sln + + - name: Build Publish Binary + run: dotnet publish ./UEAESKeyFinder/UEAESKeyFinder.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeAllContentForSelfExtract=true -p:PublishTrimmed=false -o ./publish + + - name: Get DateTime Tashkent/Uzbekistan + id: datetime + shell: bash + run: echo "now=$(date -u -d '+5 hour' '+%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV + + - name: Create or Update Release + uses: softprops/action-gh-release@v2 + with: + tag_name: latest + name: "UEAESKeyFinder Build (${{ env.now }})" + body: "Build time: ${{ env.now }}" + files: ./publish/** + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/AESKeys/Blade & Soul.md b/AESKeys/Blade & Soul.md index 59c7a64..cb19b05 100644 --- a/AESKeys/Blade & Soul.md +++ b/AESKeys/Blade & Soul.md @@ -1,4 +1,7 @@ # AES Keys for Blade & Soul Never changed afaik -Key: 0xD2E5F7F94E625EFE2726B5360C1039CE7CB9ABB760A94F37BB15A6DC08741656 \ No newline at end of file +## Closed Alpha +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| xxx | 0xD2E5F7F94E625EFE2726B5360C1039CE7CB9ABB760A94F37BB15A6DC08741656 | diff --git a/AESKeys/PUBG New State.md b/AESKeys/PUBG New State.md index fde1121..26bae21 100644 --- a/AESKeys/PUBG New State.md +++ b/AESKeys/PUBG New State.md @@ -1,6 +1,55 @@ # AES Keys for PUBG New State -## Closed Alpha | Version | Key | | ----------------- | --------------------------------------------------------------------- | -| 0.9.5.29 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | \ No newline at end of file +| 0.9.91.756 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.90.736 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.89.728 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.86.722 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.85.720 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.84.718 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.79.700 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.77.692 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.76.690 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.75.688 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.74.686 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.73.682 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.72.678 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.71.672 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.69.668 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.68.664 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.67.660 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.66.658 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.65.652 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.64.635 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.62.624 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.61.610 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.60.601 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.59.581 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.57.566 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.56.548 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.55.531 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.54.529 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.53.516 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.52.499 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.51.488 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.48.446 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.46.429 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.45.418 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.43.384 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.42.367 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.40.346 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.39.330 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.39.326 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.38.311 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.37.308 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.35.291 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.31.240 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.30.237 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.24.197 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.24.195 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.22.152 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.20.141 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.19.133 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.16.127 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | +| 0.9.16.125 | 0x27DFBADBB537388ACDE27A7C5F3EBC3721AF0AE0A7602D2D7F8A16548F37D394 | diff --git a/AESKeys/SpecialForcesGroup2.md b/AESKeys/SpecialForcesGroup2.md new file mode 100644 index 0000000..2f5d4a2 --- /dev/null +++ b/AESKeys/SpecialForcesGroup2.md @@ -0,0 +1,9 @@ +# AES Keys for SpecialForcesGroup2 + +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| 4.21 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | +| 4.2 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | +| 4.1 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | +| 4.0 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | +| 3.9 | 0xF4BB36029496A4B5AE4D96E7AB2CAB086C19F3C7E4B000289022D963B145B167 | diff --git a/AESKeys/SpecialForcesGroup3.md b/AESKeys/SpecialForcesGroup3.md new file mode 100644 index 0000000..b1e6413 --- /dev/null +++ b/AESKeys/SpecialForcesGroup3.md @@ -0,0 +1,29 @@ +# AES Keys for SpecialForcesGroup3 + +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| 1.8 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.8 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.8 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.7 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.6 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.6 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.5 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4.6 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4.5 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4.4 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4.3 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4.2 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4.1 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.4 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.3 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.3 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.3 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.2 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.2 | 0x77745B271BB1C86196B8549FA395CD404DBC469FB218974AFACA40C949E08A2E | +| 1.1 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | +| 1.0 | 0xB96978E155ECC391708269C2759E867B7D6C01F12B6DB51AE021A4E7B9A4F8A0 | diff --git a/AESKeys/Splitgate.md b/AESKeys/Splitgate.md index b9a7a4b..d1008ec 100644 --- a/AESKeys/Splitgate.md +++ b/AESKeys/Splitgate.md @@ -1,4 +1,7 @@ # AES Keys for Splitgate Never changed afaik -Key: 0xD73A797940208F2FB29256BE81A7CBC7B74CBF899441BB277F357F7F4577DBBB \ No newline at end of file +## Closed Alpha +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| xxx | 0xD73A797940208F2FB29256BE81A7CBC7B74CBF899441BB277F357F7F4577DBBB | diff --git a/AESKeys/Sword Art Online - Fatal Bullet.md b/AESKeys/Sword Art Online - Fatal Bullet.md index 2599c4b..4b7c619 100644 --- a/AESKeys/Sword Art Online - Fatal Bullet.md +++ b/AESKeys/Sword Art Online - Fatal Bullet.md @@ -1,4 +1,7 @@ # AES Keys for Sword Art Online : Fatal Bullet Never changed afaik -Key: h67GrjX2aGMgrAQeNwf9VmCYbt50ylJFeP3rIhbxh4e9bZXnqm8sbvEjWGOi6rgs \ No newline at end of file +## Closed Alpha +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| xxx | h67GrjX2aGMgrAQeNwf9VmCYbt50ylJFeP3rIhbxh4e9bZXnqm8sbvEjWGOi6rgs | diff --git a/AESKeys/Valorant.md b/AESKeys/Valorant.md index 778eef8..698dc2a 100644 --- a/AESKeys/Valorant.md +++ b/AESKeys/Valorant.md @@ -1,4 +1,7 @@ # AES Keys for Valorant Never changed afaik -Key: 0x4BE71AF2459CF83899EC9DC2CB60E22AC4B3047E0211034BBABE9D174C069DD6 \ No newline at end of file +## Closed Alpha +| Version | Key | +| ----------------- | --------------------------------------------------------------------- | +| xxx | 0x4BE71AF2459CF83899EC9DC2CB60E22AC4B3047E0211034BBABE9D174C069DD6 | diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..2b63181 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,11 @@ + + + true + true + $(NoWarn);NU1507 + + + + + + \ No newline at end of file diff --git a/ReadMe.md b/ReadMe.md index 288bfcf..f52aa06 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -25,4 +25,16 @@ Found MyProject Found 1 AES Keys in 720ms 0xD0DE16965D23CC8A46178FFFAB18130651D2C99F7B3D63ECC8FD04D691609572 (0N4Wll0jzIpGF4//qxgTBlHSyZ97PWPsyP0E1pFglXI=) at 140695177160459 -``` \ No newline at end of file + +``` + +
+ + + + + + + + +
diff --git a/UEAESKeyFinder.sln b/UEAESKeyFinder.sln index d8365f4..5a222f1 100644 --- a/UEAESKeyFinder.sln +++ b/UEAESKeyFinder.sln @@ -1,31 +1,31 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31105.61 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UEAESKeyFinder", "UEAESKeyFinder\UEAESKeyFinder.csproj", "{A184C3B9-EDEF-4BA5-96E6-207908AC9A46}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|x64.ActiveCfg = Debug|x64 - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|x64.Build.0 = Debug|x64 - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|Any CPU.ActiveCfg = Release|x64 - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|Any CPU.Build.0 = Release|x64 - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|x64.ActiveCfg = Release|x64 - {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B55F5E37-882F-4695-B88E-49404B10CC4E} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35506.116 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UEAESKeyFinder", "UEAESKeyFinder\UEAESKeyFinder.csproj", "{A184C3B9-EDEF-4BA5-96E6-207908AC9A46}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|x64.ActiveCfg = Debug|x64 + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Debug|x64.Build.0 = Debug|x64 + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|Any CPU.ActiveCfg = Release|x64 + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|Any CPU.Build.0 = Release|x64 + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|x64.ActiveCfg = Release|x64 + {A184C3B9-EDEF-4BA5-96E6-207908AC9A46}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B55F5E37-882F-4695-B88E-49404B10CC4E} + EndGlobalSection +EndGlobal diff --git a/UEAESKeyFinder/Program.cs b/UEAESKeyFinder/Program.cs index 5081b7b..6b6d91e 100644 --- a/UEAESKeyFinder/Program.cs +++ b/UEAESKeyFinder/Program.cs @@ -1,196 +1,165 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using static Searcher; - -namespace UEAesKeyFinder -{ - class Program - { - [DllImport("ntdll.dll", PreserveSig = false)] - public static extern void NtSuspendProcess(IntPtr processHandle); - public static byte[] GetHex(string hex) - { - var r = new byte[hex.Length / 2]; - for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); - return r; - } - static void Main(string[] args) - { - Searcher searcher = new Searcher(); - Process game = new Process(); - - Console.Write("Please select from where you want to get the AES Key\n0: Memory\n1: File\n2: Dump File\n3. LibUE4.so File\n4. APK File\nUse: "); - - char method = (char)Console.Read(); - string path; - string EngineVersion = "4.18.0"; - string saveName = ""; - switch (method) - { - case '0': - Console.Write("Enter the name or id of the process: "); - Console.Read(); - Console.Read(); - string ProcessName = Console.ReadLine(); - - bool found = false; - foreach (Process p in Process.GetProcesses()) - { - if (p.ProcessName == ProcessName || p.Id.ToString() == ProcessName) - { - Console.WriteLine($"\nFound {p.ProcessName}"); - saveName = p.ProcessName; - searcher = new Searcher(p); - found = true; - break; - } - } - if (!found) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the process."); - Console.ReadLine(); - return; - } - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '1': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - game = new Process() { StartInfo = { FileName = path } }; - game.Start(); - Thread.Sleep(1000); - // Not required to fully load - NtSuspendProcess(game.Handle); - - searcher = new Searcher(game); - searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '2': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the dump file."); - return; - } - - saveName = path.Split("\\")[path.Split("\\").Length-1]; - - searcher = new Searcher(File.ReadAllBytes(path)); - searcher.SetFilePath(path); - EngineVersion = searcher.SearchEngineVersion(); - if (EngineVersion != "") - { - Console.WriteLine($"Engine Version: {EngineVersion}"); - } - break; - case '3': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the lib."); - return; - } - - searcher = new Searcher(File.ReadAllBytes(path), true); - break; - case '4': - Console.Write("Please enter the file path: "); - Console.Read(); - Console.Read(); - path = Console.ReadLine().Replace("\"", ""); - - saveName = path.Split("\\")[path.Split("\\").Length - 1]; - - if (!File.Exists(path)) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("Failed to find the apk."); - return; - } - searcher = new Searcher(File.ReadAllBytes(path), true, true); - break; - } - - Dictionary aesKeys = searcher.FindAllPattern(out long took); - - if (aesKeys.Count > 0) - { - string WriteToFile = ""; - string txt = aesKeys.Count == 1 ? $"Found {aesKeys.Count} AES Key in {took}ms\n" : $"Found {aesKeys.Count} AES Keys in {took}ms\n"; - - WriteToFile += txt; - - Console.ForegroundColor = ConsoleColor.Green; - Console.Write("\n" + txt); - Console.ForegroundColor = ConsoleColor.White; - int EngineVersionI = 17; - if (EngineVersion != "") EngineVersionI = Convert.ToInt32(EngineVersion.Split(".")[1]); - if (EngineVersionI < 18) - { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; - } - else - { - foreach (KeyValuePair o in aesKeys) - { - txt = $"{aesKeys[o.Key]} ({System.Convert.ToBase64String(GetHex(aesKeys[o.Key][2..aesKeys[o.Key].Length]))}) at {o.Key}\n"; - Console.Write(txt); - WriteToFile += txt; - }; - } - - File.WriteAllText(saveName + "_aes_keys.txt", WriteToFile); - } - else - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine("\nFailed to find any AES Keys."); - } - - if (method == '1') try { game.Kill(); } catch { }; - - Console.ReadLine(); - } - } -} \ No newline at end of file +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; + +namespace UEAesKeyFinder +{ + class Program + { + [DllImport("ntdll.dll", PreserveSig = false)] + public static extern void NtSuspendProcess(IntPtr processHandle); + + public static byte[] GetHex(string hex) + { + // Убираем 0x если он есть в начале + if (hex.StartsWith("0x")) hex = hex.Substring(2); + var r = new byte[hex.Length / 2]; + for (var i = 0; i < r.Length; i++) r[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); + return r; + } + + static void Main(string[] args) + { + Searcher searcher = new Searcher(); + Process game = new Process(); + + Console.WriteLine("=== Unreal Engine AES Key Finder ==="); + Console.Write("Select method:\n0: Memory (Running Process)\n1: File (Start & Suspend)\n2: Dump File\n3: Lib File (libUE4.so / libUnreal.so)\n4: APK File\nUse: "); + + // Используем ReadKey для более чистого ввода + char method = Console.ReadKey().KeyChar; + Console.WriteLine(); + + string path; + string EngineVersion = "4.18.0"; + string saveName = "result"; + + switch (method) + { + case '0': + Console.Write("Enter process name or ID: "); + string ProcessName = Console.ReadLine(); + bool found = false; + foreach (Process p in Process.GetProcesses()) + { + if (p.ProcessName.Equals(ProcessName, StringComparison.OrdinalIgnoreCase) || p.Id.ToString() == ProcessName) + { + Console.WriteLine($"\nFound: {p.ProcessName} (PID: {p.Id})"); + saveName = p.ProcessName; + searcher = new Searcher(p); + found = true; + break; + } + } + if (!found) { ErrorExit("Process not found."); return; } + break; + + case '1': + path = GetFilePath(); + saveName = Path.GetFileName(path); + game = new Process() { StartInfo = { FileName = path } }; + game.Start(); + Thread.Sleep(1500); // Даем немного времени на инициализацию + NtSuspendProcess(game.Handle); + searcher = new Searcher(game); + searcher.SetFilePath(path); + break; + + case '2': + path = GetFilePath(); + saveName = Path.GetFileName(path); + searcher = new Searcher(File.ReadAllBytes(path)); + searcher.SetFilePath(path); + break; + + case '3': // Теперь поддерживает и libUE4.so, и libUnreal.so + path = GetFilePath(); + saveName = Path.GetFileName(path); + searcher = new Searcher(File.ReadAllBytes(path), true); + break; + + case '4': // Анализ APK (найдет и libUE4, и libUnreal внутри) + path = GetFilePath(); + saveName = Path.GetFileName(path); + searcher = new Searcher(File.ReadAllBytes(path), true, true); + break; + + default: + ErrorExit("Invalid selection."); + return; + } + + // Пытаемся определить версию движка + EngineVersion = searcher.SearchEngineVersion(); + if (!string.IsNullOrEmpty(EngineVersion)) + Console.WriteLine($"Detected Engine Version: {EngineVersion}"); + + Console.WriteLine("Searching for patterns... please wait."); + Dictionary aesKeys = searcher.FindAllPattern(out long took); + + if (aesKeys.Count > 0) + { + ProcessResults(aesKeys, took, saveName, EngineVersion); + } + else + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("\n[!] No AES Keys found."); + } + + if (method == '1') try { game.Kill(); } catch { } + Console.ResetColor(); + Console.WriteLine("\nPress Enter to exit."); + Console.ReadLine(); + } + + static string GetFilePath() + { + Console.Write("Enter file path: "); + return Console.ReadLine().Trim(' ', '\"'); + } + + static void ErrorExit(string msg) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"\n[Error] {msg}"); + Console.ReadLine(); + } + + static void ProcessResults(Dictionary keys, long time, string name, string version) + { + string output = $"Found {keys.Count} key(s) in {time}ms\n\n"; + Console.ForegroundColor = ConsoleColor.Green; + Console.Write(output); + Console.ResetColor(); + + int verMajor = 18; // Default + if (!string.IsNullOrEmpty(version) && version.Contains(".")) + int.TryParse(version.Split('.')[1], out verMajor); + + foreach (var entry in keys) + { + string keyStr = entry.Value; + string base64 = ""; + + // Для новых версий (4.18+) добавляем Base64 представление ключа + if (verMajor >= 18 && keyStr.StartsWith("0x") && keyStr.Length > 10) + { + try { + base64 = $" (Base64: {Convert.ToBase64String(GetHex(keyStr))})"; + } catch { } + } + + string line = $"Offset: 0x{entry.Key:X} | Key: {keyStr}{base64}\n"; + Console.Write(line); + output += line; + } + + File.WriteAllText(name + "_aes_keys.txt", output); + Console.WriteLine($"\nResults saved to {name}_aes_keys.txt"); + } + } +} diff --git a/UEAESKeyFinder/Searcher.cs b/UEAESKeyFinder/Searcher.cs index 8282c69..c6694fc 100644 --- a/UEAESKeyFinder/Searcher.cs +++ b/UEAESKeyFinder/Searcher.cs @@ -1,356 +1,772 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Text.RegularExpressions; - -public class Searcher -{ - private const int PAGE_SIZE = 4000; - - private bool useUE4Lib = false; - - private IntPtr hProcess; - private Process Process; - private ulong AllocationBase; - private byte[] ProcessMemory; - private string FilePath; - - public Searcher() { } - - public Searcher(Process p) - { - Process = p; - hProcess = p.Handle; - AllocationBase = (ulong)p.MainModule.BaseAddress; - ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; - - // To best honest idk why some regions are 0 if we read all at once ¯\_(ツ)_/¯ - for (int i = 0; i < ProcessMemory.Length; i += 2048) - { - byte[] bytes = new byte[2048]; - Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, bytes, 2048); - for (int ii = 0; ii < bytes.Length; ii++) - { - if (!(i + ii >= ProcessMemory.Length)) ProcessMemory[i + ii] = bytes[ii]; - else break; - }; - } - } - public Searcher(byte[] bytes) - { - AllocationBase = 0; - ProcessMemory = bytes; - } - public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) - { - if (isAPK) - { - // Find "APK Sig Block" (Opening the whole file is bad...) - int libUE4Offset = 0; - for (int i = bytes.Length - 1; i > 0; i--) - { - if ( - bytes[i] != 0x41 || - bytes[i + 1] != 0x50 || - bytes[i + 1] != 0x50 || - bytes[i + 2] != 0x4B || - bytes[i + 3] != 0x20 || - bytes[i + 4] != 0x53 || - bytes[i + 5] != 0x69 || - bytes[i + 6] != 0x67 || - bytes[i + 7] != 0x20 || - bytes[i + 8] != 0x42 || - bytes[i + 9] != 0x6C || - bytes[i + 10] != 0x6F || - bytes[i + 11] != 0x63 || - bytes[i + 12] != 0x6B - ) continue; - - libUE4Offset = i; - break; - } - - byte[] libUE4 = new byte[] { 0x6C, 0x69, 0x62, 0x2F, 0x61, 0x72, 0x6D, 0x36, 0x34, 0x2D, 0x76, 0x38, 0x61, 0x2F, 0x6C, 0x69, 0x62, 0x55, 0x45, 0x34, 0x2E, 0x73, 0x6F }; - for (int i = libUE4Offset; i < bytes.Length - 1 - libUE4.Length - 1; i++) - { - if (bytes[i] != libUE4[0]) continue; - bool c = false; - for (int ii = 0; ii < libUE4.Length - 1; ii++) - { - if (bytes[ii + i] != libUE4[ii]) - { - c = true; - break; - }; - }; - if (c) continue; - - libUE4Offset = BitConverter.ToInt32(bytes[(i - 4)..(i)]); - } - if (libUE4Offset == 0) throw new Exception("Failed to read LibUE4.so, patterns were not found!"); - - // Read compressed/uncompressed size from the header and then skip it - int compressed = BitConverter.ToInt32(bytes[(libUE4Offset + 18)..(libUE4Offset + 22)]); - int uncompressed = BitConverter.ToInt32(bytes[(libUE4Offset + 22)..(libUE4Offset + 26)]); - libUE4Offset = libUE4Offset + 53; // Header size is hardcoded, but why would it ever change? - - MemoryStream uncompressedLibUE4 = new MemoryStream(); - DeflateStream deflated = new DeflateStream(new MemoryStream(bytes[(libUE4Offset)..(libUE4Offset + compressed)]), CompressionMode.Decompress); - deflated.CopyTo(uncompressedLibUE4); - if (uncompressedLibUE4.Length != uncompressed) throw new Exception("Failed to read LibUE4.so, decompressed size does not match the decompressed size from the header!"); - ProcessMemory = uncompressedLibUE4.ToArray(); - } - else - { - ProcessMemory = bytes; - } - - useUE4Lib = useAndroid; - } - public void SetFilePath(string path) { FilePath = path; } - public string SearchEngineVersion() - { - if (FilePath != null) return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; - - // We search backwards because its mostly at the end - byte[] ProductVersion = new byte[] { 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00 }; - for (int i = ProcessMemory.Length - 1; i > 0; i--) - { - if (this.ProcessMemory[i] != ProductVersion[0]) continue; - bool c = false; - for (int ii = 0; ii < ProductVersion.Length - 1; ii++) - { - if (this.ProcessMemory[ii + i] != ProductVersion[ii]) - { - c = true; - break; - }; - }; - if (c) continue; - - UnicodeEncoding unicodeEncoding = new UnicodeEncoding(); - return new string(unicodeEncoding.GetChars(this.ProcessMemory[(i + ProductVersion.Length - 2)..(i + ProductVersion.Length - 2 + 14)], 2, 12)); - } - - return ""; - } - public int FollowJMP(int addr) - { - addr = (BitConverter.ToInt32(this.ProcessMemory[(addr + 1)..(addr + 1 + 4)].ToArray()) + 5) + addr; - if ((this.ProcessMemory[addr] == 0x0F && this.ProcessMemory[addr + 4] == 0xE9)) return FollowJMP(addr + 4); - - return addr; - } - public UInt64 DecodeADRP(int adrp) // https://chromium.googlesource.com/chromiumos/third_party/binutils/+/refs/heads/stabilize-7374.B/gold/aarch64.cc#150 - { - const int mask19 = (1 << 19) - 1; - const int mask2 = 3; - - // 21-bit imm encoded in adrp. - int imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2); - // Retrieve msb of 21-bit-signed imm for sign extension. - int msbt = (imm >> 20) & 1; - // Real value is imm multipled by 4k. Value now has 33-bit information. - int value = imm << 12; - // Sign extend to 64-bit by repeating msbt 31 (64-33) times and merge it - // with value. - return (UInt64)(((((int)(1) << 32) - msbt) << 33) | value); - } - public UInt64 DecodeADD(int add) - { - var imm12 = (add & 0x3ffc00) >> 10; - if ((imm12 & 0xc00000) != 0) imm12 <<= 12; - return (UInt64)imm12; - } - public int GetADRLAddress(int ADRPLoc) - { - UInt64 ADRP = DecodeADRP(BitConverter.ToInt32(this.ProcessMemory, ADRPLoc)); - UInt64 ADD = DecodeADD(BitConverter.ToInt32(this.ProcessMemory, ADRPLoc + 4)); - - return (int)((((UInt64)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); - } - public Dictionary FindAllPattern(out long t) - { - Stopwatch timer = Stopwatch.StartNew(); - Dictionary offsets = new Dictionary(); - - // Android - if (useUE4Lib) - { - string aesKey = ""; - // We could (should) use a function to match the pattern but idk (lazy)... - for (int i = 0; i < ProcessMemory.Length - 10; i++) - { - // if this gets no results (or too many) for some reason we could also get the addr that calls this... - - // 01 01 40 AD 01 00 00 AD C0 03 5F D6 - - // First instruction is the adrp, then add... - - // Second instruction - if (this.ProcessMemory[i] != 0x01) continue; - if (this.ProcessMemory[i + 1] != 0x01) continue; - if (this.ProcessMemory[i + 2] != 0x40) continue; - if (this.ProcessMemory[i + 3] != 0xAD) continue; - - // Third instruction - if (this.ProcessMemory[i + 4] != 0x01) continue; - if (this.ProcessMemory[i + 5] != 0x00) continue; - if (this.ProcessMemory[i + 6] != 0x00) continue; - if (this.ProcessMemory[i + 7] != 0xAD) continue; - - // Fourth instruction - if (this.ProcessMemory[i + 8] != 0xC0) continue; - if (this.ProcessMemory[i + 9] != 0x03) continue; - if (this.ProcessMemory[i + 10] != 0x5F) continue; - if (this.ProcessMemory[i + 11] != 0xD6) continue; - - aesKey = ""; - int aesKeyAddr = GetADRLAddress(i - 8); - - aesKey += BitConverter.ToString(this.ProcessMemory[aesKeyAddr..(aesKeyAddr + 32)]).ToString().Replace("-", ""); - offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); - - aesKeyAddr += 0x1000; // Please fix this, idk when its + 0x1000 and when not.... - aesKey = BitConverter.ToString(this.ProcessMemory[aesKeyAddr..(aesKeyAddr + 32)]).ToString().Replace("-", ""); - offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); - } - } - else - { - string EngineVersionStr = SearchEngineVersion(); - int EngineVersion = 17; - if (EngineVersionStr != "") EngineVersion = Convert.ToInt32(EngineVersionStr.Split(".")[1]); - if (EngineVersion < 18) - { - // Let's just try something, not sure if that works for all games - string aesKey = ""; - - for (int i = 0; i < ProcessMemory.Length-10; i++) - { - if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue; - - // Now we need to find where the first 00 starts and go back to that location - int start = i; - while (ProcessMemory[start-1] == 0x00) start -= 1; - - // Key is 64 letters long, lets make sure the first byte before the key is 0x00 - if (ProcessMemory[start - 65] != 0x00) continue; - - aesKey = Encoding.Default.GetString(ProcessMemory[(start - 64)..start]); - - // Lets make sure the key is as valid string - if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$")) - { - offsets.Add(AllocationBase + (ulong)start-64, aesKey); - break; - } - } - } - - { - // Based on "?Callback@FEncryptionKeyRegistration@@SAXQEAE@Z" - // https://github.com/EpicGames/UnrealEngine/blob/5df54b7ef1714f28fb5da319c3e83d96f0bedf08/Engine/Source/Runtime/Core/Public/Modules/ModuleManager.h#L841 - - // Should work for all newer Fortnite versions - // C7 45 D0 ? ? ? ? C7 45 D4 ? ? ? ? C7 45 D8 ? ? ? ? C7 45 DC ? ? ? ? ? ? ? ? C7 45 E0 ? ? ? ? C7 45 E4? ? ? ? C7 45 E8 ? ? ? ? C7 45 EC ? ? ? ? - - string aesKey = ""; - int verify_1 = 0xC7; - for (int i = 0; i < ProcessMemory.Length - 10; i++) - { - try - { - // Should start with smth like 48 8D 64 24 08 and end with it - if (this.ProcessMemory[i - 3] == 0x00 && this.ProcessMemory[i - 2] == 0x00 && this.ProcessMemory[i - 1] == 0x00) continue; - if (this.ProcessMemory[i] != verify_1 || (this.ProcessMemory[i + 1] != 0x45 && this.ProcessMemory[i + 1] != 0x01)) continue; - int verify_2 = this.ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45; - int verify_3 = this.ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0; - if (this.ProcessMemory[i + 1] == 0x45 && this.ProcessMemory[i + 2] != verify_3) continue; - - // It should be the first keypart - if (this.ProcessMemory[i - 7] == verify_1 && this.ProcessMemory[i - 6] == verify_2) continue; - - verify_3 += 0x04; - // Make sure this address is valid (Not following jumps yet) fuck it, lets also check the jmps - bool c = false; - int addr = i + 4 + 2 + (this.ProcessMemory[i + 1] == 0x01 ? 0 : 1); - aesKey = BitConverter.ToString(this.ProcessMemory[(addr - 4)..addr]).Replace("-", ""); // New valid start, new luck - - while (aesKey.Length != 64) - // 8 parts, we have to skip the instruction with the size of 2-3 and the key itself with the size of 4 - // older versions have a simple mov rcx, but never have mov rcx+4 - { - if (this.ProcessMemory[addr] != verify_1 && this.ProcessMemory[addr] != 0xE9) // Same for all UE4 games - { - // Sometimes one keypart has 4 useless bytes at the end, just skip it if the 3 bytes after it match the new keypart start - // JMP Right after it is possible too - if (this.ProcessMemory[addr] == 0x0F && this.ProcessMemory[addr + 4] == 0xE9) - { - addr += 4; // Skip the useless bytes - // jump to the address and check if the bytes are valid - addr = FollowJMP(addr); - if (this.ProcessMemory[addr] != verify_1 && this.ProcessMemory[addr + 1] != verify_2 && this.ProcessMemory[addr + 2] != verify_3) c = true; - } - else if (this.ProcessMemory[addr + 4] != verify_1 && this.ProcessMemory[addr + 5] != verify_2 && this.ProcessMemory[addr + 6] != verify_3) c = true; - else addr += 4; - }; - - if (this.ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr); - else - { - if (this.ProcessMemory[addr + 1] != verify_2) c = true; - if ((this.ProcessMemory[addr + 2] != verify_3)) c = true; - aesKey = aesKey + BitConverter.ToString(this.ProcessMemory[(addr + 3)..(addr + 7)]).Replace("-", ""); - addr += 4 + 3; // C7 4x xx - verify_3 += 0x04; - }; - - if (aesKey.Length == 64) - { - // This is the last, we should not be able to get another keypart, if so this is not the correct AES Keys. - // if (this.ProcessMemory[addr] == verify_1 && this.ProcessMemory[addr + 1] == verify_2) c = true; - if (this.ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr); - // && this.ProcessMemory[addr + 1] != 0x8D && this.ProcessMemory[addr + 1] != 0x64 && this.ProcessMemory[addr + 1] != 0x24 && this.ProcessMemory[addr + 1] != 0x08 - if (this.ProcessMemory[addr] != 0xC3 && this.ProcessMemory[addr] != 0x48) - { - // There might be movups so lets check 50 bytes if we still get 48 8D - if (this.ProcessMemory[addr] != 0x0F) c = true; - for (int xx = 0; xx < 30; xx++) - { - addr = addr + xx; - if (this.ProcessMemory[addr] == 0x48 && this.ProcessMemory[addr] == 0x8D) break; - } - // We should probably delete this... - if (this.ProcessMemory[addr] != 0x48 && this.ProcessMemory[addr] == 0x8D) c = true; - } - } - if (c) break; - } - if (c) continue; - - offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}"); - } - catch { } - } - } - } - - t = timer.ElapsedMilliseconds; - return offsets; - } - - public static class Win32 - { - [DllImport("kernel32.dll")] - public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); - - [DllImport("kernel32.dll")] - public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); - } -} \ No newline at end of file +/** +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; + +public class Searcher +{ + private const int PAGE_SIZE = 4000; + + private bool useUE4Lib = false; + + private IntPtr hProcess; + private Process Process; + private ulong AllocationBase; + private byte[] ProcessMemory; + private string FilePath; + + public Searcher() { } + + public Searcher(Process p) + { + Process = p; + hProcess = p.Handle; + AllocationBase = (ulong)p.MainModule.BaseAddress; + ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; + + // To best honest idk why some regions are 0 if we read all at once ¯\_(ツ)_/¯ + for (int i = 0; i < ProcessMemory.Length; i += 2048) + { + byte[] bytes = new byte[2048]; + Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, bytes, 2048); + for (int ii = 0; ii < bytes.Length; ii++) + { + if (!(i + ii >= ProcessMemory.Length)) ProcessMemory[i + ii] = bytes[ii]; + else break; + }; + } + } + public Searcher(byte[] bytes) + { + AllocationBase = 0; + ProcessMemory = bytes; + } + public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) + { + if (isAPK) + { + // Find "APK Sig Block" (Opening the whole file is bad...) + int libUE4Offset = 0; + for (int i = bytes.Length - 1; i > 0; i--) + { + if ( + bytes[i] != 0x41 || + bytes[i + 1] != 0x50 || + bytes[i + 1] != 0x50 || + bytes[i + 2] != 0x4B || + bytes[i + 3] != 0x20 || + bytes[i + 4] != 0x53 || + bytes[i + 5] != 0x69 || + bytes[i + 6] != 0x67 || + bytes[i + 7] != 0x20 || + bytes[i + 8] != 0x42 || + bytes[i + 9] != 0x6C || + bytes[i + 10] != 0x6F || + bytes[i + 11] != 0x63 || + bytes[i + 12] != 0x6B + ) continue; + + libUE4Offset = i; + break; + } + + byte[] libUE4 = new byte[] { 0x6C, 0x69, 0x62, 0x2F, 0x61, 0x72, 0x6D, 0x36, 0x34, 0x2D, 0x76, 0x38, 0x61, 0x2F, 0x6C, 0x69, 0x62, 0x55, 0x45, 0x34, 0x2E, 0x73, 0x6F }; + for (int i = libUE4Offset; i < bytes.Length - 1 - libUE4.Length - 1; i++) + { + if (bytes[i] != libUE4[0]) continue; + bool c = false; + for (int ii = 0; ii < libUE4.Length - 1; ii++) + { + if (bytes[ii + i] != libUE4[ii]) + { + c = true; + break; + }; + }; + if (c) continue; + + libUE4Offset = BitConverter.ToInt32(bytes[(i - 4)..(i)]); + } + if (libUE4Offset == 0) throw new Exception("Failed to read LibUE4.so, patterns were not found!"); + + // Read compressed/uncompressed size from the header and then skip it + int compressed = BitConverter.ToInt32(bytes[(libUE4Offset + 18)..(libUE4Offset + 22)]); + int uncompressed = BitConverter.ToInt32(bytes[(libUE4Offset + 22)..(libUE4Offset + 26)]); + libUE4Offset = libUE4Offset + 53; // Header size is hardcoded, but why would it ever change? + + MemoryStream uncompressedLibUE4 = new MemoryStream(); + DeflateStream deflated = new DeflateStream(new MemoryStream(bytes[(libUE4Offset)..(libUE4Offset + compressed)]), CompressionMode.Decompress); + deflated.CopyTo(uncompressedLibUE4); + if (uncompressedLibUE4.Length != uncompressed) throw new Exception("Failed to read LibUE4.so, decompressed size does not match the decompressed size from the header!"); + ProcessMemory = uncompressedLibUE4.ToArray(); + } + else + { + ProcessMemory = bytes; + } + + useUE4Lib = useAndroid; + } + public void SetFilePath(string path) { FilePath = path; } + public string SearchEngineVersion() + { + if (FilePath != null) return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; + + // We search backwards because its mostly at the end + byte[] ProductVersion = new byte[] { 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00 }; + for (int i = ProcessMemory.Length - 1; i > 0; i--) + { + if (this.ProcessMemory[i] != ProductVersion[0]) continue; + bool c = false; + for (int ii = 0; ii < ProductVersion.Length - 1; ii++) + { + if (this.ProcessMemory[ii + i] != ProductVersion[ii]) + { + c = true; + break; + }; + }; + if (c) continue; + + UnicodeEncoding unicodeEncoding = new UnicodeEncoding(); + return new string(unicodeEncoding.GetChars(this.ProcessMemory[(i + ProductVersion.Length - 2)..(i + ProductVersion.Length - 2 + 14)], 2, 12)); + } + + return ""; + } + public int FollowJMP(int addr) + { + addr = (BitConverter.ToInt32(this.ProcessMemory[(addr + 1)..(addr + 1 + 4)].ToArray()) + 5) + addr; + if ((this.ProcessMemory[addr] == 0x0F && this.ProcessMemory[addr + 4] == 0xE9)) return FollowJMP(addr + 4); + + return addr; + } + public UInt64 DecodeADRP(int adrp) // https://chromium.googlesource.com/chromiumos/third_party/binutils/+/refs/heads/stabilize-7374.B/gold/aarch64.cc#150 + { + const int mask19 = (1 << 19) - 1; + const int mask2 = 3; + + // 21-bit imm encoded in adrp. + int imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2); + // Retrieve msb of 21-bit-signed imm for sign extension. + int msbt = (imm >> 20) & 1; + // Real value is imm multipled by 4k. Value now has 33-bit information. + int value = imm << 12; + // Sign extend to 64-bit by repeating msbt 31 (64-33) times and merge it + // with value. + return (UInt64)(((((int)(1) << 32) - msbt) << 33) | value); + } + public UInt64 DecodeADD(int add) + { + var imm12 = (add & 0x3ffc00) >> 10; + if ((imm12 & 0xc00000) != 0) imm12 <<= 12; + return (UInt64)imm12; + } + public int GetADRLAddress(int ADRPLoc) + { + UInt64 ADRP = DecodeADRP(BitConverter.ToInt32(this.ProcessMemory, ADRPLoc)); + UInt64 ADD = DecodeADD(BitConverter.ToInt32(this.ProcessMemory, ADRPLoc + 4)); + + return (int)((((UInt64)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); + } + public Dictionary FindAllPattern(out long t) + { + Stopwatch timer = Stopwatch.StartNew(); + Dictionary offsets = new Dictionary(); + + // Android + if (useUE4Lib) + { + string aesKey = ""; + // We could (should) use a function to match the pattern but idk (lazy)... + for (int i = 0; i < ProcessMemory.Length - 10; i++) + { + // if this gets no results (or too many) for some reason we could also get the addr that calls this... + + // 01 01 40 AD 01 00 00 AD C0 03 5F D6 + + // First instruction is the adrp, then add... + + // Second instruction + if (this.ProcessMemory[i] != 0x01) continue; + if (this.ProcessMemory[i + 1] != 0x01) continue; + if (this.ProcessMemory[i + 2] != 0x40) continue; + if (this.ProcessMemory[i + 3] != 0xAD) continue; + + // Third instruction + if (this.ProcessMemory[i + 4] != 0x01) continue; + if (this.ProcessMemory[i + 5] != 0x00) continue; + if (this.ProcessMemory[i + 6] != 0x00) continue; + if (this.ProcessMemory[i + 7] != 0xAD) continue; + + // Fourth instruction + if (this.ProcessMemory[i + 8] != 0xC0) continue; + if (this.ProcessMemory[i + 9] != 0x03) continue; + if (this.ProcessMemory[i + 10] != 0x5F) continue; + if (this.ProcessMemory[i + 11] != 0xD6) continue; + + aesKey = ""; + int aesKeyAddr = GetADRLAddress(i - 8); + + aesKey += BitConverter.ToString(this.ProcessMemory[aesKeyAddr..(aesKeyAddr + 32)]).ToString().Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); + + aesKeyAddr += 0x1000; // Please fix this, idk when its + 0x1000 and when not.... + aesKey = BitConverter.ToString(this.ProcessMemory[aesKeyAddr..(aesKeyAddr + 32)]).ToString().Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); + } + } + else + { + string EngineVersionStr = SearchEngineVersion(); + int EngineVersion = 17; + if (EngineVersionStr != "") EngineVersion = Convert.ToInt32(EngineVersionStr.Split(".")[1]); + if (EngineVersion < 18) + { + // Let's just try something, not sure if that works for all games + string aesKey = ""; + + for (int i = 0; i < ProcessMemory.Length-10; i++) + { + if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue; + + // Now we need to find where the first 00 starts and go back to that location + int start = i; + while (ProcessMemory[start-1] == 0x00) start -= 1; + + // Key is 64 letters long, lets make sure the first byte before the key is 0x00 + if (ProcessMemory[start - 65] != 0x00) continue; + + aesKey = Encoding.Default.GetString(ProcessMemory[(start - 64)..start]); + + // Lets make sure the key is as valid string + if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$")) + { + offsets.Add(AllocationBase + (ulong)start-64, aesKey); + break; + } + } + } + + { + // Based on "?Callback@FEncryptionKeyRegistration@@SAXQEAE@Z" + // https://github.com/EpicGames/UnrealEngine/blob/5df54b7ef1714f28fb5da319c3e83d96f0bedf08/Engine/Source/Runtime/Core/Public/Modules/ModuleManager.h#L841 + + // Should work for all newer Fortnite versions + // C7 45 D0 ? ? ? ? C7 45 D4 ? ? ? ? C7 45 D8 ? ? ? ? C7 45 DC ? ? ? ? ? ? ? ? C7 45 E0 ? ? ? ? C7 45 E4? ? ? ? C7 45 E8 ? ? ? ? C7 45 EC ? ? ? ? + + string aesKey = ""; + int verify_1 = 0xC7; + for (int i = 0; i < ProcessMemory.Length - 10; i++) + { + try + { + // Should start with smth like 48 8D 64 24 08 and end with it + if (this.ProcessMemory[i - 3] == 0x00 && this.ProcessMemory[i - 2] == 0x00 && this.ProcessMemory[i - 1] == 0x00) continue; + if (this.ProcessMemory[i] != verify_1 || (this.ProcessMemory[i + 1] != 0x45 && this.ProcessMemory[i + 1] != 0x01)) continue; + int verify_2 = this.ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45; + int verify_3 = this.ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0; + if (this.ProcessMemory[i + 1] == 0x45 && this.ProcessMemory[i + 2] != verify_3) continue; + + // It should be the first keypart + if (this.ProcessMemory[i - 7] == verify_1 && this.ProcessMemory[i - 6] == verify_2) continue; + + verify_3 += 0x04; + // Make sure this address is valid (Not following jumps yet) fuck it, lets also check the jmps + bool c = false; + int addr = i + 4 + 2 + (this.ProcessMemory[i + 1] == 0x01 ? 0 : 1); + aesKey = BitConverter.ToString(this.ProcessMemory[(addr - 4)..addr]).Replace("-", ""); // New valid start, new luck + + while (aesKey.Length != 64) + // 8 parts, we have to skip the instruction with the size of 2-3 and the key itself with the size of 4 + // older versions have a simple mov rcx, but never have mov rcx+4 + { + if (this.ProcessMemory[addr] != verify_1 && this.ProcessMemory[addr] != 0xE9) // Same for all UE4 games + { + // Sometimes one keypart has 4 useless bytes at the end, just skip it if the 3 bytes after it match the new keypart start + // JMP Right after it is possible too + if (this.ProcessMemory[addr] == 0x0F && this.ProcessMemory[addr + 4] == 0xE9) + { + addr += 4; // Skip the useless bytes + // jump to the address and check if the bytes are valid + addr = FollowJMP(addr); + if (this.ProcessMemory[addr] != verify_1 && this.ProcessMemory[addr + 1] != verify_2 && this.ProcessMemory[addr + 2] != verify_3) c = true; + } + else if (this.ProcessMemory[addr + 4] != verify_1 && this.ProcessMemory[addr + 5] != verify_2 && this.ProcessMemory[addr + 6] != verify_3) c = true; + else addr += 4; + }; + + if (this.ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr); + else + { + if (this.ProcessMemory[addr + 1] != verify_2) c = true; + if ((this.ProcessMemory[addr + 2] != verify_3)) c = true; + aesKey = aesKey + BitConverter.ToString(this.ProcessMemory[(addr + 3)..(addr + 7)]).Replace("-", ""); + addr += 4 + 3; // C7 4x xx + verify_3 += 0x04; + }; + + if (aesKey.Length == 64) + { + // This is the last, we should not be able to get another keypart, if so this is not the correct AES Keys. + // if (this.ProcessMemory[addr] == verify_1 && this.ProcessMemory[addr + 1] == verify_2) c = true; + if (this.ProcessMemory[addr] == 0xE9) addr = FollowJMP(addr); + // && this.ProcessMemory[addr + 1] != 0x8D && this.ProcessMemory[addr + 1] != 0x64 && this.ProcessMemory[addr + 1] != 0x24 && this.ProcessMemory[addr + 1] != 0x08 + if (this.ProcessMemory[addr] != 0xC3 && this.ProcessMemory[addr] != 0x48) + { + // There might be movups so lets check 50 bytes if we still get 48 8D + if (this.ProcessMemory[addr] != 0x0F) c = true; + for (int xx = 0; xx < 30; xx++) + { + addr = addr + xx; + if (this.ProcessMemory[addr] == 0x48 && this.ProcessMemory[addr] == 0x8D) break; + } + // We should probably delete this... + if (this.ProcessMemory[addr] != 0x48 && this.ProcessMemory[addr] == 0x8D) c = true; + } + } + if (c) break; + } + if (c) continue; + + offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}"); + } + catch { } + } + } + } + + t = timer.ElapsedMilliseconds; + return offsets; + } + + public static class Win32 + { + [DllImport("kernel32.dll")] + public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); + + [DllImport("kernel32.dll")] + public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); + } + +}*/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Text.RegularExpressions; +using System.Runtime.InteropServices; + +public class Searcher +{ + private const int PAGE_SIZE = 4000; + + private bool useUE4Lib = false; + + private IntPtr hProcess; + private Process Process; + private ulong AllocationBase; + private byte[] ProcessMemory; + private string FilePath; + + public Searcher() { } + + public Searcher(Process p) + { + Process = p; + hProcess = p.Handle; + AllocationBase = (ulong)p.MainModule.BaseAddress; + ProcessMemory = new byte[p.MainModule.ModuleMemorySize]; + + // Читаем память кусками по 2048 байт (могут быть пустые регионы, поэтому по частям) + for (int i = 0; i < ProcessMemory.Length; i += 2048) + { + int bytesToRead = Math.Min(2048, ProcessMemory.Length - i); + byte[] bytes = new byte[bytesToRead]; + if (Win32.ReadProcessMemory(hProcess, AllocationBase + (ulong)i, bytes, bytesToRead)) + { + Array.Copy(bytes, 0, ProcessMemory, i, bytesToRead); + } + else + { + // Если чтение не удалось — просто продолжаем, можно логировать при необходимости + } + } + } + + public Searcher(byte[] bytes) + { + AllocationBase = 0; + ProcessMemory = bytes; + } + + public Searcher(byte[] bytes, bool useAndroid, bool isAPK = false) +{ + if (isAPK) + { + // 1. Locate the "APK Sig Block" (APK Signature Scheme block) + int sigBlockOffset = 0; + byte[] apkSigBlock = Encoding.ASCII.GetBytes("APK Sig Block"); + + for (int i = bytes.Length - apkSigBlock.Length - 1; i >= 0; i--) + { + bool matched = true; + for (int j = 0; j < apkSigBlock.Length; j++) + { + if (bytes[i + j] != apkSigBlock[j]) + { + matched = false; + break; + } + } + if (matched) + { + sigBlockOffset = i; + break; + } + } + + if (sigBlockOffset == 0) + throw new Exception("Failed to read APK: APK Sig Block not found!"); + + // 2. Universal search for the Engine Library (UE4 or UE5/Unreal) + // We check both naming conventions used by the engine + string[] targetLibs = { "lib/arm64-v8a/libUE4.so", "lib/arm64-v8a/libUnreal.so" }; + int foundOffset = 0; + + foreach (string libName in targetLibs) + { + byte[] pattern = Encoding.ASCII.GetBytes(libName); + + // Search for the library path in the Central Directory (located after the Sig Block) + for (int i = sigBlockOffset; i < bytes.Length - pattern.Length - 4; i++) + { + bool matched = true; + for (int ii = 0; ii < pattern.Length; ii++) + { + if (bytes[i + ii] != pattern[ii]) + { + matched = false; + break; + } + } + + if (matched) + { + // Read the relative offset of the Local File Header (4 bytes before the path string) + foundOffset = BitConverter.ToInt32(bytes, i - 4); + break; + } + } + if (foundOffset != 0) break; // Exit loop if a library is found + } + + if (foundOffset == 0) + throw new Exception("Engine library (libUE4.so or libUnreal.so) not found in APK!"); + + // 3. Dynamic Calculation of Data Start (Local File Header parsing) + // ZIP Format: Header(30 bytes) + FileNameLength(2 bytes) + ExtraFieldLength(2 bytes) + int compressed = BitConverter.ToInt32(bytes, foundOffset + 18); + int uncompressed = BitConverter.ToInt32(bytes, foundOffset + 22); + short fileNameLen = BitConverter.ToInt16(bytes, foundOffset + 26); + short extraLen = BitConverter.ToInt16(bytes, foundOffset + 28); + + // Accurate start of the compressed data stream + int dataStart = foundOffset + 30 + fileNameLen + extraLen; + + + + using (var compressedStream = new MemoryStream(bytes, dataStart, compressed)) + using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress)) + using (var uncompressedLib = new MemoryStream()) + { + deflateStream.CopyTo(uncompressedLib); + + if (uncompressedLib.Length != uncompressed) + throw new Exception("Decompression failed: Size mismatch!"); + + ProcessMemory = uncompressedLib.ToArray(); + } + } + else + { + ProcessMemory = bytes; + } + + useUE4Lib = useAndroid; +} + + public void SetFilePath(string path) => FilePath = path; + + public string SearchEngineVersion() + { + if (FilePath != null) + return FileVersionInfo.GetVersionInfo(FilePath).FileVersion; + + // Паттерн ProductVersion в юникоде (бэкап) + byte[] ProductVersion = new byte[] + { + 0x01, 0x00, 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, + 0x64, 0x00, 0x75, 0x00, 0x63, 0x00, 0x74, 0x00, + 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x73, 0x00, + 0x69, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x00, 0x00 + }; + + for (int i = ProcessMemory.Length - ProductVersion.Length - 1; i >= 0; i--) + { + bool matched = true; + for (int j = 0; j < ProductVersion.Length; j++) + { + if (ProcessMemory[i + j] != ProductVersion[j]) + { + matched = false; + break; + } + } + if (!matched) continue; + + var unicodeEncoding = new UnicodeEncoding(); + return unicodeEncoding.GetString(ProcessMemory, i + ProductVersion.Length - 2, 12); // 6 символов юникода (12 байт) + } + + return ""; + } + + public int FollowJMP(int addr) + { + int offset = BitConverter.ToInt32(ProcessMemory, addr + 1); + int newAddr = addr + offset + 5; + if (ProcessMemory[newAddr] == 0x0F && ProcessMemory[newAddr + 4] == 0xE9) + return FollowJMP(newAddr + 4); + return newAddr; + } + + public ulong DecodeADRP(int adrp) + { + const int mask19 = (1 << 19) - 1; + const int mask2 = 3; + + int imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2); + int msbt = (imm >> 20) & 1; + int value = imm << 12; + + // Поддержка знакового расширения для 64-битного значения + long signedValue = (long)value; + if (msbt == 1) + signedValue |= -1L << 33; + + return (ulong)signedValue; + } + + public ulong DecodeADD(int add) + { + var imm12 = (add & 0x3ffc00) >> 10; + if ((imm12 & 0xc00000) != 0) + imm12 <<= 12; + return (ulong)imm12; + } + + public int GetADRLAddress(int ADRPLoc) + { + ulong ADRP = DecodeADRP(BitConverter.ToInt32(ProcessMemory, ADRPLoc)); + ulong ADD = DecodeADD(BitConverter.ToInt32(ProcessMemory, ADRPLoc + 4)); + + return (int)((((ulong)ADRPLoc & 0xFFFFF000) + ADRP + ADD) & 0xFFFFFFFF); + } + + public Dictionary FindAllPattern(out long elapsedMilliseconds) + { + Stopwatch timer = Stopwatch.StartNew(); + var offsets = new Dictionary(); + + if (useUE4Lib) + { + // Android UE4 key search pattern + for (int i = 0; i < ProcessMemory.Length - 12; i++) + { + if (ProcessMemory[i] != 0x01) continue; + if (ProcessMemory[i + 1] != 0x01) continue; + if (ProcessMemory[i + 2] != 0x40) continue; + if (ProcessMemory[i + 3] != 0xAD) continue; + if (ProcessMemory[i + 4] != 0x01) continue; + if (ProcessMemory[i + 5] != 0x00) continue; + if (ProcessMemory[i + 6] != 0x00) continue; + if (ProcessMemory[i + 7] != 0xAD) continue; + if (ProcessMemory[i + 8] != 0xC0) continue; + if (ProcessMemory[i + 9] != 0x03) continue; + if (ProcessMemory[i + 10] != 0x5F) continue; + if (ProcessMemory[i + 11] != 0xD6) continue; + + int aesKeyAddr = GetADRLAddress(i - 8); + if (aesKeyAddr < 0 || aesKeyAddr + 32 > ProcessMemory.Length) continue; + + string aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); + + aesKeyAddr += 0x1000; // Необходимо проверить корректность смещения +0x1000 для разных игр + if (aesKeyAddr + 32 > ProcessMemory.Length) continue; + + aesKey = BitConverter.ToString(ProcessMemory, aesKeyAddr, 32).Replace("-", ""); + offsets.Add(AllocationBase + (ulong)aesKeyAddr, $"0x{aesKey}"); + } + } + else + { + string EngineVersionStr = SearchEngineVersion(); + int EngineVersion = 17; + if (!string.IsNullOrEmpty(EngineVersionStr)) + { + string[] parts = EngineVersionStr.Split('.'); + if (parts.Length > 1 && int.TryParse(parts[1], out int ver)) + EngineVersion = ver; + } + + if (EngineVersion < 18) + { + // Старый способ поиска ключа + for (int i = 0; i < ProcessMemory.Length - 10; i++) + { + if (ProcessMemory[i] != 0x00 || ProcessMemory[i + 1] != 0x30 || ProcessMemory[i + 2] != 0x78) continue; + + int start = i; + while (start > 0 && ProcessMemory[start - 1] == 0x00) + start--; + + if (start - 65 < 0 || ProcessMemory[start - 65] != 0x00) continue; + + string aesKey = Encoding.Default.GetString(ProcessMemory, start - 64, 64); + + if (Regex.IsMatch(aesKey, @"^[a-zA-Z0-9]+$")) + { + offsets.Add(AllocationBase + (ulong)(start - 64), aesKey); + break; + } + } + } + + // Новый способ поиска ключа (например, для Fortnite и новых версий UE4) + int verify_1 = 0xC7; + for (int i = 0; i < ProcessMemory.Length - 10; i++) + { + try + { + if (i < 3) continue; + + if (ProcessMemory[i - 3] == 0x00 && ProcessMemory[i - 2] == 0x00 && ProcessMemory[i - 1] == 0x00) + continue; + + if (ProcessMemory[i] != verify_1 || (ProcessMemory[i + 1] != 0x45 && ProcessMemory[i + 1] != 0x01)) + continue; + + int verify_2 = ProcessMemory[i + 1] == 0x01 ? 0x41 : 0x45; + int verify_3 = ProcessMemory[i + 1] == 0x01 ? 0 : 0xD0; + + if (ProcessMemory[i + 1] == 0x45 && ProcessMemory[i + 2] != verify_3) + continue; + + if (ProcessMemory[i - 7] == verify_1 && ProcessMemory[i - 6] == verify_2) + continue; + + verify_3 += 0x04; + bool invalid = false; + int addr = i + 4 + 2 + (ProcessMemory[i + 1] == 0x01 ? 0 : 1); + string aesKey = BitConverter.ToString(ProcessMemory, addr - 4, 4).Replace("-", ""); + + while (aesKey.Length != 64) + { + if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr] != 0xE9) + { + if (ProcessMemory[addr] == 0x0F && ProcessMemory[addr + 4] == 0xE9) + { + addr += 4; + addr = FollowJMP(addr); + if (ProcessMemory[addr] != verify_1 && ProcessMemory[addr + 1] != verify_2 && ProcessMemory[addr + 2] != verify_3) + invalid = true; + } + else if (ProcessMemory[addr + 4] != verify_1 && ProcessMemory[addr + 5] != verify_2 && ProcessMemory[addr + 6] != verify_3) + { + invalid = true; + } + else + { + addr += 4; + } + } + + if (ProcessMemory[addr] == 0xE9) + addr = FollowJMP(addr); + else + { + if (ProcessMemory[addr + 1] != verify_2 || ProcessMemory[addr + 2] != verify_3) + invalid = true; + + aesKey += BitConverter.ToString(ProcessMemory, addr + 3, 4).Replace("-", ""); + addr += 7; // 4 + 3 + verify_3 += 0x04; + } + + if (aesKey.Length == 64) + { + if (ProcessMemory[addr] == 0xE9) + addr = FollowJMP(addr); + + if (ProcessMemory[addr] != 0xC3 && ProcessMemory[addr] != 0x48) + { + if (ProcessMemory[addr] != 0x0F) + invalid = true; + + bool found = false; + for (int xx = 0; xx < 30; xx++) + { + int checkAddr = addr + xx; + if (checkAddr >= ProcessMemory.Length) break; + if (ProcessMemory[checkAddr] == 0x48 && ProcessMemory[checkAddr + 1] == 0x8D) + { + found = true; + break; + } + } + if (!found) + invalid = true; + } + } + + if (invalid) + break; + } + + if (invalid) continue; + + offsets.Add(AllocationBase + (ulong)i, $"0x{aesKey}"); + } + catch + { + // Игнорируем ошибки парсинга + } + } + } + + timer.Stop(); + elapsedMilliseconds = timer.ElapsedMilliseconds; + return offsets; + } + + public static class Win32 + { + [DllImport("kernel32.dll")] +public static extern bool ReadProcessMemory(IntPtr hProcess, ulong lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, int lpNumberOfBytesRead = 0); + + [DllImport("kernel32.dll")] + public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); + } +} + diff --git a/UEAESKeyFinder/UEAESKeyFinder.csproj b/UEAESKeyFinder/UEAESKeyFinder.csproj index d3ac3ac..3d58563 100644 --- a/UEAESKeyFinder/UEAESKeyFinder.csproj +++ b/UEAESKeyFinder/UEAESKeyFinder.csproj @@ -1,9 +1,17 @@ - - - - Exe - netcoreapp3.1 - AnyCPU;x64 - - - + + + Exe + netcoreapp8.0 + AnyCPU;x64 + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + \ No newline at end of file