34 Commits
v1.2.0 ... main

Author SHA1 Message Date
Wibus
595d395202 docs: stop 2024-10-06 22:55:49 +08:00
github-actions[bot]
b09d07313f chore: bump Build Number to 8 (#54)
Co-authored-by: GitHub Action <action@github.com>
2024-10-05 21:56:01 +08:00
wibus-wee
df3854b8c9 chore: bump version to v1.2.3 2024-10-05 21:54:20 +08:00
Wibus
85abf256db perf(Injector): use semaphore and URLSession to download remote resource (#40) 2024-09-12 15:15:30 +08:00
Wibus
ecf16b33ac fix(Injector): add "" between tool path (#31) 2024-09-12 13:51:55 +08:00
Wibus
4bf17a9bef ci(build): multi-arch support (#35) 2024-09-09 23:47:52 +08:00
github-actions[bot]
d35cea9e75 chore: bump Build Number to 7 (#25)
Co-authored-by: GitHub Action <action@github.com>
2024-08-29 20:15:40 +08:00
wibus-wee
80e55d3a4d chore: bump version to v1.2.2 2024-08-29 20:14:20 +08:00
wibus-wee
60b8688b5f fix: unable to view the injection running window 2024-08-29 20:11:19 +08:00
wibus-wee
032a842c7b fix: start app failed after injection, fix #23 2024-08-29 20:01:06 +08:00
wibus-wee
199453137c perf: set all path format type to bash 2024-08-28 10:27:39 +08:00
wibus-wee
ca063ce96e fix!: change setapp documentation link 2024-08-28 10:27:38 +08:00
wibus-wee
069e9e0d13 chore: add localizable 2024-08-28 10:27:38 +08:00
wibus-wee
a06052e005 feat: add "Report an issue" button to error alert 2024-08-28 10:27:38 +08:00
wibus-wee
aa0b27d219 docs: update readme important content 2024-08-23 23:39:40 +08:00
wibus-wee
c6d8616076 docs: update readme warning text 2024-08-23 17:13:41 +08:00
wibus-wee
a9e7c98523 ci: remove update action with dispatch event 2024-08-23 17:07:30 +08:00
Wibus
114205aa4d docs: third-party website warning 2024-08-23 17:04:58 +08:00
Wibus
1cbf2b8ca0 ci(build): add artifact download comment (#22) 2024-08-22 10:33:22 +08:00
Wibus
0c0d06df66 fix(SoftwareManager): missing handle Adobe software directory structure (#21) 2024-08-22 09:25:20 +08:00
wibus-wee
dba45c1d79 fix(sparkle): set a wrong SUFeedURL 2024-08-17 17:24:51 +08:00
github-actions[bot]
0e447de050 chore: bump Build Number to 6 (#19)
Co-authored-by: GitHub Action <action@github.com>
2024-08-17 17:06:50 +08:00
wibus-wee
4f9df79875 chore: bump version to 1.2.1 2024-08-17 17:05:48 +08:00
wibus-wee
77cccc8416 ci: set a job to upload appcast artifact for testing [skip ci] 2024-08-17 17:01:26 +08:00
wibus-wee
b116fd5084 ci: add description and cp dmg 2024-08-17 16:57:10 +08:00
Wibus
17a25e4783 fix: special characters in shell without specially handling (#18)
Co-authored-by: RayPS <ray@rayps.com>
2024-08-17 16:21:09 +08:00
wibus-wee
0dec3ad00a chore: setup injectioniii 2024-08-11 10:00:17 +08:00
wibus-wee
f105a21510 chore: set deployment target to 12.0 2024-08-10 11:58:23 +08:00
wibus-wee
e6c28a1c77 perf(AboutView): bring about view back to original shape 2024-08-10 11:51:50 +08:00
wibus-wee
77e77b2ced chore(deps): downgrade cache version to 7.2.0 2024-08-10 11:36:36 +08:00
阿卜杜热伊木·阿卜杜力木
1eaa3d6071 feat: enhance app views and sidebar (#17)
* add "Run App" button

* add Simplified Chinese Localization

* refactor welcomeview

* use group instead of folder
2024-08-10 00:28:17 +08:00
wibus-wee
541e83c63b fix: something get wrong with low version swift and xcode 2024-08-08 22:28:29 +08:00
BirdMichael
d427614b01 feat: add app detail isInjected (#16) 2024-08-08 20:04:54 +08:00
wibus-wee
d513754977 fix(injector): missing noSignTarget config 2024-08-08 19:24:53 +08:00
25 changed files with 1521 additions and 690 deletions

View File

@@ -2,7 +2,7 @@ name: CI - Build Application
on:
push:
branches:
- '**'
- 'main'
paths-ignore:
- 'LICENSE'
- '**.md'
@@ -17,7 +17,7 @@ permissions:
jobs:
build-mac:
name: Build for macOS (Universal)
runs-on: macos-13
runs-on: macOS-latest
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -54,8 +54,9 @@ jobs:
shell: bash
run: |
hdiutil create -volname "InjectGUI" -srcfolder "build/Build/Products/${{ env.APP_CONF }}/InjectGUI.app" -ov -format UDZO "InjectGUI.dmg"
cp "InjectGUI.dmg" "$RUNNER_TEMP/InjectGUI.dmg"
- name: Upload DMG Artifact
id: upload-artifact
uses: actions/upload-artifact@v4
with:
name: InjectGUI
@@ -90,7 +91,7 @@ jobs:
# RELEASE_NOTES_URL: "https://codeedit.app/whats-new/"
# RELEASE_NOTES_PREFIX: "https://codeedit.app/sparkle/"
run: |
SPARKLE_BIN="$RUNNER_TEMP/DerivedData/SourcePackages/artifacts/sparkle/Sparkle/bin"
SPARKLE_BIN="$RUNNER_TEMP/build/SourcePackages/artifacts/sparkle/Sparkle/bin"
SPARKLE_ARCHIVE="$RUNNER_TEMP/Sparkle_Archive"
echo -n "$SPARKLE_KEY" | tee "$RUNNER_TEMP/sparkle_key"
mkdir "$SPARKLE_ARCHIVE"
@@ -103,4 +104,75 @@ jobs:
if: github.event_name == 'release'
uses: softprops/action-gh-release@v2
with:
files: "${{ RUNNER.TEMP }}/Sparkle_Archive/appcast.xml"
files: "${{ RUNNER.TEMP }}/Sparkle_Archive/appcast.xml"
- name: Upload Sparkle Artifact
if: github.event_name == 'release'
uses: actions/upload-artifact@v4
with:
name: appcast.xml
path: "${{ RUNNER.TEMP }}/Sparkle_Archive/appcast.xml"
- name: Find Comment
if: github.event_name == 'pull_request'
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: 'InjectGUI'
- name: Gen Previous Builds List
if: github.event_name == 'pull_request'
run: |
shas_and_links=$(echo "${{ steps.fc.outputs.comment-body }}" | sed -n 's/.*\[\([^!][^]]*\)\].*/\1/p')
result_list=""
IFS=',' # 设置内部字段分隔符为逗号
for entry in $shas_and_links; do
sha=$(echo "$entry" | cut -d';' -f1)
link=$(echo "$entry" | cut -d';' -f2)
result_list+="<sup><li><a href='${link}'>${sha}</a></li></sup>"
done
echo "PREVIOUS_BUILDS=${result_list}" >> $GITHUB_ENV
- name: Add new build to Previous Builds List
if: github.event_name == 'pull_request'
run: |
new_sha="${{github.sha}}"
new_link="${{ steps.upload-artifact.outputs.artifact-id }}"
new_link="https://github.com/wibus-wee/InjectGUI/actions/runs/${{ github.run_id }}/artifacts/${new_link}"
comment_body="${{ steps.fc.outputs.comment-body }}"
if [ -z "$new_link" ]; then
echo "Artifact URL is empty. Skipping update."
else
shas_and_links=$(echo "$comment_body" | sed -n 's/.*\[\([^!][^]]*\)\].*/\1/p')
shas_and_links="${shas_and_links},[${new_sha};${new_link}]"
echo "NEW_COMMENT_LIST=${shas_and_links}" >> $GITHUB_ENV
fi
- name: Create Pull Request Comment
if: github.event_name == 'pull_request'
uses: peter-evans/create-or-update-comment@v4
with:
edit-mode: replace
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
Hi, thanks for your contribution!
**InjectGUI** (${{ github.sha }}) has been built and is available for download to test. (Based on PR ${{ github.event.pull_request.number }})
You can download the latest artifact from here: https://github.com/wibus-wee/InjectGUI/actions/runs/${{ github.run_id }}/artifacts/${{ steps.upload-artifact.outputs.artifact-id }}
> [!WARNING]
> This is a PR build and may not be stable. It's only for testing purposes.
---
<!--${{ env.NEW_COMMENT_LIST }}-->
<sup>Previous builds:</sup>
${{ env.PREVIOUS_BUILDS }}

View File

@@ -22,7 +22,7 @@ jobs:
git add .
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git commit -m "bump build number to $APP_BUILD"
git commit -m "chore: bump build number to $APP_BUILD"
- name: Push Changes
uses: ad-m/github-push-action@v0.6.0
with:
@@ -38,4 +38,11 @@ jobs:
delete-branch: true
base: main
title: "chore: bump Build Number to ${{ env.APP_BUILD }}"
body: Automatically bump build number of all targets to ${{ env.APP_BUILD }}
body: |
Automatically bump build number of all targets to ${{ env.APP_BUILD }}
cc @wibus-wee
---
<sup>This pull request was automatically created by a GitHub Action.</sup>

View File

@@ -13,7 +13,7 @@
381027E02C5F784F00348460 /* Extension+Scene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 381027DF2C5F784F00348460 /* Extension+Scene.swift */; };
381027E22C5F7C4800348460 /* Extension+Font.swift in Sources */ = {isa = PBXBuildFile; fileRef = 381027E12C5F7C4800348460 /* Extension+Font.swift */; };
381027E42C5F7E1100348460 /* Extension+URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 381027E32C5F7E1100348460 /* Extension+URL.swift */; };
381027E72C5F816500348460 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 381027E92C5F816500348460 /* Localizable.strings */; };
383943792C68458F00E5984E /* Inject in Frameworks */ = {isa = PBXBuildFile; productRef = 383943782C68458F00E5984E /* Inject */; };
387CEA792C5BD9C400E3A5AC /* AdminPrivilegeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 387CEA782C5BD9C400E3A5AC /* AdminPrivilegeView.swift */; };
387CEA7B2C5DEF0600E3A5AC /* Extension+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 387CEA7A2C5DEF0600E3A5AC /* Extension+String.swift */; };
38877A1D2C4A6F83009F5910 /* InjectGUIApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38877A1C2C4A6F83009F5910 /* InjectGUIApp.swift */; };
@@ -30,12 +30,13 @@
38AD95EA2C58E70E0032E79F /* Injector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AD95E92C58E70E0032E79F /* Injector.swift */; };
38AD95EE2C58F59C0032E79F /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38AD95ED2C58F59C0032E79F /* StatusView.swift */; };
38BC1F532C4B587A00C3B60E /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38BC1F522C4B587900C3B60E /* SidebarView.swift */; };
38BC1F552C4B622500C3B60E /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38BC1F542C4B622500C3B60E /* WelcomeView.swift */; };
38BC1F5A2C4B98A300C3B60E /* AppDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38BC1F592C4B98A300C3B60E /* AppDetailView.swift */; };
38BC1F5C2C4BB02200C3B60E /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38BC1F5B2C4BB02200C3B60E /* SettingsView.swift */; };
38E944F22C5A761B00B252A3 /* Executor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E944F12C5A761B00B252A3 /* Executor.swift */; };
38E944F52C5A7F6A00B252A3 /* Cache in Frameworks */ = {isa = PBXBuildFile; productRef = 38E944F42C5A7F6A00B252A3 /* Cache */; };
38E944F72C5A85E200B252A3 /* Cache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38E944F62C5A85E200B252A3 /* Cache.swift */; };
96BC7ED02C65C5E200149818 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 96BC7ECF2C65C5E200149818 /* Localizable.xcstrings */; };
96BC7ED62C65E7B100149818 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BC7ED52C65E7B100149818 /* AboutView.swift */; };
96BC7ED82C65E8C200149818 /* GeneralView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BC7ED72C65E8C200149818 /* GeneralView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -45,8 +46,6 @@
381027DF2C5F784F00348460 /* Extension+Scene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+Scene.swift"; sourceTree = "<group>"; };
381027E12C5F7C4800348460 /* Extension+Font.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+Font.swift"; sourceTree = "<group>"; };
381027E32C5F7E1100348460 /* Extension+URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+URL.swift"; sourceTree = "<group>"; };
381027E82C5F816500348460 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
381027EA2C5F817400348460 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
387CEA782C5BD9C400E3A5AC /* AdminPrivilegeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminPrivilegeView.swift; sourceTree = "<group>"; };
387CEA7A2C5DEF0600E3A5AC /* Extension+String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Extension+String.swift"; sourceTree = "<group>"; };
38877A192C4A6F83009F5910 /* InjectGUI.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InjectGUI.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -65,11 +64,13 @@
38AD95E92C58E70E0032E79F /* Injector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Injector.swift; sourceTree = "<group>"; };
38AD95ED2C58F59C0032E79F /* StatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusView.swift; sourceTree = "<group>"; };
38BC1F522C4B587900C3B60E /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = "<group>"; };
38BC1F542C4B622500C3B60E /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
38BC1F592C4B98A300C3B60E /* AppDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDetailView.swift; sourceTree = "<group>"; };
38BC1F5B2C4BB02200C3B60E /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
38E944F12C5A761B00B252A3 /* Executor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Executor.swift; sourceTree = "<group>"; };
38E944F62C5A85E200B252A3 /* Cache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cache.swift; sourceTree = "<group>"; };
96BC7ECF2C65C5E200149818 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = Localizable.xcstrings; path = InjectGUI/Localizables/Localizable.xcstrings; sourceTree = "<group>"; };
96BC7ED52C65E7B100149818 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
96BC7ED72C65E8C200149818 /* GeneralView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -77,8 +78,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
38E944F52C5A7F6A00B252A3 /* Cache in Frameworks */,
380D777B2C61EA56005F3150 /* Sparkle in Frameworks */,
383943792C68458F00E5984E /* Inject in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -88,7 +89,7 @@
38877A102C4A6F83009F5910 = {
isa = PBXGroup;
children = (
381027E92C5F816500348460 /* Localizable.strings */,
96BC7ECF2C65C5E200149818 /* Localizable.xcstrings */,
38877A1B2C4A6F83009F5910 /* InjectGUI */,
38877A1A2C4A6F83009F5910 /* Products */,
);
@@ -143,12 +144,11 @@
38877A2C2C4A6FD7009F5910 /* View */ = {
isa = PBXGroup;
children = (
96BC7EDB2C65ED2800149818 /* Settings */,
38877A1E2C4A6F83009F5910 /* ContentView.swift */,
38BC1F522C4B587900C3B60E /* SidebarView.swift */,
38BC1F542C4B622500C3B60E /* WelcomeView.swift */,
38BC1F592C4B98A300C3B60E /* AppDetailView.swift */,
38AD95ED2C58F59C0032E79F /* StatusView.swift */,
38BC1F5B2C4BB02200C3B60E /* SettingsView.swift */,
387CEA782C5BD9C400E3A5AC /* AdminPrivilegeView.swift */,
380D777C2C61EAB9005F3150 /* CheckForUpdatesView.swift */,
);
@@ -185,6 +185,16 @@
path = Util;
sourceTree = "<group>";
};
96BC7EDB2C65ED2800149818 /* Settings */ = {
isa = PBXGroup;
children = (
96BC7ED52C65E7B100149818 /* AboutView.swift */,
96BC7ED72C65E8C200149818 /* GeneralView.swift */,
38BC1F5B2C4BB02200C3B60E /* SettingsView.swift */,
);
path = Settings;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -202,8 +212,8 @@
);
name = InjectGUI;
packageProductDependencies = (
38E944F42C5A7F6A00B252A3 /* Cache */,
380D777A2C61EA56005F3150 /* Sparkle */,
383943782C68458F00E5984E /* Inject */,
);
productName = InjectGUI;
productReference = 38877A192C4A6F83009F5910 /* InjectGUI.app */;
@@ -235,8 +245,8 @@
);
mainGroup = 38877A102C4A6F83009F5910;
packageReferences = (
38E944F32C5A7F6A00B252A3 /* XCRemoteSwiftPackageReference "Cache" */,
380D77792C61EA56005F3150 /* XCRemoteSwiftPackageReference "Sparkle" */,
383943772C68458F00E5984E /* XCRemoteSwiftPackageReference "Inject" */,
);
productRefGroup = 38877A1A2C4A6F83009F5910 /* Products */;
projectDirPath = "";
@@ -253,8 +263,8 @@
buildActionMask = 2147483647;
files = (
38877A252C4A6F87009F5910 /* Preview Assets.xcassets in Resources */,
96BC7ED02C65C5E200149818 /* Localizable.xcstrings in Resources */,
380D77842C620464005F3150 /* config.json in Resources */,
381027E72C5F816500348460 /* Localizable.strings in Resources */,
38877A212C4A6F87009F5910 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -278,11 +288,12 @@
38877A332C4A7222009F5910 /* PublishedStorage.swift in Sources */,
381027E02C5F784F00348460 /* Extension+Scene.swift in Sources */,
38E944F22C5A761B00B252A3 /* Executor.swift in Sources */,
38BC1F552C4B622500C3B60E /* WelcomeView.swift in Sources */,
38877A2E2C4A6FFA009F5910 /* InjectConfiguration.swift in Sources */,
38BC1F532C4B587A00C3B60E /* SidebarView.swift in Sources */,
38877A352C4A7254009F5910 /* ViewKit.swift in Sources */,
96BC7ED62C65E7B100149818 /* AboutView.swift in Sources */,
38877A3A2C4A730F009F5910 /* Constants.swift in Sources */,
96BC7ED82C65E8C200149818 /* GeneralView.swift in Sources */,
381027E42C5F7E1100348460 /* Extension+URL.swift in Sources */,
38BC1F5C2C4BB02200C3B60E /* SettingsView.swift in Sources */,
381027E22C5F7C4800348460 /* Extension+Font.swift in Sources */,
@@ -294,19 +305,6 @@
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
381027E92C5F816500348460 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
381027E82C5F816500348460 /* en */,
381027EA2C5F817400348460 /* zh-Hans */,
);
name = Localizable.strings;
path = InjectGUI/Localizables;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
38877A262C4A6F87009F5910 /* Debug */ = {
isa = XCBuildConfiguration;
@@ -359,10 +357,12 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
@@ -412,9 +412,11 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-O";
};
name = Release;
@@ -426,7 +428,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = InjectGUI/InjectGUI.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5;
CURRENT_PROJECT_VERSION = 8;
DEVELOPMENT_ASSET_PATHS = "\"InjectGUI/Preview Content\"";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -446,7 +448,12 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.2.0;
MARKETING_VERSION = 1.2.3;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"-Xlinker",
"-interposable",
);
PRODUCT_BUNDLE_IDENTIFIER = "dev.wibus-wee.InjectGUI";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
@@ -465,7 +472,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = InjectGUI/InjectGUI.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 5;
CURRENT_PROJECT_VERSION = 8;
DEVELOPMENT_ASSET_PATHS = "\"InjectGUI/Preview Content\"";
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -485,7 +492,7 @@
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 12.0;
MARKETING_VERSION = 1.2.0;
MARKETING_VERSION = 1.2.3;
PRODUCT_BUNDLE_IDENTIFIER = "dev.wibus-wee.InjectGUI";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
@@ -529,12 +536,12 @@
minimumVersion = 2.0.0;
};
};
38E944F32C5A7F6A00B252A3 /* XCRemoteSwiftPackageReference "Cache" */ = {
383943772C68458F00E5984E /* XCRemoteSwiftPackageReference "Inject" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/hyperoslo/Cache";
repositoryURL = "https://github.com/krzysztofzablocki/Inject.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 7.0.0;
minimumVersion = 1.5.2;
};
};
/* End XCRemoteSwiftPackageReference section */
@@ -545,10 +552,10 @@
package = 380D77792C61EA56005F3150 /* XCRemoteSwiftPackageReference "Sparkle" */;
productName = Sparkle;
};
38E944F42C5A7F6A00B252A3 /* Cache */ = {
383943782C68458F00E5984E /* Inject */ = {
isa = XCSwiftPackageProductDependency;
package = 38E944F32C5A7F6A00B252A3 /* XCRemoteSwiftPackageReference "Cache" */;
productName = Cache;
package = 383943772C68458F00E5984E /* XCRemoteSwiftPackageReference "Inject" */;
productName = Inject;
};
/* End XCSwiftPackageProductDependency section */
};

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1520"
version = "1.8">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
@@ -28,8 +28,6 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"

