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:
tkgstrator
2026-04-08 12:52:41 +00:00
co-authored by Claude Opus 4.6
parent fa0621346c
commit be363d6857
6 changed files with 952 additions and 6 deletions
@@ -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");