Skip to content

Commit

Permalink
Fixes #79: Implement subject_hash_old without OpenSSL
Browse files Browse the repository at this point in the history
  • Loading branch information
baltpeter authored and zner0L committed May 31, 2023
1 parent 3022843 commit 49ede74
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 30 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ Additionally, you will need to [prepare the target device/emulator](#device-prep

If you want to work with physical devices, [some setup may be necessary depending on your system](https://developer.android.com/studio/run/device#setting-up). On Ubuntu, you need to be a member of the `plugdev` group (`sudo usermod -aG plugdev <username>`) and have `udev` rules for your device (`sudo apt install android-sdk-platform-tools-common`). For other distributions, see [android-udev-rules](https://github.com/M0Rf30/android-udev-rules).

You also need `openssl`.

### Host dependencies for iOS

For iOS, you need [`libimobiledevice`](https://libimobiledevice.org/) and `ideviceinstaller`. The distribution packages are fine, if available. On Windows, you will additionally need the Apple Device Driver and the Apple Application Support service. You can get those by installing iTunes.
Expand Down
12 changes: 6 additions & 6 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ An ID of a known permission on Android.

#### Defined in

[android.ts:876](https://github.com/tweaselORG/appstraction/blob/main/src/android.ts#L876)
[android.ts:882](https://github.com/tweaselORG/appstraction/blob/main/src/android.ts#L882)

___

Expand Down Expand Up @@ -112,7 +112,7 @@ An ID of a known permission on iOS.

#### Defined in

[ios.ts:397](https://github.com/tweaselORG/appstraction/blob/main/src/ios.ts#L397)
[ios.ts:393](https://github.com/tweaselORG/appstraction/blob/main/src/ios.ts#L393)

___

Expand Down Expand Up @@ -320,7 +320,7 @@ The IDs of known permissions on Android.

#### Defined in

[android.ts:745](https://github.com/tweaselORG/appstraction/blob/main/src/android.ts#L745)
[android.ts:751](https://github.com/tweaselORG/appstraction/blob/main/src/android.ts#L751)

___

Expand All @@ -332,7 +332,7 @@ The IDs of known permissions on iOS.

#### Defined in

[ios.ts:380](https://github.com/tweaselORG/appstraction/blob/main/src/ios.ts#L380)
[ios.ts:376](https://github.com/tweaselORG/appstraction/blob/main/src/ios.ts#L376)

## Functions

Expand Down Expand Up @@ -371,7 +371,7 @@ An object with the properties listed above, or `undefined` if the file doesn't e

#### Defined in

[util.ts:67](https://github.com/tweaselORG/appstraction/blob/main/src/util.ts#L67)
[util.ts:68](https://github.com/tweaselORG/appstraction/blob/main/src/util.ts#L68)

___

Expand All @@ -393,7 +393,7 @@ Pause for a given duration.

#### Defined in

[util.ts:44](https://github.com/tweaselORG/appstraction/blob/main/src/util.ts#L44)
[util.ts:45](https://github.com/tweaselORG/appstraction/blob/main/src/util.ts#L45)

___

Expand Down
20 changes: 13 additions & 7 deletions src/android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { decompress as decompressXz } from '@napi-rs/lzma/xz';
import { runAndroidDevTool } from 'andromatic';
import { getVenv } from 'autopy';
import fetch from 'cross-fetch';
import { execa } from 'execa';
import { createHash } from 'crypto';
import { fileTypeFromFile } from 'file-type';
import frida from 'frida';
import { open, rm, writeFile } from 'fs/promises';
Expand All @@ -28,6 +28,7 @@ import {
getObjFromFridaScript,
isRecord,
parseAppMeta,
parsePemCertificateFromFile,
retryCondition,
tmpFileFromZipEntry,
} from './util';
Expand Down Expand Up @@ -173,12 +174,17 @@ export const androidApi = <RunTarget extends SupportedRunTarget<'android'>>(
});
},

getCertificateSubjectHashOld: (path: string) =>
execa('openssl', ['x509', '-inform', 'PEM', '-subject_hash_old', '-in', path]).then(
// The `trim()` is necessary for Windows:
// https://github.com/tweaselORG/meta/issues/25#issuecomment-1507665763
({ stdout }) => stdout.split('\n')[0]?.trim()
),
// This imitates `openssl x509 -inform PEM -subject_hash_old -in <path>`.
// See: https://github.com/tweaselORG/appstraction/issues/79
getCertificateSubjectHashOld: async (path: string) => {
const { cert } = await parsePemCertificateFromFile(path);

const hash = createHash('md5').update(Buffer.from(cert.subject.valueBeforeDecode)).digest();
const truncated = hash.subarray(0, 4);
const ulong = (truncated[0]! | (truncated[1]! << 8) | (truncated[2]! << 16) | (truncated[3]! << 24)) >>> 0;

return ulong.toString(16);
},
hasCertificateAuthority: (filename) =>
adb(['shell', 'ls', `/system/etc/security/cacerts/${filename}`], { reject: false }).then(
({ exitCode }) => exitCode === 0
Expand Down
24 changes: 10 additions & 14 deletions src/ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import { getVenv } from 'autopy';
import { createHash } from 'crypto';
import { execa } from 'execa';
import frida from 'frida';
import { readFile } from 'fs/promises';
import { NodeSSH } from 'node-ssh';
import { Certificate } from 'pkijs';
import type { PlatformApi, PlatformApiOptions, Proxy, SupportedCapability, SupportedRunTarget } from '.';
import { venvOptions } from '../scripts/common/python';
import { asyncUnimplemented, getObjFromFridaScript, isRecord, retryCondition } from './util';
import {
asyncUnimplemented,
getObjFromFridaScript,
isRecord,
parsePemCertificateFromFile,
retryCondition,
} from './util';

const venv = getVenv(venvOptions);
const python = async (...args: Parameters<Awaited<typeof venv>>) => (await venv)(...args);
Expand Down Expand Up @@ -316,16 +320,10 @@ export const iosApi = <RunTarget extends SupportedRunTarget<'ios'>>(
if (!options.capabilities.includes('ssh'))
throw new Error('SSH is required for installing a certificate authority.');

const certPem = await readFile(path, 'utf8');

// A PEM certificate is just a base64-encoded DER certificate with a header and footer.
const certBase64 = certPem.replace(/(-----(BEGIN|END) CERTIFICATE-----|[\r\n])/g, '');
const certDer = Buffer.from(certBase64, 'base64');

const c = Certificate.fromBER(certDer);
const { cert, certDer } = await parsePemCertificateFromFile(path);

const sha256 = createHash('sha256').update(certDer).digest('hex');
const subj = Buffer.from(c.subject.toSchema().valueBlock.toBER()).toString('hex');
const subj = Buffer.from(cert.subject.toSchema().valueBlock.toBER()).toString('hex');
const tset = Buffer.from(
`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
Expand All @@ -343,9 +341,7 @@ export const iosApi = <RunTarget extends SupportedRunTarget<'ios'>>(
if (!options.capabilities.includes('ssh'))
throw new Error('SSH is required for removing a certificate authority.');

const certPem = await readFile(path, 'utf8');
const certBase64 = certPem.replace(/(-----(BEGIN|END) CERTIFICATE-----|[\r\n])/g, '');
const certDer = Buffer.from(certBase64, 'base64');
const { certDer } = await parsePemCertificateFromFile(path);
const sha256 = createHash('sha256').update(certDer).digest('hex');

await this._internal.ssh(
Expand Down
13 changes: 12 additions & 1 deletion src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import frida from 'frida';
import { createWriteStream } from 'fs';
import fs from 'fs-extra';
import type { FileHandle } from 'fs/promises';
import { open } from 'fs/promises';
import { open, readFile } from 'fs/promises';
import _ipaInfo from 'ipa-extract-info';
import { Certificate } from 'pkijs';
import type { Readable } from 'stream';
import { temporaryFile } from 'tempy';
import type { Entry, ZipFile } from 'yauzl';
Expand Down Expand Up @@ -313,3 +314,13 @@ export type XapkManifest = {
expansions?: { file: string; install_location: string; install_path: string }[];
split_apks?: { file: string; id: string }[];
};

export const parsePemCertificateFromFile = async (path: string) => {
const certPem = await readFile(path, 'utf8');

// A PEM certificate is just a base64-encoded DER certificate with a header and footer.
const certBase64 = certPem.replace(/(-----(BEGIN|END) CERTIFICATE-----|[\r\n])/g, '');
const certDer = Buffer.from(certBase64, 'base64');

return { cert: Certificate.fromBER(certDer), certPem, certDer };
};

0 comments on commit 49ede74

Please sign in to comment.