View File

@@ -14,6 +14,10 @@ let softwareManager = SoftwareManager.shared
let injector = Injector.shared
let executor = Executor.shared
#if DEBUG
let injectIII = Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/macOSInjection.bundle")?.load()
#endif
@main
struct InjectGUIApp: App {
@AppStorage("showAdminPrivilegeView") private var showAdminPrivilegeView: Bool = true

View File

@@ -4,17 +4,17 @@
//
// Created by wibus on 2024/7/31.
//
import Cache
enum CacheKey {
static let latestCommitHash = "latestCommitHash"
}
// MARK: - Cache Configuration
// main/latest commit hash
let diskConfig = DiskConfig(name: "InjectLib")
let memoryConfig = MemoryConfig(expiry: .never, countLimit: 10, totalCostLimit: 10)
let transformer = TransformerFactory.forCodable(ofType: String.self)
let storage = try! Storage<String, String>(diskConfig: diskConfig, memoryConfig: memoryConfig, transformer: transformer)
//
//import Cache
//
//enum CacheKey {
// static let latestCommitHash = "latestCommitHash"
//}
//
//// MARK: - Cache Configuration
//
//// main/latest commit hash
//let diskConfig = DiskConfig(name: "InjectLib")
//let memoryConfig = MemoryConfig(expiry: .never, countLimit: 10, totalCostLimit: 10)
//let transformer = TransformerFactory.forCodable(ofType: String.self)
//let storage = try! Storage<String, String>(diskConfig: diskConfig, memoryConfig: memoryConfig, transformer: transformer)

View File

@@ -88,7 +88,7 @@ class Executor: ObservableObject {
task.arguments = ["-c", command]
// task.arguments = ["-c", "echo 123"]
#if DEBUG
print("command: \(command)")
print("[*] command: \(command)")
#endif
let pipe = Pipe()
@@ -134,8 +134,12 @@ class Executor: ObservableObject {
task.standardOutput = outputPipe
task.standardError = errorPipe
let escapedCommand = command.replacingOccurrences(of: "\"", with: "\\\"")
task.arguments = ["-c", "echo \(self.password) | sudo -S bash -c \"\(escapedCommand)\""]
let escapedCommand = command
.replacingOccurrences(of: "\"", with: "\\\"")
#if DEBUG
print("[*] Escaped command: \(escapedCommand)")
#endif
task.arguments = ["-c", "echo \(self.password) | base64 --decode | sudo -S bash -c \"\(escapedCommand)\""]
task.executableURL = URL(fileURLWithPath: "/bin/bash")
do {

View File

@@ -329,8 +329,43 @@ class InjectConfiguration: ObservableObject {
guard let conf = remoteConf else {
return false
}
let package = conf.appList.first { $0.packageName.allStrings.contains(package) }
guard package != nil else {
let packageDetail = conf.appList.first { $0.packageName.allStrings.contains(package) }
guard packageDetail != nil else {
// Bundle ID
// config.json
// FIXME: . packageName 使 Bundle ID
let localPackageInfo = softwareManager.appListCache[package]
if localPackageInfo != nil {
let app = conf.appList.first { $0.appBaseLocate == localPackageInfo?.path.replacingOccurrences(of: "/Contents", with: "") }
if app != nil {
print("[*] Found \(package) in local cache, try to add to supported list...")
let newApp = AppList(
packageName: .string(package),
appBaseLocate: app?.appBaseLocate,
bridgeFile: app?.bridgeFile,
injectFile: app?.injectFile,
needCopyToAppDir: app?.needCopyToAppDir,
noSignTarget: app?.noSignTarget,
autoHandleHelper: app?.autoHandleHelper,
helperFile: app?.helperFile,
tccutil: app?.tccutil,
forQiuChenly: app?.forQiuChenly,
onlysh: app?.onlysh,
extraShell: app?.extraShell,
smExtra: app?.smExtra,
componentApp: app?.componentApp,
deepSignApp: app?.deepSignApp,
noDeep: app?.noDeep,
entitlements: app?.entitlements,
useOptool: app?.useOptool,
autoHandleSetapp: app?.autoHandleSetapp,
keygen: app?.keygen
)
remoteConf!.appList.append(newApp)
print("[*] Added \(package) to supported list.")
return true
}
}
return false
}
return true
@@ -354,7 +389,7 @@ struct InjectConfigurationModel: Codable, Equatable {
let project, author: String
let version: Double
let basePublicConfig: BasePublicConfig
let appList: [AppList]
var appList: [AppList]
enum CodingKeys: String, CodingKey {
case project

View File

@@ -33,25 +33,25 @@ extension InjectStage {
var description: String {
switch self {
case .start:
return "Start Injecting"
return String(localized: "Start Injecting")
case .copyExecutableFileAsBackup:
return "Copying Executable File as Backup"
return String(localized: "Copying Executable File as Backup")
case .checkPermissionAndRun:
return "Checking Permission and Run"
return String(localized: "Checking Permission and Run")
case .handleKeygen:
return "Handling Keygen"
return String(localized: "Handling Keygen")
case .handleDeepCodeSign:
return "Handling Deep Code Sign"
return String(localized: "Handling Deep Code Sign")
case .handleAutoHandleHelper:
return "Handling Auto Handle Helper"
return String(localized: "Handling Auto Handle Helper")
case .handleTccutil:
return "Handling Tccutil"
return String(localized: "Handling Tccutil")
case .handleExtraShell:
return "Handling Extra Shell"
return String(localized: "Handling Extra Shell")
case .handleInjectLibInject:
return "Handling Inject Lib Inject"
return String(localized: "Handling InjectLib Inject")
case .end:
return "Injecting Finished"
return String(localized: "Injecting Finished")
}
}
}
@@ -94,19 +94,20 @@ class Injector: ObservableObject {
func startInjectApp(package: String) {
// MARK: - Setapp
if package.contains("com.setapp") {
let alert = NSAlert()
alert.messageText = "Please read the Setapp inject document first"
alert.informativeText = "It's important to read the Setapp inject document first before using the tool. Please"
alert.messageText = String(localized: "Please read the Setapp inject document first")
alert.informativeText = String(localized: "It's important to read the Setapp inject document first before using the tool. Please")
alert.alertStyle = .informational
alert.addButton(withTitle: "I have read the document")
alert.addButton(withTitle: "Read the document")
alert.addButton(withTitle: "Cancel")
alert.addButton(withTitle: String(localized: "I have read the document"))
alert.addButton(withTitle: String(localized: "Read the document"))
alert.addButton(withTitle: String(localized: "Cancel"))
let response = alert.runModal()
if response == .alertFirstButtonReturn {
// Continue
} else if response == .alertSecondButtonReturn {
let url = URL(string: "https://qiuchenlyopensource.github.io/Documentaions/setapp%E6%BF%80%E6%B4%BB%E5%BF%85%E8%AF%BB.html")!
let url = URL(string: "https://qiuchenlyopensource.github.io/Documentaions/setapp.html")!
NSWorkspace.shared.open(url)
return
} else {
@@ -118,10 +119,10 @@ class Injector: ObservableObject {
}
if injectConfiguration.allToolsExist() == false {
let alert = NSAlert()
alert.messageText = "Inject Tools Not Found"
alert.informativeText = "Inject tools not found, it may be caused by network issues or the tools are not available. Please try again later."
alert.messageText = String(localized: "Inject Tools Not Found")
alert.informativeText = String(localized: "Inject tools not found, it may be caused by network issues or the tools are not available. Please try again later.")
alert.alertStyle = .warning
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: String(localized: "OK"))
alert.runModal()
return
}
@@ -131,6 +132,8 @@ class Injector: ObservableObject {
guard let injectDetail = injectConfiguration.injectDetail(package: package) else {
return
}
print("----------------------------")
print("[*] Start inject \(package)")
self.injectDetail = injectDetail
self.appDetail = appDetail
self.shouldShowStatusSheet = true
@@ -161,8 +164,8 @@ class Injector: ObservableObject {
.sink(receiveCompletion: { completion in
if case .failure(let error) = completion {
let alert = NSAlert()
alert.messageText = "Command Execution Error"
alert.messageText = String(localized: "Command Execution Error")
// Extracting the AppleScript error message
var errorMessage = error.localizedDescription
if let appleScriptError = error as NSError? {
@@ -171,11 +174,37 @@ class Injector: ObservableObject {
}
}
alert.informativeText = "\(errorMessage) \n\nPlease check your application integrity and try again.\n\n(Stage: \(stage.description))"
alert.informativeText = String(localized: "\(errorMessage) \n\nPlease check your application integrity and try again.\n\n(Stage: \(stage.description))")
alert.alertStyle = .critical
alert.addButton(withTitle: "OK")
alert.runModal()
// Open an issue GitHub Issues
alert.addButton(withTitle: String(localized: "OK"))
alert.addButton(withTitle: String(localized: "Report an issue"))
let response = alert.runModal()
if response == .alertSecondButtonReturn {
// issue
let title = "[Bug] Error when injecting \(self.appDetail?.name ?? "")"
let body = """
### Error Message
```
\(errorMessage)
```
### Info
- Name: \(self.appDetail?.name ?? "")
- Identifier: \(self.appDetail?.identifier ?? "")
- \(self.appDetail?.name ?? "") Version: \(self.appDetail?.version ?? "")
- Stage: \(stage.description)
- InjectGUI version: \(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "")
"""
let url = URL(string: "\(Constants.projectUrl)/issues/new?assignees=&labels=bug&title=\(title.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")&body=\(body.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")")!
NSWorkspace.shared.open(url)
}
self.updateInjectStage(stage: stage, message: "Error: \(errorMessage)", progress: 1, status: .error, error: InjectRunningError(error: errorMessage, stage: stage))
self.isRunning = false
self.emergencyStop = true
} else {
self.updateInjectStage(stage: stage, message: stage.description, progress: 1, status: .finished)
self.executeNextStage(stages: stages, index: index + 1)
@@ -249,45 +278,57 @@ class Injector: ObservableObject {
case appleScript
case bash
}
private func getBridgeDir(executable: Bool? = nil) -> String {
if (executable ?? false) || self.injectDetail?.autoHandleSetapp == true {
return "/MacOS/"
self.getBridgeDir(executable: executable, injectDetail: self.injectDetail)
}
private func getBridgeDir(executable: Bool? = nil, injectDetail: AppList?) -> String {
if (executable ?? false) || injectDetail?.autoHandleSetapp == true {
return "/MacOS/"
} else {
return self.injectDetail?.bridgeFile?.replacingOccurrences(of: "/Contents", with: "") ?? "/Frameworks/"
return injectDetail?.bridgeFile?.replacingOccurrences(of: "/Contents", with: "") ?? "/Frameworks/"
}
}
private func genSourcePath(for type: GenScriptType) -> String {
func genSourcePath(for type: GenScriptType) -> String {
let bridgeDir = self.getBridgeDir()
let source = (self.appDetail?.path ?? "") + bridgeDir + (self.injectDetail?.injectFile ?? self.appDetail?.executable ?? "")
return self.transformPath(path: source, to: type)
}
private func genSourcePath(for type: GenScriptType, executable: Bool? = nil) -> String {
private func genSourcePath(for type: GenScriptType, executable: Bool) -> String {
let bridgeDir = self.getBridgeDir(executable: executable)
let source = (self.appDetail?.path ?? "") + bridgeDir + (executable ?? true ? (self.appDetail?.executable ?? "") : (self.injectDetail?.injectFile ?? ""))
let source = (self.appDetail?.path ?? "") + bridgeDir + (executable ? (self.appDetail?.executable ?? "") : (self.injectDetail?.injectFile ?? ""))
return self.transformPath(path: source, to: type)
}
private func genSourcePath(for type: GenScriptType, file: String? = nil) -> String {
let bridgeDir = self.getBridgeDir()
let source = (self.appDetail?.path ?? "") + bridgeDir + (file ?? (self.injectDetail?.injectFile ?? self.appDetail?.executable ?? ""))
func genSourcePath(for type: GenScriptType, file: String?) -> String {
self.genSourcePath(for: type, appList: self.injectDetail, file: file)
}
func genSourcePath(for type: GenScriptType, appList: AppList?, file: String?) -> String {
let bridgeDir = self.getBridgeDir(injectDetail: appList)
let source = (self.appDetail?.path ?? "") + bridgeDir + (file ?? "")
return self.transformPath(path: source, to: type)
}
private func genSourcePath(for type: GenScriptType, path: String? = nil) -> String {
let bridgeDir = self.getBridgeDir()
let source = path ?? ((self.appDetail?.path ?? "") + bridgeDir + (self.injectDetail?.injectFile ?? self.appDetail?.executable ?? ""))
func genSourcePath(for type: GenScriptType, appList: AppList) -> String {
let bridgeDir = self.getBridgeDir(injectDetail: appList)
let source = (self.appDetail?.path ?? "") + bridgeDir + (self.injectDetail?.injectFile ?? self.appDetail?.executable ?? "")
return self.transformPath(path: source, to: type)
}
private func genSourcePath(for type: GenScriptType, path: String?) -> String {
return self.transformPath(path: path ?? "", to: type)
}
private func transformPath(path: String, to type: GenScriptType) -> String {
switch type {
case .none:
return path.replacingOccurrences(of: "%20", with: " ")
case .appleScript:
return path.replacingOccurrences(of: "%20", with: " ").replacingOccurrences(of: " ", with: "\\\\ ")
return path.replacingOccurrences(of: "%20", with: " ").replacingOccurrences(of: " ", with: "\\\\ ")
case .bash:
return path.replacingOccurrences(of: "%20", with: " ").replacingOccurrences(of: " ", with: "\\ ")
}
@@ -300,15 +341,15 @@ class Injector: ObservableObject {
let destination = source.appending(".backup")
if !FileManager.default.fileExists(atPath: source) {
print("Source file not found: \(source)")
return [("echo Source file not found: \(source.transformTo(to: .appleScript)) && exit 1", true)] // AppleScript
print("[*] Source file not found: \(source)")
return [("echo Source file not found: \(source.transformTo(to: .bash)) && exit 1", true)] // AppleScript
}
if FileManager.default.fileExists(atPath: destination) {
print("Destination file already exists: \(destination)")
print("[*] Destination file already exists: \(destination)")
return []
}
return [
("sudo cp \(source.transformTo(to: .appleScript)) \(destination.transformTo(to: .appleScript))", true)
("sudo cp \(source.transformTo(to: .bash)) \(destination.transformTo(to: .bash))", true)
]
}
@@ -316,14 +357,14 @@ class Injector: ObservableObject {
func checkPermissionAndRunCommands() -> [(command: String, isAdmin: Bool)] {
var shells: [(command: String, isAdmin: Bool)] = []
let source = self.genSourcePath(for: .appleScript)
let source = self.genSourcePath(for: .bash)
shells.append(("sudo xattr -cr \(source)", true))
shells.append(("sudo chmod -R 777 \(source)", true))
// ,
let isRunning = NSRunningApplication.runningApplications(withBundleIdentifier: self.appDetail?.identifier ?? "").count > 0
if isRunning {
shells.append(("sudo pkill -f \(self.genSourcePath(for: .appleScript, executable: true))", true))
shells.append(("sudo pkill -f \(self.genSourcePath(for: .bash, executable: true))", true))
}
return shells
}
@@ -352,14 +393,17 @@ class Injector: ObservableObject {
if insert_dylib_URL == nil || QiuchenlyDylib_URL == nil {
let alert = NSAlert()
alert.messageText = "Inject Tools Path Not Found"
alert.informativeText = "This should not happen here, please report to the developer (Area: MainInject)"
alert.messageText = String(localized: "Inject Tools Path Not Found")
alert.informativeText = String(localized: "This should not happen here, please report to the developer (Area: MainInject)")
alert.alertStyle = .warning
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: String(localized: "OK"))
alert.runModal()
print("[*] Inject Tools Path Not Found.")
return [("echo Inject Tools Path Not Found && exit 1", true)]
}
if self.injectDetail?.needCopyToAppDir == true {
print("[*] Copying 91Qiuchenly.dylib to app dir")
// let copyedQiuchenly_URL = (self.appDetail?.path ?? "") + bridgeDir + "91Qiuchenly.dylib"
let copyedQiuchenly_URL = self.genSourcePath(for: .none, file: "91Qiuchenly.dylib")
let softLink = ("sudo ln -f -s '\(QiuchenlyDylib_URL!)' '\(copyedQiuchenly_URL)'", true) // 使
@@ -371,12 +415,12 @@ class Injector: ObservableObject {
let componentApp = componentAppList.map { appBaseLocate + $0 }
let componentAppExecutable = componentApp.map { $0 + "/Contents/MacOS/" + (self.readExecutableFile(app: URL(fileURLWithPath: $0)) ?? "") }
let desireAppList = desireApp + componentAppExecutable
let insert_dylib_commands = desireAppList.map { "sudo \(self.genSourcePath(for: .appleScript, path: insert_dylib_URL!)) '\(copyedQiuchenly_URL)' '\(destination.transformTo(to: .none))' '\($0)'" }
let insert_dylib_commands = desireAppList.map { "sudo \(self.genSourcePath(for: .bash, path: insert_dylib_URL!)) '\(copyedQiuchenly_URL)' '\(destination.transformTo(to: .none))' '\($0)'" }
return [softLink] + insert_dylib_commands.map { ($0, true) }
}
return [("sudo \(self.genSourcePath(for: .appleScript, path: insert_dylib_URL!)) '\(QiuchenlyDylib_URL!)' '\(source.transformTo(to: .none))' '\(destination.transformTo(to: .none))'", true)]
return [("sudo \(self.genSourcePath(for: .bash, path: insert_dylib_URL!)) '\(QiuchenlyDylib_URL!)' '\(source.transformTo(to: .none))' '\(destination.transformTo(to: .none))'", true)]
}
// MARK: - DeepCodeSign
@@ -392,23 +436,42 @@ class Injector: ObservableObject {
}
let entitlements = self.injectDetail?.entitlements
if let entitlements = entitlements {
if let entitlements {
let entitlementDownloadURL = injectConfiguration.generateInjectToolDownloadURL(name: entitlements)
let downloadIntoTmpPath = try? FileManager.default.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: URL(fileURLWithPath: "/"), create: true)
let entitlementsPath = downloadIntoTmpPath?.appendingPathComponent(entitlements).path
let downloadCommand = "curl -L -o \(entitlementsPath!) \(entitlementDownloadURL!)"
shells.append((downloadCommand, false))
// let downloadCommand = "curl -L -o \(entitlementsPath!) \(entitlementDownloadURL!)"
let semaphore = DispatchSemaphore(value: 0)
let task = URLSession.shared.downloadTask(with: entitlementDownloadURL!) { (location, response, error) in
if let location = location {
try? FileManager.default.moveItem(at: location, to: URL(fileURLWithPath: entitlementsPath!))
print("[*] Download Entitlements Success: \(entitlementDownloadURL!)")
} else {
print("[*] Download Entitlements Failed: \(entitlementDownloadURL!)")
shells.append(("echo Download Entitlements Failed: \(entitlementDownloadURL!) && exit 1", false))
}
semaphore.signal()
}
task.resume()
semaphore.wait()
sign_prefix_with_deep += " --entitlements \(entitlementsPath!)"
}
let dest = self.genSourcePath(for: .bash)
let dest = self.genSourcePath(for: .none)
shells.append((sign_prefix_with_deep + " '\(dest)'", false))
if !(injectDetail?.noSignTarget ?? false) {
shells.append((sign_prefix_with_deep + " '\(dest)'", true))
}
// shells.append((sign_prefix_with_deep + " '\(dest)'", false))
let deepSignApp = self.injectDetail?.deepSignApp // Bool
if deepSignApp == true {
let deepSignAppPath = self.genSourcePath(for: .bash, path: (self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: ""))
shells.append((sign_prefix_with_deep + " '\(deepSignAppPath)'", false))
let deepSignAppPath = self.genSourcePath(for: .none, path: (self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: ""))
shells.append((sign_prefix_with_deep + " '\(deepSignAppPath)'", true))
}
// let disableLibraryValidate = self.injectDetail?.dis
@@ -439,42 +502,60 @@ class Injector: ObservableObject {
try? FileManager.default.createDirectory(at: extraShellDir, withIntermediateDirectories: true, attributes: nil)
}
let downloadPath = downloadIntoTmpPath.appendingPathComponent(extraShell).path
let downloadCommand = "curl -L -o \(downloadPath) \(getToolDownloadURL)"
// let downloadCommand = "curl -L -o \(downloadPath) \(getToolDownloadURL)"
//
let dest = self.genSourcePath(for: .appleScript)
let semaphore = DispatchSemaphore(value: 0)
let task = URLSession.shared.downloadTask(with: getToolDownloadURL) { (location, response, error) in
if let location = location {
try? FileManager.default.moveItem(at: location, to: URL(fileURLWithPath: downloadPath))
print("[*] Download Extra Shell Success: \(getToolDownloadURL)")
} else {
print("[*] Download Extra Shell Failed: \(getToolDownloadURL)")
shells.append(("echo Download Extra Shell Failed: \(getToolDownloadURL) && exit 1", false))
}
semaphore.signal()
}
task.resume()
semaphore.wait()
let dest = self.genSourcePath(for: .bash)
// MARK: - Sub: shell
var replaceSpecialShell: [(String, String)] = [] // (from, to)
// tool/optool
let optoolPath = self.genSourcePath(for: .appleScript, path: injectConfiguration.getInjecToolPath(name: "optool")?.pathWithFallback())
let optoolPath = self.genSourcePath(for: .bash, path: injectConfiguration.getInjecToolPath(name: "optool")?.pathWithFallback())
replaceSpecialShell.append(("tool/optool", optoolPath))
replaceSpecialShell.append(("./tool/optool", optoolPath))
// tool/insert_dylib
let insert_dylibPath = self.genSourcePath(for: .appleScript, path: injectConfiguration.getInjecToolPath(name: "insert_dylib")?.pathWithFallback())
let insert_dylibPath = self.genSourcePath(for: .bash, path: injectConfiguration.getInjecToolPath(name: "insert_dylib")?.pathWithFallback())
replaceSpecialShell.append(("tool/insert_dylib", insert_dylibPath))
replaceSpecialShell.append(("./tool/insert_dylib", insert_dylibPath))
// tool/91QiuChenly.dylib
let dylibPath = self.genSourcePath(for: .appleScript, path: injectConfiguration.getInjecToolPath(name: "91Qiuchenly.dylib")?.pathWithFallback())
let dylibPath = self.genSourcePath(for: .bash, path: injectConfiguration.getInjecToolPath(name: "91Qiuchenly.dylib")?.pathWithFallback())
replaceSpecialShell.append(("tool/91QiuChenly.dylib", dylibPath))
replaceSpecialShell.append(("./tool/91QiuChenly.dylib", dylibPath))
// tool/GenShineImpactStarter
let genShineImpactStarterPath = self.genSourcePath(for: .appleScript, path: injectConfiguration.getInjecToolPath(name: "GenShineImpactStarter")?.pathWithFallback())
let genShineImpactStarterPath = self.genSourcePath(for: .bash, path: injectConfiguration.getInjecToolPath(name: "GenShineImpactStarter")?.pathWithFallback())
replaceSpecialShell.append(("tool/GenShineImpactStarter", genShineImpactStarterPath))
replaceSpecialShell.append(("./tool/GenShineImpactStarter", genShineImpactStarterPath))
// [0] [1]
let replaceCommands = replaceSpecialShell.map { from, to in
"sed -i '' 's|\(from)|\(to)|g' \(downloadPath)"
"sed -i '' 's|\(from)|\"\(to)\"|g' \(downloadPath)"
}
shells.append((downloadCommand, false))
shells.append(("chmod +x \(downloadPath)", false))
if replaceCommands.count > 0 {
// shells.append((downloadCommand, false))
shells.append(("sudo chmod -R 777 \(downloadPath)", true))
shells.append(("chmod +x \(downloadPath)", true))
if !replaceCommands.isEmpty {
shells.append(contentsOf: replaceCommands.map { ($0, false) })
}
shells.append(("sudo sh \(downloadPath)", true))
@@ -488,7 +569,7 @@ class Injector: ObservableObject {
var shells: [(command: String, isAdmin: Bool)] = []
let helperFile = self.injectDetail?.helperFile?.allStrings // [String]?
let autoHandleHelper = self.injectDetail?.autoHandleHelper // Bool?
if let helperFile = helperFile, let autoHandleHelper = autoHandleHelper {
if let helperFile, let autoHandleHelper {
var helpers: [String] = []
if autoHandleHelper {
helpers = helperFile
@@ -496,10 +577,10 @@ class Injector: ObservableObject {
for helper in helpers {
let genShineImpactStarterURL = self.genSourcePath(for: .bash, path: injectConfiguration.getInjecToolPath(name: "GenShineImpactStarter")?.pathWithFallback())
var targetHelper = (self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: "") + helper
let bridgeFile = (self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: "") + (self.injectDetail?.bridgeFile ?? "")
let bridgeFile = (self.appDetail?.path ?? "") + (self.getBridgeDir())
let insertDylibURL = self.genSourcePath(for: .bash, path: injectConfiguration.getInjecToolPath(name: "insert_dylib")?.pathWithFallback())
let helperName = targetHelper.split(separator: "/").last
let target = self.genSourcePath(for: .appleScript, path: "/Library/LaunchDaemons/\(helperName!).plist")
let target = self.genSourcePath(for: .bash, path: "/Library/LaunchDaemons/\(helperName!).plist")
var srcInfo = [(self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: "") + "/Contents/Info.plist"]
if let componentApps = self.injectDetail?.componentApp {
@@ -519,7 +600,7 @@ class Injector: ObservableObject {
let rmCommand = ("sudo /bin/rm \(target)", true)
let rmPrivilegedHelper = "sudo /bin/rm /Library/PrivilegedHelperTools/\(helperName!)"
let xattrCommand = "sudo xattr -c '\((self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: ""))'"
let codeSignHelperCommand = "/usr/bin/codesign -f -s - --all-architectures --deep '\(targetHelper)'"
let codeSignAppCommand = "/usr/bin/codesign -f -s - --all-architectures --deep '\((self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: ""))'"
@@ -551,7 +632,7 @@ class Injector: ObservableObject {
func handleTccutilCommands() -> [(command: String, isAdmin: Bool)] {
let tccutil = self.injectDetail?.tccutil?.allStrings // [String]?
if let tccutil = tccutil {
if let tccutil {
var ids = [self.appDetail?.identifier]
if let componentApp = self.injectDetail?.componentApp {
ids.append(contentsOf: componentApp.map { self.readBundleID(app: URL(fileURLWithPath: (self.appDetail?.path ?? "").replacingOccurrences(of: "/Contents", with: "") + $0)) })

View File

@@ -15,29 +15,32 @@ struct AppDetail {
let path: String // -> path
let executable: String // -> CFBundleExecutable
let icon: NSImage
var isInjected: Bool = false
}
class SoftwareManager: ObservableObject {
static let shared = SoftwareManager()
@Published var appListCache: [String: AppDetail] = [:]
@Published var isLoading = false
@Published var isLoading = false
private init() {
refreshAppList()
}
private init() {
refreshAppList()
}
func refreshAppList() {
DispatchQueue.main.async {
self.isLoading = true
}
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
self?.getList()
DispatchQueue.main.async {
self?.isLoading = false
}
}
}
DispatchQueue.main.async {
self.isLoading = true
}
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
self?.getList()
DispatchQueue.main.async {
self?.isLoading = false
}
}
}
private func loadAppInfo(
from plistPath: String
) -> AppDetail? {
@@ -64,16 +67,7 @@ class SoftwareManager: ObservableObject {
let iconFileRaw = plist["CFBundleIconFile"] as? String ?? plist["CFBundleIconName"] as? String
//
let iconFile: String?
if let iconFileRaw = iconFileRaw {
iconFile = iconFileRaw.hasSuffix(
".icns"
) ? iconFileRaw : iconFileRaw.appending(
".icns"
)
} else {
iconFile = nil
}
let iconFile: String? = iconFileRaw?.hasSuffix(".icns") ?? false ? iconFileRaw : iconFileRaw?.appending(".icns")
// iconFile nil
guard let finalIconFile = iconFile else {
@@ -102,38 +96,51 @@ class SoftwareManager: ObservableObject {
version: bundleVersion,
path: path,
executable: bundleExecutable,
icon: icon ?? NSImage()
icon: icon ?? NSImage(),
isInjected: checkForInjection(appPath: path, package: bundleIdentifier)
)
}
func getList() {
print("[*] Getting app list...")
let applicationDirectories = [
"/Applications",
"/Applications/Setapp",
]
let fileManager = FileManager.default
print("[*] Getting app list...")
let applicationDirectories = [
"/Applications",
"/Applications/Setapp",
]
let fileManager = FileManager.default
var newAppListCache: [String: AppDetail] = [:]
var newAppListCache: [String: AppDetail] = [:]
for directory in applicationDirectories {
guard let appPaths = try? fileManager.contentsOfDirectory(atPath: directory) else {
continue
}
for directory in applicationDirectories {
guard let appPaths = try? fileManager.contentsOfDirectory(atPath: directory) else {
continue
}
for appPath in appPaths {
let fullPath = "\(directory)/\(appPath)"
let infoPlistPath = "\(fullPath)/Contents/Info.plist"
if let appInfo = loadAppInfo(from: infoPlistPath) {
newAppListCache[appInfo.identifier] = appInfo
}
}
}
for appPath in appPaths {
let fullPath = "\(directory)/\(appPath)"
let infoPlistPath = "\(fullPath)/Contents/Info.plist"
// MARK: - Adobe
if appPath.hasPrefix("Adobe") {
let adobeAppPaths = try? fileManager.contentsOfDirectory(atPath: fullPath)
for adobeAppPath in adobeAppPaths ?? [] { // Adobe
let adobeFullPath = "\(fullPath)/\(adobeAppPath)"
let adobeInfoPlistPath = "\(adobeFullPath)/Contents/Info.plist"
if let appInfo = loadAppInfo(from: adobeInfoPlistPath) {
newAppListCache[appInfo.identifier] = appInfo
}
}
} else {
if let appInfo = loadAppInfo(from: infoPlistPath) {
newAppListCache[appInfo.identifier] = appInfo
}
}
}
}
DispatchQueue.main.async { [weak self] in
self?.appListCache = newAppListCache
}
}
DispatchQueue.main.async { [weak self] in
self?.appListCache = newAppListCache
}
}
func addAnMaybeExistAppToList(appBaseLocate: String) {
// print("[*] try to add \(appBaseLocate) to list...")
@@ -150,4 +157,19 @@ class SoftwareManager: ObservableObject {
print("[*] Checking if \(package) is installed...")
return appListCache[package] != nil
}
private func checkForInjection(appPath: String, package: String) -> Bool {
let injector = Injector.shared
guard let appList = InjectConfiguration.shared.injectDetail(package: package) else {
return false
}
let paths = [
injector.genSourcePath(for: .none, appList: appList, file: "91Qiuchenly.dylib"),
injector.genSourcePath(for: .none, appList: appList).appending("_backup"),
injector.genSourcePath(for: .none, appList: appList).appending(".backup"),
]
return paths.contains { FileManager.default.fileExists(atPath: appPath + $0) }
}
}

View File

@@ -23,4 +23,11 @@ extension String {
return original.replacingOccurrences(of: " ", with: "\\ ")
}
}
func encode() -> String {
if let data = self.data(using: .utf8) {
return data.base64EncodedString()
}
return ""
}
}

View File

@@ -76,8 +76,8 @@ enum ViewKit {
static func useSheet(
title: String,
secondaryButton: String = "Cancel",
primaryButton: String = "OK",
secondaryButton: String = String(localized: "Cancel"),
primaryButton: String = String(localized: "OK"),
toolbar: (() -> (AnyView))? = nil,
content: @escaping () -> AnyView,
action: @escaping (IsClickPrimaryButton) -> Void?
@@ -97,8 +97,8 @@ enum ViewKit {
static func useAlert(
title: String,
message: String,
primaryButton: String = "OK",
secondaryButton: String = "Cancel",
primaryButton: String = String(localized: "OK"),
secondaryButton: String = String(localized: "Cancel"),
action: @escaping (IsClickPrimaryButton) -> Void?
) {
let alert = NSAlert()
@@ -127,8 +127,8 @@ enum ViewKit {
static func useFilePanel(
title: String,
message: String,
primaryButton: String = "OK",
secondaryButton: String = "Cancel",
primaryButton: String = String(localized: "OK"),
secondaryButton: String = String(localized: "Cancel"),
action: @escaping (URL?) -> Void
) {
let panel = NSOpenPanel()
@@ -147,8 +147,8 @@ enum ViewKit {
static func useDirectoryPanel(
title: String,
message: String,
primaryButton: String = "OK",
secondaryButton: String = "Cancel",
primaryButton: String = String(localized: "OK"),
secondaryButton: String = String(localized: "Cancel"),
action: @escaping (URL?) -> Void
) {
let panel = NSOpenPanel()
@@ -167,8 +167,8 @@ enum ViewKit {
static func saveFilePanel(
title: String,
message: String,
primaryButton: String = "OK",
secondaryButton: String = "Cancel",
primaryButton: String = String(localized: "OK"),
secondaryButton: String = String(localized: "Cancel"),
action: @escaping (URL?) -> Void
) {
let panel = NSSavePanel()

View File

@@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>SUFeedURL</key>
<string>https://github.com/wibus-wee/InjectGUI/releases/download/latest/appcast.xml</string>
<string>https://github.com/wibus-wee/InjectGUI/releases/latest/download/appcast.xml</string>
<key>SUPublicEDKey</key>
<string>uj8dRzY60RpQhFt88Lm4vwy7a5TEceTqRWmBeZpb4e0=</string>
</dict>

View File

@@ -0,0 +1,947 @@
{
"sourceLanguage" : "en",
"strings" : {
"" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : ""
}
}
},
"shouldTranslate" : false
},
"%@" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "%@"
}
}
}
},
"%@ \n\nPlease check your application integrity and try again.\n\n(Stage: %@)" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "%1$@ \n\nPlease check your application integrity and try again.\n\n(Stage: %2$@)"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "%1$@ \n\n请检查您的应用程序完整性并重试。\n\n(阶段: %2$@)"
}
}
}
},
"%lld%%" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "%lld%%"
}
}
}
},
"About" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "关于"
}
}
}
},
"Are you sure you want to stop injecting?" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "你确定要停止注入吗?"
}
}
}
},
"Branch" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "分支"
}
}
}
},
"By wibus. Made with ❤️" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "由 wibus 制作. Made with ❤️"
}
}
}
},
"Cancel" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "取消"
}
}
}
},
"Check for Updates…" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "检查更新…"
}
}
}
},
"Checking Permission and Run" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "正在检查权限并运行"
}
}
}
},
"Click here to close this view." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "点击以关闭此视图"
}
}
}
},
"Command Execution Error" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "命令执行错误"
}
}
}
},
"Commit" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "提交"
}
}
}
},
"Compatible Version" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "兼容的版本"
}
}
}
},
"Copy Bundle ID" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "复制 Bundle ID"
}
}
}
},
"Copying Executable File as Backup" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "正在复制可执行文件作为备份"
}
}
}
},
"Enter password again" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "请再次输入密码"
}
}
}
},
"Error: %@" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "错误:%@"
}
}
}
},
"Finished. Close" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "完成. 关闭"
}
}
}
},
"General" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "通用"
}
}
}
},
"Handling Auto Handle Helper" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "正在处理自动处理助手"
}
}
}
},
"Handling Deep Code Sign" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "正在处理 Deep Code SIgn"
}
}
}
},
"Handling Extra Shell" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "正在处理额外的Shell"
}
}
}
},
"Handling InjectLib Inject" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "处理 InjectLib 注入"
}
}
}
},
"Handling Keygen" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "正在处理 Keygen"
}
}
}
},
"Handling Tccutil" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "正在处理 Tccutil"
}
}
}
},
"I have read the document" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "我已阅读该文件"
}
}
}
},
"Incorrect password" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "密码错误"
}
}
}
},
"Information About InjectLib" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "关于 InjectLib 的信息"
}
}
}
},
"Inject" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "注入"
}
}
}
},
"Inject Configurations" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "注入配置"
}
}
}
},
"Inject is running" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "注入已经在运行"
}
}
}
},
"Inject Tools Not Found" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "未找到注入工具"
}
}
}
},
"Inject tools not found, it may be caused by network issues or the tools are not available. Please try again later." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "未找到注入工具,可能是网络问题或工具不可用造成的,请稍后重试"
}
}
}
},
"Inject Tools Path Not Found" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "未找到注入工具路径"
}
}
}
},
"Injected" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "已注入"
}
}
}
},
"InjectGUI" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "InjectGUI"
}
}
}
},
"InjectGUI requires your password to perform administrative tasks." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "InjectGUI 需要您的密码来执行任务。"
}
}
}
},
"Injecting Finished" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "注入完成"
}
}
}
},
"InjectLib" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "InjectLib"
}
}
}
},
"It will only show when the injector is running." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "这仅在注入器运行时显示。"
}
}
}
},
"It's a abnormal situation, it shouldn't be running, please report to developer." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "这是异常情况,它不应该运行,请向开发人员报告。"
}
}
}
},
"It's important to read the Setapp inject document first before using the tool. Please" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "在使用该工具之前,首先阅读 Setapp 注入文档非常重要!"
}
}
}
},
"Local Mode" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "本地模式"
}
}
}
},
"No apps found." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "找不到软件。"
}
}
}
},
"OK" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "好的"
}
}
}
},
"Open in Finder" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "在访达中打开"
}
}
}
},
"Open Support Folder" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "打开应用支持文件夹"
}
}
}
},
"Password" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "密码"
}
}
}
},
"Please contact the developer if you see this view without running the injector." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "如果您在未运行注入器的情况下看到此视图,请联系开发人员。"
}
}
}
},
"Please enter your password to continue." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "请输入密码以继续"
}
}
}
},
"Please read the Setapp inject document first" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "请先阅读 Setapp 注入文档"
}
}
}
},
"Please try again." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "请重试"
}
}
}
},
"Progress: %lld%%" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "进度:%lld%%"
}
}
}
},
"Read the document" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "阅读文件"
}
}
}
},
"Refresh app list" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "刷新软件列表"
}
}
}
},
"Released under GPLv3. Based on QiuChenly/InjectLib." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "根据 GPLv3 发布. 基于 QiuChenly/InjectLib."
}
}
}
},
"Remote InjectLib Branch" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "指定 InjectLib 远程分支"
}
}
}
},
"Remote InjectLib Commit" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "指定 InjectLib 提交 Commit"
}
}
}
},
"Remote InjectLib Git URL" : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "指定 InjectLib 远程 Git URL"
}
}
}
},
"Remote Mode" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "远程模式"
}
}
}
},
"Report an issue" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "提交 issue"
}
}
}
},
"Restarting the app will require your password again." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "重新启动应用程序将需要再次输入您的密码。"
}
}
}
},
"Run App" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "启动 App"
}
}
}
},
"Scanning Apps..." : {
"extractionState" : "manual",
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "扫描所有 App"
}
}
}
},
"Search all local app" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "搜索所有本地软件"
}
}
}
},
"Settings" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "设置"
}
}
}
},
"Settings About InjectGUI" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "关于 InjectGUI 的设置"
}
}
}
},
"Start Injecting" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "开始注入"
}
}
}
},
"Status" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "状态"
}
}
}
},
"Stop" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "停止"
}
}
}
},
"Stop Injecting" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "停止注入"
}
}
}
},
"Submit" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "提交"
}
}
}
},
"The app may not work properly if you stop injecting." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "如果您停止注入,该应用程序可能无法正常工作。"
}
}
}
},
"This app can use keygen to generate key" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "此应用程序可以使用keygen生成密钥"
}
}
}
},
"This app does not have extraShell" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "此软件没有 extraShell"
}
}
}
},
"This app has extraShell" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "此软件有extraShell"
}
}
}
},
"This app has sub app" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "此软件有附属软件"
}
}
}
},
"This app is supported by InjectLib." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "此应用程序由 InjectLib 支持。"
}
}
}
},
"This app need auto handle helper" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "此应用程序需要自动处理Helper"
}
}
}
},
"This app need deep codesign" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "此软件需要 deep codesign"
}
}
}
},
"This app needs to use tccutil to reset" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "此软件需要使用 tccutil 来重置"
}
}
}
},
"This is a status view for the injector." : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "这是注入器的状态视图。"
}
}
}
},
"This should not happen here, please report to the developer (Area: MainInject)" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "这里不应该发生这种情况请向开发人员报告区域MainInject"
}
}
}
},
"Toggle Sidebar" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "切换侧边栏"
}
}
}
},
"Universal Version (? Maybe)" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "通用版本(?应该是吧)"
}
}
}
},
"Update InjectLib & Tools" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "更新 InjectLib & 相关工具"
}
}
}
},
"URL" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "链接"
}
}
}
},
"Version: %@" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "版本:%@"
}
}
}
},
"Welcome to InjectGUI" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "欢迎使用 InjectGUI"
}
}
}
},
"Why you are seeing this?" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "你怎么看到的?"
}
}
}
},
"You're in" : {
"localizations" : {
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "您处于"
}
}
}
}
},
"version" : "1.0"
}

