Reverse Engineering Asked by Dan Lewis on May 5, 2021
I was directed tyhis way from the main superuser site:
I have zero experience with Java or Android apps (have coding experience in C) and tried reverse engineering a [now defunct/unsupported] app which would take a serial number* and encrypt it using RSA with PKCS1Padding. I’m happy to share what I have as the creator is uncontactable and only a very select few people have files (or access to the software) in relation to this so the key can be ‘in the wild’. What I believe to be the code is below… I roughly understand what it is doing.
.class public Lcom/h1dd3n/securefiles/Keygen;
.super Ljava/lang/Object;
.source "Keygen.java"
# static fields
.field static key:Ljava/lang/String;
.field static modulus:Ljava/math/BigInteger;
.field static pubExp:Ljava/math/BigInteger;
# direct methods
.method static constructor <clinit>()V
.locals 3
.prologue
const/16 v2, 0x10
.line 15
const-string v0, "00FB451D3F45D82B92FC6F243D50441DD75F2DB995842A3D389FC4536A27F42242C9C8DCB4DA0E2573E5CA2E5D0A2AD7E790D8A79CAC2DE68BEAF99D21E229A9CF04ABD09D61C8C66C4FE3B32456496305792FF9D2D2198B87BFAB2637518C1D3F44D27B93EF2C0ED3379993D04944EB356D4BDE343017CFB13405B403A3D81D2C7B099AE5651CF14DD3CBE21C435076F244D9DA8F54DA19BA6301AF1F7DA699E2EBFD9C0BB2778D812E8D9BE66089B2783B9E60FA28FA83CD3B356669BC15BC84058FEEE493CCFBE2E13E0B53B01886D47EB75BFC75758A5CFA5A1836E697FD51846578B4BDEDE3A6BD1FE4D49ABAC072AED433AC5A19BF94C9F6C7F4D95740EF"
sput-object v0, Lcom/h1dd3n/securefiles/Keygen;->key:Ljava/lang/String;
.line 16
new-instance v0, Ljava/math/BigInteger;
sget-object v1, Lcom/h1dd3n/securefiles/Keygen;->key:Ljava/lang/String;
invoke-direct {v0, v1, v2}, Ljava/math/BigInteger;-><init>(Ljava/lang/String;I)V
sput-object v0, Lcom/h1dd3n/securefiles/Keygen;->modulus:Ljava/math/BigInteger;
.line 17
new-instance v0, Ljava/math/BigInteger;
const-string v1, "010001"
invoke-direct {v0, v1, v2}, Ljava/math/BigInteger;-><init>(Ljava/lang/String;I)V
sput-object v0, Lcom/h1dd3n/securefiles/Keygen;->pubExp:Ljava/math/BigInteger;
return-void
.end method
.method public constructor <init>()V
.locals 0
.prologue
.line 14
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
.method public static encrypt(Ljava/lang/String;)[B
.locals 7
.param p0, "text" # Ljava/lang/String;
.prologue
.line 23
:try_start_0
new-instance v2, Ljava/security/spec/RSAPublicKeySpec;
sget-object v5, Lcom/h1dd3n/securefiles/Keygen;->modulus:Ljava/math/BigInteger;
sget-object v6, Lcom/h1dd3n/securefiles/Keygen;->pubExp:Ljava/math/BigInteger;
invoke-direct {v2, v5, v6}, Ljava/security/spec/RSAPublicKeySpec;-><init>(Ljava/math/BigInteger;Ljava/math/BigInteger;)V
.line 24
.local v2, "keySpec":Ljava/security/spec/RSAPublicKeySpec;
const-string v5, "RSA"
invoke-static {v5}, Ljava/security/KeyFactory;->getInstance(Ljava/lang/String;)Ljava/security/KeyFactory;
move-result-object v3
.line 25
.local v3, "kf":Ljava/security/KeyFactory;
invoke-virtual {v3, v2}, Ljava/security/KeyFactory;->generatePublic(Ljava/security/spec/KeySpec;)Ljava/security/PublicKey;
move-result-object v4
.line 27
.local v4, "publicKey":Ljava/security/PublicKey;
const-string v5, "RSA/ECB/PKCS1Padding"
invoke-static {v5}, Ljavax/crypto/Cipher;->getInstance(Ljava/lang/String;)Ljavax/crypto/Cipher;
move-result-object v0
.line 28
.local v0, "cipher":Ljavax/crypto/Cipher;
const/4 v5, 0x1
invoke-virtual {v0, v5, v4}, Ljavax/crypto/Cipher;->init(ILjava/security/Key;)V
.line 29
invoke-virtual {p0}, Ljava/lang/String;->getBytes()[B
move-result-object v5
invoke-virtual {v0, v5}, Ljavax/crypto/Cipher;->doFinal([B)[B
:try_end_0
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
move-result-object v5
.line 35
.end local v0 # "cipher":Ljavax/crypto/Cipher;
.end local v2 # "keySpec":Ljava/security/spec/RSAPublicKeySpec;
.end local v3 # "kf":Ljava/security/KeyFactory;
.end local v4 # "publicKey":Ljava/security/PublicKey;
:goto_0
return-object v5
.line 31
:catch_0
move-exception v1
.line 33
.local v1, "e":Ljava/lang/Exception;
invoke-virtual {v1}, Ljava/lang/Exception;->printStackTrace()V
.line 35
const/4 v5, 0x0
goto :goto_0
.end method
Now I have a file of which I wish to view the original contents that was encrypted using this method.
How on earth do I do that? I’d also be interested in creating new files in a similar way on a Windows based system if possible…
Full apk source (minus resources as some confidential stuff in there) is available here (it only worked on some v5/v6 Android devices)
File I wish to decrypt is [here]
the file is generated in SecureKeySourcesmalicomtmsecsecurediskActivator and saved onto the SD Card.
*Serial number is the manufacturer’s data from an SD card which is then encrypted and used by another piece of software to allow access. The device was password write protected to prevent deletion/formatting. If you lost this SD card you were screwed as you couldn’t access the programs! The device would be checked and deny access if there was a data mismatch. This was the most secure method on an extremely low budget that was thought of at the time of creation – a few years back now; why they did it this way I do not know… I have key backups but no way of associating them with cards or no methods of creating new cards without buying old android tablets/phones and hoping they work with microSD cards (no idea how they originally did it on normal SD cards!! OTG adapters don’t seem to show the manufacturer data?). I do have the password to remove write access allowing the cards to be formatted however as that was recorded at least.
I was hoping that (looking at the extracted source code) there would be some sort of KeyGenerator class where I could enter the new ID as a string and then run.
Have you tried using the Frida.re, is an excellent tool for intercepting Android functions, for example you can hook this function responsible for calculating the RSA, see an example from the frida documentation itself:
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
// Function to hook is defined here
var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');
// Whenever button is clicked
var onClick = MainActivity.onClick;
onClick.implementation = function (v) {
// Show a message to know that the function got called
send('onClick');
// Call the original onClick handler
onClick.call(this, v);
// Set our values after running the original onClick handler
this.m.value = 0;
this.n.value = 1;
this.cnt.value = 999;
// Log to the console that it's done, and we should have the flag!
console.log('Done:' + JSON.stringify(this.cnt));
};
});
"""
process = frida.get_usb_device().attach('com.example.seccon2015.rock_paper_scissors')
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running CTF')
script.load()
sys.stdin.read()
To use this framework you will need to have adb installed on your computer and activate the usb debbug settings, after accepting the RSA that is generated on the connection.
Please for a more complete explanation consider reading the following part of the documentation: Solving Android CTF
For beginners a step-by-step tutorial on dynamic analysis with Frida use: Frida Start
Answered by 0x0A on May 5, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP