Compare commits

...

42 Commits
v1.6 ... main

Author SHA1 Message Date
Li
01d3f6bf0f Update readme.md 2024-06-01 09:10:52 +00:00
Li
3a0ff41699 update native modules and stuff 2024-06-01 20:31:41 +12:00
ddd8055465 Update readme.md 2024-06-01 08:30:47 +00:00
Li
c6b2398a36 Merge pull request 'major changes to the readme, add new category' (#2) from randomhuman-patch-1 into main
Reviewed-on: #2
2024-05-31 01:36:21 +00:00
44fc4cbb7b major changes to the readme, add new category
made *major* changes to the readme.md, so putting it in a pull request instead of directly commiting like i always would otherwise
added a new section to show which patches exist, and what they actually do
2024-05-31 01:33:37 +00:00
Li
aecd07844d remove uneeeded imports 2024-05-27 16:05:25 +12:00
Li
3c6499b247 refactor more? 2024-05-27 16:03:36 +12:00
Li
333250c416 Fix shared prefs generation 2024-05-27 15:21:05 +12:00
Li
0bbbb239ef Refactor like, alot 2024-05-27 15:17:31 +12:00
Li
f087d5160c Update to v2.0 2024-05-27 02:50:39 +12:00
Li
3cd316a805 Update readme 2024-05-26 11:11:48 +12:00
Li
6e597b9063 Merge branch 'main' of https://silica.codes/Li/NoPsmDrm-Android 2024-05-26 11:10:04 +12:00
Li
e6bda25715 Bypass PS1 certified check (xposed) 2024-05-26 11:09:49 +12:00
d1a07e035d Update readme.md 2024-05-24 19:18:46 +00:00
Li
8e6f8b09b1 fix busyboxs 2024-05-24 18:44:45 +12:00
Li
5579925e66 Merge branch 'main' of https://silica.codes/Li/NoPsmDrm-Android 2024-05-24 18:40:37 +12:00
Li
3366036bf0 Create xposed module to hook psm network info 2024-05-24 18:37:11 +12:00
Li
a6dc9fbd93 oops forgot this 2024-05-20 09:07:01 +00:00
Li
83b155437f Merge branch 'main' of https://silica.codes/Li/NoPsmDrm-Android 2024-05-20 14:37:32 +12:00
Li
6fcf0643c7 supress warnings in gradle 2024-05-20 14:37:18 +12:00
Li
5dfdea82d0 Update readme.md 2024-05-20 02:11:46 +00:00
d3838fdb28 Update readme.md 2024-05-19 23:25:22 +00:00
Li
3305933dc9 Update readme.md 2024-05-19 23:18:41 +00:00
Li
d904e40b77 Update readme.md 2024-05-19 23:18:09 +00:00
Li
a416efaebc Fix some issues, skip failed to download message in PSM if zpak exists .. 2024-05-20 10:59:34 +12:00
Li
513f30b1da aa 2024-05-19 18:30:52 +12:00
Li
584e6c913d Remove AndroidX 2024-05-19 17:59:42 +12:00
Li
6e157330cf Fix crashing (temporary fix) 2024-05-19 15:29:12 +12:00
Li
0fce254917 catch more possible errors from PSM 2024-05-19 15:23:00 +12:00
Li
f821147c21 Make game start even if ZPAK download fails.: 2024-05-19 14:18:57 +12:00
Li
f7555574d6 Make np ticket generation more correct -ty olebeck 2024-05-19 13:07:40 +12:00
Li
6acd9fb269 Add NoPs1Drm !! 2024-05-17 17:32:46 +12:00
Li
780278c9fa aaa 2024-05-16 14:07:50 +12:00
Li
8bc60d1083 Update Busybox to a newer one 2024-05-15 18:14:06 +12:00
Li
68e38ba358 Merge branch 'main' of https://silica.codes/Li/NoPsmDrm-Android 2024-05-13 21:46:39 +12:00
Li
9fae0324af update to use modern android apis ig 2024-05-13 21:46:20 +12:00
Li
08b6881b63 Update Modern-Android.md 2024-05-11 05:16:43 +00:00
3e3dfb1a50 Update readme.md 2024-05-10 22:14:50 +00:00
Li
1b93b198b2 Merge branch 'main' of https://silica.codes/Li/NoPsmDrm-Android 2024-05-11 03:02:10 +12:00
Li
bba0b4e3ea allow parsing plaintext xml file too 2024-05-11 02:54:50 +12:00
Li
997c50ec3e Update readme.md 2024-05-10 14:31:11 +00:00
Li
d6ab6ee992 Update readme.md 2024-05-10 14:29:23 +00:00
71 changed files with 2496 additions and 754 deletions

4
.gitignore vendored
View File

@ -16,4 +16,6 @@
local.properties
libsuperuser/build/*
app/build/*
app/release/*
app/release/*
libNpTicket/*

View File

@ -1 +1 @@
nopsmdrm
nopssdrm

View File

@ -10,7 +10,7 @@
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/libAXML" />
<option value="$PROJECT_DIR$/libABX" />
<option value="$PROJECT_DIR$/libsuperuser" />
</set>
</option>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="UNCHECKED_WARNING" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

177
LICENSE Normal file
View File

@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -30,7 +30,9 @@ if your on android >14, this *has* to be done via ADB with USB Debugging
because that gives you access to the `--bypass-low-target-sdk-block` argument
(You will get a Play Protect warning for both of these)
```adb install --bypass-low-target-sdk-block PSM.apk```
```adb install --bypass-low-target-sdk-block NoPsmDrm-Android.apk```
Once installed, you need to tap on both apps and goto permissions and grant *everything*

View File

@ -4,16 +4,16 @@ plugins {
android {
namespace 'com.psmreborn.nopsmdrm'
//noinspection GradleDependency were targeting the xperia play
//noinspection GradleDependency needs to work properly on android 2.x
compileSdk 10
defaultConfig {
applicationId "com.psmreborn.nopsmdrm"
minSdk 10
//noinspection ExpiredTargetSdkVersion dont care about google play
targetSdk 10
versionCode 6
versionName "1.6"
//noinspection ExpiredTargetSdkVersion app is not distributed on google play -- this app breaks all its rules anyway
targetSdk 23
versionCode 200
versionName "2.0"
}
buildTypes {
@ -25,13 +25,20 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
dependencies {
applicationVariants.configureEach { variant ->
variant.outputs.configureEach {
def versionName = variant.versionName
outputFileName = "${applicationName}.apk"
}
}
}
dependencies {
implementation project(':libsuperuser')
implementation project(':libAXML')
implementation project(':libABX')
implementation 'yuv.pink:npticket:1.6'
//noinspection GradleDependency new version doesnt support xperia play.
compileOnly 'de.robv.android.xposed:api:53'
//noinspection GradleDependency new version doesnt support xperia play.
implementation 'com.android.support:support-core-utils:25.0.0'
}

View File

@ -1,25 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
xmlns:tools="http://schemas.android.com/tools" tools:ignore="ProtectedPermissions, ScopedStorage">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.playstation.psstore.permission" />
<application
android:allowBackup="true"
android:icon="@drawable/app_icon"
android:label="@string/app_name"
tools:targetApi="10">
<activity android:name="com.psmreborn.nopsmdrm.MainActivity">
android:label="@string/app_name">
<activity android:name="com.psmreborn.nopsmdrm.MainActivity" android:label="@string/name_nopsmdrm">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:launchMode="singleTop" android:name="com.psmreborn.nops1drm.LicenseCheckActivity" android:label="@string/name_nops1drm_ticket" android:icon="@drawable/nops1drm_ticket_icon" android:screenOrientation="landscape">
<intent-filter>
<action android:name="com.playstation.android.intent.action.CHECK_ENTITLEMENT"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity android:name="com.psmreborn.nops1drm.DownloadActivity" android:label="@string/name_nops1drm_update" android:icon="@drawable/nops1drm_update_icon" android:screenOrientation="landscape" android:windowSoftInputMode="stateAlwaysHidden">
<intent-filter>
<action android:name="com.playstation.android.intent.action.DOWNLOAD_ZPAK"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="Java patches to PSM.apk" />
<meta-data
android:name="xposedminversion"
android:value="53" />
<meta-data
android:name="xposedscope"
android:resource="@array/scope" />
</application>
</manifest>

View File

@ -0,0 +1 @@
com.psmreborn.xposed.JavaPatch

View File

@ -0,0 +1,142 @@
package com.psmreborn.nops1drm;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import com.psmreborn.nopsmdrm.MainActivity;
import com.psmreborn.nopsmdrm.R;
import com.psmreborn.shared.Downloader;
import com.psmreborn.shared.Logger;
import java.io.File;
public class DownloadActivity extends Activity {
private static final int RESPONSE_CODE = 20000305;
private String entitlementId = null;
private String appName = null;
private boolean forceDownload = false;
private String skuId = null;
private String returnToActivity = null;
private String returnToPackage = null;
private String zpakLocation = null;
@SuppressLint("WrongConstant")
public void respond(int resp){
Log.i("ZpakDl", "Responding with response code: "+resp);
Intent resIntent = new Intent();
this.setResult(resp, resIntent);
this.finish();
}
private void forwardIntentToPsm(){
Log.i("ZpakDl", "Forwarding intent to PSM ...");
Intent respondIntent = new Intent("com.playstation.android.intent.action.DOWNLOAD_ZPAK");
respondIntent.putExtra("entitlement_id", this.entitlementId);
respondIntent.putExtra("app_name", this.appName);
respondIntent.putExtra("force_download", this.forceDownload);
if(this.returnToPackage != null)
respondIntent.putExtra("return_to_package", this.returnToPackage);
if(this.returnToActivity != null)
respondIntent.putExtra("return_to_activity", this.returnToActivity);
if(this.skuId != null)
respondIntent.putExtra("sku_id", this.skuId);
respondIntent.setPackage(this.getResources().getString(R.string.psm_app_package_id));
startActivityForResult(respondIntent, RESPONSE_CODE);
}
private boolean checkZpakExist(){
Log.i("ZpakDl", "Checking for zpak file.");
File zpakLoc = new File(this.zpakLocation);
if(!zpakLoc.exists()) return false;
String[] zpaks = zpakLoc.list();
if(zpaks == null) return false;
for(String zpak : zpaks){
Log.i("ZpakDl", "Chceking file: "+zpak);
if(zpak.endsWith(".zpak")){
return true;
}
}
return false;
}
@Override
protected void onActivityResult(int res, int res_code, Intent intent) {
if(res == RESPONSE_CODE) {
Logger.logText( "RES", String.valueOf(res_code));
if(res_code == 0) {
if(checkZpakExist()){
Log.i("ZpakDownload", "Update failed, faking sucess code.");
res_code = -1;
}
}
respond(res_code);
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = this.getIntent();
this.entitlementId = intent.getStringExtra("entitlement_id");
this.appName = intent.getStringExtra("app_name");
this.returnToPackage = intent.getStringExtra("return_to_package");
this.returnToActivity = intent.getStringExtra("return_to_activity");
this.forceDownload = intent.getBooleanExtra("force_download", false);
this.skuId = intent.getStringExtra("sku_id");
this.zpakLocation = new File(new File(new File(new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data"), this.returnToPackage), "files"), "content").getAbsolutePath();
Logger.logText("entitlement_id", this.entitlementId);
Logger.logText("app_name", this.appName);
Logger.logText("return_to_package", this.returnToPackage);
Logger.logText("return_to_activity", this.returnToActivity);
Logger.logText("force_download", String.valueOf(this.forceDownload));
Logger.logText("sku_id", this.skuId);
Logger.logText("zpak_location", this.zpakLocation);
Handler handler = new Handler(this.getMainLooper());
new Thread(() -> {
if(!checkZpakExist()){
handler.post(() -> {
new AlertDialog.Builder(this)
.setTitle("zPAK not found!")
.setMessage("No zPAK was found in:\n\""+this.zpakLocation+"\"\nI can start PSM to download it\nOr i could just tell "+this.appName+" to completely ignore it\n(this will probably crash the game)")
.setCancelable(false)
.setPositiveButton("Launch PSM.", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
forwardIntentToPsm();
}
})
.setNegativeButton("Ignore it.", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
respond(-1);
}
}).show();
});
}
else{
handler.post(() -> {
respond(-1);
});
}
}).start();
}
}

View File

@ -0,0 +1,99 @@
package com.psmreborn.nops1drm;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import com.psmreborn.nopsmdrm.R;
import com.psmreborn.shared.Files;
import com.psmreborn.shared.Logger;
import com.psmreborn.shared.Root;
public class LicenseCheckActivity extends Activity {
private static final int RESPONSE_CODE = 19941203;
private String entitlementId = null;
private byte[] inputData = null;
private ProgressDialog dialog = null;
public void respond(byte[] response, int resp){
Intent resIntent = new Intent();
resIntent.putExtra("result_data", response);
this.setResult(resp, resIntent);
this.dialog.dismiss();
this.finish();
}
@Override
protected void onActivityResult(int res, int res_code, Intent intent) {
if(res == RESPONSE_CODE) {
byte[] result_data = intent.getByteArrayExtra("result_data");
Logger.logBytes("RESULT_DATA", result_data);
Logger.logText( "RES", String.valueOf(res_code));
respond(result_data, res_code);
}
}
private void forwardIntentToPsm() {
Log.i("TicketGen", "Forwarding intent to PSM ...");
Intent intent = new Intent("com.playstation.android.intent.action.CHECK_ENTITLEMENT");
intent.putExtra("entitlement_id", this.entitlementId);
intent.putExtra("input_data", this.inputData);
intent.setPackage(this.getResources().getString(R.string.psm_app_package_id));
startActivityForResult(intent, RESPONSE_CODE);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Read intent from pss game
this.entitlementId = this.getIntent().getStringExtra("entitlement_id");
this.inputData = this.getIntent().getByteArrayExtra("input_data");
this.dialog = new ProgressDialog(this);
this.dialog.setTitle("Generating NP Ticket ...");
this.dialog.setMessage("Faking entitlement: "+this.entitlementId);
this.dialog.setIndeterminate(true);
this.dialog.setCancelable(false);
this.dialog.show();
Logger.logText("ENTITLEMENT_ID", this.entitlementId);
Logger.logBytes("INPUT_DATA", this.inputData);
// Generate ticket and put it into com.playstation.psstore databases.
Handler handler = new Handler(this.getMainLooper());
new Thread(()->{
try {
Files.cleanupFiles(this);
if(Root.init(this)){
PsmStartupCache.addEntitlement(this, this.entitlementId);
handler.post(() ->{
this.forwardIntentToPsm();
});
}
else{
handler.post(() -> {
respond(new byte[]{}, 0);
});
}
} catch (Exception e) {
Log.e("LicenseCheckerActivity", e.toString());
handler.post(() -> {
respond(new byte[]{}, 0);
});
}
}).start();
}
}

View File

@ -0,0 +1,112 @@
package com.psmreborn.nops1drm;
import android.util.Log;
import com.psmreborn.shared.Logger;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.spec.ECGenParameterSpec;
import java.util.ArrayList;
import yuv.pink.npticket.BrokenTicketException;
import yuv.pink.npticket.Cipher;
import yuv.pink.npticket.Entitlement;
import yuv.pink.npticket.NPDate;
import yuv.pink.npticket.NPTicket;
import yuv.pink.npticket.Subject;
public class NpTicketGenerator {
private static KeyPair generateRsaKeys() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1536);
return kpg.generateKeyPair();
}
private static KeyPair generateEcKeys() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(new ECGenParameterSpec("secp521r1"), new SecureRandom());
return kpg.generateKeyPair();
}
public static boolean ticketExpired(byte[] ticket) {
try {
NPTicket nptik = new NPTicket();
nptik.parse(ticket, new Cipher(0x79F97BD), false);
return nptik.isExpired();
} catch (IOException e){
return true;
} catch (BrokenTicketException e) {
return true;
} catch (Exception e){
return true;
}
}
public static byte[] generateTicket(String entitlementId, String language, String onlineId, Long accountId) throws Exception {
NPTicket ticket = new NPTicket();
String serviceId = entitlementId.substring(0x0, 0x13);
Log.i("TicketGen", "Generating ticket... ("+serviceId+") ("+entitlementId+")");
// set ticket haeder
ticket.majorVersion = 4;
ticket.minorVersion = 0;
ticket.packetID = 1;
ticket.partialPacket = false;
ticket.jumboPacket = false;
// set ticket serial ..
Log.i("TicketGen", "Generating serial number...");
ticket.serial = new byte[0x14];
new SecureRandom().nextBytes(ticket.serial);
Logger.logBytes("TICKET_SERIAL", ticket.serial);
ticket.issuerID = 256;
ticket.issuedDate = System.currentTimeMillis();
ticket.notOnOrAfterDate = System.currentTimeMillis() + (1209600 * 1000);
Log.i("TicketGen", "Setting DOB...");
// set DOB
NPDate npDob = new NPDate();
npDob.year = 1999;
npDob.day = 1;
npDob.month = 1;
// set subject
Log.i("TicketGen", "Setting Subject...");
ticket.subject = new Subject(accountId, onlineId,
new byte[] { language.getBytes("UTF-8")[0], language.getBytes("UTF-8")[1], 0x00, 0x04 },
"d7", serviceId, npDob, 0x19000200, 0);
// set cookie
ticket.cookie = null;
// set entitlement
Log.i("TicketGen", "Setting Entitlements ...");
ticket.entitlements = new ArrayList<Entitlement>();
ticket.entitlements.add(new Entitlement(entitlementId, System.currentTimeMillis(), 0L, 0, 0, 0));
// set footer
Log.i("TicketGen", "Setting Footer...");
ticket.roles = null;
ticket.platform = "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000";
ticket.consoleID = new byte[0x40];
ticket.dontHaveRSASignature = false;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Log.i("TicketGen", "Writing ticket to memory stream...");
//TODO: fix ecdsa cipher, and actually generate a signature
ticket.writeTo(stream, new Cipher(0x79F97BD));
return stream.toByteArray();
}
}

View File

@ -0,0 +1,150 @@
package com.psmreborn.nops1drm;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.psmreborn.nopsmdrm.PsmSharedPrefs;
import com.psmreborn.nopsmdrm.R;
import com.psmreborn.shared.Logger;
import com.psmreborn.shared.Packages;
import com.psmreborn.shared.PsmApplication;
import com.psmreborn.shared.Root;
import com.psmreborn.shared.StringEncryptor;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;
public class PsmStartupCache {
private static StringEncryptor stringEncryptor = null;
private static String sha256HexStr(String str) {
String lowerStr = str.toLowerCase();
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(lowerStr.getBytes("UTF-8"));
byte[] digest = messageDigest.digest();
StringBuilder sb = new StringBuilder();
for (byte hexDigit : digest) {
sb.append(Integer.toHexString(hexDigit & 255));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
return null;
} catch (UnsupportedEncodingException e) {
return null;
}
}
private static void copyToPsm(Context ctx) throws Exception {
String psmDataDir = PsmApplication.getPsmAppInfo(ctx).dataDir;
String psmDatabasesDir = new File(psmDataDir, "databases").getAbsolutePath();
String psmStartContentDb = new File(psmDatabasesDir, "start_content.db").getAbsolutePath();
String myDataDir = Packages.getAppInfo(ctx, ctx.getPackageName()).dataDir;
String myDatabasesDir = new File(myDataDir, "databases").getAbsolutePath();
String myStartContentDb = new File(myDatabasesDir, "start_content.db").getAbsolutePath();
Root.mkdirAndChmodChown(psmDatabasesDir, 771, String.valueOf(PsmApplication.getPsmUid(ctx)));
Root.copyChmodAndChown(myStartContentDb, psmStartContentDb, 660, String.valueOf(PsmApplication.getPsmUid(ctx)));
}
private static void copyFromPsm(Context ctx) throws Exception {
String psmDataDir = PsmApplication.getPsmAppInfo(ctx).dataDir;
String psmDatabasesDir = new File(psmDataDir, "databases").getAbsolutePath();
String psmStartContentDb = new File(psmDatabasesDir, "start_content.db").getAbsolutePath();
String myDataDir = Packages.getAppInfo(ctx, ctx.getPackageName()).dataDir;
String myDatabasesDir = new File(myDataDir, "databases").getAbsolutePath();
String myStartContentDb = new File(myDatabasesDir, "start_content.db").getAbsolutePath();
Root.mkdirAndChmodChown(myDatabasesDir, 771, String.valueOf(Packages.getAppUid(ctx, ctx.getPackageName())));
Root.copyChmodAndChown(psmStartContentDb, myStartContentDb, 660, String.valueOf(Packages.getAppUid(ctx, ctx.getPackageName())));
}
private static int dbRemoveEntry(StartContentDbOpenHelper cdb, String entitlementId) {
Logger.logText("DELETE, ENTITLEMENT_ID", entitlementId);
SQLiteDatabase db = cdb.getWritableDatabase();
int v = db.delete("cache_table", "entitlement_id = ?", new String[]{entitlementId});
db.close();
return v;
}
private static byte[] dbGetEntry(StartContentDbOpenHelper cdb, String entitlementId){
Log.i("TicketGen", "dbGetEntry, "+entitlementId);
SQLiteDatabase db = cdb.getReadableDatabase();
Cursor query = db.query("cache_table", new String[] {"_id", "user_id", "entitlement_id", "ticket_data", "last_update"}, "entitlement_id = ?", new String[]{entitlementId}, null, null, null);
if (query == null || query.getCount() != 1) {
db.close();
return null;
} else {
query.moveToFirst();
byte[] ticketEnc = query.getBlob(query.getColumnIndex("ticket_data"));
query.close();
db.close();
return stringEncryptor.decrypt(ticketEnc);
}
}
private static void dbAddEntry(StartContentDbOpenHelper cdb, String userId, String entitlementId, byte[] ticket) throws Exception {
Log.i("TicketGen", "dbAddEntry, "+entitlementId);
SQLiteDatabase db = cdb.getWritableDatabase();
ContentValues values = new ContentValues();
byte[] encryptedTicket = stringEncryptor.encrypt(ticket);
String shaUserId = sha256HexStr(userId);
values.put("user_id", shaUserId);
values.put("entitlement_id", entitlementId);
values.put("ticket_data", encryptedTicket);
values.put("last_update", System.currentTimeMillis());
Logger.logText("USER_ID", shaUserId);
Logger.logText("ENTITLEMENT_ID", entitlementId);
Logger.logBytes("TICKET_DATA", ticket);
Logger.logBytes("ENCRYPTED_TICKET_DATA", encryptedTicket);
Long pos = db.insert("cache_table", null, values);
db.close();
if(pos <= -1)
throw new Exception("failed to insert");
}
public static void addEntitlement(Context ctx, String entitlement_id) throws Exception {
stringEncryptor = new StringEncryptor(PsmApplication.getAndroidIdOfPsm(ctx), PsmApplication.getPsmUid(ctx));
try{copyFromPsm(ctx);} catch (Exception ignored) { };
// get default values for onlineid, accountid, etc.
String email = stringEncryptor.decryptString(PsmApplication.getSharedPrefFromPsm(ctx, "SigninInfo", "SignedInUsername"), ctx.getResources().getString(R.string.default_email));
Long accountId = Long.valueOf(stringEncryptor.decryptString(PsmApplication.getSharedPrefFromPsm(ctx, "com.playstation.psstore_preferences", "last_signin_account_id"), String.valueOf(PsmSharedPrefs.getDefaultAccountId(ctx))));
String onlineId = ctx.getResources().getString(R.string.default_online_id);
// read psm start content.db file ..
StartContentDbOpenHelper scdb = new StartContentDbOpenHelper(ctx);
byte[] ticket = dbGetEntry(scdb, entitlement_id);
if(ticket == null){
Log.i("TicketGen", "No ticket found, generating new!");
dbAddEntry(scdb, email, entitlement_id, NpTicketGenerator.generateTicket(entitlement_id, Locale.getDefault().getLanguage(), onlineId, accountId));
}
else{
if(NpTicketGenerator.ticketExpired(ticket)) {
Log.i("TicketGen", "Previous ticket expired, generating new one!");
dbRemoveEntry(scdb, entitlement_id);
dbAddEntry(scdb, email, entitlement_id, NpTicketGenerator.generateTicket(entitlement_id, Locale.getDefault().getLanguage(), onlineId, accountId));
}
else{
Log.i("TicketGen", "Existing ticket is all good! continuing to PSM!");
}
}
scdb.close();
try{copyToPsm(ctx);} catch (Exception ignored) { };
}
}

View File

@ -0,0 +1,26 @@
package com.psmreborn.nops1drm;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
final class StartContentDbOpenHelper extends SQLiteOpenHelper {
public StartContentDbOpenHelper(Context context) {
this(context, "start_content.db");
}
private StartContentDbOpenHelper(Context context, String dbName) {
super(context, dbName, (SQLiteDatabase.CursorFactory) null, 1);
}
@Override // android.database.sqlite.SQLiteOpenHelper
public final void onCreate(SQLiteDatabase sqllite) {
sqllite.execSQL("CREATE TABLE IF NOT EXISTS cache_table (_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,user_id TEXT,entitlement_id TEXT NOT NULL,ticket_data BLOB NOT NULL,last_update LONG NOT NULL)");
}
@Override // android.database.sqlite.SQLiteOpenHelper
public final void onUpgrade(SQLiteDatabase sqllite, int i, int i2) {
sqllite.execSQL("DROP TABLE IF EXISTS cache_table");
sqllite.execSQL("CREATE TABLE IF NOT EXISTS cache_table (_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,user_id TEXT,entitlement_id TEXT NOT NULL,ticket_data BLOB NOT NULL,last_update LONG NOT NULL)");
}
}

View File

@ -1,6 +1,7 @@
package com.psmreborn.nopsmdrm;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
@ -10,10 +11,20 @@ import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import com.psmreborn.shared.PsmApplication;
import java.io.File;
import java.io.IOException;
public class DumpAllRifs {
private static void showErrorMessage(Activity ctx, String errorMsg){
new AlertDialog.Builder(ctx)
.setTitle("Error Occurred.")
.setMessage(errorMsg)
.setCancelable(false)
.setPositiveButton("Ok",null).show();
}
public static void setDumpAllFlagAndStartPsm(Activity ctx) {
ProgressDialog dialog = new ProgressDialog(ctx);
dialog.setTitle("Starting PSM ...");
@ -26,14 +37,26 @@ public class DumpAllRifs {
Handler handler = new Handler(ctx.getMainLooper());
File psmFolder = new File(Environment.getExternalStorageDirectory(), "psm");
File psmDumpAllFlagFile = new File(psmFolder, "dump_all");
File psmAndroidFolder = new File(new File(new File(new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data"), "com.playstation.psstore"), "files"), "psm");
File psmAndroidFolder = new File(new File(new File(new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data"), ctx.getResources().getString(R.string.psm_app_package_id)), "files"), "psm");
// stop psm
Helper.killPsm();
PsmApplication.killPsm(ctx);
if(!psmAndroidFolder.exists()){
handler.post(() -> {
dialog.dismiss();
showErrorMessage(ctx, psmAndroidFolder.getAbsolutePath()+" does not exist (do you have no games?)");
});
return;
}
try {
psmDumpAllFlagFile.createNewFile();
} catch (IOException e) {
handler.post(() -> {
dialog.dismiss();
showErrorMessage(ctx, e.toString());
});
return;
}
@ -47,7 +70,11 @@ public class DumpAllRifs {
}
}
if(titleId.equals("")){
if(titleId.isEmpty()) {
handler.post(() -> {
dialog.dismiss();
showErrorMessage(ctx, "No title id could be found.");
});
return;
}
@ -70,6 +97,11 @@ public class DumpAllRifs {
});
}
else{
handler.post(() -> {
dialog.dismiss();
showErrorMessage(ctx, "WifiManager was null");
});
return;
}
}).start();

View File

@ -3,20 +3,13 @@ package com.psmreborn.nopsmdrm;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import com.psmreborn.shared.PsmApplication;
import com.psmreborn.shared.Root;
import java.io.File;
import java.io.IOException;
import eu.chainfire.libsuperuser.Shell;
public class FixPermissions {
public static void changePermissionsToPsm(Activity ctx) {
@ -30,12 +23,12 @@ public class FixPermissions {
new Thread(() -> {
Handler handler = new Handler(ctx.getMainLooper());
Helper.killPsm();
PsmApplication.killPsm(ctx);
String androidDataFolder = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data").getAbsolutePath();
try {
String groupId = Root.getFileGroup(androidDataFolder);
String userId = String.valueOf(Helper.getPsmAppInfo().uid);
Root.chownRoot(new File(new File(new File(androidDataFolder, "com.playstation.psstore"), "files"), "psm").getAbsolutePath(), userId, groupId);
String userId = String.valueOf(PsmApplication.getPsmAppInfo(ctx).uid);
Root.chownRoot(new File(new File(new File(androidDataFolder, ctx.getResources().getString(R.string.psm_app_package_id)), "files"), "psm").getAbsolutePath(), userId, groupId);
handler.post(() -> {
dialog.dismiss();

View File

@ -1,117 +0,0 @@
package com.psmreborn.nopsmdrm;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.provider.Settings;
import android.util.Log;
import com.rosstonovsky.abxutils.BinaryXmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import eu.chainfire.libsuperuser.Shell;
public class Helper {
private static Context ctx;
public static void setContext(Context context){
ctx = context.getApplicationContext();
}
public static boolean isNoPsmDrmAlreadyInstalled() {
try {
if(new File(getPsmAppInfo().nativeLibraryDir, "libdefault_real.so").exists()){
return true;
}
} catch (PackageManager.NameNotFoundException e) {
return false;
}
return false;
}
public static boolean isPsmInstalled(){
try {
ctx.getPackageManager().getApplicationInfo("com.playstation.psstore", 0);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
return true;
}
public static String getAndroidIdOfPsm() throws Exception {
if(Build.VERSION.SDK_INT >= 26){
String[] files = Root.findFiles("/data/system", "settings_ssaid.xml");
File ssaidCacheFile = new File(ctx.getCacheDir(), "settings_ssaid.xml");
for(String ssaidFile : files){
Root.copyChmodAndChown(ssaidFile, ssaidCacheFile.getAbsolutePath(), 777, String.valueOf(getAppInfo(ctx.getPackageName()).uid));
FileInputStream stream = new FileInputStream(ssaidCacheFile);
// parse binary xml file ...
BinaryXmlPullParser xpp = new BinaryXmlPullParser();
xpp.setInput(stream, "UTF-8");
int eventType = xpp.getEventType();
while (eventType != BinaryXmlPullParser.END_DOCUMENT) {
if(eventType == BinaryXmlPullParser.START_TAG) {
if(xpp.getName().equalsIgnoreCase("setting")){
String packageName = xpp.getAttributeValue("package");
if(packageName != null && packageName.equalsIgnoreCase("com.playstation.psstore")){
return xpp.getAttributeValue("value");
}
}
}
eventType = xpp.next();
}
}
return null;
}
else {
return Settings.Secure.getString(ctx.getContentResolver(), "android_id");
}
}
public static int getPsmUid() {
try{
ApplicationInfo psmAppInfo = ctx.getPackageManager().getApplicationInfo("com.playstation.psstore", 0);
if(psmAppInfo != null) {
return psmAppInfo.uid;
}
}
catch (PackageManager.NameNotFoundException e) { };
return 0;
}
public static void killPsm(){
ActivityManager am = (ActivityManager) ctx.getSystemService(Activity.ACTIVITY_SERVICE);
am.killBackgroundProcesses("com.playstation.psstore");
}
public static PackageInfo getPackageInfo(String pkg) throws PackageManager.NameNotFoundException {
PackageInfo pkgInfo = ctx.getPackageManager().getPackageInfo(pkg, 0);
return pkgInfo;
}
public static ApplicationInfo getAppInfo(String pkg) throws PackageManager.NameNotFoundException {
ApplicationInfo appInfo = ctx.getPackageManager().getApplicationInfo(pkg, 0);
return appInfo;
}
public static PackageInfo getPsmAppPkg() throws PackageManager.NameNotFoundException {
return getPackageInfo("com.playstation.psstore");
}
public static ApplicationInfo getPsmAppInfo() throws PackageManager.NameNotFoundException {
return getAppInfo("com.playstation.psstore");
}
public static String getDateTime(){
String formattedDate = new SimpleDateFormat("MM_dd_yyyy_HH_mm_ss").format(new Date());
return formattedDate;
}
}

View File

@ -4,19 +4,16 @@ package com.psmreborn.nopsmdrm;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import com.psmreborn.nopsmdrm.pscertified.PlayStationCertified;
import com.psmreborn.nopsmdrm.pscertified.PsCertificatesInstaller;
import com.psmreborn.pscertified.PlayStationCertified;
import com.psmreborn.pscertified.PsCertificatesInstaller;
import com.psmreborn.shared.Downloader;
import com.psmreborn.shared.PsmApplication;
import com.psmreborn.shared.Root;
import java.io.File;
import java.io.IOException;
import eu.chainfire.libsuperuser.Shell;
public class MainActivity extends Activity {
@ -25,9 +22,8 @@ public class MainActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Helper.setContext(this);
(new Startup(this)).execute();
Startup.runStartupCheck(this);
}
@Override
@ -37,66 +33,83 @@ public class MainActivity extends Activity {
super.onBackPressed();
}
public static void setPsmInstalled(Activity activity){
Button installButton = (Button) activity.findViewById(R.id.installPsm);
Button backupGamesButton = (Button) activity.findViewById(R.id.dumpGames);
Button takeOwnershipButton = (Button) activity.findViewById(R.id.takeOwnership);
installButton.setText("Update NoPsmDrm");
backupGamesButton.setEnabled(true);
if(Build.VERSION.SDK_INT >= 30)
takeOwnershipButton.setEnabled(true);
private void downloadPsmAlert() {
new AlertDialog.Builder(this)
.setTitle("PSM App not found!")
.setMessage("Would you like to download and install the PlayStation Mobile app?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Downloader.downloadAndInstall(MainActivity.this,
"http://psmreborn.com/psm-android/Psm1.7.0.apk");
}
})
.setNegativeButton("No", null).show();
}
private void psCertifiedAlert(){
new AlertDialog.Builder((Activity)this)
private void psCertifiedAlert() {
new AlertDialog.Builder(this)
.setTitle("PS Certification Missing")
.setMessage("Your device appears to not be\"PlayStation Certified\"\nDo you want to certify it?\n(Warning: modifies /system/)")
.setMessage((Root.isMagiskRoot()) ? "Your device appears to not be \"PlayStation Certified\"\nOn android 6+ this requires installing a Magisk module\nWould you like to install \"MagiskCertify.zip\"" :
"Your device appears to not be \"PlayStation Certified\"\nDo you want to install them?\n(Warning: modifies /system/)")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
(new PsCertificatesInstaller(MainActivity.this)).execute();
PsCertificatesInstaller.installPsCertificates(MainActivity.this);
}
})
.setNegativeButton("No", null).show();
}
public void installStart(View view) {
PlayStationCertified playStationCertified = new PlayStationCertified(this);
if(!playStationCertified.isPlaystationCertified()){
psCertifiedAlert();
}
else {
(new NoPsmDrmInstaller(this)).execute();
public void installStart(View view) {;
if(Permissions.checkPermsAlert(this)){
if(!PlayStationCertified.isPlaystationCertified(this.getApplicationContext())){
psCertifiedAlert();
}
else if(!PsmApplication.isPsmInstalled(this)) {
downloadPsmAlert();
}
else {
NoPsmDrmInstaller.installNoPsmDrm(this);
}
}
}
public void givePsmOwnership(View view){
new AlertDialog.Builder((Activity)this)
.setTitle("Fix permissions")
.setMessage("On android 10+, games wont always show up in PSM when placed in \"com.playstation.psstore\" folder due to file permissions.\nWould you like to fix all the file permissions for PSM Games? (can take awhile)")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
FixPermissions.changePermissionsToPsm(MainActivity.this);
}
})
.setNegativeButton("No", null).show();
}
public void dumpAllRifs(View view){
String rifOutput = new File(Environment.getExternalStorageDirectory(), "psm").getAbsolutePath();
new AlertDialog.Builder((Activity)this)
.setTitle("Backup licenses")
.setMessage("This will show a black screen for awhile\nthen a PlayStation Mobile game will start\nOnce the game starts, a backup of all your PSM licenses can be found at\n\""+rifOutput+"\"\nDo you want to do this?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
DumpAllRifs.setDumpAllFlagAndStartPsm(MainActivity.this);
}
})
.setNegativeButton("No", null).show();
if(Permissions.checkPermsAlert(this)){
String rifOutput = new File(Environment.getExternalStorageDirectory(), "psm").getAbsolutePath();
new AlertDialog.Builder(this)
.setTitle("Backup licenses")
.setMessage("This will show a black screen for awhile\nthen a PlayStation Mobile game will start\nOnce the game starts, a backup of all your PSM licenses can be found at\n\""+rifOutput+"\"\nDo you want to do this?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
DumpAllRifs.setDumpAllFlagAndStartPsm(MainActivity.this);
}
})
.setNegativeButton("No", null).show();
}
}
public void givePsmOwnership(View view){
if(Permissions.checkPermsAlert(this)){
new AlertDialog.Builder(this)
.setTitle("Fix permissions")
.setMessage("On android 10+, games wont always show up in PSM when placed in \"com.playstation.psstore\" folder due to file permissions.\nWould you like to fix all the file permissions for PSM Games? (can take awhile)")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
FixPermissions.changePermissionsToPsm(MainActivity.this);
}
})
.setNegativeButton("No", null).show();
}
}
}

View File

@ -1,62 +1,48 @@
package com.psmreborn.nopsmdrm;
import static com.psmreborn.nopsmdrm.Helper.*;
import static com.psmreborn.nopsmdrm.Root.*;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Toast;
import org.xmlpull.v1.XmlPullParserException;
import com.psmreborn.nopsmdrm.library_db.LibraryDbOpenHelper;
import com.psmreborn.shared.Files;
import com.psmreborn.shared.MountPoint;
import com.psmreborn.shared.Packages;
import com.psmreborn.shared.PsmApplication;
import com.psmreborn.shared.Root;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Locale;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import java.util.Date;
import eu.chainfire.libsuperuser.Shell;
// Sorry if this is kinda hard to follow ...
public class NoPsmDrmInstaller extends AsyncTask<Void, Void, Void> {
private boolean wasError = false;
private String errorMsg = "";
private ProgressDialog dialog = null;
public class NoPsmDrmInstaller{
private Context ctx = null;
private Handler handler = null;
public NoPsmDrmInstaller(Context context){
this.ctx = context;
this.handler = new Handler(ctx.getMainLooper());
private NoPsmDrmInstaller(Context context) {
ctx = context;
handler = new Handler(ctx.getMainLooper());
}
private void setError(String msg) throws Exception {
this.wasError = true;
this.errorMsg = msg;
Log.e("INSTALLER",errorMsg);
throw new Exception(msg);
}
@SuppressLint("MissingPermission")
private void generateDeviceFingerprint(String outputFilename) throws Exception {
TelephonyManager tm = ((TelephonyManager) ctx.getSystemService( Context.TELEPHONY_SERVICE));
String deviceId = "(blank)";
@ -95,8 +81,8 @@ public class NoPsmDrmInstaller extends AsyncTask<Void, Void, Void> {
type = "(blank)";
PrintWriter writer = new PrintWriter(outputFilename, "UTF-8");
writer.println(String.valueOf(Helper.getPsmUid()));
writer.println(Helper.getAndroidIdOfPsm());
writer.println(String.valueOf(PsmApplication.getPsmUid(ctx)));
writer.println(PsmApplication.getAndroidIdOfPsm(ctx));
writer.println(deviceId);
writer.println(serial);
writer.println(brand);
@ -109,16 +95,16 @@ public class NoPsmDrmInstaller extends AsyncTask<Void, Void, Void> {
}
@SuppressLint("SimpleDateFormat")
private void backupPsm() throws Exception {
String psmFilesDir = new File(getPsmAppInfo().dataDir, "files").getAbsolutePath();
String psmFilesDir = new File(PsmApplication.getPsmAppInfo(ctx).dataDir, "files").getAbsolutePath();
String psmKdcDir = new File(psmFilesDir, "kdc").getAbsolutePath();
String psmActFile = new File(psmKdcDir, "act.dat").getAbsolutePath();
String psmAndroidFolder = new File(new File(new File(new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data"), "com.playstation.psstore"), "files"), "psm").getAbsolutePath();
String psmAndroidFolder = new File(new File(new File(new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data"), ctx.getResources().getString(R.string.psm_app_package_id)), "files"), "psm").getAbsolutePath();
String psmSdcardFolder = new File(Environment.getExternalStorageDirectory(), "psm").getAbsolutePath();
String devinfoFile = new File(ctx.getCacheDir(), "devinfo.txt").getAbsolutePath();
String tarFilename = new File(psmSdcardFolder, "psm_"+getDateTime() + ".tar").getAbsolutePath();
String tarFilename = new File(psmSdcardFolder, "psm_"+ new SimpleDateFormat("MM_dd_yyyy_HH_mm_ss").format(new Date()) + ".tar").getAbsolutePath();
// check if "act.dat" exists -- they've used PSM before ...
@ -130,7 +116,7 @@ public class NoPsmDrmInstaller extends AsyncTask<Void, Void, Void> {
generateDeviceFingerprint(devinfoFile);
ArrayList<String> filesToTar = new ArrayList<String>();
filesToTar.add(getPsmAppInfo().dataDir);
filesToTar.add(PsmApplication.getPsmAppInfo(ctx).dataDir);
filesToTar.add(devinfoFile);
// add all license folders ....
@ -144,7 +130,7 @@ public class NoPsmDrmInstaller extends AsyncTask<Void, Void, Void> {
}
// tar up the files
tarRoot(filesToTar, tarFilename);
Root.tarRoot(filesToTar, tarFilename);
handler.post(() -> {
Toast.makeText(ctx.getApplicationContext(),"Backed up existing PSM Data to:\n\""+tarFilename+"\"", Toast.LENGTH_LONG).show();
@ -153,128 +139,54 @@ public class NoPsmDrmInstaller extends AsyncTask<Void, Void, Void> {
}
private void makeDirs() throws PackageManager.NameNotFoundException, Shell.ShellDiedException {
mkdirAndChmodChown(new File(getPsmAppInfo().dataDir, "cache").getAbsolutePath(), 771, String.valueOf(Helper.getPsmUid()));
mkdirAndChmodChown(new File(getPsmAppInfo().dataDir, "shared_prefs").getAbsolutePath(), 771, String.valueOf(Helper.getPsmUid()));
mkdirAndChmodChown(new File(getPsmAppInfo().dataDir, "files").getAbsolutePath(), 771, String.valueOf(Helper.getPsmUid()));
mkdirAndChmodChown(new File(new File(getPsmAppInfo().dataDir, "files"), "kdc").getAbsolutePath(), 771, String.valueOf(Helper.getPsmUid()));
mkdirAndChmodChown(new File(getPsmAppInfo().dataDir, "databases").getAbsolutePath(), 771, String.valueOf(Helper.getPsmUid()));
Root.mkdirAndChmodChown(new File(PsmApplication.getPsmAppInfo(ctx).dataDir, "cache").getAbsolutePath(), 771, String.valueOf(PsmApplication.getPsmUid(ctx)));
Root.mkdirAndChmodChown(new File(PsmApplication.getPsmAppInfo(ctx).dataDir, "shared_prefs").getAbsolutePath(), 771, String.valueOf(PsmApplication.getPsmUid(ctx)));
Root.mkdirAndChmodChown(new File(PsmApplication.getPsmAppInfo(ctx).dataDir, "files").getAbsolutePath(), 771, String.valueOf(PsmApplication.getPsmUid(ctx)));
Root.mkdirAndChmodChown(new File(new File(PsmApplication.getPsmAppInfo(ctx).dataDir, "files"), "kdc").getAbsolutePath(), 771, String.valueOf(PsmApplication.getPsmUid(ctx)));
Root.mkdirAndChmodChown(new File(PsmApplication.getPsmAppInfo(ctx).dataDir, "databases").getAbsolutePath(), 771, String.valueOf(PsmApplication.getPsmUid(ctx)));
}
private void generateWorkaroundSharedPrefs(String sharedPrefsPath, String androidId, int psmUid) throws Exception {
StringEncryptor stringEncryptor = new StringEncryptor(this.ctx, androidId, psmUid);
String emailAddress = stringEncryptor.encryptString("nopsmdrm@transrights.lgbt");
String password = stringEncryptor.encryptString("password");
// get the cache folder for our 'shared_prefs'
private void installSharedPref(String sharedPrefData, String sharedPrefName) throws Exception {
String sharedPrefsPath = new File(PsmApplication.getPsmAppInfo(ctx).dataDir, "shared_prefs").getAbsolutePath();
String tmpPrefsFolder = ctx.getCacheDir().getAbsolutePath();
String myPrefFile = new File(tmpPrefsFolder, sharedPrefName).getAbsolutePath();
String psmPrefFile = new File(sharedPrefsPath, sharedPrefName).getAbsolutePath();
String aidSigninInfo = new File(tmpPrefsFolder, "AIDSigninInfo.xml").getAbsolutePath();
String signinInfo = new File(sharedPrefsPath, "SigninInfo.xml").getAbsolutePath();
// generate shared_prefs
writeTxtFile(aidSigninInfo, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
"<map>\n<string name=\"SignedInUsername\">"+emailAddress+"</string>\n" +
"<boolean name=\"PassSave\" value=\"true\" />\n" +
"<string name=\"Password\">"+password+"</string>\n" +
"<boolean name=\"AutoSignIn\" value=\"true\" />\n" +
"</map>\n");
copyChmodAndChown(aidSigninInfo, signinInfo, 660, String.valueOf(Helper.getPsmUid()));
}
private void generateSharedPrefs(String sharedPrefsPath, String androidId, int psmUid) throws Exception {
StringEncryptor stringEncryptor = new StringEncryptor(this.ctx, androidId, psmUid);
// encrypt the actual strings, username, password, etc
String emailAddress = stringEncryptor.encryptString("nopsmdrm@transrights.lgbt");
String password = stringEncryptor.encryptString("password");
String accountId = stringEncryptor.encryptString(String.valueOf(0x123456789ABCDEFL));
// get the cache folder for our 'shared_prefs'
String tmpPrefsFolder = ctx.getCacheDir().getAbsolutePath();
// work out paths to each file ...
String c_signinInfo = new File(tmpPrefsFolder, "SigninInfo.xml").getAbsolutePath();
String c_psstorePrefs = new File(tmpPrefsFolder, "com.playstation.psstore_preferences.xml").getAbsolutePath();
String c_runningContentInfo = new File(tmpPrefsFolder, "RunningContentInfo.xml").getAbsolutePath();
String c_localLibrary = new File(tmpPrefsFolder, "LocalLibrary.xml").getAbsolutePath();
String r_signinInfo = new File(sharedPrefsPath, "SigninInfo.xml").getAbsolutePath();
String r_psstorePrefs = new File(sharedPrefsPath, "com.playstation.psstore_preferences.xml").getAbsolutePath();
String r_runningContentInfo = new File(sharedPrefsPath, "RunningContentInfo.xml").getAbsolutePath();
String r_localLibrary = new File(sharedPrefsPath, "LocalLibrary.xml").getAbsolutePath();
// generate shared_prefs
writeTxtFile(c_signinInfo, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
"<map>\n<string name=\"SignedInUsername\">"+emailAddress+"</string>\n" +
"<boolean name=\"PassSave\" value=\"true\" />\n" +
"<string name=\"Password\">"+password+"</string>\n" +
"<boolean name=\"AutoSignIn\" value=\"true\" />\n" +
"</map>\n");
writeTxtFile(c_psstorePrefs, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
"<map>\n" +
"<boolean name=\"key_upgradeDownloadTableForNeedWifi\" value=\"true\" />\n" +
"<string name=\"last_signin_account_id\">"+accountId+"</string>\n" +
"<long name=\"last_signin_account_region\" value=\"2\" />\n" +
"<int name=\"key_psstore\" value=\"1\" />\n" +
"<int name=\"key_downloader\" value=\"1\" />\n" +
"<int name=\"psm_license_agree_version_code\" value=\"1170\" />\n" +
"<int name=\"key_xmlcache\" value=\"1\" />\n" +
"<string name=\"last_signin_account_country\">US</string>\n" +
"<int name=\"key_startcontent\" value=\"1\" />\n<int name=\"key_nsxevent\" value=\"1\" />\n" +
"<boolean name=\"key_upgradeLibraryTableForLocationUseConfirmationDate\" value=\"true\" />\n" +
"<int name=\"key_install\" value=\"1\" />\n" +
"<string name=\"update_md5\">387ce7e424258aef426aaa5be8a1638a</string>\n" +
"<boolean name=\"psm_license_agree\" value=\"true\" />\n" +
"<int name=\"key_guestinfo\" value=\"1\" />\n" +
"<string name=\"last_signin_account_language\">"+ Locale.getDefault().getLanguage()+"</string>\n" +
"<int name=\"key_cache\" value=\"2\" />\n" +
"<int name=\"key_signinfo\" value=\"2\" />\n" +
"</map>\n");
writeTxtFile(c_runningContentInfo, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
"<map>\n<null name=\"title_id\" />\n" +
"<null name=\"next_title_id\" />\n" +
"</map>\n");
writeTxtFile(c_localLibrary, "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
"<map>\n<boolean name=\"notDisplayAgain\" value=\"true\" />\n" +
"<int name=\"sortType\" value=\"0\" />\n" +
"<boolean name=\"isList\" value=\"false\" />\n" +
"</map>\n");
// copy to the correct place and set permissions properly.
copyChmodAndChown(c_signinInfo, r_signinInfo, 660, String.valueOf(Helper.getPsmUid()));
copyChmodAndChown(c_psstorePrefs, r_psstorePrefs, 660, String.valueOf(Helper.getPsmUid()));
copyChmodAndChown(c_runningContentInfo, r_runningContentInfo, 660, String.valueOf(Helper.getPsmUid()));
copyChmodAndChown(c_localLibrary, r_localLibrary, 660, String.valueOf(Helper.getPsmUid()));
Files.writeTxtFile(myPrefFile, sharedPrefData);
Root.copyChmodAndChown(myPrefFile, psmPrefFile, 660, String.valueOf(PsmApplication.getPsmUid(ctx)));
}
private void installSharedPrefs() throws Exception {
// get the path to the shared_prefs folder
String sharedPrefsPath = new File(getPsmAppInfo().dataDir, "shared_prefs").getAbsolutePath();
String tmpPrefsFolder = ctx.getCacheDir().getAbsolutePath();
File aidSigninFile = new File(tmpPrefsFolder, "AIDSigninInfo.xml");
String sharedPrefsPath = new File(PsmApplication.getPsmAppInfo(ctx).dataDir, "shared_prefs").getAbsolutePath();
// setup StringEncryptor
String androidId = PsmApplication.getAndroidIdOfPsm(ctx);
PsmSharedPrefs sharedPrefGenerator = new PsmSharedPrefs((androidId == null) ? ctx.getResources().getString(R.string.default_account_id) : androidId, PsmApplication.getPsmUid(ctx));
// check if signininfo.xml file exists or not ...
boolean signinInfoExist = fileExistRoot(new File(sharedPrefsPath, "SigninInfo.xml").getAbsolutePath());
boolean aidSigninExist = ( aidSigninFile.exists() && Build.VERSION.SDK_INT >= 26 );
if (!signinInfoExist || aidSigninExist) { // if no signininfo file was found
// then generate our own shared_prefs
String androidId = Helper.getAndroidIdOfPsm();
int psmUid = Helper.getPsmUid();
boolean signinInfoExist = Root.fileExistRoot(new File(sharedPrefsPath, "SigninInfo.xml").getAbsolutePath());
boolean isNoPsmDrmAccount = signinInfoExist && sharedPrefGenerator.checkEncryptedAccountIdEquals(PsmApplication.getSharedPrefFromPsm(ctx, "com.playstation.psstore_preferences", "last_signin_account_id"), PsmSharedPrefs.getDefaultAccountId(ctx));
if (!signinInfoExist || isNoPsmDrmAccount) {
// if no SigninInfo.xml file was not found
// or if it was found, but it is using the default account id ...
// then generate our own shared_prefs
if(androidId != null){
generateSharedPrefs(sharedPrefsPath, androidId, psmUid);
aidSigninFile.delete();
installSharedPref(sharedPrefGenerator.generateSigninInfo(ctx.getResources().getString(R.string.default_email), ctx.getResources().getString(R.string.default_password)), "SigninInfo.xml");
installSharedPref(sharedPrefGenerator.generatePsmPreferences(PsmSharedPrefs.getDefaultAccountId(ctx)), "com.playstation.psstore_preferences.xml");
installSharedPref(sharedPrefGenerator.generateRunningContentInfo(), "RunningContentInfo.xml");
installSharedPref(sharedPrefGenerator.generateLocalLibrary(), "LocalLibrary.xml");
}
else {
generateWorkaroundSharedPrefs(sharedPrefsPath, "1234567890abcdef", psmUid);
Helper.killPsm();
installSharedPref(sharedPrefGenerator.generateSigninInfo(ctx.getResources().getString(R.string.default_email), ctx.getResources().getString(R.string.default_password)), "SigninInfo.xml");
PsmApplication.killPsm(ctx);
handler.post(() ->{
new AlertDialog.Builder((Activity)ctx)
.setTitle("Android 7+ android_id...")
.setTitle("Android 8+ android_id...")
.setMessage("Could not obtain PSMs \"android_id\"\nPlease start the PSM app one time.\nthen run this application again!")
.setCancelable(false)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@ -310,31 +222,28 @@ public class NoPsmDrmInstaller extends AsyncTask<Void, Void, Void> {
}
private void installNoPsmDrmModules() throws Exception {
String nativeLibsFolder = getPsmAppInfo().nativeLibraryDir;
String nativeLibsFolder = PsmApplication.getPsmAppInfo(ctx).nativeLibraryDir;
String libPsmKdcFile = new File(nativeLibsFolder, "libpsmkdc_jni.so").getAbsolutePath();
String libDefaultFile = new File(nativeLibsFolder, "libdefault.so").getAbsolutePath();
String realLibDefaultFile = new File(nativeLibsFolder, "libdefault_real.so").getAbsolutePath();
String folderContainingLibs = new File(nativeLibsFolder).getParentFile().getAbsolutePath();
// Check if the app is installed to the SD card or not.
boolean installedToSd = !nativeLibsFolder.startsWith("/data");
// This is the case when the app is installed to the SD Card, or /system/
boolean needRemount = MountPoint.getMountPointAt(nativeLibsFolder).isRo();
// get the owner of the file ..
String systemUid = getFileOwner(libPsmKdcFile);
String systemUid = MountPoint.getMountPointAt(nativeLibsFolder).isFat() ? null : Root.getFileOwner(libPsmKdcFile); // /mnt/sec is FAT, and doesnt support unix permissions.
if(needRemount) Root.remountRw(nativeLibsFolder); // the /mnt/asec folder is read only normally, so make ir read/write
if(installedToSd) systemUid = null; // /mnt/sec is FAT32, and doesnt support unix permissions.
if(installedToSd) remountRw(folderContainingLibs); // the /mnt/asec folder is read only normally, so make ir read/write
if(!fileExistRoot(realLibDefaultFile)) {
if(!Root.fileExistRoot(realLibDefaultFile)) {
// if libdefault_real.so not found, then rename libdefault.so to libdefault_real.so ...
moveFileRoot(libDefaultFile, realLibDefaultFile);
Root.moveFileRoot(libDefaultFile, realLibDefaultFile);
// if were on android 5.x or later, we need to patch libdefault.
if(Build.VERSION.SDK_INT >= 21){
String cachedLibDefault = new File(ctx.getCacheDir(), "libdefault_real.so").toString();
Root.copyChmodAndChown(realLibDefaultFile, cachedLibDefault, 777, String.valueOf(Helper.getAppInfo(ctx.getPackageName()).uid));
Root.copyChmodAndChown(realLibDefaultFile, cachedLibDefault, 777, String.valueOf(Packages.getAppUid(ctx, ctx.getPackageName())));
patchLibdefault(cachedLibDefault);
Root.copyChmodAndChown(cachedLibDefault, realLibDefaultFile, 755, systemUid);
}
@ -342,98 +251,107 @@ public class NoPsmDrmInstaller extends AsyncTask<Void, Void, Void> {
}
// unpack the library files ...
unpackResourceToLocationRoot(R.raw.libdefault, libDefaultFile, 755, systemUid);
unpackResourceToLocationRoot(R.raw.libpsmkdc_jni, libPsmKdcFile, 755, systemUid);
if(installedToSd) remountRo(folderContainingLibs);
Root.unpackResourceToLocationRoot(ctx, R.raw.libdefault, libDefaultFile, 755, systemUid);
Root.unpackResourceToLocationRoot(ctx, R.raw.libpsmkdc_jni, libPsmKdcFile, 755, systemUid);
if(needRemount) Root.remountRo(nativeLibsFolder);
}
private void installDatabase() throws Exception {
String databasesFolder = new File(getPsmAppInfo().dataDir, "databases").getAbsolutePath();
String libraryDbFile = new File(databasesFolder, "library.db").getAbsolutePath();
String psmDatabasesFolder = new File(PsmApplication.getPsmAppInfo(ctx).dataDir, "databases").getAbsolutePath();
String psmLibraryDbFile = new File(psmDatabasesFolder, "library.db").getAbsolutePath();
unpackResourceToLocationRoot(R.raw.library, libraryDbFile, 660, String.valueOf(Helper.getPsmUid()));
String myDatabasesFolder = new File(Packages.getAppInfo(ctx, ctx.getPackageName()).dataDir, "databases").getAbsolutePath();
String myLibraryDbFile = new File(myDatabasesFolder, "library.db").getAbsolutePath();
Root.mkdirAndChmodChown(myDatabasesFolder, 771, String.valueOf(Packages.getAppUid(ctx, ctx.getPackageName())));
if(Root.fileExistRoot(psmLibraryDbFile))
Root.copyChmodAndChown(psmLibraryDbFile, myLibraryDbFile, 660, String.valueOf(Packages.getAppUid(ctx, ctx.getPackageName())));
// got psm library db file
LibraryDbOpenHelper lbdb = new LibraryDbOpenHelper(ctx);
SQLiteDatabase db = lbdb.getWritableDatabase();
// add sql trigger to bypass content_id.
db.execSQL("CREATE TRIGGER IF NOT EXISTS CONTENT_ID_MEMES AFTER INSERT ON LibraryTable BEGIN UPDATE LibraryTable SET content_id=\"UM0105-\" || title_id || \"_00-0000000000000000\" WHERE title_id = new.title_id; END;");
// close sql db
db.close();
lbdb.close();
if(Root.fileExistRoot(myLibraryDbFile))
Root.copyChmodAndChown(myLibraryDbFile, psmLibraryDbFile, 660, String.valueOf(PsmApplication.getPsmUid(ctx)));
}
private void installDeviceList() throws PackageManager.NameNotFoundException, IOException, Shell.ShellDiedException {
String cacheFolder = new File(getPsmAppInfo().dataDir, "cache").getAbsolutePath();
String cacheFolder = new File(PsmApplication.getPsmAppInfo(ctx).dataDir, "cache").getAbsolutePath();
// extract the regular device list ...
String deviceList2File = new File(cacheFolder, "deviceList2.dat").getAbsolutePath();
unpackResourceToLocationRoot(R.raw.device_list2, deviceList2File, 660, String.valueOf(Helper.getPsmUid()));
Root.unpackResourceToLocationRoot(ctx, R.raw.device_list2, deviceList2File, 660, String.valueOf(PsmApplication.getPsmUid(ctx)));
// extract the additional certified devices list ...
String additionalCertsFile = new File(cacheFolder, "addtionalCertifiedDevList.dat").getAbsolutePath();
unpackResourceToLocationRoot(R.raw.aditional_certified_devices_list, additionalCertsFile, 660, String.valueOf(Helper.getPsmUid()));
Root.unpackResourceToLocationRoot(ctx, R.raw.aditional_certified_devices_list, additionalCertsFile, 660, String.valueOf(PsmApplication.getPsmUid(ctx)));
}
@Override
protected void onPreExecute() {
dialog = new ProgressDialog(ctx);
if(!Helper.isNoPsmDrmAlreadyInstalled())
public static boolean isNoPsmDrmInstalled(Context ctx) {
try {
if(new File(PsmApplication.getPsmAppInfo(ctx).nativeLibraryDir, "libdefault_real.so").exists()){
return true;
}
} catch (PackageManager.NameNotFoundException e) {
return false;
}
return false;
}
public static void installNoPsmDrm(Activity activity) {
ProgressDialog dialog = new ProgressDialog(activity);
if(!isNoPsmDrmInstalled(activity))
dialog.setTitle("Installing NoPsmDrm ...");
else
dialog.setTitle("Updating NoPsmDrm ...");
dialog.setTitle("Reinstalling NoPsmDrm ...");
dialog.setMessage("Please Wait ...");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
NoPsmDrmInstaller drmInstaller = new NoPsmDrmInstaller(activity.getApplicationContext());
new Thread(() -> {
try {
Files.cleanupFiles(activity);
PsmApplication.killPsm(activity);
}
drmInstaller.backupPsm();
drmInstaller.makeDirs();
drmInstaller.installSharedPrefs();
drmInstaller.installDeviceList();
drmInstaller.installNoPsmDrmModules();
drmInstaller.installDatabase();
@Override
protected Void doInBackground(Void... voids) {
try {
Helper.killPsm();
if (isPsmInstalled()) {
backupPsm();
makeDirs();
installSharedPrefs();
installDeviceList();
installNoPsmDrmModules();
installDatabase();
}
else{
this.setError("PSM app is not installed.");
catch(Exception e){
dialog.dismiss();
drmInstaller.handler.post(() -> {
new AlertDialog.Builder(activity)
.setTitle("Error Occurred.")
.setMessage(e.toString())
.setCancelable(false)
.setPositiveButton("Ok",null).show();
});
}
}
catch(Exception e){
this.wasError = true;
this.errorMsg = e.toString();
return null;
}
return null;
}
@Override
protected void onPostExecute(Void result) {
handler.post(() -> {
dialog.dismiss();
});
if(wasError) {
new AlertDialog.Builder((Activity)ctx)
.setTitle("Error Occurred.")
.setMessage(this.errorMsg)
.setCancelable(false)
.setPositiveButton("Ok",null).show();
}
else{
handler.post(() ->{
MainActivity.setPsmInstalled((Activity)ctx);
drmInstaller.handler.post(() -> {
dialog.dismiss();
Startup.setPsmInstalled(activity);
new AlertDialog.Builder(activity)
.setTitle("Finished!")
.setMessage("Your PSM Application was patched successfully!\n\nNote: WI-FI has to be turned off for games to work.")
.setCancelable(false)
.setPositiveButton("Ok", null).show();
});
new AlertDialog.Builder((Activity)ctx)
.setTitle("Finished!")
.setMessage("Your PSM Application was patched successfully!\n\nNote: WI-FI has to be turned off for games to work.")
.setCancelable(false)
.setPositiveButton("Ok", null).show();
}
}).start();
}
}

View File

@ -0,0 +1,76 @@
package com.psmreborn.nopsmdrm;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import java.util.ArrayList;
public class Permissions {
public static boolean checkHavePermissions(Activity ctx, String[] permissions) {
for(String permission : permissions){
if (ActivityCompat.checkSelfPermission(ctx, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
public static void requestPermissionsIfNeeded(Activity ctx, String[] permissions) {
ArrayList<String> needPerms = new ArrayList<String>();
for(String perm : permissions){
if(ActivityCompat.checkSelfPermission(ctx, perm) != PackageManager.PERMISSION_GRANTED){
needPerms.add(perm);
}
}
if(!needPerms.isEmpty())
ActivityCompat.requestPermissions(ctx, needPerms.toArray(new String[0]), 1);
}
public static boolean checkPermsAlert(Activity ctx){
if(Build.VERSION.SDK_INT >= 23){
if (!Permissions.checkHavePermissions(ctx, new String[] {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.KILL_BACKGROUND_PROCESSES,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.ACCESS_WIFI_STATE,
Manifest.permission.INTERNET,
Manifest.permission.READ_PHONE_STATE,
})) {
new AlertDialog.Builder(ctx)
.setTitle("Missing permissions")
.setMessage("Missing required permissions, please grant all requested permissions\nPHONE is needed only to read IMEI for deriving \"ConsoleID\"\nSTORAGE is needed for backing up existing PSM data")
.setCancelable(false)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
Permissions.requestPermissions(ctx);
}
}).show();
return false;
}
else{
return true;
}
}
else{
return true;
}
}
public static void requestPermissions(Activity ctx) {
if(Build.VERSION.SDK_INT >= 23){
requestPermissionsIfNeeded(ctx, new String[] {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.KILL_BACKGROUND_PROCESSES,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.ACCESS_WIFI_STATE,
Manifest.permission.INTERNET,
Manifest.permission.READ_PHONE_STATE,
});
}
}
}

View File

@ -0,0 +1,69 @@
package com.psmreborn.nopsmdrm;
import android.content.Context;
import com.psmreborn.shared.StringEncryptor;
import java.util.Locale;
public class PsmSharedPrefs {
private StringEncryptor encryptor = null;
public PsmSharedPrefs(StringEncryptor stringEncryptor){
this.encryptor = stringEncryptor;
}
public PsmSharedPrefs(String androidId, int psmUid) {
this.encryptor = new StringEncryptor(androidId, psmUid);
}
public String generatePsmPreferences(Long accountId) {
return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
"<map>\n" +
"<boolean name=\"key_upgradeDownloadTableForNeedWifi\" value=\"true\" />\n" +
"<string name=\"last_signin_account_id\">"+encryptor.encryptString(String.valueOf(accountId))+"</string>\n" +
"<long name=\"last_signin_account_region\" value=\"2\" />\n" +
"<int name=\"key_psstore\" value=\"1\" />\n" +
"<int name=\"key_downloader\" value=\"1\" />\n" +
"<int name=\"psm_license_agree_version_code\" value=\"1170\" />\n" +
"<int name=\"key_xmlcache\" value=\"1\" />\n" +
"<string name=\"last_signin_account_country\">US</string>\n" +
"<int name=\"key_startcontent\" value=\"1\" />\n<int name=\"key_nsxevent\" value=\"1\" />\n" +
"<boolean name=\"key_upgradeLibraryTableForLocationUseConfirmationDate\" value=\"true\" />\n" +
"<int name=\"key_install\" value=\"1\" />\n" +
"<string name=\"update_md5\">387ce7e424258aef426aaa5be8a1638a</string>\n" +
"<boolean name=\"psm_license_agree\" value=\"true\" />\n" +
"<int name=\"key_guestinfo\" value=\"1\" />\n" +
"<string name=\"last_signin_account_language\">"+ Locale.getDefault().getLanguage()+"</string>\n" +
"<int name=\"key_cache\" value=\"2\" />\n" +
"<int name=\"key_signinfo\" value=\"2\" />\n" +
"</map>\n";
}
public String generateSigninInfo(String emailAddress, String password) {
return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
"<map>\n<string name=\"SignedInUsername\">"+encryptor.encryptString(emailAddress)+"</string>\n" +
"<boolean name=\"PassSave\" value=\"true\" />\n" +
"<string name=\"Password\">"+encryptor.encryptString(password)+"</string>\n" +
"<boolean name=\"AutoSignIn\" value=\"true\" />\n" +
"</map>\n";
}
public String generateRunningContentInfo() {
return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
"<map>\n<null name=\"title_id\" />\n" +
"<null name=\"next_title_id\" />\n" +
"</map>\n";
}
public String generateLocalLibrary() {
return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" +
"<map>\n<boolean name=\"notDisplayAgain\" value=\"true\" />\n" +
"<int name=\"sortType\" value=\"0\" />\n" +
"<boolean name=\"isList\" value=\"false\" />\n" +
"</map>\n";
}
public boolean checkEncryptedAccountIdEquals(String encryptedAccountId, Long checkAccountId) throws Exception {
return ( Long.parseLong(encryptor.decryptString(encryptedAccountId, String.valueOf(checkAccountId))) == checkAccountId );
}
public static long getDefaultAccountId(Context ctx){
return Long.valueOf(ctx.getResources().getString(R.string.default_account_id), 16);
}
}

View File

@ -1,87 +1,86 @@
package com.psmreborn.nopsmdrm;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.widget.Button;
import android.widget.TextView;
import eu.chainfire.libsuperuser.Shell;
import static com.psmreborn.nopsmdrm.Helper.*;
import com.psmreborn.pscertified.PlayStationCertified;
import com.psmreborn.shared.Files;
import com.psmreborn.shared.PsmApplication;
import com.psmreborn.shared.Root;
import com.psmreborn.nopsmdrm.pscertified.PlayStationCertified;
import com.psmreborn.nopsmdrm.pscertified.PsCertificatesInstaller;
public class Startup {
import java.io.File;
public class Startup extends AsyncTask<Void, Void, Void> {
private Context ctx;
private Handler handler;
private boolean wasError = false;
private String errorMsg = "";
public static void updateNames(Activity activity) {
Button installButton = (Button) activity.findViewById(R.id.installPsm);
public Startup(Context context) {
this.handler = new Handler(context.getMainLooper());
this.ctx = context;
if(!PlayStationCertified.isPlaystationCertified(activity.getApplicationContext()))
installButton.setText("Install PS Certify");
else if(!PsmApplication.isPsmInstalled(activity))
installButton.setText("Install PSM");
else if(NoPsmDrmInstaller.isNoPsmDrmInstalled(activity))
installButton.setText("Repair NoPsmDrm");
else
installButton.setText("Install NoPsmDrm");
}
@Override
protected Void doInBackground(Void... params) {
try {
public static void setPsmInstalled(Activity activity){
Button backupGamesButton = (Button) activity.findViewById(R.id.dumpGames);
Button takeOwnershipButton = (Button) activity.findViewById(R.id.takeOwnership);
if(!Shell.SU.available()){
wasError = true;
errorMsg = "Unable to get root permission.";
return null;
}
if(!Helper.isPsmInstalled()){
wasError = true;
errorMsg = "PSM Application is not installed, please install it first!";
return null;
}
if(Helper.getPsmAppPkg().versionCode != 1170) {
wasError = true;
errorMsg = "PSM Application is installed, but an older version, please update to 1.7.0 (have: "+String.valueOf(Helper.getPsmAppPkg().versionCode)+")";
return null;
}
if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
wasError = true;
errorMsg = "No SD Card inserted.";
return null;
}
backupGamesButton.setEnabled(true);
if(Build.VERSION.SDK_INT >= 30)
takeOwnershipButton.setEnabled(true);
Root.setContext(this.ctx);
} catch (Exception e) {
wasError = true;
errorMsg = e.getMessage();
}
return null;
updateNames(activity);
}
@Override
protected void onPostExecute(Void result) {
TextView statusTV = (TextView) ((Activity)ctx).findViewById(R.id.errorMsg);
Button installButton = (Button) ((Activity)ctx).findViewById(R.id.installPsm);
public static void runStartupCheck(Activity activity) {
Handler handler = new Handler(activity.getMainLooper());
TextView statusTV = (TextView) activity.findViewById(R.id.errorMsg);
Button installButton = (Button) activity.findViewById(R.id.installPsm);
new Thread(() -> {
try {
Files.cleanupFiles(activity);
if (!Root.init(activity)) {
handler.post(() -> { statusTV.setText("Unable to get root permission."); });
return;
}
if (PsmApplication.isPsmInstalled(activity) && PsmApplication.getPsmAppPkg(activity).versionCode != 1170) {
handler.post(() -> { statusTV.setText("PSM Application is installed, but an older version, please update to 1.7.0 (have: " + String.valueOf(PsmApplication.getPsmVersion(activity)) + ")");});
return;
}
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
handler.post(() -> { statusTV.setText("No SD Card inserted.");});
}
Permissions.requestPermissions(activity);
PsmApplication.killPsm(activity);
handler.post(() -> {
statusTV.setText("");
if(NoPsmDrmInstaller.isNoPsmDrmInstalled(activity)){
setPsmInstalled(activity);
};
updateNames(activity);
installButton.setEnabled(true);
});
if(!wasError) {
handler.post(() -> {
statusTV.setText("");
if(Helper.isNoPsmDrmAlreadyInstalled()){
MainActivity.setPsmInstalled((Activity)ctx);
};
installButton.setEnabled(true);
});
}
else{
handler.post(() -> {
statusTV.setText("Error: " + errorMsg);
});
}
} catch (Exception e) {
handler.post(() ->{
statusTV.setText(e.getMessage());
});
}
}).start();
}
}
}

View File

@ -0,0 +1,22 @@
package com.psmreborn.nopsmdrm.library_db;
import java.util.Arrays;
import java.util.List;
public class DownloadTableEntry extends TableEntryBase {
@Override
public final String tableName() {
return "DownloadTable";
}
@Override
protected final List<String> tableRows() {
return Arrays.asList("req_date", "stat", "progress", "dlm_id", "size", "need_wifi");
}
@Override
protected final List<String> tableTypes() {
return Arrays.asList("integer default 0", "integer default 0", "integer default 0", "integer", "integer default 0", "integer default 0");
}
}

View File

@ -0,0 +1,58 @@
package com.psmreborn.nopsmdrm.library_db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class LibraryDbOpenHelper extends SQLiteOpenHelper {
public LibraryTableEntry libraryTableEntries;
public DownloadTableEntry downloadTableEntries;
public MetadataTableEntry metadataTableEntries;
private Context ctx = null;
private void createTables(SQLiteDatabase sqlDb) {
sqlDb.execSQL(getLibraryTableEntries().createTable());
sqlDb.execSQL(getDownloadTableEntries().createTable());
sqlDb.execSQL(getMetadataTableEntries().createTable());
}
private LibraryTableEntry getLibraryTableEntries() {
if (this.libraryTableEntries == null) {
this.libraryTableEntries = new LibraryTableEntry();
}
return this.libraryTableEntries;
}
private DownloadTableEntry getDownloadTableEntries() {
if (this.downloadTableEntries == null) {
this.downloadTableEntries = new DownloadTableEntry();
}
return this.downloadTableEntries;
}
private MetadataTableEntry getMetadataTableEntries() {
if (this.metadataTableEntries == null) {
this.metadataTableEntries = new MetadataTableEntry();
}
return this.metadataTableEntries;
}
public LibraryDbOpenHelper(Context context) {
super(context, "library.db", (SQLiteDatabase.CursorFactory) null, 2);
this.ctx = context;
}
@Override
public void onCreate(SQLiteDatabase sqlDb) {
createTables(sqlDb);
}
@Override
public void onUpgrade(SQLiteDatabase sqlDb, int i, int i1) {
if (i <= 1) {
sqlDb.execSQL(getLibraryTableEntries().dropTable());
sqlDb.execSQL(getDownloadTableEntries().dropTable());
sqlDb.execSQL(getMetadataTableEntries().dropTable());
createTables(sqlDb);
}
}
}

View File

@ -0,0 +1,22 @@
package com.psmreborn.nopsmdrm.library_db;
import java.util.Arrays;
import java.util.List;
public class LibraryTableEntry extends TableEntryBase {
@Override
public final String tableName() {
return "LibraryTable";
}
@Override
protected final List<String> tableRows() {
return Arrays.asList("update_date", "last_play", "reg_date", "content_id", "location_use_confirmation_date");
}
@Override
protected final List<String> tableTypes() {
return Arrays.asList("integer default 0", "integer default 0", "integer default 0", "text", "integer");
}
}

View File

@ -0,0 +1,22 @@
package com.psmreborn.nopsmdrm.library_db;
import java.util.Arrays;
import java.util.List;
public class MetadataTableEntry extends TableEntryBase {
@Override
public final String tableName() {
return "MetaDataTable";
}
@Override
protected final List<String> tableRows() {
return Arrays.asList("app_ver", "run_ver", "p_lock", "genre", "web", "copyright", "l_code", "r_code", "app_name", "app_s_name", "dev_name");
}
@Override
protected final List<String> tableTypes() {
return Arrays.asList("text", "text", "integer", "text", "text", "text", "text", "text", "text", "text", "text");
}
}

View File

@ -0,0 +1,51 @@
package com.psmreborn.nopsmdrm.library_db;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public abstract class TableEntryBase {
protected abstract String tableName();
protected abstract List<String> tableRows();
protected abstract List<String> tableTypes();
private HashMap<String, String> defaultTypes() {
HashMap<String, String> defaultsMap = new HashMap<String, String>();
defaultsMap.put("_id", "integer primary key autoincrement");
defaultsMap.put("enable", "integer default 1");
defaultsMap.put("title_id", "text unique");
List<String> tableRows = tableRows();
List<String> tableDefaults = tableTypes();
for (int i = 0; i < tableRows.size(); i++) {
defaultsMap.put(tableRows.get(i), tableDefaults.get(i));
}
return defaultsMap;
}
private ArrayList<String> defaultEntries() {
ArrayList<String> defaultsList = new ArrayList<String>();
defaultsList.add("_id");
defaultsList.add("enable");
defaultsList.add("title_id");
defaultsList.addAll(tableRows());
return defaultsList;
}
public final String createTable() {
StringBuilder sqlCommand = new StringBuilder(String.format("create table %s (", tableName()));
ArrayList<String> defaultEntries = defaultEntries();
HashMap<String, String> defaultDefaults = defaultTypes();
sqlCommand.append((String) defaultEntries.get(0)).append(" ").append((String) defaultDefaults.get(defaultEntries.get(0)));
for (int i = 1; i < defaultEntries.size(); i++) {
sqlCommand.append(String.format(",%s %s", defaultEntries.get(i), defaultDefaults.get(defaultEntries.get(i))));
}
sqlCommand.append(")");
return sqlCommand.toString();
}
public final String dropTable() {
return String.format("drop table %s;", tableName());
}
}

View File

@ -1,101 +0,0 @@
package com.psmreborn.nopsmdrm.pscertified;
import static com.psmreborn.nopsmdrm.Root.*;
import com.psmreborn.nopsmdrm.MainActivity;
import com.psmreborn.nopsmdrm.NoPsmDrmInstaller;
import com.psmreborn.nopsmdrm.R;
import com.psmreborn.nopsmdrm.Root;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.PowerManager;
import java.io.File;
import java.io.IOException;
import eu.chainfire.libsuperuser.Shell;
public class PsCertificatesInstaller extends AsyncTask<Void, Void, Void> {
private ProgressDialog dialog = null;
private boolean wasError = false;
private String errorMsg = "";
private Context ctx = null;
public void installPlaystationCertification() throws IOException, Shell.ShellDiedException {
// remount /system as read-write
remountRw("/system");
String psCertifiedPermissionFile = "/system/etc/permissions/com.playstation.playstationcertified.xml";
String psCertifiedJarFile = "/system/framework/com.playstation.playstationcertified.jar";
unpackResourceToLocationRoot(R.raw.ps_certified_permission, psCertifiedPermissionFile, 644, "0");
unpackResourceToLocationRoot(R.raw.ps_certified_jar, psCertifiedJarFile, 644, "0");
// make it read-only again.
remountRo("/system");
}
public PsCertificatesInstaller(Context context) {
this.ctx = context;
}
@Override
protected void onPreExecute() {
dialog = new ProgressDialog(ctx);
dialog.setTitle("Installing PlayStation Certificates ...");
dialog.setMessage("Please Wait ...");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
}
@Override
protected Void doInBackground(Void... voids) {
try {
installPlaystationCertification();
}
catch(Exception e){
this.wasError = true;
this.errorMsg = e.getMessage();
return null;
}
return null;
}
@Override
protected void onPostExecute(Void result) {
dialog.dismiss();
if(wasError) {
new AlertDialog.Builder((Activity)ctx)
.setTitle("Error Occurred.")
.setMessage(this.errorMsg)
.setCancelable(false)
.setPositiveButton("Ok",null).show();
}
else{
new AlertDialog.Builder((Activity)ctx)
.setTitle("Installed!")
.setMessage("Your device is now \"Playstation Certified\"\n(You have to reboot for changes to take effect)\nReboot?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Handler handler = new Handler(PsCertificatesInstaller.this.ctx.getMainLooper());
handler.post(() -> {
try {
Root.reboot();
} catch (Shell.ShellDiedException e) {}
});
}
})
.setNegativeButton("No", null).show();
}
}
}

View File

@ -1,4 +1,4 @@
package com.psmreborn.nopsmdrm.pscertified;
package com.psmreborn.pscertified;
public class CertifiedDeviceEntry {

View File

@ -1,4 +1,4 @@
package com.psmreborn.nopsmdrm.pscertified;
package com.psmreborn.pscertified;
public class CertifiedDeviceList {
private String manufacturer = null;

View File

@ -1,35 +1,29 @@
package com.psmreborn.nopsmdrm.pscertified;
package com.psmreborn.pscertified;
import android.content.Context;
import android.os.Build;
import java.util.Arrays;
import java.util.HashSet;
public class PlayStationCertified {
private static CertifiedDeviceList[] certifiedDevices = {
new CertifiedDeviceList("Sony", new CertifiedDeviceEntry[] {new CertifiedDeviceEntry("SO-02E"), new CertifiedDeviceEntry("C6602"), new CertifiedDeviceEntry("C6603"), new CertifiedDeviceEntry("C6616"), new CertifiedDeviceEntry("C6606"), new CertifiedDeviceEntry("SOL22"), new CertifiedDeviceEntry("C6502"), new CertifiedDeviceEntry("C6503"), new CertifiedDeviceEntry("C6506"), new CertifiedDeviceEntry("SGP321"), new CertifiedDeviceEntry("SGP312"), new CertifiedDeviceEntry("SGP311"), new CertifiedDeviceEntry("SGP341"), new CertifiedDeviceEntry("SO-03E"), new CertifiedDeviceEntry("SGP351"), new CertifiedDeviceEntry("SO-04E"), new CertifiedDeviceEntry("C5503"), new CertifiedDeviceEntry("C5502"), new CertifiedDeviceEntry("L36h"), new CertifiedDeviceEntry("L35h"), new CertifiedDeviceEntry("M36h"), new CertifiedDeviceEntry("C5303"), new CertifiedDeviceEntry("C5306"), new CertifiedDeviceEntry("C5302"), new CertifiedDeviceEntry("M35h"), new CertifiedDeviceEntry("M35c"), new CertifiedDeviceEntry("C2105"), new CertifiedDeviceEntry("C2104"), new CertifiedDeviceEntry("S36h"), new CertifiedDeviceEntry("C1904"), new CertifiedDeviceEntry("C1905"), new CertifiedDeviceEntry("C2004"), new CertifiedDeviceEntry("C2005"), new CertifiedDeviceEntry("SOL23"), new CertifiedDeviceEntry("C6903"), new CertifiedDeviceEntry("SO-01F"), new CertifiedDeviceEntry("C6906"), new CertifiedDeviceEntry("C6902"), new CertifiedDeviceEntry("C6916"), new CertifiedDeviceEntry("C6833"), new CertifiedDeviceEntry("C6806"), new CertifiedDeviceEntry("C6802"), new CertifiedDeviceEntry("SOL24"), new CertifiedDeviceEntry("SGP412"), new CertifiedDeviceEntry("SO-02F"), new CertifiedDeviceEntry("D5503"), new CertifiedDeviceEntry("SO-04F"), new CertifiedDeviceEntry("C6843"), new CertifiedDeviceEntry("XL39h"), new CertifiedDeviceEntry("M51w"), new CertifiedDeviceEntry("D5303"), new CertifiedDeviceEntry("D5322"), new CertifiedDeviceEntry("D5306"), new CertifiedDeviceEntry("D5316"), new CertifiedDeviceEntry("D5316N"), new CertifiedDeviceEntry("D2303"), new CertifiedDeviceEntry("D2306"), new CertifiedDeviceEntry("D2305"), new CertifiedDeviceEntry("D2302"), new CertifiedDeviceEntry("D2403"), new CertifiedDeviceEntry("D2406"), new CertifiedDeviceEntry("S50h"), new CertifiedDeviceEntry("D6502"), new CertifiedDeviceEntry("D6503"), new CertifiedDeviceEntry("SO-03F"), new CertifiedDeviceEntry("D6543"), new CertifiedDeviceEntry("SOL25"), new CertifiedDeviceEntry("D6563"), new CertifiedDeviceEntry("SGP541"), new CertifiedDeviceEntry("SGP521"), new CertifiedDeviceEntry("SO-05F"), new CertifiedDeviceEntry("SOT21"), new CertifiedDeviceEntry("SGP551"), new CertifiedDeviceEntry("SGP511"), new CertifiedDeviceEntry("SGP512"), new CertifiedDeviceEntry("D6542"), new CertifiedDeviceEntry("M35t"), new CertifiedDeviceEntry("M35ts"), new CertifiedDeviceEntry("SO-01C"), new CertifiedDeviceEntry("SO-02C"), new CertifiedDeviceEntry("SO-02D"), new CertifiedDeviceEntry("SO-03D"), new CertifiedDeviceEntry("IS12S"), new CertifiedDeviceEntry("LT26i") }), new CertifiedDeviceList("Sony Ericsson", new CertifiedDeviceEntry[] { new CertifiedDeviceEntry("R800i"), new CertifiedDeviceEntry("R800a"), new CertifiedDeviceEntry("R800x"), new CertifiedDeviceEntry("R800at") }),
new CertifiedDeviceList("Sony", new CertifiedDeviceEntry[] {new CertifiedDeviceEntry("SO-02E"), new CertifiedDeviceEntry("C6602"), new CertifiedDeviceEntry("C6603"), new CertifiedDeviceEntry("C6616"), new CertifiedDeviceEntry("C6606"), new CertifiedDeviceEntry("SOL22"), new CertifiedDeviceEntry("C6502"), new CertifiedDeviceEntry("C6503"), new CertifiedDeviceEntry("C6506"), new CertifiedDeviceEntry("SGP321"), new CertifiedDeviceEntry("SGP312"), new CertifiedDeviceEntry("SGP311"), new CertifiedDeviceEntry("SGP341"), new CertifiedDeviceEntry("SO-03E"), new CertifiedDeviceEntry("SGP351"), new CertifiedDeviceEntry("SO-04E"), new CertifiedDeviceEntry("C5503"), new CertifiedDeviceEntry("C5502"), new CertifiedDeviceEntry("L36h"), new CertifiedDeviceEntry("L35h"), new CertifiedDeviceEntry("M36h"), new CertifiedDeviceEntry("C5303"), new CertifiedDeviceEntry("C5306"), new CertifiedDeviceEntry("C5302"), new CertifiedDeviceEntry("M35h"), new CertifiedDeviceEntry("M35c"), new CertifiedDeviceEntry("C2105"), new CertifiedDeviceEntry("C2104"), new CertifiedDeviceEntry("S36h"), new CertifiedDeviceEntry("C1904"), new CertifiedDeviceEntry("C1905"), new CertifiedDeviceEntry("C2004"), new CertifiedDeviceEntry("C2005"), new CertifiedDeviceEntry("SOL23"), new CertifiedDeviceEntry("C6903"), new CertifiedDeviceEntry("SO-01F"), new CertifiedDeviceEntry("C6906"), new CertifiedDeviceEntry("C6902"), new CertifiedDeviceEntry("C6916"), new CertifiedDeviceEntry("C6833"), new CertifiedDeviceEntry("C6806"), new CertifiedDeviceEntry("C6802"), new CertifiedDeviceEntry("SOL24"), new CertifiedDeviceEntry("SGP412"), new CertifiedDeviceEntry("SO-02F"), new CertifiedDeviceEntry("D5503"), new CertifiedDeviceEntry("SO-04F"), new CertifiedDeviceEntry("C6843"), new CertifiedDeviceEntry("XL39h"), new CertifiedDeviceEntry("M51w"), new CertifiedDeviceEntry("D5303"), new CertifiedDeviceEntry("D5322"), new CertifiedDeviceEntry("D5306"), new CertifiedDeviceEntry("D5316"), new CertifiedDeviceEntry("D5316N"), new CertifiedDeviceEntry("D2303"), new CertifiedDeviceEntry("D2306"), new CertifiedDeviceEntry("D2305"), new CertifiedDeviceEntry("D2302"), new CertifiedDeviceEntry("D2403"), new CertifiedDeviceEntry("D2406"), new CertifiedDeviceEntry("S50h"), new CertifiedDeviceEntry("D6502"), new CertifiedDeviceEntry("D6503"), new CertifiedDeviceEntry("SO-03F"), new CertifiedDeviceEntry("D6543"), new CertifiedDeviceEntry("SOL25"), new CertifiedDeviceEntry("D6563"), new CertifiedDeviceEntry("SGP541"), new CertifiedDeviceEntry("SGP521"), new CertifiedDeviceEntry("SO-05F"), new CertifiedDeviceEntry("SOT21"), new CertifiedDeviceEntry("SGP551"), new CertifiedDeviceEntry("SGP511"), new CertifiedDeviceEntry("SGP512"), new CertifiedDeviceEntry("D6542"), new CertifiedDeviceEntry("M35t"), new CertifiedDeviceEntry("M35ts"), new CertifiedDeviceEntry("SO-01C"), new CertifiedDeviceEntry("SO-02C"), new CertifiedDeviceEntry("SO-02D"), new CertifiedDeviceEntry("SO-03D"), new CertifiedDeviceEntry("IS12S"), new CertifiedDeviceEntry("LT26i") }),
new CertifiedDeviceList("Sony Ericsson", new CertifiedDeviceEntry[] { new CertifiedDeviceEntry("R800i"), new CertifiedDeviceEntry("R800a"), new CertifiedDeviceEntry("R800x"), new CertifiedDeviceEntry("R800at") }),
new CertifiedDeviceList("HTC", new CertifiedDeviceEntry[] { new CertifiedDeviceEntry("primou", 15) }),
new CertifiedDeviceList("SHARP", new CertifiedDeviceEntry[] { new CertifiedDeviceEntry("SH09D", 15), new CertifiedDeviceEntry("SHI16", 15) , new CertifiedDeviceEntry("SBM106SH", 15) , new CertifiedDeviceEntry("SBM203SH", 16) }),
new CertifiedDeviceList("FUJITSU MOBILE COMMUNICATIONS LIMITED", new CertifiedDeviceEntry[] { new CertifiedDeviceEntry("F05E", 15) }),
};
private Context ctx;
public PlayStationCertified(Context context){
ctx = context;
}
private boolean isInDeviceList() {
String brand = Build.BRAND;
private static boolean isInDeviceList() {
String device = Build.DEVICE;
String manufacturer = Build.MANUFACTURER;
int osVer = Build.VERSION.SDK_INT;
for(int i = 0; i < certifiedDevices.length; i++){
if(certifiedDevices[i].checkDevice(manufacturer, brand, osVer)){
if(certifiedDevices[i].checkDevice(manufacturer, device, osVer)){
return true;
}
}
@ -38,7 +32,7 @@ public class PlayStationCertified {
return false;
}
public boolean isPlaystationCertified() {
public static boolean isPlaystationCertified(Context ctx) {
HashSet installedFrameworks = new HashSet(Arrays.asList(ctx.getPackageManager().getSystemSharedLibraryNames()));
if(installedFrameworks.contains("com.playstation.playstationcertified")){
return true;

View File

@ -0,0 +1,96 @@
package com.psmreborn.pscertified;
import static com.psmreborn.shared.Root.*;
import com.psmreborn.nopsmdrm.R;
import com.psmreborn.shared.Files;
import com.psmreborn.shared.Root;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Handler;
import java.io.File;
import java.io.IOException;
import eu.chainfire.libsuperuser.Shell;
public class PsCertificatesInstaller {
private static void installPlaystationCertificationMagisk(Context ctx) throws Exception {
File magiskZip = new File(ctx.getCacheDir(), "MagiskCertify.zip");
Files.unpackResource(ctx, R.raw.magiskcertify, magiskZip);
Root.installMagiskModule(magiskZip.getAbsolutePath());
}
private static void installPlaystationCertification(Context ctx) throws IOException, Shell.ShellDiedException {
// remount /system as read-write
String psCertifiedPermissionFile = "/system/etc/permissions/com.playstation.playstationcertified.xml";
String psCertifiedJarFile = "/system/framework/com.playstation.playstationcertified.jar";
// remount partitions as read-write
remountRw(psCertifiedPermissionFile);
remountRw(psCertifiedJarFile);
Root.unpackResourceToLocationRoot(ctx, R.raw.ps_certified_permission, psCertifiedPermissionFile, 644, "0");
Root.unpackResourceToLocationRoot(ctx, R.raw.ps_certified_jar, psCertifiedJarFile, 644, "0");
// make it read-only again.
remountRo(psCertifiedPermissionFile);
remountRo(psCertifiedJarFile);
}
public static void installPsCertificates(Activity activity) {
ProgressDialog dialog = new ProgressDialog(activity);
dialog.setTitle("Installing PlayStation Certificates ...");
dialog.setMessage("Please Wait ...");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
Handler handler = new Handler(activity.getMainLooper());
new Thread(() -> {
try {
if(Root.isMagiskRoot()) {
installPlaystationCertificationMagisk(activity);
}
else{
installPlaystationCertification(activity);
}
handler.post(() -> {
dialog.dismiss();
new AlertDialog.Builder(activity)
.setTitle("Installed!")
.setMessage("Your device is now \"Playstation Certified\"\n(You have to reboot for changes to take effect)\nDo you want to reboot?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
try {
Root.reboot();
} catch (Shell.ShellDiedException ignored) { }
}
})
.setNegativeButton("No", null).show();
});
}
catch(Exception e){
handler.post(() -> {
dialog.dismiss();
new AlertDialog.Builder(activity)
.setTitle("Error Occurred.")
.setMessage(e.toString())
.setCancelable(false)
.setPositiveButton("Ok",null).show();
});
}
}).start();
}
}

View File

@ -0,0 +1,126 @@
package com.psmreborn.shared;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.os.Handler;
import com.psmreborn.nopsmdrm.Startup;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class Downloader {
private static void showErrorMessage(Activity ctx, String errorMsg){
new AlertDialog.Builder(ctx)
.setTitle("Error Occurred.")
.setMessage(errorMsg)
.setCancelable(false)
.setPositiveButton("Ok",null).show();
}
private static void showInstallSuccessMsg(Activity ctx, String filename){
new AlertDialog.Builder((Activity)ctx)
.setTitle("Install complete!")
.setMessage("Sucessfully installed: "+ filename)
.setCancelable(false)
.setPositiveButton("Ok", null).show();
}
private static void showDownloadSuccessMsg(Activity ctx, String filename){
new AlertDialog.Builder((Activity)ctx)
.setTitle("Download complete!")
.setMessage("Sucessfully downloaded: "+ filename)
.setCancelable(false)
.setPositiveButton("Ok",null).show();
}
private static void download(String uri, String dstFile) throws IOException {
URL url = new URL(uri);
URLConnection connection = url.openConnection();
connection.connect();
InputStream input = new BufferedInputStream(url.openStream(), 8192);
OutputStream output = new FileOutputStream(dstFile);
Files.copyTo(input, output);
output.close();
input.close();
}
public static void downloadAndInstall(Activity ctx, String url){
ProgressDialog dialog = new ProgressDialog(ctx);
final String filename = new File(url).getName();
final String saveLocation = new File(ctx.getCacheDir(), filename).getAbsolutePath();
dialog.setTitle("Downloading "+filename+"...");
dialog.setMessage("Please Wait ...");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
new Thread(() -> {
Handler handler = new Handler(ctx.getMainLooper());
PsmApplication.killPsm(ctx);
try {
download(url, saveLocation);
handler.post(() -> {dialog.setTitle("Installing "+filename+"...");});
Root.installRoot(saveLocation);
} catch (Exception e) {
handler.post(() -> {
dialog.dismiss();
showErrorMessage(ctx, e.toString());
});
return;
}
handler.post(() -> {
dialog.dismiss();
Startup.updateNames(ctx);
showInstallSuccessMsg(ctx, filename);
});
}).start();
}
public static void downloadFile(Activity ctx, String url, String saveLocation) {
ProgressDialog dialog = new ProgressDialog(ctx);
final String filename = new File(url).getName();
dialog.setTitle("Downloading "+filename+"...");
dialog.setMessage("Please Wait ...");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
new Thread(() -> {
Handler handler = new Handler(ctx.getMainLooper());
PsmApplication.killPsm(ctx);
try {
download(url, saveLocation);
} catch (IOException e) {
handler.post(() -> {
dialog.dismiss();
showErrorMessage(ctx, e.toString());
return;
});
}
handler.post(() -> {
dialog.dismiss();
Startup.updateNames(ctx);
showDownloadSuccessMsg(ctx, filename);
});
}).start();
}
}

View File

@ -0,0 +1,66 @@
package com.psmreborn.shared;
import android.content.Context;
import android.content.pm.PackageManager;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class Files {
public static void cleanupFiles(Context ctx) throws PackageManager.NameNotFoundException {
File databasesFolder = new File(Packages.getAppInfo(ctx, ctx.getPackageName()).dataDir, "databases");
Log.d("FILES", "DatabaseFolder: "+databasesFolder);
if(databasesFolder.exists()){
for(String db : databasesFolder.list()){
Log.d("FILES", "Deleting: "+db);
new File(databasesFolder, db).delete();
}
}
File sharedPrefFolder = new File(Packages.getAppInfo(ctx, ctx.getPackageName()).dataDir, "shared_prefs");
Log.d("FILES", "SharedPrefsFolder: "+sharedPrefFolder);
if(sharedPrefFolder.exists()){
for(String pref : sharedPrefFolder.list()){
Log.d("FILES", "Deleting: "+pref);
new File(sharedPrefFolder, pref).delete();
}
}
}
public static void unpackResource(Context ctx, int resourceId, File outputFile) throws IOException {
Log.i("FILES", "Unpacking resource to : " + outputFile);
InputStream resourceStream = ctx.getResources().openRawResource(resourceId);
FileOutputStream fs = new FileOutputStream(outputFile, false);
copyTo(resourceStream, fs);
fs.close();
resourceStream.close();
}
public static void writeTxtFile(String txtFile, String txt) throws IOException {
Log.i("FILES", "Writing: " + txtFile);
FileWriter txtStream = new FileWriter(txtFile);
txtStream.write(txt);
txtStream.close();
}
public static void copyTo(InputStream inpStream, OutputStream outStream) throws IOException {
byte[] buffer = new byte[8192];
int totalRead;
while(true){
totalRead = inpStream.read(buffer, 0, buffer.length);
if(totalRead == -1)
break;
outStream.write(buffer, 0, totalRead);
}
outStream.flush();
}
}

View File

@ -0,0 +1,24 @@
package com.psmreborn.shared;
import android.util.Log;
public class Logger {
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
private static String bytesToHex(byte[] bytes) {
if(bytes == null) return "(nulL)";
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
public static void logText(String description, String text){
Log.i("NoPsmDrmApp", description.toUpperCase() + " = "+text);
}
public static void logBytes(String description, byte[] data){
Log.i("NoPsmDrmApp", description.toUpperCase() + " = " + bytesToHex(data));
}
}

View File

@ -0,0 +1,80 @@
package com.psmreborn.shared;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
public class MountPoint {
private final HashSet<String> mountFlags = new HashSet<String>();
private final String filesystem;
private final String mountPoint;
public static MountPoint getMountPointAt(String path) {
try{
BufferedReader reader = new BufferedReader(new FileReader("/proc/mounts"));
String line = reader.readLine();
MountPoint mp = null;
do {
String[] mountParts = line.split(" ");
if(mountParts.length < 1) continue;
File gotMountPoint = new File(mountParts[1]);
String gotMountFs = mountParts[2];
String gotMountFlags = mountParts[3];
Log.d("MOUNTPOINT", gotMountPoint.getAbsolutePath());
// check if filepath is a child of this mount point.
File filePath = new File(path);
do{
if((filePath.equals(gotMountPoint)) && (mp == null || (gotMountPoint.getAbsolutePath().length() > mp.getMountPoint().length()))) {
mp = new MountPoint(gotMountPoint.getAbsolutePath(), gotMountFs, gotMountFlags);
}
filePath = filePath.getParentFile();
}while(filePath != null);
line = reader.readLine();
} while(line != null);
return mp;
}
catch (IOException e){
Log.e("MOUNTPOINT", e.toString());
return new MountPoint(path, "yaffs2", "rw");
}
}
public MountPoint(String point, String filesystem, String flags){
this.mountFlags.addAll(Arrays.asList(flags.split(",")));
this.mountPoint = point;
this.filesystem = filesystem;
}
public String getFilesystem() {
return filesystem;
}
public String getMountPoint() {
return mountPoint;
}
public boolean hasFlag(String flag){
return mountFlags.contains(flag);
}
public boolean isRw(){
return hasFlag("rw") && !hasFlag("ro");
}
public boolean isRo(){
return hasFlag("ro") && !hasFlag("rw");
}
public boolean isFat(){
return filesystem.contains("fat");
}
}

View File

@ -0,0 +1,75 @@
package com.psmreborn.shared;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.provider.Settings;
import com.psmreborn.nopsmdrm.R;
import java.io.File;
public class Packages {
@SuppressLint("HardwareIds")
public static String getAndroidIdOfApp(Context ctx, String packageId) {
if(Build.VERSION.SDK_INT >= 26){
try{
String[] files = Root.findFiles("/data/system", "settings_ssaid.xml");
String ssaidCacheFile = new File(ctx.getCacheDir(), "settings_ssaid.xml").getAbsolutePath();
for(String ssaidFile : files){
try {
Root.copyChmodAndChown(ssaidFile, ssaidCacheFile, 777, String.valueOf(Packages.getAppUid(ctx, ctx.getPackageName())));
return SsaidParser.parseSsaid(ssaidCacheFile, packageId);
} catch (Exception e) {
continue;
}
}
}
catch (Exception e){
return null;
}
return null;
}
else {
return Settings.Secure.getString(ctx.getContentResolver(), "android_id");
}
}
public static void killApp(Context ctx, String packageName) {
if (Build.VERSION.SDK_INT >= 34) {
new Thread(() -> {
try {
Root.killProcess(packageName);
} catch (Exception e) {
return;
}
}).start();
} else {
ActivityManager am = (ActivityManager) ctx.getSystemService(Activity.ACTIVITY_SERVICE);
if (am != null) {
am.killBackgroundProcesses(packageName);
}
}
}
public static int getAppUid(Context ctx, String packageName){
try{
ApplicationInfo appInfo = ctx.getPackageManager().getApplicationInfo(packageName, 0);
if(appInfo != null) {
return appInfo.uid;
}
}
catch (PackageManager.NameNotFoundException ignored) { };
return 0;
}
public static PackageInfo getPackageInfo(Context ctx, String pkg) throws PackageManager.NameNotFoundException {
return ctx.getPackageManager().getPackageInfo(pkg, 0);
}
public static ApplicationInfo getAppInfo(Context ctx, String pkg) throws PackageManager.NameNotFoundException {
return ctx.getPackageManager().getApplicationInfo(pkg, 0);
}
}

View File

@ -0,0 +1,66 @@
package com.psmreborn.shared;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import com.psmreborn.nopsmdrm.R;
import java.io.File;
public class PsmApplication {
public static int getPsmVersion(Context ctx){
try {
return PsmApplication.getPsmAppPkg(ctx).versionCode;
} catch (PackageManager.NameNotFoundException e) {
return 0;
}
}
public static boolean isPsmInstalled(Context ctx){
try {
ctx.getPackageManager().getApplicationInfo(ctx.getResources().getString(R.string.psm_app_package_id), 0);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
return true;
}
public static int getPsmUid(Context ctx) {
return Packages.getAppUid(ctx, ctx.getResources().getString(R.string.psm_app_package_id));
}
public static void killPsm(Context ctx){
Packages.killApp(ctx, ctx.getResources().getString(R.string.psm_app_package_id));
}
public static PackageInfo getPsmAppPkg(Context ctx) throws PackageManager.NameNotFoundException {
return Packages.getPackageInfo(ctx,ctx.getResources().getString(R.string.psm_app_package_id));
}
public static ApplicationInfo getPsmAppInfo(Context ctx) throws PackageManager.NameNotFoundException {
return Packages.getAppInfo(ctx,ctx.getResources().getString(R.string.psm_app_package_id));
}
public static String getAndroidIdOfPsm(Context ctx){
return Packages.getAndroidIdOfApp(ctx, ctx.getResources().getString(R.string.psm_app_package_id));
}
public static String getSharedPrefFromPsm(Context ctx, String prefName, String prefKey) throws Exception {
String psmSharedPrefs = new File(getPsmAppInfo(ctx).dataDir, "shared_prefs").getAbsolutePath();
String psmPrefFile = new File(psmSharedPrefs, prefName+".xml").getAbsolutePath();
String mySharedPrefs = new File(Packages.getAppInfo(ctx, ctx.getPackageName()).dataDir, "shared_prefs").getAbsolutePath();
String myPrefFile = new File(mySharedPrefs, prefName+".xml").getAbsolutePath();
if(!Root.fileExistRoot(psmSharedPrefs)) return null;
Root.mkdirAndChmodChown(mySharedPrefs, 771, String.valueOf(Packages.getAppUid(ctx, ctx.getPackageName())));
Root.copyChmodAndChown(psmPrefFile, myPrefFile, 660, String.valueOf(Packages.getAppUid(ctx, ctx.getPackageName())));
SharedPreferences pref = ctx.getSharedPreferences(prefName, 0);
return pref.getString(prefKey, null);
}
}

View File

@ -1,10 +1,17 @@
package com.psmreborn.nopsmdrm;
package com.psmreborn.shared;
import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import com.psmreborn.nopsmdrm.R;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
@ -16,11 +23,68 @@ import eu.chainfire.libsuperuser.Shell;
public class Root {
private static String busyboxBinary = null;
private static Context ctx = null;
public static boolean isMagiskRoot() {
String[] pathLocations = System.getenv("PATH").split(":");
for(String pathLocation : pathLocations){
if(new File(pathLocation, "magisk").exists()){
return true;
}
}
return false;
}
public static void killProcess(String processName) throws Shell.ShellDiedException {
int res = 0;
do{
res = Shell.Pool.SU.run(new String[] { busyboxBinary + " pkill -9 '"+processName+"'" });
} while(res == 0);
}
public static void installMagiskModule(String magiskModuleFile) throws Exception {
int res = 0;
res = Shell.Pool.SU.run(new String[] { "magisk --install-module '"+magiskModuleFile+"'"});
if(res != 0){
throw new Exception("Magisk module failed to install.");
}
}
public static void installRoot(String apkFile) throws Exception {
ArrayList<String> results = new ArrayList<String>();
int res = 0;
String apkFileName = new File("/data/local/tmp", new File(apkFile).getName()).getAbsolutePath();
moveChmodAndChown(apkFile, apkFileName, 777, null);
if(Build.VERSION.SDK_INT >= 34){
res = Shell.Pool.SU.run(new String[] { "pm install --bypass-low-target-sdk-block '"+apkFileName+"'"}, results, null, true);
}
else{
res = Shell.Pool.SU.run(new String[] { "pm install '"+apkFileName+"'" },results, null, true);
}
// delete apk (for some reason this sometimes doesn't work, (file is in use somehow?) so we try a few times to remove it
for(int i = 0; i < 500; i++){
try{
deleteRoot(apkFileName);
break;
}
catch (Exception ignored) { }
}
if(res != 0) {
throw new Exception(results.get(0));
}
}
public static void deleteRoot(String filename) throws Shell.ShellDiedException, IOException {
Log.i("ROOT", "deleting file: "+filename);
int res = Shell.Pool.SU.run(new String[] { busyboxBinary + " rm -f '"+filename+"'" });
if(res != 0){
throw new IOException("failed to remove file \""+filename+"\"");
}
}
public static void reboot() throws Shell.ShellDiedException {
Log.i("ROOT", "Rebooting");
Shell.Pool.SU.run(new String[] { busyboxBinary + " reboot" });
Shell.Pool.SU.run(new String[] { "reboot" });
}
public static boolean fileExistRoot(String filename) throws Shell.ShellDiedException {
Log.i("ROOT", "FileExistRoot: " + filename);
@ -65,7 +129,7 @@ public class Root {
return results.get(0);
}
Log.e("ROOT", "error (" + String.valueOf(res) +")");
throw new IOException("failed to get owner: "+filePath);
return null;
}
public static void tarRoot(ArrayList<String> src, String dst) throws IOException, Shell.ShellDiedException {
@ -90,18 +154,26 @@ public class Root {
}
public static void remountRo(String foldername) throws IOException, Shell.ShellDiedException {
Log.i("ROOT", "Remounting as RO: " + foldername);
int res = Shell.Pool.SU.run(new String[] { busyboxBinary +" mount -o ro,remount '"+foldername+"'"});
if(res != 0){
Log.e("ROOT", "error (" + String.valueOf(res) +")");
throw new IOException("Failed to remmount: "+foldername + " ("+String.valueOf(res)+")");
MountPoint mountPoint = MountPoint.getMountPointAt(foldername);
if(mountPoint.isRw()) {
int res = Shell.Pool.SU.run(new String[]{busyboxBinary + " mount -o ro,remount '" + mountPoint.getMountPoint() + "'"});
if (res != 0) {
Log.e("ROOT", "error (" + String.valueOf(res) + ")");
throw new IOException("Failed to remmount: " + foldername + " (" + String.valueOf(res) + ")");
}
}
}
public static void remountRw(String foldername) throws IOException, Shell.ShellDiedException {
Log.i("ROOT", "Remounting as RW: " + foldername);
int res = Shell.Pool.SU.run(new String[] { busyboxBinary +" mount -o rw,remount '"+foldername+"'"});
if(res != 0){
Log.e("ROOT", "error (" + String.valueOf(res) +")");
throw new IOException("Failed to remmount: "+foldername + " ("+String.valueOf(res)+")");
MountPoint mountPoint = MountPoint.getMountPointAt(foldername);
if(mountPoint.isRo()){
int res = Shell.Pool.SU.run(new String[] { busyboxBinary +" mount -o rw,remount '"+mountPoint.getMountPoint()+"'"});
if(res != 0){
Log.e("ROOT", "error (" + String.valueOf(res) +")");
throw new IOException("Failed to remmount: "+foldername + " ("+String.valueOf(res)+")");
}
}
}
public static void moveFileRoot(String filename, String destFilename) throws IOException, Shell.ShellDiedException {
@ -120,6 +192,35 @@ public class Root {
busyboxBinary + " chown " + chown +":"+chown+" '"+directory+"'"
});
}
public static void moveChmodAndChown(String srcFile, String dstFile, int chmod, String chown) throws IOException, Shell.ShellDiedException {
Log.i("ROOT", "MoveAndChown: " + srcFile +" : "+dstFile);
int chownRes = 0;
int mvRes = 0;
int chmodRes = 0;
mvRes = Shell.Pool.SU.run(new String[]{busyboxBinary + " mv '" + srcFile +"' '"+dstFile+"'"});
if(chmod != 0)
chmodRes = Shell.Pool.SU.run(new String[]{busyboxBinary + " chmod " + String.valueOf(chmod) +" '"+dstFile+"'"});
if(chown != null)
chownRes = Shell.Pool.SU.run(new String[]{busyboxBinary + " chown " + chown +":"+chown+" '"+dstFile+"'"});
if(mvRes != 0){
Log.e("ROOT", "error (" + String.valueOf(mvRes) +")");
throw new IOException("Failed to move file. " + String.valueOf(mvRes));
}
if(chmodRes != 0){
Log.e("ROOT", "error (" + String.valueOf(chmodRes) +")");
throw new IOException("Failed to chmod file. " + String.valueOf(chmodRes));
}
if(chownRes != 0){
Log.e("ROOT", "error (" + String.valueOf(chownRes) +")");
throw new IOException("Failed to chown file. " + String.valueOf(chownRes));
}
}
public static void copyChmodAndChown(String srcFile, String dstFile, int chmod, String chown) throws IOException, Shell.ShellDiedException {
Log.i("ROOT", "CopyAndChown: " + srcFile +" : "+dstFile);
@ -136,76 +237,50 @@ public class Root {
if(cpRes != 0){
Log.e("ROOT", "error (" + String.valueOf(cpRes) +")");
throw new IOException("Failed to copy file.");
throw new IOException("Failed to move file. " + String.valueOf(cpRes));
}
if(chmodRes != 0){
Log.e("ROOT", "error (" + String.valueOf(cpRes) +")");
throw new IOException("Failed to chmod file.");
Log.e("ROOT", "error (" + String.valueOf(chmodRes) +")");
throw new IOException("Failed to chmod file. " + String.valueOf(chmodRes));
}
if(chownRes != 0){
Log.e("ROOT", "error (" + String.valueOf(cpRes) +")");
throw new IOException("Failed to chown file.");
Log.e("ROOT", "error (" + String.valueOf(chownRes) +")");
throw new IOException("Failed to chown file. " + String.valueOf(chownRes));
}
}
private static void copyTo(InputStream inpStream, OutputStream outStream) throws IOException {
int totalRead = 0;
byte[] buffer = new byte[0x1000];
do {
totalRead = inpStream.read(buffer, 0, buffer.length);
outStream.write(buffer, 0, totalRead);
}
while(totalRead >= buffer.length);
outStream.flush();
}
public static void unpackResourceToLocationRoot(int resourceId, String outputDir, int chmod, String chown) throws IOException, Shell.ShellDiedException {
public static void unpackResourceToLocationRoot(Context ctx, int resourceId, String outputDir, int chmod, String chown) throws IOException, Shell.ShellDiedException {
Log.i("ROOT", "Unpacking resource and root copying to : " + outputDir);
File tmpFile = File.createTempFile("tmp", "file", ctx.getCacheDir());
tmpFile.createNewFile();
tmpFile.deleteOnExit();
unpackResource(resourceId, tmpFile);
copyChmodAndChown(tmpFile.getAbsolutePath(), outputDir, chmod, chown);
tmpFile.delete();
Files.unpackResource(ctx, resourceId, tmpFile);
moveChmodAndChown(tmpFile.getAbsolutePath(), outputDir, chmod, chown);
}
private static void unpackResource(int resourceId, File outputFile) throws IOException {
Log.i("ROOT", "Unpacking resource to : " + outputFile);
InputStream resourceStream = ctx.getResources().openRawResource(resourceId);
FileOutputStream fs = new FileOutputStream(outputFile, false);
copyTo(resourceStream, fs);
fs.close();
resourceStream.close();
}
public static void writeTxtFile(String txtFile, String txt) throws IOException {
Log.i("ROOT", "Writing: " + txtFile);
FileWriter txtStream = new FileWriter(txtFile);
txtStream.write(txt);
txtStream.close();
}
private static void setupBusyBox() throws IOException {
private static void setupBusyBox(Context ctx) throws IOException {
Log.i("ROOT","Creating busybox binary");
File tmpFile = new File(ctx.getCacheDir(), "busybox");
if(!tmpFile.exists()) {
tmpFile.createNewFile();
File busyboxPath = new File(ctx.getCacheDir(), "busybox");
if(!busyboxPath.exists()) {
busyboxPath.createNewFile();
if(tmpFile.setExecutable(true,false)) {
unpackResource(R.raw.busybox, tmpFile);
if(busyboxPath.setExecutable(true,false)) {
Files.unpackResource(ctx, R.raw.busybox, busyboxPath);
}
else {
throw new IOException("failed to extract busybox binary.");
}
}
busyboxBinary = tmpFile.getAbsolutePath();
busyboxBinary = busyboxPath.getAbsolutePath();
}
public static void setContext(Context context) throws IOException {
ctx = context.getApplicationContext();
Shell.setRedirectDeprecated(false);
setupBusyBox();
public static boolean init(Context ctx) throws IOException {
if(Shell.SU.available()){
setupBusyBox(ctx);
return true;
}
return false;
}
}

View File

@ -0,0 +1,67 @@
package com.psmreborn.shared;
import com.rosstonovsky.abxutils.BinaryXmlPullParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.FileInputStream;
import java.io.IOException;
public class SsaidParser {
private static String parseSsaidText(FileInputStream stream, String searchPackage) throws XmlPullParserException, IOException {
// parse xml file ...
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(false);
XmlPullParser xpp = factory.newPullParser();
xpp.setInput(stream, "UTF-8");
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if(eventType == XmlPullParser.START_TAG) {
if(xpp.getName().equalsIgnoreCase("setting")){
String packageName = xpp.getAttributeValue(null,"package");
if(packageName != null && packageName.equalsIgnoreCase(searchPackage)){
return xpp.getAttributeValue(null,"value");
}
}
}
eventType = xpp.next();
}
return null;
}
private static String parseSsaidBinary(FileInputStream stream, String searchPackage) throws XmlPullParserException, IOException {
// parse binary xml file ...
BinaryXmlPullParser xpp = new BinaryXmlPullParser();
xpp.setInput(stream, "UTF-8");
int eventType = xpp.getEventType();
while (eventType != BinaryXmlPullParser.END_DOCUMENT) {
if(eventType == BinaryXmlPullParser.START_TAG) {
if(xpp.getName().equalsIgnoreCase("setting")){
String packageName = xpp.getAttributeValue("package");
if(packageName != null && packageName.equalsIgnoreCase(searchPackage)){
return xpp.getAttributeValue("value");
}
}
}
eventType = xpp.next();
}
return null;
}
public static String parseSsaid(String filename, String searchPackage) throws XmlPullParserException, IOException {
try{
FileInputStream stream = new FileInputStream(filename);
return parseSsaidBinary(stream, searchPackage);
}
catch (XmlPullParserException e){
FileInputStream stream = new FileInputStream(filename);
return parseSsaidText(stream, searchPackage);
}
}
}

View File

@ -1,9 +1,6 @@
package com.psmreborn.nopsmdrm;
package com.psmreborn.shared;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.provider.Settings;
import android.util.Base64;
import android.util.Log;
@ -12,32 +9,27 @@ import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import eu.chainfire.libsuperuser.Shell;
public class StringEncryptor {
private static final byte[] iv = {-126, -30, -6, -75, -99, -117, -66, 117, 39, -65, -126, -27, -12, 38, -99, 86};
private static final byte[] salt = {-92, -102, -105, -123, 71, -33, 69, -39, -27, -32, 21, 33, 126, -81, 69, 59, 57, 29, -83, -15};
private Context ctx;
private String android_id = null;
private int psm_uid = 0;
StringEncryptor(Context ctx, String androidId, int psmUid) {
this.ctx = ctx;
public StringEncryptor(String androidId, int psmUid) {
this.android_id = androidId;
this.psm_uid = psmUid;
}
private String base64(byte[] data){
private String encodeBase64(byte[] data){
return Base64.encodeToString(data, Base64.DEFAULT);
}
private byte[] decodeBase64(String str) {return Base64.decode(str, Base64.DEFAULT);}
public String encryptString(String str) {
byte[] data = str.getBytes();
byte[] encryptedData = encrypt(data);
@ -46,12 +38,44 @@ public class StringEncryptor {
encodedData[encodedData.length - 2] = 1;
encodedData[encodedData.length - 1] = 1;
return base64(encodedData);
return encodeBase64(encodedData);
}
return null;
}
private byte[] encrypt(byte[] input){
public String decryptString(String str, String def) {
if(str == null) return def;
byte[] data = decodeBase64(str);
byte[] arrayData = Arrays.copyOf(data, data.length - 2);
byte[] decryptedData = decrypt(arrayData);
if(decryptedData != null){
try {
return new String(decryptedData, "UTF-8");
} catch (UnsupportedEncodingException e) {
Log.e("STRINGENCRYPTOR", e.toString());
return def;
}
}
return def;
}
public byte[] decrypt(byte[] input){
if(input == null) return null;
try {
Cipher cipher = generateKeyCipher(StringEncryptor.salt, StringEncryptor.iv, Cipher.DECRYPT_MODE);
if (cipher != null) {
return cipher.doFinal(input);
}
else {
Log.e("STRINGENCRYPTOR", "cipher was null");
}
} catch (BadPaddingException | IllegalBlockSizeException e) { System.out.println(e.toString()) ;}
return null;
}
public byte[] encrypt(byte[] input){
if(input == null) return null;
try {
Cipher cipher = generateKeyCipher(StringEncryptor.salt, StringEncryptor.iv, Cipher.ENCRYPT_MODE);
if (cipher != null) {

View File

@ -0,0 +1,199 @@
package com.psmreborn.xposed;
import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
public class JavaPatch implements IXposedHookLoadPackage {
private void hookCdnUrls(LoadPackageParam lpparam) {
/*
* Fix CDN
*/
XposedHelpers.findAndHookMethod("com.playstation.psstore.util.db", lpparam.classLoader, "b", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("NoPssDrmXposed","versionInfoUrl called!");
param.setResult("http://zeus.dl.playstation.net/pss/store/update/versionInfo.dat");
}
});
Log.d("NoPssDrmXposed","versionInfoUrl hooked.");
XposedHelpers.findAndHookMethod("com.playstation.psstore.util.db", lpparam.classLoader, "c", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("NoPssDrmXposed","pssZpakCdn called!");
param.setResult("http://zeus.dl.playstation.net/pss/cGxheXN0YXRpb25z/");
}
});
Log.d("NoPssDrmXposed","pssZpakCdn hooked.");
XposedHelpers.findAndHookMethod("com.playstation.psstore.util.db", lpparam.classLoader, "d", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("NoPssDrmXposed","deviceList2Cdn called!");
param.setResult("http://zeus.dl.playstation.net/pss/store/manifest/deviceList2.dat");
}
});
Log.d("NoPssDrmXposed","deviceList2Cdn hooked.");
XposedHelpers.findAndHookMethod("com.playstation.psstore.util.db", lpparam.classLoader, "e", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("NoPssDrmXposed","comingSoonDevListUrl called!");
param.setResult("http://zeus.dl.playstation.net/psm/store/manifest/comingSoonDevList.dat");
}
});
Log.d("NoPssDrmXposed","comingSoonDevListUrl hooked.");
XposedHelpers.findAndHookMethod("com.playstation.psstore.util.db", lpparam.classLoader, "f", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("NoPssDrmXposed","comingSoonUrl called!");
param.setResult("http://zeus.dl.playstation.net/psm/store/manifest/comingSoon.dat");
}
});
Log.d("NoPssDrmXposed","comingSoonUrl hooked.");
XposedHelpers.findAndHookMethod("com.playstation.psstore.util.db", lpparam.classLoader, "g", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("NoPssDrmXposed","addtionalCertifiedDevListUrl called!");
param.setResult("http://zeus.dl.playstation.net/psm/store/manifest/addtionalCertifiedDevList.dat");
}
});
Log.d("NoPssDrmXposed","addtionalCertifiedDevListUrl hooked.");
}
private void hookTls(LoadPackageParam lpparam) {
/*
* Hook TLS
*/
XposedHelpers.findAndHookMethod("yl", lpparam.classLoader, "e", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("NoPssDrmXposed","shouldIgnoreTls called!");
param.setResult(true);
}
});
Log.d("NoPssDrmXposed","shouldIgnoreTls hooked.");
}
private void hookPlaystationCertifiedChecks(LoadPackageParam lpparam) {
/*
* PS1 CERTIFIED CHECK BYPASS
*/
// verifyWithPsSuiteCer
XposedHelpers.findAndHookMethod("com.playstation.psstore.util.ct", lpparam.classLoader, "a", Context.class, "com.playstation.psstore.util.cv", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("NoPssDrmXposed","verifyWithPsSuiteCer called!");
param.setResult(true);
}
});
Log.d("NoPssDrmXposed","verifyWithPsSuiteCer hooked.");
// getSupportedDevices
XposedHelpers.findAndHookMethod("com.playstation.psstore.util.de", lpparam.classLoader, "a", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("NoPssDrmXposed","getSupportedDevices called!");
// get Device Info Class.
Class<?> deviceInfoClass = XposedHelpers.findClass("com.playstation.psstore.util.dd", lpparam.classLoader); // com.playstation.psstore.util.DeviceInfo (deobfuscated name)
Log.d("NoPssDrmXposed","got deviceInfoClass "+deviceInfoClass.toString());
// get arraylist of supported devices
ArrayList<Object> supportedDevicesArray = (ArrayList<Object>)param.getResult();
Log.d("NoPssDrmXposed","got array!");
// get DeviceInfo constructor
Constructor<?> deviceInfoConstructor = deviceInfoClass.getDeclaredConstructor(String.class, String.class, String.class, String.class, int.class, int.class, boolean.class);
Log.d("NoPssDrmXposed","got constructor!");
// make it public
deviceInfoConstructor.setAccessible(true);
Log.d("NoPssDrmXposed","made constructor public!");
// create new object...
Object myDeviceInfo = deviceInfoConstructor.newInstance("PLAY", "PN.XPERIA", Build.BRAND, Build.DEVICE, -1, -1, true);
Log.d("NoPssDrmXposed","got new obj \""+myDeviceInfo.toString()+"\"!");
// add new deviceInfo to list of supported devices
supportedDevicesArray.add(myDeviceInfo);
Log.d("NoPssDrmXposed","add newObj to array!");
// set new array as result.
param.setResult(supportedDevicesArray);
}
});
Log.d("NoPssDrmXposed","getSupportedDevices hooked.");
}
private void hookWifiCheck(LoadPackageParam lpparam) {
/*
* WIFI CHECK BYPASS
*/
XposedHelpers.findAndHookMethod("li", lpparam.classLoader, "b", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("NoPssDrmXposed","appUpdateRequired: "+ param.getResult());
param.setResult(1);
}
});
XposedHelpers.findAndHookMethod("jd", lpparam.classLoader, "b", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("NoPssDrmXposed","wifiRequired: "+ param.getResult());
param.setResult(2);
}
});
XposedHelpers.findAndHookMethod("android.net.wifi.WifiInfo", lpparam.classLoader, "getMacAddress", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.d("NoPssDrmXposed","getMacAddress called!");
String macAddress = (String)param.getResult();
Log.d("NoPssDrmXposed", "got macAddress: \""+macAddress+"\"");
if(TextUtils.isEmpty(macAddress)) {
param.setResult("00:11:22:33:44:55");
}
}
});
Log.d("NoPssDrmXposed","getMacAddress hooked.");
}
public void handleLoadPackage(LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("com.playstation.psstore"))
return;
if(lpparam.processName.equals("com.playstation.psstore:PsmApp"))
return;
Log.d("NoPssDrmXposed","Loaded app: " + lpparam.packageName + " // "+ lpparam.processName);
hookWifiCheck(lpparam);
hookPlaystationCertifiedChecks(lpparam);
hookCdnUrls(lpparam);
hookTls(lpparam);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -8,7 +8,8 @@
android:paddingBottom="15dp"
android:keepScreenOn="true"
android:noHistory="true"
android:orientation="vertical">
android:orientation="vertical"
android:background="@color/black">
<TextView
android:id="@+id/errorMsg"
@ -22,31 +23,32 @@
android:id="@+id/installPsm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:onClick="installStart"
android:textSize="30sp"
android:enabled="false"
android:layout_below="@+id/errorMsg"
android:text="Install NoPsmDrm"/>
android:textColor="@color/white"
android:text="Waiting..."/>
<Button
android:id="@+id/takeOwnership"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:onClick="givePsmOwnership"
android:textSize="30sp"
android:enabled="false"
android:layout_below="@+id/installPsm"
android:text="Fix permissions"/>
android:textColor="@color/white"
android:text="Fix permissions" />
<Button
android:id="@+id/dumpGames"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:onClick="dumpAllRifs"
android:textSize="30sp"
android:enabled="false"
android:layout_below="@+id/takeOwnership"
android:text="Backup all licenses"/>
android:textColor="@color/white"
android:text="Backup all RIFs" />
</RelativeLayout>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -5,7 +5,7 @@
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="gray">#80808080</color>
<color name="gray">#8f8f8f8f</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@ -1,3 +1,15 @@
<resources>
<string name="app_name">NoPsmDrm</string>
<string name="app_name">NoPssDrm</string>
<string name="name_nopsmdrm">NoPsmDrm</string>
<string name="name_nops1drm_ticket">NoPs1Drm - Ticket Generator</string>
<string name="name_nops1drm_update">NoPs1Drm - zPAK Updater</string>
<string name="default_email">nopsmdrm@transrights.lgbt</string>
<string name="default_password">password</string>
<string name="default_online_id">TransgenderPS1</string>
<string name="default_account_id">0123456789abcdef</string>
<string name="psm_app_package_id">com.playstation.psstore</string>
<string-array name="scope">
<item>com.playstation.psstore</item>
</string-array>
</resources>