View File

@@ -1,174 +0,0 @@
/*
Localizable.strings
InjectGUI
Created by wibus on 2024/8/4.
*/
/* No comment provided by engineer. */
"" = "";
/* No comment provided by engineer. */
"%@" = "%@";
/* No comment provided by engineer. */
"%lld%%" = "%lld%%";
/* No comment provided by engineer. */
"About" = "About";
/* No comment provided by engineer. */
"Branch" = "Branch";
/* No comment provided by engineer. */
"By wibus. Made with ❤️" = "By wibus. Made with ❤️";
/* No comment provided by engineer. */
"Click here to close this view." = "Click here to close this view.";
/* No comment provided by engineer. */
"Commit" = "Commit";
/* No comment provided by engineer. */
"Compatible Version" = "Compatible Version";
/* No comment provided by engineer. */
"Copy Bundle ID" = "Copy Bundle ID";
/* No comment provided by engineer. */
"Enter password again" = "Enter password again";
/* No comment provided by engineer. */
"Error: %@" = "Error: %@";
/* No comment provided by engineer. */
"Finished. Close" = "Finished. Close";
/* No comment provided by engineer. */
"General" = "General";
/* No comment provided by engineer. */
"Information About InjectLib" = "Information About InjectLib";
/* No comment provided by engineer. */
"Inject" = "Inject";
/* No comment provided by engineer. */
"Inject Configurations" = "Inject Configurations";
/* No comment provided by engineer. */
"InjectGUI" = "InjectGUI";
/* No comment provided by engineer. */
"InjectGUI requires your password to perform administrative tasks." = "InjectGUI requires your password to perform administrative tasks.";
/* No comment provided by engineer. */
"InjectLib" = "InjectLib";
/* No comment provided by engineer. */
"It will only show when the injector is running." = "It will only show when the injector is running.";
/* No comment provided by engineer. */
"No apps found." = "No apps found.";
/* No comment provided by engineer. */
"Open in Finder" = "Open in Finder";
/* No comment provided by engineer. */
"Open Support Folder" = "Open Support Folder";
/* No comment provided by engineer. */
"Password" = "Password";
/* No comment provided by engineer. */
"Please contact the developer if you see this view without running the injector." = "Please contact the developer if you see this view without running the injector.";
/* No comment provided by engineer. */
"Please enter your password to continue." = "Please enter your password to continue.";
/* No comment provided by engineer. */
"Progress: %lld%%" = "Progress: %lld%%";
/* No comment provided by engineer. */
"Refresh app list" = "Refresh app list";
/* No comment provided by engineer. */
"Released under GPLv3. Based on QiuChenly/InjectLib." = "Released under GPLv3. Based on QiuChenly/InjectLib.";
/* No comment provided by engineer. */
"Restarting the app will require your password again." = "Restarting the app will require your password again.";
/* No comment provided by engineer. */
"Search all local app" = "Search all local app";
/* No comment provided by engineer. */
"Settings" = "Settings";
/* No comment provided by engineer. */
"Settings About InjectGUI" = "Settings About InjectGUI";
/* No comment provided by engineer. */
"Status" = "Status";
/* No comment provided by engineer. */
"Stop Injecting" = "Stop Injecting";
/* No comment provided by engineer. */
"Submit" = "Submit";
/* No comment provided by engineer. */
"This app can use keygen to generate key" = "This app can use keygen to generate key";
/* No comment provided by engineer. */
"This app does not have extraShell" = "This app does not have extraShell";
/* No comment provided by engineer. */
"This app has extraShell" = "This app has extraShell";
/* No comment provided by engineer. */
"This app has sub app" = "This app has sub app";
/* No comment provided by engineer. */
"This app is supported by InjectLib." = "This app is supported by InjectLib.";
/* No comment provided by engineer. */
"This app need auto handle helper" = "This app need auto handle helper";
/* No comment provided by engineer. */
"This app need deep codesign" = "This app need deep codesign";
/* No comment provided by engineer. */
"This app needs to use tccutil to reset" = "This app needs to use tccutil to reset";
/* No comment provided by engineer. */
"This is a status view for the injector." = "This is a status view for the injector.";
/* No comment provided by engineer. */
"Toggle Sidebar" = "Toggle Sidebar";
/* No comment provided by engineer. */
"Universal Version (? Maybe)" = "Universal Version (? Maybe)";
/* No comment provided by engineer. */
"Update InjectLib & Tools" = "Update InjectLib & Tools";
/* No comment provided by engineer. */
"URL" = "URL";
/* No comment provided by engineer. */
"Version: %@" = "Version: %@";
/* No comment provided by engineer. */
"Welcome to InjectGUI" = "Welcome to InjectGUI";
/* No comment provided by engineer. */
"Why you are seeing this?" = "Why you are seeing this?";
/* No comment provided by engineer. */
"Remote InjectLib Git URL" = "Remote InjectLib Git URL";
/* No comment provided by engineer. */
"Remote InjectLib Branch" = "Remote InjectLib Branch";
/* No comment provided by engineer. */
"Remote InjectLib Commit" = "Remote InjectLib Commit";

