mirror of
https://github.com/qtmleap/revkit.git
synced 2026-10-10 03:41:51 +02:00
feat(tweak,msl): EVPフック追加・無効化とKDF鍵更新実装
- Tweak: EVP_CipherInit_ex/Update/Final, EVP_DecryptInit_ex/Update/Final フック追加 → NFWebCrypto内のEVPはTFIT(ENC)専用でMSL復号には使われないことを確認 → RSA public key not found エラーの原因となるため #if 0 で無効化 - Python: kdf_renew() 実装と検証データによるテスト - constants: IOS_KDF_PSK / IOS_KDF_NONCE をハードコード定数として追加 - docs: KDF解析仕様書と鍵関係図(Mermaid)を追加 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
fa0621346c
commit
be363d6857
@@ -4,6 +4,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <dlfcn.h>
|
||||
// Security types come from Foundation/CoreFoundation headers
|
||||
|
||||
static os_log_t g_log = NULL;
|
||||
static NSString *g_logFile = nil;
|
||||
@@ -15,6 +16,13 @@ static NSMutableArray *g_aesKeys = nil;
|
||||
static NSMutableArray *g_hmacKeys = nil;
|
||||
static BOOL g_appbootDone = NO;
|
||||
|
||||
// DH shared secret — stored so HMAC_Update can compare
|
||||
static uint8_t g_dhSharedSecret[256];
|
||||
static int g_dhSharedSecretLen = 0;
|
||||
|
||||
// Reentrancy guard for hooks that may be called by TLS internally
|
||||
static volatile int g_inHook = 0;
|
||||
|
||||
#define NFXKEY_LOG(fmt, ...) \
|
||||
do { \
|
||||
if (g_log) { os_log(g_log, fmt, ##__VA_ARGS__); } \
|
||||
@@ -144,6 +152,10 @@ static int hook_DH_generate_key(DH *dh) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// SHA function pointers (resolved in constructor)
|
||||
static unsigned char *(*fn_SHA384)(const unsigned char *d, size_t n, unsigned char *md);
|
||||
static unsigned char *(*fn_SHA256)(const unsigned char *d, size_t n, unsigned char *md);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Hook: DH_compute_key
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -156,6 +168,63 @@ static int hook_DH_compute_key(unsigned char *key, const BIGNUM *pub_key, DH *dh
|
||||
file_log([NSString stringWithFormat:@"[DH_compute_key] shared_secret (%d bytes)=%@",
|
||||
ret, hex]);
|
||||
g_keys[@"dh_shared_secret"] = hex;
|
||||
|
||||
// Store shared secret globally for HMAC_Update comparison
|
||||
int copyLen = ret < (int)sizeof(g_dhSharedSecret) ? ret : (int)sizeof(g_dhSharedSecret);
|
||||
memcpy(g_dhSharedSecret, key, copyLen);
|
||||
g_dhSharedSecretLen = copyLen;
|
||||
|
||||
// Compute SHA-384 and SHA-256 of shared_secret as PSK candidates
|
||||
// Guard against re-entrancy since SHA functions may invoke hooked code
|
||||
if (!g_inHook) {
|
||||
g_inHook = 1;
|
||||
|
||||
if (fn_SHA384) {
|
||||
uint8_t digest384[48];
|
||||
if (fn_SHA384(key, (size_t)ret, digest384)) {
|
||||
NSString *sha384Hex = hexEncode(digest384, 48);
|
||||
file_log([NSString stringWithFormat:@"[DH_compute_key] SHA384(shared_secret)=%@", sha384Hex]);
|
||||
g_keys[@"dh_sha384"] = sha384Hex;
|
||||
// First 16 bytes as PSK candidate
|
||||
g_keys[@"dh_sha384_16"] = hexEncode(digest384, 16);
|
||||
file_log([NSString stringWithFormat:@"[DH_compute_key] PSK_candidate_sha384[:16]=%@",
|
||||
g_keys[@"dh_sha384_16"]]);
|
||||
}
|
||||
}
|
||||
|
||||
if (fn_SHA256) {
|
||||
uint8_t digest256[32];
|
||||
if (fn_SHA256(key, (size_t)ret, digest256)) {
|
||||
NSString *sha256Hex = hexEncode(digest256, 32);
|
||||
file_log([NSString stringWithFormat:@"[DH_compute_key] SHA256(shared_secret)=%@", sha256Hex]);
|
||||
g_keys[@"dh_sha256"] = sha256Hex;
|
||||
// First 16 bytes as PSK candidate
|
||||
g_keys[@"dh_sha256_16"] = hexEncode(digest256, 16);
|
||||
file_log([NSString stringWithFormat:@"[DH_compute_key] PSK_candidate_sha256[:16]=%@",
|
||||
g_keys[@"dh_sha256_16"]]);
|
||||
}
|
||||
}
|
||||
|
||||
// null-padded shared_secret (leading zero padding to 256 bytes) SHA-384
|
||||
// MSL Java reference uses a fixed-length big-endian representation
|
||||
if (fn_SHA384 && ret < 256) {
|
||||
uint8_t padded[256];
|
||||
memset(padded, 0, sizeof(padded));
|
||||
memcpy(padded + 256 - ret, key, ret);
|
||||
uint8_t digest384p[48];
|
||||
if (fn_SHA384(padded, 256, digest384p)) {
|
||||
NSString *sha384pHex = hexEncode(digest384p, 48);
|
||||
file_log([NSString stringWithFormat:@"[DH_compute_key] SHA384(padded_shared_secret)=%@", sha384pHex]);
|
||||
g_keys[@"dh_sha384_padded"] = sha384pHex;
|
||||
g_keys[@"dh_sha384_padded_16"] = hexEncode(digest384p, 16);
|
||||
file_log([NSString stringWithFormat:@"[DH_compute_key] PSK_candidate_sha384_padded[:16]=%@",
|
||||
g_keys[@"dh_sha384_padded_16"]]);
|
||||
}
|
||||
}
|
||||
|
||||
g_inHook = 0;
|
||||
}
|
||||
|
||||
saveKeysToFile();
|
||||
}
|
||||
return ret;
|
||||
@@ -167,6 +236,8 @@ static int hook_DH_compute_key(unsigned char *key, const BIGNUM *pub_key, DH *dh
|
||||
|
||||
static int (*orig_AES_set_encrypt_key)(const unsigned char *userKey, int bits, void *key);
|
||||
static int hook_AES_set_encrypt_key(const unsigned char *userKey, int bits, void *key) {
|
||||
if (g_inHook) return orig_AES_set_encrypt_key(userKey, bits, key);
|
||||
g_inHook = 1;
|
||||
int keyLen = bits / 8;
|
||||
if (keyLen == 16 || keyLen == 32) {
|
||||
NSString *hex = hexEncode(userKey, keyLen);
|
||||
@@ -184,16 +255,20 @@ static int hook_AES_set_encrypt_key(const unsigned char *userKey, int bits, void
|
||||
g_keys[g_appbootDone ? @"session_enc_key" : @"pre_session_enc_key"] = hex;
|
||||
}
|
||||
}
|
||||
g_inHook = 0;
|
||||
return orig_AES_set_encrypt_key(userKey, bits, key);
|
||||
}
|
||||
|
||||
static int (*orig_AES_set_decrypt_key)(const unsigned char *userKey, int bits, void *key);
|
||||
static int hook_AES_set_decrypt_key(const unsigned char *userKey, int bits, void *key) {
|
||||
if (g_inHook) return orig_AES_set_decrypt_key(userKey, bits, key);
|
||||
g_inHook = 1;
|
||||
int keyLen = bits / 8;
|
||||
if (keyLen == 16 || keyLen == 32) {
|
||||
file_log([NSString stringWithFormat:@"[AES_set_decrypt_key] bits=%d key=%@",
|
||||
bits, hexEncode(userKey, keyLen)]);
|
||||
}
|
||||
g_inHook = 0;
|
||||
return orig_AES_set_decrypt_key(userKey, bits, key);
|
||||
}
|
||||
|
||||
@@ -205,6 +280,8 @@ static unsigned char *(*orig_HMAC)(const void *evp_md, const void *key, int key_
|
||||
const unsigned char *d, size_t n, unsigned char *md, unsigned int *md_len);
|
||||
static unsigned char *hook_HMAC(const void *evp_md, const void *key, int key_len,
|
||||
const unsigned char *d, size_t n, unsigned char *md, unsigned int *md_len) {
|
||||
if (g_inHook) return orig_HMAC(evp_md, key, key_len, d, n, md, md_len);
|
||||
g_inHook = 1;
|
||||
if (key_len == 32) {
|
||||
NSString *hex = hexEncode((const uint8_t *)key, key_len);
|
||||
file_log([NSString stringWithFormat:@"[HMAC] key_len=%d key=%@", key_len, hex]);
|
||||
@@ -217,6 +294,7 @@ static unsigned char *hook_HMAC(const void *evp_md, const void *key, int key_len
|
||||
// Update current session hmac_key
|
||||
g_keys[g_appbootDone ? @"session_hmac_key" : @"pre_session_hmac_key"] = hex;
|
||||
}
|
||||
g_inHook = 0;
|
||||
return orig_HMAC(evp_md, key, key_len, d, n, md, md_len);
|
||||
}
|
||||
|
||||
@@ -278,6 +356,360 @@ static int hook_HKDF_expand(uint8_t *out_key, size_t out_len,
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Hook: HMAC_Init_ex / HMAC_Update / HMAC_Final (streaming HMAC API)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
typedef struct hmac_ctx_st HMAC_CTX;
|
||||
typedef struct env_md_st EVP_MD;
|
||||
typedef struct engine_st ENGINE;
|
||||
|
||||
static int (*orig_HMAC_Init_ex)(HMAC_CTX *ctx, const void *key, int key_len,
|
||||
const EVP_MD *md, ENGINE *impl);
|
||||
static int hook_HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int key_len,
|
||||
const EVP_MD *md, ENGINE *impl) {
|
||||
int ret = orig_HMAC_Init_ex(ctx, key, key_len, md, impl);
|
||||
if (g_inHook) return ret;
|
||||
g_inHook = 1;
|
||||
if (key != NULL && key_len > 0 && key_len <= 256) {
|
||||
NSString *keyHex = hexEncode((const uint8_t *)key, key_len);
|
||||
file_log([NSString stringWithFormat:@"[HMAC_Init_ex] ctx=%p key_len=%d key=%@",
|
||||
(void *)ctx, key_len, keyHex]);
|
||||
|
||||
// PSK-size key detection (16 bytes = possible PSK)
|
||||
if (key_len == 16) {
|
||||
file_log([NSString stringWithFormat:@"[HMAC_Init_ex] *** PSK-SIZE KEY *** ctx=%p key=%@",
|
||||
(void *)ctx, keyHex]);
|
||||
|
||||
// Check if PSK matches or is contained in the DH shared_secret
|
||||
if (g_dhSharedSecretLen >= 16) {
|
||||
BOOL found = NO;
|
||||
for (int offset = 0; offset <= g_dhSharedSecretLen - 16; offset++) {
|
||||
if (memcmp((const uint8_t *)key, g_dhSharedSecret + offset, 16) == 0) {
|
||||
file_log([NSString stringWithFormat:
|
||||
@"[HMAC_Init_ex] *** PSK MATCHES DH shared_secret at offset %d ***",
|
||||
offset]);
|
||||
found = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
file_log(@"[HMAC_Init_ex] PSK-size key does NOT match DH shared_secret");
|
||||
}
|
||||
} else {
|
||||
file_log(@"[HMAC_Init_ex] PSK-size key seen (no DH shared_secret yet)");
|
||||
}
|
||||
}
|
||||
}
|
||||
g_inHook = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int (*orig_HMAC_Update)(HMAC_CTX *ctx, const unsigned char *data, size_t len);
|
||||
static int hook_HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, size_t len) {
|
||||
int ret = orig_HMAC_Update(ctx, data, len);
|
||||
if (g_inHook) return ret;
|
||||
g_inHook = 1;
|
||||
if (data != NULL && len <= 256) {
|
||||
NSString *dataHex = hexEncode(data, (int)len);
|
||||
file_log([NSString stringWithFormat:@"[HMAC_Update] ctx=%p len=%zu data=%@",
|
||||
(void *)ctx, len, dataHex]);
|
||||
|
||||
// Check if the input matches the stored DH shared_secret
|
||||
if (g_dhSharedSecretLen > 0 && len >= 16) {
|
||||
int cmpLen = (int)len < g_dhSharedSecretLen ? (int)len : g_dhSharedSecretLen;
|
||||
if (memcmp(data, g_dhSharedSecret, cmpLen) == 0) {
|
||||
file_log([NSString stringWithFormat:
|
||||
@"[HMAC_Update] *** DATA MATCHES DH shared_secret (first %d bytes) ctx=%p ***",
|
||||
cmpLen, (void *)ctx]);
|
||||
}
|
||||
}
|
||||
} else if (data != NULL && len > 256) {
|
||||
file_log([NSString stringWithFormat:@"[HMAC_Update] ctx=%p len=%zu (data too long, skipping hex)",
|
||||
(void *)ctx, len]);
|
||||
}
|
||||
g_inHook = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int (*orig_HMAC_Final)(HMAC_CTX *ctx, unsigned char *md, unsigned int *md_len);
|
||||
static int hook_HMAC_Final(HMAC_CTX *ctx, unsigned char *md, unsigned int *md_len) {
|
||||
int ret = orig_HMAC_Final(ctx, md, md_len);
|
||||
if (g_inHook) return ret;
|
||||
g_inHook = 1;
|
||||
if (ret == 1 && md != NULL && md_len != NULL && *md_len > 0) {
|
||||
NSString *digestHex = hexEncode(md, (int)*md_len);
|
||||
file_log([NSString stringWithFormat:@"[HMAC_Final] ctx=%p digest_len=%u digest=%@",
|
||||
(void *)ctx, *md_len, digestHex]);
|
||||
}
|
||||
g_inHook = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Hook: AES_cbc_encrypt
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
typedef struct aes_key_st AES_KEY;
|
||||
|
||||
static void (*orig_AES_cbc_encrypt)(const unsigned char *in, unsigned char *out, size_t length,
|
||||
const AES_KEY *key, unsigned char *ivec, int enc);
|
||||
static void hook_AES_cbc_encrypt(const unsigned char *in, unsigned char *out, size_t length,
|
||||
const AES_KEY *key, unsigned char *ivec, int enc) {
|
||||
// Capture IV before it's modified by AES-CBC
|
||||
uint8_t ivCopy[16];
|
||||
if (ivec) memcpy(ivCopy, ivec, 16);
|
||||
|
||||
orig_AES_cbc_encrypt(in, out, length, key, ivec, enc);
|
||||
|
||||
if (g_inHook) return;
|
||||
// ENC mode: skip large operations (bulk TLS data)
|
||||
// DEC mode: only log small ops (<=128 bytes) to capture server response decryption
|
||||
if (enc && length > 512) return;
|
||||
if (!enc && length > 128) return;
|
||||
g_inHook = 1;
|
||||
|
||||
NSString *direction = enc ? @"ENC" : @"DEC";
|
||||
NSString *ivHex = ivec ? hexEncode(ivCopy, 16) : @"(null)";
|
||||
|
||||
// DEC mode: log all bytes to capture full decrypted result
|
||||
int logLen = enc ? (int)(length < 48 ? length : 48) : (int)length;
|
||||
NSString *inHex = in ? hexEncode(in, logLen) : @"(null)";
|
||||
NSString *outHex = out ? hexEncode(out, logLen) : @"(null)";
|
||||
|
||||
file_log([NSString stringWithFormat:
|
||||
@"[AES_cbc_encrypt] dir=%@ len=%zu iv=%@ in[0:%d]=%@ out[0:%d]=%@",
|
||||
direction, length, ivHex, logLen, inHex, logLen, outHex]);
|
||||
|
||||
// For DEC mode: flag if decrypted output starts with PSK-size patterns
|
||||
if (!enc && length <= 128 && out) {
|
||||
file_log([NSString stringWithFormat:@"[AES_cbc_encrypt] DEC full_out(%zu)=%@",
|
||||
length, hexEncode(out, (int)length)]);
|
||||
}
|
||||
g_inHook = 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Hook: EVP_CipherInit_ex / EVP_CipherUpdate / EVP_CipherFinal_ex
|
||||
// DISABLED: EVP hooks cause "RSA public key not found" error.
|
||||
// NFWebCrypto's OpenSSL EVP is only used for TFIT (ENC), never for MSL decrypt.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#if 0 // EVP hooks disabled — kept for reference
|
||||
|
||||
// Opaque EVP_CIPHER_CTX — we only need the pointer as a tracking key
|
||||
typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
|
||||
typedef struct evp_cipher_st EVP_CIPHER;
|
||||
typedef struct engine_st ENGINE;
|
||||
|
||||
// Track per-context state: direction + key + iv
|
||||
#define MAX_EVP_TRACK 32
|
||||
static struct {
|
||||
void *ctx;
|
||||
int enc; // 1=encrypt, 0=decrypt
|
||||
uint8_t key[32];
|
||||
int keyLen;
|
||||
uint8_t iv[16];
|
||||
} g_evpTrack[MAX_EVP_TRACK];
|
||||
static int g_evpTrackCount = 0;
|
||||
|
||||
static int evpTrackFind(void *ctx) {
|
||||
for (int i = 0; i < g_evpTrackCount; i++) {
|
||||
if (g_evpTrack[i].ctx == ctx) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// EVP_CipherInit_ex(ctx, type, impl, key, iv, enc)
|
||||
static int (*orig_EVP_CipherInit_ex)(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
|
||||
ENGINE *impl, const unsigned char *key,
|
||||
const unsigned char *iv, int enc);
|
||||
static int hook_EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
|
||||
ENGINE *impl, const unsigned char *key,
|
||||
const unsigned char *iv, int enc) {
|
||||
int ret = orig_EVP_CipherInit_ex(ctx, type, impl, key, iv, enc);
|
||||
|
||||
if (g_inHook) return ret;
|
||||
g_inHook = 1;
|
||||
|
||||
if (key) {
|
||||
int idx = evpTrackFind(ctx);
|
||||
if (idx < 0 && g_evpTrackCount < MAX_EVP_TRACK) {
|
||||
idx = g_evpTrackCount++;
|
||||
}
|
||||
if (idx >= 0) {
|
||||
g_evpTrack[idx].ctx = ctx;
|
||||
g_evpTrack[idx].enc = enc;
|
||||
g_evpTrack[idx].keyLen = 16;
|
||||
memcpy(g_evpTrack[idx].key, key, 16);
|
||||
memset(g_evpTrack[idx].iv, 0, 16);
|
||||
if (iv) memcpy(g_evpTrack[idx].iv, iv, 16);
|
||||
}
|
||||
|
||||
if (iv) {
|
||||
NSString *direction = enc ? @"ENC" : @"DEC";
|
||||
NSString *keyHex = hexEncode(key, 16);
|
||||
NSString *ivHex = hexEncode(iv, 16);
|
||||
file_log([NSString stringWithFormat:@"[EVP_CipherInit_ex] dir=%@ key=%@ iv=%@",
|
||||
direction, keyHex, ivHex]);
|
||||
}
|
||||
}
|
||||
|
||||
g_inHook = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// EVP_CipherUpdate(ctx, out, outl, in, inl)
|
||||
static int (*orig_EVP_CipherUpdate)(EVP_CIPHER_CTX *ctx, unsigned char *out,
|
||||
int *outl, const unsigned char *in, int inl);
|
||||
static int hook_EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
|
||||
int *outl, const unsigned char *in, int inl) {
|
||||
int ret = orig_EVP_CipherUpdate(ctx, out, outl, in, inl);
|
||||
|
||||
if (g_inHook) return ret;
|
||||
g_inHook = 1;
|
||||
|
||||
int idx = evpTrackFind(ctx);
|
||||
if (idx < 0) { g_inHook = 0; return ret; }
|
||||
static const uint8_t zeroIv[16] = {0};
|
||||
if (memcmp(g_evpTrack[idx].iv, zeroIv, 16) == 0) { g_inHook = 0; return ret; }
|
||||
|
||||
int enc = g_evpTrack[idx].enc;
|
||||
NSString *direction = (enc == 1) ? @"ENC" : (enc == 0) ? @"DEC" : @"???";
|
||||
|
||||
int logLen = (inl < 64) ? inl : 64;
|
||||
int outLen = (outl && *outl < 64) ? *outl : 64;
|
||||
NSString *inHex = in ? hexEncode(in, logLen) : @"(null)";
|
||||
NSString *outHex = (out && outl) ? hexEncode(out, outLen) : @"(null)";
|
||||
|
||||
file_log([NSString stringWithFormat:@"[EVP_CipherUpdate] dir=%@ inl=%d outl=%d in[:%d]=%@ out[:%d]=%@",
|
||||
direction, inl, outl ? *outl : 0, logLen, inHex, outLen, outHex]);
|
||||
|
||||
if (enc == 0 && outl && *outl <= 128 && *outl > 0 && out) {
|
||||
file_log([NSString stringWithFormat:@"[EVP_CipherUpdate] DEC full_out(%d)=%@",
|
||||
*outl, hexEncode(out, *outl)]);
|
||||
}
|
||||
|
||||
g_inHook = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// EVP_CipherFinal_ex(ctx, out, outl)
|
||||
static int (*orig_EVP_CipherFinal_ex)(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
|
||||
static int hook_EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl) {
|
||||
int ret = orig_EVP_CipherFinal_ex(ctx, out, outl);
|
||||
|
||||
if (g_inHook) return ret;
|
||||
g_inHook = 1;
|
||||
|
||||
int idx = evpTrackFind(ctx);
|
||||
int enc = (idx >= 0) ? g_evpTrack[idx].enc : -1;
|
||||
NSString *direction = (enc == 1) ? @"ENC" : (enc == 0) ? @"DEC" : @"???";
|
||||
|
||||
if (outl && *outl > 0 && out) {
|
||||
file_log([NSString stringWithFormat:@"[EVP_CipherFinal_ex] dir=%@ outl=%d out=%@",
|
||||
direction, *outl, hexEncode(out, *outl)]);
|
||||
} else {
|
||||
file_log([NSString stringWithFormat:@"[EVP_CipherFinal_ex] dir=%@ outl=%d",
|
||||
direction, outl ? *outl : 0]);
|
||||
}
|
||||
|
||||
if (idx >= 0) {
|
||||
g_evpTrack[idx] = g_evpTrack[--g_evpTrackCount];
|
||||
}
|
||||
|
||||
g_inHook = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Hook: EVP_DecryptInit_ex / EVP_DecryptUpdate / EVP_DecryptFinal_ex
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
static int (*orig_EVP_DecryptInit_ex)(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
|
||||
ENGINE *impl, const unsigned char *key,
|
||||
const unsigned char *iv);
|
||||
static int hook_EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type,
|
||||
ENGINE *impl, const unsigned char *key,
|
||||
const unsigned char *iv) {
|
||||
int ret = orig_EVP_DecryptInit_ex(ctx, type, impl, key, iv);
|
||||
|
||||
if (g_inHook) return ret;
|
||||
g_inHook = 1;
|
||||
|
||||
if (key) {
|
||||
int idx = evpTrackFind(ctx);
|
||||
if (idx < 0 && g_evpTrackCount < MAX_EVP_TRACK) {
|
||||
idx = g_evpTrackCount++;
|
||||
}
|
||||
if (idx >= 0) {
|
||||
g_evpTrack[idx].ctx = ctx;
|
||||
g_evpTrack[idx].enc = 0;
|
||||
memcpy(g_evpTrack[idx].key, key, 16);
|
||||
memset(g_evpTrack[idx].iv, 0, 16);
|
||||
if (iv) memcpy(g_evpTrack[idx].iv, iv, 16);
|
||||
}
|
||||
|
||||
NSString *keyHex = hexEncode(key, 16);
|
||||
NSString *ivHex = iv ? hexEncode(iv, 16) : @"(null)";
|
||||
file_log([NSString stringWithFormat:@"[EVP_DecryptInit_ex] key=%@ iv=%@", keyHex, ivHex]);
|
||||
}
|
||||
|
||||
g_inHook = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int (*orig_EVP_DecryptUpdate)(EVP_CIPHER_CTX *ctx, unsigned char *out,
|
||||
int *outl, const unsigned char *in, int inl);
|
||||
static int hook_EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out,
|
||||
int *outl, const unsigned char *in, int inl) {
|
||||
int ret = orig_EVP_DecryptUpdate(ctx, out, outl, in, inl);
|
||||
|
||||
if (g_inHook) return ret;
|
||||
g_inHook = 1;
|
||||
|
||||
int logLen = (inl < 64) ? inl : 64;
|
||||
int outLen = (outl && *outl < 64) ? *outl : 64;
|
||||
NSString *inHex = in ? hexEncode(in, logLen) : @"(null)";
|
||||
NSString *outHex = (out && outl) ? hexEncode(out, outLen) : @"(null)";
|
||||
|
||||
file_log([NSString stringWithFormat:@"[EVP_DecryptUpdate] inl=%d outl=%d in[:%d]=%@ out[:%d]=%@",
|
||||
inl, outl ? *outl : 0, logLen, inHex, outLen, outHex]);
|
||||
|
||||
if (outl && *outl <= 128 && *outl > 0 && out) {
|
||||
file_log([NSString stringWithFormat:@"[EVP_DecryptUpdate] full_out(%d)=%@",
|
||||
*outl, hexEncode(out, *outl)]);
|
||||
}
|
||||
|
||||
g_inHook = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int (*orig_EVP_DecryptFinal_ex)(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
|
||||
static int hook_EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl) {
|
||||
int ret = orig_EVP_DecryptFinal_ex(ctx, out, outl);
|
||||
|
||||
if (g_inHook) return ret;
|
||||
g_inHook = 1;
|
||||
|
||||
if (outl && *outl > 0 && out) {
|
||||
file_log([NSString stringWithFormat:@"[EVP_DecryptFinal_ex] outl=%d out=%@",
|
||||
*outl, hexEncode(out, *outl)]);
|
||||
} else {
|
||||
file_log([NSString stringWithFormat:@"[EVP_DecryptFinal_ex] outl=%d", outl ? *outl : 0]);
|
||||
}
|
||||
|
||||
int idx = evpTrackFind(ctx);
|
||||
if (idx >= 0) {
|
||||
g_evpTrack[idx] = g_evpTrack[--g_evpTrackCount];
|
||||
}
|
||||
|
||||
g_inHook = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // EVP hooks disabled
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Hook: IosMslClient.setDidAppboot:
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -314,6 +746,8 @@ static void hook_setDidAppboot(id self, SEL _cmd, BOOL value) {
|
||||
}
|
||||
}
|
||||
|
||||
// SSL bypass removed — use Frida ssl-pinning.ts if needed
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -332,6 +766,37 @@ __attribute__((constructor)) static void init(void) {
|
||||
file_log(@"=== AppbootKeyExtract loaded ===");
|
||||
NFXKEY_LOG("AppbootKeyExtract loaded");
|
||||
|
||||
// Keychain clear trigger: if /tmp/clear_keychain exists, delete all Keychain items
|
||||
// Uses dlsym to avoid linking Security.framework (caused crashes before)
|
||||
NSString *triggerPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"clear_keychain"];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:triggerPath]) {
|
||||
file_log(@"[!] clear_keychain trigger found — deleting Keychain items");
|
||||
void *secLib = dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_NOLOAD);
|
||||
if (!secLib) secLib = dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY);
|
||||
if (secLib) {
|
||||
typedef int32_t (*SecItemDelete_t)(CFDictionaryRef);
|
||||
SecItemDelete_t secItemDeleteFn = (SecItemDelete_t)dlsym(secLib, "SecItemDelete");
|
||||
if (secItemDeleteFn) {
|
||||
NSArray *classNames = @[@"genp", @"inet", @"keys", @"cert"];
|
||||
for (NSString *cls_str in classNames) {
|
||||
NSDictionary *query = @{@"class": cls_str};
|
||||
int32_t status = secItemDeleteFn((__bridge CFDictionaryRef)query);
|
||||
file_log([NSString stringWithFormat:@"[!] SecItemDelete(class=%@) status=%d",
|
||||
cls_str, status]);
|
||||
}
|
||||
file_log(@"[!] Keychain cleared");
|
||||
} else {
|
||||
file_log(@"[!] SecItemDelete not found via dlsym");
|
||||
}
|
||||
} else {
|
||||
file_log(@"[!] Security.framework not loaded");
|
||||
}
|
||||
[[NSFileManager defaultManager] removeItemAtPath:triggerPath error:nil];
|
||||
file_log(@"[!] trigger file removed");
|
||||
}
|
||||
|
||||
// SSL bypass removed — use Frida ssl-pinning.ts if needed
|
||||
|
||||
// Hook IosMslClient.setDidAppboot:
|
||||
Class cls = objc_getClass("IosMslClient");
|
||||
if (cls) {
|
||||
@@ -353,6 +818,20 @@ __attribute__((constructor)) static void init(void) {
|
||||
fn_BN_num_bits = (int (*)(const BIGNUM *))dlsym(nfwc, "BN_num_bits");
|
||||
fn_BN_bn2bin = (int (*)(const BIGNUM *, unsigned char *))dlsym(nfwc, "BN_bn2bin");
|
||||
|
||||
// Resolve SHA functions for PSK candidate derivation from DH shared_secret
|
||||
fn_SHA384 = (unsigned char *(*)(const unsigned char *, size_t, unsigned char *))dlsym(nfwc, "SHA384");
|
||||
fn_SHA256 = (unsigned char *(*)(const unsigned char *, size_t, unsigned char *))dlsym(nfwc, "SHA256");
|
||||
if (fn_SHA384) {
|
||||
file_log(@"[+] SHA384 resolved");
|
||||
} else {
|
||||
file_log(@"[-] SHA384 not found in NFWebCrypto");
|
||||
}
|
||||
if (fn_SHA256) {
|
||||
file_log(@"[+] SHA256 resolved");
|
||||
} else {
|
||||
file_log(@"[-] SHA256 not found in NFWebCrypto");
|
||||
}
|
||||
|
||||
// DH hooks
|
||||
void *dhGenKey = dlsym(nfwc, "DH_generate_key");
|
||||
void *dhCompKey = dlsym(nfwc, "DH_compute_key");
|
||||
@@ -426,6 +905,55 @@ __attribute__((constructor)) static void init(void) {
|
||||
file_log(@"[-] HKDF_expand not found (tried HKDF_expand / HKDF_Expand)");
|
||||
NFXKEY_LOG(" [-] HKDF_expand not found");
|
||||
}
|
||||
|
||||
// Streaming HMAC hooks
|
||||
void *hmacInitEx = dlsym(nfwc, "HMAC_Init_ex");
|
||||
void *hmacUpdate = dlsym(nfwc, "HMAC_Update");
|
||||
void *hmacFinal = dlsym(nfwc, "HMAC_Final");
|
||||
|
||||
if (hmacInitEx) {
|
||||
MSHookFunction(hmacInitEx, (void *)hook_HMAC_Init_ex, (void **)&orig_HMAC_Init_ex);
|
||||
file_log(@"[+] HMAC_Init_ex hooked");
|
||||
NFXKEY_LOG(" [+] HMAC_Init_ex hooked");
|
||||
} else {
|
||||
file_log(@"[-] HMAC_Init_ex not found");
|
||||
NFXKEY_LOG(" [-] HMAC_Init_ex not found");
|
||||
}
|
||||
|
||||
if (hmacUpdate) {
|
||||
MSHookFunction(hmacUpdate, (void *)hook_HMAC_Update, (void **)&orig_HMAC_Update);
|
||||
file_log(@"[+] HMAC_Update hooked");
|
||||
NFXKEY_LOG(" [+] HMAC_Update hooked");
|
||||
} else {
|
||||
file_log(@"[-] HMAC_Update not found");
|
||||
NFXKEY_LOG(" [-] HMAC_Update not found");
|
||||
}
|
||||
|
||||
if (hmacFinal) {
|
||||
MSHookFunction(hmacFinal, (void *)hook_HMAC_Final, (void **)&orig_HMAC_Final);
|
||||
file_log(@"[+] HMAC_Final hooked");
|
||||
NFXKEY_LOG(" [+] HMAC_Final hooked");
|
||||
} else {
|
||||
file_log(@"[-] HMAC_Final not found");
|
||||
NFXKEY_LOG(" [-] HMAC_Final not found");
|
||||
}
|
||||
|
||||
// AES-CBC hook
|
||||
void *aesCbcFn = dlsym(nfwc, "AES_cbc_encrypt");
|
||||
|
||||
if (aesCbcFn) {
|
||||
MSHookFunction(aesCbcFn, (void *)hook_AES_cbc_encrypt, (void **)&orig_AES_cbc_encrypt);
|
||||
file_log(@"[+] AES_cbc_encrypt hooked");
|
||||
NFXKEY_LOG(" [+] AES_cbc_encrypt hooked");
|
||||
} else {
|
||||
file_log(@"[-] AES_cbc_encrypt not found");
|
||||
NFXKEY_LOG(" [-] AES_cbc_encrypt not found");
|
||||
}
|
||||
|
||||
// EVP hooks disabled — they cause "RSA public key not found" error
|
||||
// NFWebCrypto's OpenSSL is NOT used for MSL payload decrypt
|
||||
// (EVP_CipherInit only fires ENC, EVP_Decrypt* never fires)
|
||||
file_log(@"[i] EVP hooks disabled (not used for MSL decrypt)");
|
||||
} else {
|
||||
file_log(@"[-] NFWebCrypto not loaded");
|
||||
NFXKEY_LOG(" [-] NFWebCrypto not loaded");
|
||||
|
||||
Reference in New Issue
Block a user