View File

@ -3,4 +3,3 @@ plugins {
id 'com.android.application' version '8.3.2' apply false
id 'com.android.library' version '8.3.2' apply false
}

View File

@ -14,10 +14,12 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
android.useAndroidX=false
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
# make it work how it used to ??
android.nonFinalResIds=false
android.nonFinalResIds=false
applicationName = NoPssDrm

View File

@ -4,6 +4,7 @@ plugins {
android {
namespace 'com.rosstonovsky.abxutils'
//noinspection GradleDependency needs to work properly on android 2.x
compileSdk 10
defaultConfig {
@ -22,5 +23,6 @@ android {
}
dependencies {
implementation 'androidx.annotation:annotation-jvm:1.7.1'
//noinspection GradleDependency new version has min sdk 14
implementation 'com.android.support:support-core-utils:25.0.0'
}

View File

@ -35,8 +35,7 @@ import static com.rosstonovsky.abxutils.BinaryXmlSerializer.TYPE_STRING_INTERNED
import android.text.TextUtils;
import android.util.Base64;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.support.annotation.*;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

View File

@ -16,7 +16,7 @@
*/
package com.rosstonovsky.abxutils;
import androidx.annotation.NonNull;
import android.support.annotation.*;
import java.io.Closeable;
import java.io.DataInput;

View File

@ -16,8 +16,7 @@
*/
package com.rosstonovsky.abxutils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.support.annotation.*;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

View File

@ -4,6 +4,7 @@ plugins {
android {
namespace 'eu.chainfire.libsuperuser'
//noinspection GradleDependency needs to work properly on android 2.x
compileSdk 10
defaultConfig {
@ -22,5 +23,6 @@ android {
}
dependencies {
implementation 'androidx.annotation:annotation-jvm:1.7.1'
//noinspection GradleDependency new version has min sdk 14
implementation 'com.android.support:support-core-utils:25.0.0'
}

View File

@ -20,9 +20,8 @@ import android.content.Context;
import android.os.Handler;
import android.widget.Toast;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.support.annotation.*;
/**
* Base application class to extend from, solving some issues with

View File

@ -20,9 +20,8 @@ import android.os.Looper;
import android.util.Log;
import android.os.Process;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.support.annotation.*;
/**
* Utility class for logging and debug features that (by default) does nothing when not in debug mode

View File

@ -20,9 +20,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import android.support.annotation.*;
@SuppressWarnings("WeakerAccess")
@AnyThread

View File

@ -19,10 +19,8 @@ package eu.chainfire.libsuperuser;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import android.support.annotation.*;
/**
* Helper class for modifying SELinux policies, reducing the number of calls to a minimum.

View File

@ -42,10 +42,7 @@ import java.util.concurrent.TimeUnit;
import java.lang.Object;
import java.lang.String;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import android.support.annotation.*;
import eu.chainfire.libsuperuser.StreamGobbler.OnLineListener;
import eu.chainfire.libsuperuser.StreamGobbler.OnStreamClosedListener;

View File

@ -23,10 +23,8 @@ import java.io.InputStreamReader;
import java.util.List;
import java.util.Locale;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import android.support.annotation.*;
/**
* Thread utility class continuously reading from an InputStream

View File

@ -21,9 +21,7 @@ import android.os.Build;
import java.util.List;
import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import android.support.annotation.*;
/**
* Utility class to decide between toolbox and toybox calls on M.

View File

@ -1,43 +1,65 @@
-- How to use it?
## How to use it?
Your device must be rooted & it must be on android 4.4.2 or older,
Your device must be rooted & on android 2.3.3 or later.
Install the PlayStation Mobile App :
https://web.archive.org/web/20150311233351/http://playstation.com/psm/store/en.html, if you do not already have it.
Install the NoPssDrm.apk file;
Approve all permissions and grant it root access.
then install the NoPsmDrm Android app, and click the "Install" button. grant it root permission and you should be good to go!
- If your device is not PlayStation Certified, you will have to "Certify" it first,
the NoPssDrm app itself can do this, just press the "PS Certify" button.
Put NoPsmDrm backups of games into /sdcard/data/android/com.playstation.psstore/files/psm ...
- If you do not have the PlayStation Mobile app installed, you will see a "Install PSM" button.
this will download the PSM.apk from psmreborn.com, and then install it onto your phone.
(this automatically does the android 14+ ``--bypass-low-target-sdk-block`` if needed.)
- Once both PSM and the PlayStation Certificates are installed, press the "Install NoPsmDrm" button.
this will install the actual NoPsmDrm patches to the app, and allow PSM games to be played with it.
You can then just place NoPsmDrm backups of games into /sdcard/data/android/com.playstation.psstore/files/psm ...
the format Vita NoPsmDrm backups come in usually contains an RW/ and RO/ folder,
which isn't present on the Android version. you can just move all the files from RO and RW
into the games Title ID folder (the NPXAXXXX one..), and that should get it working
## "PlayStation Suite" / PSP & PS1 Games
When starting one of these games you'll be given the option to launch either with ``PlayStation Mobile`` or ``NoPs1Drm``
you just need to select the ``NoPs1Drm`` option, both times, and the game should launch assuming you have the correct matching APK & ZPAK pair
for the game, and the game can actually run on your device.
-- known issues:
- Games only run with wifi off .. (i think its trying to check for updates or something..)
- 8008103F error code when starting -- on some devices, wifi is required to be turned on when starting the app for the first time.
this is not actually accessing a server and its just so it can read your Mac Address, which PSM needs for (something???)
## Known Issues:
- ~~Games only run with wifi off .. (i think its trying to check for updates or something..)~~ (fixed with the Xposed module)
- ~~8008103F error code when starting -- on some devices, wifi is required to be turned on when starting the app for the first time.
this is not actually accessing a server and its just so it can read your Mac Address, which PSM needs for (something???)~~ (fixed with the Xposed module)
-- source code?
## What patches there are // what they do
### /system files (or the magisk module on android 6+)
Marks your device as "Playstation Certified", allowing the Playstation Mobile application to run.
### Patches to the shared_prefs files (SigninInfo.xml , com.playstation.psstore_preferences.xml)
Gets PSM to think that you've already logged in, as well as getting it to not check for updates.
### The libdefault.so mod
Does most of the work on the PSM side, allowing the games to run and things like that.
### The Xposed module
Patches a couple things, networking checks, and PS1 certification checks.
## source code? // external libraries
libdefault.so mod in here is https://silica.codes/Li/libdefault_proxy
libpsmkdc_jni mod is https://silica.codes/Li/libpsmkdc_stub
the library.db has had the following trigger added :
```
CREATE TRIGGER CONTENT_ID_MEMES AFTER INSERT ON LibraryTable
BEGIN
UPDATE LibraryTable SET content_id="UM0105-" || title_id || "_00-0000000000000000" WHERE title_id = new.title_id;
END;
```
... the rest should be in the installer apk code so :d
``com.playstation.playstationcertified.jar``
is taken from an xperia play, however its an empty JAR file with only a class constructor in it and nothing else.
NP Ticket generation is done via a library created by olebeck; see https://github.com/olebeck/npticketjava
-- Permissions explaination
## Permissions explaination
SuperUser - This app works by modifying the internal data for the PSM application (at /data/data/com.playstation.psstore) so it sees it as if you have already logged into the app and setup an account, then it patches libdefault and libpsmkdc for nopsmdrm-like patches,
@ -49,10 +71,13 @@ Phone calls - This is litterally, just to read your IMEI number, you see PsmDrm
com.playstation.psscertified - this is a specical permission given to be able to access the pscertified framework, which is used to check if your device is "PlayStation Certified".
-- Credits
## Credits
- frangarj for the original NoPsmDrm on PSVita https://github.com/frangarcj/NoPsmDrm/
- Chaser - an absolute legend who still had their old XPLAY with PSM running on it *still going* all these years later, could never have done it without ya!
- frangarj, for the original NoPsmDrm on PSVita https://github.com/frangarcj/NoPsmDrm/
- Chaser, an absolute legend who still had their old XPLAY with PSM running on it *still going* all these years later, could never have done it without ya!
- random(), helping us work out what was wrong on Android 5+ (and plenty of editing to this readme.md)
- rosstonovsky, this random library for reading android's binary XML files; https://github.com/rosstonovsky/ABXUtils/tree/master/abxutils
- Li, for being the single person who has done the most research into PSM out of anyone ever ..
- ele7enxxh, for the hooking library that actually works on android 2.2.X https://github.com/ele7enxxh/Android-Inline-Hook/tree/master
- olebeck, for their knowledge of NP Tickets, and how to generate them https://github.com/olebeck/npticketjava
- Whomever was around before me, Thanks for keeping the dream alive..
- ele7enxxh for the hooking library that actually works on android 2.2.X https://github.com/ele7enxxh/Android-Inline-Hook/tree/master

View File

@ -10,10 +10,17 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io' }
jcenter()
maven {
name = "olebeck-silica.codes"
url = uri("https://silica.codes/api/packages/olebeck/maven")
}
maven {
url 'https://jitpack.io'
}
}
}
rootProject.name = "nopsmdrm"
rootProject.name = "nopssdrm"
include ':app'
include ':libsuperuser'
include ':libAXML'
include ':libABX'