View File

@@ -1,175 +0,0 @@
/*
Localizable.strings
InjectGUI
Created by JerryXU on 2024/8/4.
*/
/* No comment provided by engineer. */
"" = "";
/* No comment provided by engineer. */
"%@" = "%@";
/* No comment provided by engineer. */
"%lld%%" = "%lld%%";
/* No comment provided by engineer. */
"About" = "关于";
/* No comment provided by engineer. */
"Branch" = "分支";
/* No comment provided by engineer. */
"By wibus. Made with ❤️" = "由 wibus 制作. Made with ❤️";
/* No comment provided by engineer. */
"Click here to close this view." = "点击以关闭此视图";
/* No comment provided by engineer. */
"Commit" = "提交";
/* No comment provided by engineer. */
"Compatible Version" = "兼容的版本";
/* No comment provided by engineer. */
"Copy Bundle ID" = "复制 Bundle ID";
/* No comment provided by engineer. */
"Enter password again" = "请再次输入密码";
/* No comment provided by engineer. */
"Error: %@" = "错误:%@";
/* No comment provided by engineer. */
"Finished. Close" = "完成. 关闭";
/* No comment provided by engineer. */
"General" = "通用";
/* No comment provided by engineer. */
"Information About InjectLib" = "关于 InjectLib 的信息";
/* No comment provided by engineer. */
"Inject" = "注入";
/* No comment provided by engineer. */
"Inject Configurations" = "注入配置";
/* No comment provided by engineer. */
"InjectGUI" = "InjectGUI";
/* No comment provided by engineer. */
"InjectGUI requires your password to perform administrative tasks." = "InjectGUI 需要您的密码来执行任务。";
/* No comment provided by engineer. */
"InjectLib" = "InjectLib";
/* No comment provided by engineer. */
"It will only show when the injector is running." = "这仅在注入器运行时显示。";
/* No comment provided by engineer. */
"No apps found." = "找不到软件。";
/* No comment provided by engineer. */
"Open in Finder" = "在访达中打开";
/* No comment provided by engineer. */
"Open Support Folder" = "打开应用支持文件夹";
/* No comment provided by engineer. */
"Password" = "密码";
/* No comment provided by engineer. */
"Please contact the developer if you see this view without running the injector." = "如果您在未运行注入器的情况下看到此视图,请联系开发人员。";
/* No comment provided by engineer. */
"Please enter your password to continue." = "请输入密码以继续";
/* No comment provided by engineer. */
"Progress: %lld%%" = "进度:%lld%%";
/* No comment provided by engineer. */
"Refresh app list" = "刷新软件列表";
/* No comment provided by engineer. */
"Released under GPLv3. Based on QiuChenly/InjectLib." = "根据 GPLv3 发布. 基于 QiuChenly/InjectLib。";
/* No comment provided by engineer. */
"Restarting the app will require your password again." = "重新启动应用程序将需要再次输入您的密码。";
/* No comment provided by engineer. */
"Search all local app" = "搜索所有本地软件";
/* No comment provided by engineer. */
"Settings" = "设置";
/* No comment provided by engineer. */
"Settings About InjectGUI" = "关于 InjectGUI 的设置";
/* No comment provided by engineer. */
"Status" = "状态";
/* No comment provided by engineer. */
"Stop Injecting" = "停止注入";
/* No comment provided by engineer. */
"Submit" = "提交";
/* No comment provided by engineer. */
"This app can use keygen to generate key" = "此应用程序可以使用keygen生成密钥";
/* No comment provided by engineer. */
"This app does not have extraShell" = "此软件没有 extraShell";
/* No comment provided by engineer. */
"This app has extraShell" = "此软件有extraShell";
/* No comment provided by engineer. */
"This app has sub app" = "此软件有附属软件";
/* No comment provided by engineer. */
"This app is supported by InjectLib." = "此应用程序由 InjectLib 支持。";
/* No comment provided by engineer. */
"This app need auto handle helper" = "此应用程序需要自动处理Helper";
/* No comment provided by engineer. */
"This app need deep codesign" = "此软件需要 deep codesign";
/* No comment provided by engineer. */
"This app needs to use tccutil to reset" = "此软件需要使用 tccutil 来重置";
/* No comment provided by engineer. */
"This is a status view for the injector." = "这是注入器的状态视图。";
/* No comment provided by engineer. */
"Toggle Sidebar" = "切换侧边栏";
/* No comment provided by engineer. */
"Universal Version (? Maybe)" = "通用版本(?应该是吧)";
/* No comment provided by engineer. */
"Update InjectLib & Tools" = "更新 InjectLib & 相关工具";
/* No comment provided by engineer. */
"URL" = "链接";
/* No comment provided by engineer. */
"Version: %@" = "版本:%@";
/* No comment provided by engineer. */
"Welcome to InjectGUI" = "欢迎使用 InjectGUI";
/* No comment provided by engineer. */
"Why you are seeing this?" = "你怎么看到的?";
/* No comment provided by engineer. */
"Remote InjectLib Git URL" = "指定 InjectLib 远程 Git URL";
/* No comment provided by engineer. */
"Remote InjectLib Branch" = "指定 InjectLib 远程分支";
/* No comment provided by engineer. */
"Remote InjectLib Commit" = "指定 InjectLib 提交 Commit";

View File

@@ -55,15 +55,15 @@ struct AdminPrivilegeView: View {
}
func checkAndSavePassword(password: String) {
executor.password = password
executor.password = password.encode()
executor.executeAdminCommand("sudo -v")
.sink(receiveCompletion: { completion in
if case .failure = completion {
let alert = NSAlert()
alert.messageText = "Incorrect password"
alert.informativeText = "Please try again."
alert.messageText = String(localized: "Incorrect password")
alert.informativeText = String(localized: "Please try again.")
alert.alertStyle = .warning
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: String(localized: "OK"))
alert.runModal()
}
}, receiveValue: { _ in

View File

@@ -35,6 +35,7 @@ struct AppDetailView: View {
if getAppDetailFromSoftwareManager != nil {
self.appDetail = SoftwareManager.shared.appListCache[appId]!
} else {
print("[*] Can't find app detail in SoftwareManager, it's a abnormal situation, please report to developer. appId: \(appId)")
self.appDetail = AppDetail(name: appInjectConfDetail?.packageName.allStrings.first ?? "", identifier: appInjectConfDetail?.packageName.allStrings.first ?? "", version: "", path: "", executable: "", icon: NSImage())
}
// self._appDetail = State(wrappedValue: SoftwareManager.shared.appListCache[appId] ?? AppDetail(name: "", identifier: "", version: "", path: "", icon: NSImage()))
@@ -54,7 +55,7 @@ struct AppDetailView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.onAppear {
let inInjectLibList = injectConfiguration.checkPackageIsSupported(package: appId)
self.compatibility = Compatibility(
compatibility = Compatibility(
id: appId,
inInjectLibList: inInjectLibList
)
@@ -70,8 +71,15 @@ struct AppDetailView: View {
.frame(width: 64, height: 64)
.cornerRadius(4)
VStack(alignment: .leading, spacing: 4) {
Text(appDetail.name)
.font(.headline)
HStack {
Text(appDetail.name)
.font(.headline)
Label("Injected", systemImage: "checkmark.circle.fill")
.font(.caption2)
.foregroundStyle(.green)
.opacity(appDetail.isInjected ? 1 : 0)
}
Text(appDetail.identifier)
.font(.subheadline)
.foregroundColor(.secondary)
@@ -87,10 +95,10 @@ struct AppDetailView: View {
injector.startInjectApp(package: appId)
} else {
let alert = NSAlert()
alert.messageText = "Inject is running"
alert.informativeText = "It's a abnormal situation, it shouldn't be running, please report to developer."
alert.messageText = String(localized: "Inject is running")
alert.informativeText = String(localized: "It's a abnormal situation, it shouldn't be running, please report to developer.")
alert.alertStyle = .warning
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: String(localized: "OK"))
alert.runModal()
}
}
@@ -145,8 +153,8 @@ struct AppDetailView: View {
}
HStack {
Image(systemName: (appInjectConfDetail?.componentApp ?? []).count > 0 ? CompatibilityIcon.compatible.rawValue : CompatibilityIcon.incompatible.rawValue)
.foregroundColor((appInjectConfDetail?.componentApp ?? []).count > 0 ? .green : .red)
Image(systemName: !(appInjectConfDetail?.componentApp ?? []).isEmpty ? CompatibilityIcon.compatible.rawValue : CompatibilityIcon.incompatible.rawValue)
.foregroundColor(!(appInjectConfDetail?.componentApp ?? []).isEmpty ? .green : .red)
HStack {
Text("This app has sub app")
@@ -165,8 +173,8 @@ struct AppDetailView: View {
}
HStack {
Image(systemName: (appInjectConfDetail?.tccutil?.allStrings ?? []).count > 0 ? CompatibilityIcon.compatible.rawValue : CompatibilityIcon.incompatible.rawValue)
.foregroundColor((appInjectConfDetail?.tccutil?.allStrings ?? []).count > 0 ? .green : .red)
Image(systemName: !(appInjectConfDetail?.tccutil?.allStrings ?? []).isEmpty ? CompatibilityIcon.compatible.rawValue : CompatibilityIcon.incompatible.rawValue)
.foregroundColor(!(appInjectConfDetail?.tccutil?.allStrings ?? []).isEmpty ? .green : .red)
HStack {
Text("This app needs to use tccutil to reset")

View File

@@ -4,6 +4,7 @@
//
// Created by wibus on 2024/7/19.
//
import SwiftUI
struct ContentView: View {
@@ -18,7 +19,7 @@ struct ContentView: View {
}
.navigationTitle(Constants.appName)
WelcomeView()
AboutView()
}
.toolbar {
ToolbarItem(placement: .navigation) {

View File

@@ -1,13 +1,13 @@
//
// WelcomeView.swift
// AboutView.swift
// InjectGUI
//
// Created by wibus on 2024/7/20.
// Created by Licardo on 2024/8/9.
//
import SwiftUI
struct WelcomeView: View {
struct AboutView: View {
var version: String {
var ret = "Version: " +
(Constants.appVersion)
@@ -20,7 +20,8 @@ struct WelcomeView: View {
}
var body: some View {
ZStack {
VStack {
Spacer()
VStack(spacing: 4) {
Image("Avatar")
.antialiased(true)
@@ -43,25 +44,30 @@ struct WelcomeView: View {
Text("By wibus. Made with ❤️")
.foregroundColor(.secondary)
}
Spacer().frame(height: 24)
}
Spacer()
VStack {
Spacer()
Text(version)
Text("Released under GPLv3. Based on QiuChenly/InjectLib.")
.font(.system(size: 12, weight: .semibold, design: .rounded))
.opacity(0.5)
.onTapGesture {
let alert = NSAlert()
alert.messageText = "InjectGUI"
alert.informativeText = "You're in \(injectConfiguration.mode == injectConfigurationMode.local ? "Local Mode" : "Remote Mode")."
alert.addButton(withTitle: "OK")
alert.runModal()
}
}
.onTapGesture {
let alert = NSAlert()
alert.messageText = "InjectGUI"
alert.informativeText = String(localized: "You're in") + (injectConfiguration.mode == injectConfigurationMode.local ? String(localized: "Local Mode") : String(localized: "Remote Mode"))
alert.addButton(withTitle: String(localized: "OK"))
alert.runModal()
}
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.tabItem {
Label("About", systemImage: "info.circle")
}
}
}
#Preview {
AboutView()
}

View File

@@ -1,16 +1,15 @@
//
// SettingsView.swift
// GeneralView.swift
// InjectGUI
//
// Created by wibus on 2024/7/20.
// Created by Licardo on 2024/8/9.
//
import Foundation
import SwiftUI
struct SettingsView: View {
struct GeneralView: View {
@StateObject var configuration = Configuration.shared
var version: String {
var ret = "Version: " +
(Constants.appVersion)
@@ -21,34 +20,8 @@ struct SettingsView: View {
#endif
return ret
}
private enum Tabs: Hashable {
case general
case about
}
var body: some View {
return TabView {
generalView
aboutView
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
func SettingItemView(_ title: String, @ViewBuilder content: @escaping () -> some View) -> some View {
HStack {
Text(title)
Spacer()
content()
}
.padding(10)
.frame(maxWidth: .infinity, alignment: .leading)
.background(.secondary.opacity(0.1))
.cornerRadius(6)
}
var generalView: some View {
ScrollView {
VStack(alignment: .leading) {
// Project info
@@ -181,47 +154,17 @@ struct SettingsView: View {
.tabItem {
Label("General", systemImage: "gear")
}
.tag(Tabs.general)
}
var aboutView: some View {
ZStack {
VStack(spacing: 4) {
Image("Avatar")
.antialiased(true)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 128, height: 128)
Spacer().frame(height: 16)
if #available(macOS 13.0, *) {
Text("InjectGUI")
.font(.system(.title2, design: .rounded, weight: .bold))
Text("By wibus. Made with ❤️")
.font(.system(.body, design: .rounded, weight: .bold))
.foregroundColor(.secondary)
} else {
Text("Welcome to InjectGUI")
.font(.title)
.fontWeight(.bold)
Text("By wibus. Made with ❤️")
.foregroundColor(.secondary)
}
Spacer().frame(height: 24)
}
VStack {
Spacer()
Text("Released under GPLv3. Based on QiuChenly/InjectLib.")
.font(.system(size: 12, weight: .semibold, design: .rounded))
.opacity(0.5)
}
.padding()
func SettingItemView(_ title: String, @ViewBuilder content: @escaping () -> some View) -> some View {
HStack {
Text(title)
Spacer()
content()
}
.tabItem {
Label("About", systemImage: "info.circle")
}
.tag(Tabs.about)
.padding(10)
.frame(maxWidth: .infinity, alignment: .leading)
.background(.secondary.opacity(0.1))
.cornerRadius(6)
}
}

View File

@@ -0,0 +1,25 @@
//
// SettingsView.swift
// InjectGUI
//
// Created by wibus on 2024/7/20.
//
import Foundation
import SwiftUI
struct SettingsView: View {
private enum Tabs: Hashable {
case general
case about
}
var body: some View {
return TabView {
GeneralView().tag(Tabs.general)
AboutView().tag(Tabs.about)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}

View File

@@ -38,7 +38,7 @@ struct SidebarView: View {
//
ZStack {
if softwareManager.isLoading {
ProgressView("Scanning apps...")
ProgressView("Scanning Apps...")
} else if filteredApps.isEmpty {
Text("No apps found.")
.font(.headline)
@@ -55,13 +55,11 @@ struct SidebarView: View {
}
.onChange(of: injectConfiguration.remoteConf) { _ in
updateFilteredApps()
softwareManager.refreshAppList()
}
.onChange(of: searchText) { _ in
updateFilteredApps()
}
.onAppear {
softwareManager.refreshAppList()
}
}
private var appList: some View {
@@ -75,8 +73,17 @@ struct SidebarView: View {
.frame(width: 32, height: 32)
.cornerRadius(4)
VStack(alignment: .leading) {
Text(app.detail.name)
.font(.headline)
HStack {
Text(app.detail.name)
.font(.headline)
Label("Injected", systemImage: "checkmark.circle.fill")
.font(.caption2)
.foregroundStyle(.green)
.labelStyle(.iconOnly)
.opacity(app.detail.isInjected ? 1 : 0)
}
Text(app.detail.identifier)
.font(.subheadline)
.foregroundColor(.secondary)
@@ -88,6 +95,9 @@ struct SidebarView: View {
.padding(.vertical, 4)
}
.contextMenu {
Button("Run App") {
NSWorkspace.shared.openApplication(at: URL(fileURLWithPath: app.detail.path).deletingLastPathComponent(), configuration: NSWorkspace.OpenConfiguration())
}
Button("Open in Finder") {
NSWorkspace.shared.selectFile(nil, inFileViewerRootedAtPath: app.detail.path)
}
@@ -109,9 +119,9 @@ struct SidebarView: View {
return apps
.filter { app in
injectConfiguration.checkPackageIsSupported(package: app.detail.identifier) &&
(searchText.isEmpty ||
app.detail.name.lowercased().contains(searchText.lowercased()) ||
app.detail.identifier.lowercased().contains(searchText.lowercased()))
(searchText.isEmpty ||
app.detail.name.lowercased().contains(searchText.lowercased()) ||
app.detail.identifier.lowercased().contains(searchText.lowercased()))
}
.sorted { $0.detail.name < $1.detail.name }
}

View File

@@ -12,11 +12,10 @@ struct StatusView: View {
@Environment(\.dismiss) var dismiss // dismiss the sheet
@StateObject var injector = Injector.shared
@State var appDetail: AppDetail = .init(name: "", identifier: "", version: "", path: "", executable: "", icon: NSImage())
var body: some View {
VStack(alignment: .leading, spacing: 10) {
if appDetail.name.isEmpty {
if self.injector.appDetail?.name == nil {
Text("Why you are seeing this?")
.font(.title)
.bold()
@@ -45,18 +44,18 @@ struct StatusView: View {
// MARK: - App Info Display Box
HStack(spacing: 20) {
Image(nsImage: appDetail.icon)
Image(nsImage: self.injector.appDetail!.icon)
.resizable()
.frame(width: 64, height: 64)
.cornerRadius(4)
// Spacer()
VStack(alignment: .leading, spacing: 4) {
Text(appDetail.name)
Text(self.injector.appDetail!.name)
.font(.headline)
Text(appDetail.identifier)
Text(self.injector.appDetail!.identifier)
.font(.subheadline)
.foregroundColor(.secondary)
Text("Version: \(appDetail.version)")
Text("Version: \(self.injector.appDetail!.version)")
.font(.caption)
.foregroundColor(.secondary)
}
@@ -138,10 +137,10 @@ struct StatusView: View {
return
}
let alert = NSAlert()
alert.messageText = "Are you sure you want to stop injecting?"
alert.informativeText = "The app may not work properly if you stop injecting."
alert.addButton(withTitle: "Stop")
alert.addButton(withTitle: "Cancel")
alert.messageText = String(localized: "Are you sure you want to stop injecting?")
alert.informativeText = String(localized: "The app may not work properly if you stop injecting.")
alert.addButton(withTitle: String(localized: "Stop"))
alert.addButton(withTitle: String(localized: "Cancel"))
alert.beginSheetModal(for: NSApp.keyWindow!) { response in
if response == .alertFirstButtonReturn {
injector.stopInjectApp()
@@ -155,20 +154,15 @@ struct StatusView: View {
.frame(maxWidth: .infinity)
}
}
.onChange(of: injector.stage.appId) { appId in
guard let appDetail = softwareManager.appListCache[appId] else {
return
}
self.appDetail = appDetail
}
.frame(minWidth: 350, minHeight: appDetail.name.isEmpty ? 200 : 400)
// .onChange(of: injector.isRunning) { _ in
// let appId = injector.stage.appId
// guard let self.injector.appDetail = softwareManager.appListCache[appId] else {
// return
// }
// self.self.injector.appDetail = self.injector.appDetail
// }
.frame(minWidth: 350, minHeight: 400)
.padding()
.onAppear {
guard let appDetail = softwareManager.appListCache[injector.stage.appId] else {
return
}
self.appDetail = appDetail
}
.background(Color(.windowBackgroundColor))
}
}

View File

@@ -1,9 +1,17 @@
> [!IMPORTANT]
>
> InjectGUI is about to complete its mission and reach the end of its development.
>
> Where is the next generation? Stay tuned!
<p align="center">
<p align="center">
<img width="150" height="150" src="/InjectGUI/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png" alt="Logo">
</p>
<h1 align="center"><b>InjectGUI</b></h1>
<p align="center">
<b><small><code>STOP MAINTENANCE</code></small><b>
<br>
macOS Integrated Injection Framework (GUI version)
<br />
<br />
@@ -13,23 +21,24 @@
</p>
</p>
## Stop maintenance
> I know that InjectGUI currently has a lot of injection problems on some systems and models, but it takes a lot of time to solve it. Maybe I don't have so much time, QiuChenly will launch more powerful software.
>
> So far, thanks QiuChenlyOpenSource.
## ⚠️ Warning
_**New to Swift. Poor code quality.**_
## 🌍 Localization
The localization of InjectGUI is in progress, and some content has not been translated yet. PRs are welcome.
- [x] English
- [ ] Chinese (Simplified) (Not yet)
Once you meet any problems with *InjectGUI*, You should back to the [InjectLib](https://github.com/QiuChenly/InjectLib) project to continue your operation.
## 🌟 Features
- [x] Basic Functionality
- [x] Backup Executable File
- [x] Check Permission and kill process
- [x] Handle Keygen
- [ ] Handle Keygen *(Coming soon)*
- [x] Handle Deep Code Sign
- [x] Handle Tccutil
- [x] Handle Extra Shell