apphx 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -9
- package/dist/cli.js +41 -11
- package/dist/cli.js.map +1 -1
- package/dist/config.js +4 -2
- package/dist/config.js.map +1 -1
- package/dist/integrations.js +177 -91
- package/dist/integrations.js.map +1 -1
- package/dist/notify.js +267 -0
- package/dist/notify.js.map +1 -0
- package/dist/policy.js +3 -16
- package/dist/policy.js.map +1 -1
- package/dist/runner.js +119 -20
- package/dist/runner.js.map +1 -1
- package/dist/schemas.js +57 -2
- package/dist/schemas.js.map +1 -1
- package/dist/vendor/obfuscator/dist/config.js +119 -6
- package/dist/vendor/obfuscator/dist/config.js.map +1 -1
- package/dist/vendor/obfuscator/dist/dfa.js +528 -0
- package/dist/vendor/obfuscator/dist/dfa.js.map +1 -0
- package/dist/vendor/obfuscator/dist/flutter.js +3266 -0
- package/dist/vendor/obfuscator/dist/flutter.js.map +1 -0
- package/dist/vendor/obfuscator/dist/image-utils.js +335 -0
- package/dist/vendor/obfuscator/dist/image-utils.js.map +1 -0
- package/dist/vendor/obfuscator/dist/junk.js +1501 -0
- package/dist/vendor/obfuscator/dist/junk.js.map +1 -0
- package/dist/vendor/obfuscator/dist/lexicon-data.js +972 -0
- package/dist/vendor/obfuscator/dist/lexicon-data.js.map +1 -0
- package/dist/vendor/obfuscator/dist/obfuscate.js +51 -2
- package/dist/vendor/obfuscator/dist/obfuscate.js.map +1 -1
- package/dist/vendor/obfuscator/dist/objc-symbols.js +1819 -0
- package/dist/vendor/obfuscator/dist/objc-symbols.js.map +1 -0
- package/dist/vendor/obfuscator/dist/project-identity.js +852 -0
- package/dist/vendor/obfuscator/dist/project-identity.js.map +1 -0
- package/dist/vendor/obfuscator/dist/string-encrypt.js +330 -0
- package/dist/vendor/obfuscator/dist/string-encrypt.js.map +1 -0
- package/examples/android-java.factory.json +40 -5
- package/examples/expo.factory.json +68 -5
- package/examples/flutter.factory.json +40 -4
- package/examples/luggage.json +1 -0
- package/examples/multi-role-agents.md +129 -0
- package/examples/mygo.factory.json +54 -4
- package/examples/queue-loop-demo.md +146 -0
- package/examples/react-native.factory.json +58 -5
- package/examples/roles.example.json +31 -0
- package/examples/swift.factory.json +60 -5
- package/examples/uni-app.factory.json +58 -5
- package/package.json +1 -1
- package/skills/apphx/SKILL.md +39 -4
- package/skills/apphx/resources/requirement.json +1 -0
- package/skills/apphx-obfuscator/LICENSE +21 -0
- package/skills/apphx-obfuscator/SKILL.md +72 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-identity.js","names":[],"sources":["../src/project-identity.ts"],"sourcesContent":["import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from 'node:fs';\nimport { basename, dirname, join, relative, sep } from 'node:path';\nimport type { TransformOutcome, WalkEntry } from './common.js';\nimport {\n classifyLanguage, deriveHash, isImageFile, isTextFile, seededInt, seededRandom,\n} from './common.js';\n\n/**\n * Deterministic \"project identity rename\" pipeline.\n *\n * Every source of randomness is derived from the caller-supplied `seed` via the\n * helpers in common.js. GUI/network/authorization code, the wordnet/nltk lexicon\n * and the xpinyin/pypinyin transliteration libraries are intentionally dropped;\n * Chinese folder names fall back to a deterministic Latin token (see getMissFolderName).\n */\n\nexport interface ProjectIdentityOptions {\n /** Replacement .xcodeproj base name. Defaults to a seed-derived token. */\n projectName?: string;\n /** Prefix prepended to every generated folder name. */\n folderPrefix?: string;\n /** Prefix prepended to generated image names. */\n imagePrefix?: string;\n /** Image rename algorithm: 0 = create_miss_image_name, 3 = letters only. */\n imageAlgorithm?: 0 | 1 | 3;\n}\n\nexport interface PbxGroupRow {\n udid: string;\n name: string;\n missName: string;\n isa: string;\n /** Raw `children = (...)` contents (comment names joined by comma). */\n children: string;\n /** Comma-joined child comment names, used as the map key. */\n childrenNames: string;\n path: string;\n sourceTree: string;\n}\n\n/** Same list the original uses to refuse renaming directories. */\nexport const IGNORE_FOLDERS: readonly string[] = [\n '.xcassets', 'Tests', 'UITests', 'Products', 'Pods', 'Frameworks',\n '.xcworkspace', '.xcodeproj', '.lproj', '.git', '.DS_Store', '.idea', 'Icon\\r',\n];\n\nexport const XIB_KEY_WORDS: readonly string[] = ['initialViewController', 'sceneID'];\nexport const XIB_PASS_KEY_WORDS: readonly string[] = ['id', 'destination', 'firstItem', 'secondItem'];\n\n/** ISO language codes (keys), plus the literal 'zh'. */\nexport const LANGUAGE_CODES: readonly string[] = [\n 'af', 'sq', 'ar', 'be', 'bg', 'ca', 'zh-CN', 'zh-TW', 'hr', 'cs', 'da', 'nl', 'en',\n 'eo', 'et', 'tl', 'fi', 'fr', 'gl', 'de', 'el', 'iw', 'hi', 'hu', 'is', 'id', 'ga',\n 'it', 'ja', 'ko', 'la', 'lv', 'lt', 'mk', 'ms', 'mt', 'no', 'fa', 'pl', 'pt', 'ro',\n 'ru', 'sr', 'sk', 'sl', 'es', 'sw', 'sv', 'th', 'tr', 'uk', 'vi', 'cy', 'yi', 'zh',\n];\n\n/**\n * Small deterministic lexicon stand-in. Only the shape matters (a clean\n * lowercase word), not the exact entry.\n */\nconst LEXICON: readonly string[] = [\n 'amber', 'badger', 'cobalt', 'dune', 'ember', 'falcon', 'granite', 'harbor',\n 'indigo', 'juniper', 'kestrel', 'lumen', 'mica', 'nimbus', 'onyx', 'prairie',\n 'quartz', 'russet', 'sable', 'tundra', 'umber', 'valley', 'willow', 'xenon',\n 'yarrow', 'zephyr', 'acorn', 'birch', 'cedar', 'delta', 'eagle', 'frost',\n 'grove', 'hollow', 'iron', 'jade', 'kelp', 'larch', 'meadow', 'north',\n 'oak', 'pine', 'quill', 'ridge', 'stone', 'thicket', 'violet', 'wheat',\n];\n\nfunction isContainChinese(value: string): boolean {\n return /[一-鿿]/.test(value);\n}\n\nfunction isNumberString(value: string): boolean {\n return /[0-9]+/.test(value);\n}\n\nfunction isDigitString(value: string): boolean {\n return value.length > 0 && /^[0-9]+$/.test(value);\n}\n\n/** Python str.title() for a single alphanumeric fragment. */\nfunction titleCase(value: string): string {\n return value.length ? value[0]!.toUpperCase() + value.slice(1).toLowerCase() : value;\n}\n\n/** Python str.capitalize() for a single alphanumeric fragment. */\nfunction capitalize(value: string): string {\n return value.length ? value[0]!.toUpperCase() + value.slice(1) : value;\n}\n\nfunction isLowerAscii(value: string): boolean {\n return /^[a-z]$/.test(value);\n}\n\nfunction randomChars(seed: string, salt: string, alphabet: string, length: number): string {\n const random = seededRandom(deriveHash(seed, salt));\n let out = '';\n for (let index = 0; index < length; index += 1) out += alphabet[Math.floor(random() * alphabet.length)]!;\n return out;\n}\n\nconst ALNUM = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\nconst LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\n\nfunction randomStringWithNumber(seed: string, salt: string, length: number): string {\n return randomChars(seed, salt, ALNUM, length);\n}\n\nfunction randomStringNoNumber(seed: string, salt: string, length: number): string {\n return randomChars(seed, salt, LETTERS, length);\n}\n\nfunction randomWord(seed: string, salt: string): string {\n return LEXICON[seededInt(seed, salt, 0, LEXICON.length - 1)]!;\n}\n\nfunction randomWordTitle(seed: string, salt: string): string {\n return capitalize(randomWord(seed, salt));\n}\n\n/**\n * Folder rename: strip a \"(...)\" suffix, replace dots with\n * underscores, remove digits, split camelCase on uppercase boundaries and keep\n * only the last meaningful fragment, then prepend a seed-derived title word.\n */\nexport function getMissFolderName(folderName: string, seed: string): string {\n if (folderName.includes('(')) folderName = folderName.slice(0, folderName.indexOf('('));\n if (folderName.includes('.')) folderName = folderName.replace(/\\./g, '_');\n if (isContainChinese(folderName)) {\n const stripped = folderName.replace(/[^\\u4e00-\\u9fa5a-zA-Z0-9]/g, '');\n return capitalize(stripped) || randomWordTitle(seed, 'folder:chinese');\n }\n for (let num = 0; num < 10; num += 1) folderName = folderName.split(String(num)).join('');\n let folderNameList = folderName.replace(/[A-Z]/g, match => `_${match}`).split('_').filter(item => item.length > 1);\n if (folderNameList.length > 2) folderNameList = folderNameList.slice(-1);\n folderNameList = [randomWordTitle(seed, `folder:${folderName}`), ...folderNameList];\n return folderNameList.join('');\n}\n\n/**\n * Parse the PBXGroup section of a project.pbxproj file into\n * `childrenNames -> row`.\n */\nexport function parsePbxGroups(content: string, seed: string, folderPrefix: string): Map<string, PbxGroupRow> {\n const map = new Map<string, PbxGroupRow>();\n const lines = content.split('\\n');\n let inGroup = false;\n let row = '';\n for (const line of lines) {\n if (line.includes('/* Begin PBXGroup section */')) { inGroup = true; continue; }\n if (line.includes('/* End PBXGroup section */')) { inGroup = false; continue; }\n if (inGroup && !line.includes('/* Begin PBXGroup section */')) {\n row += line;\n if (line.includes('};')) {\n const parsed = parsePbxGroupOneRow(row, seed, folderPrefix);\n if (parsed) map.set(parsed.childrenNames, parsed);\n row = '';\n }\n }\n }\n return map;\n}\n\nfunction parsePbxGroupOneRow(row: string, seed: string, folderPrefix: string): PbxGroupRow | undefined {\n let udid = '';\n let name = '';\n let isa = '';\n let children = '';\n let childrenNames = '';\n let path = '';\n let sourceTree = '';\n\n const flat = row.replace(/\\n/g, '').split(/\\s+/).join('');\n const udidName = flat.slice(0, flat.indexOf('='));\n if (udidName.includes('/*') && udidName.includes('*/')) {\n udid = udidName.slice(0, udidName.indexOf('/*'));\n name = udidName.slice(udidName.indexOf('/*') + 2, udidName.indexOf('*/'));\n } else {\n udid = udidName;\n }\n if (name.length === 0) return undefined;\n\n const body = flat.slice(flat.indexOf('={') + 2, flat.indexOf('};'));\n for (const value of body.split(';')) {\n if (value.includes('isa=')) { isa = value.split('isa=').pop()!; continue; }\n if (value.includes('children=')) {\n children = value.split('children=').pop()!.slice(1, -2);\n const names: string[] = [];\n for (const item of children.split(',')) {\n if (item.includes('/*') && item.includes('*/')) {\n names.push(item.slice(item.indexOf('/*') + 2, item.indexOf('*/')));\n }\n }\n childrenNames = names.join(',');\n continue;\n }\n if (value.includes('path=')) { path = value.split('path=').pop()!; continue; }\n if (value.includes('sourceTree=')) { sourceTree = value.split('sourceTree=').pop()!; continue; }\n }\n\n const missName = folderPrefix + getMissFolderName(name, seed);\n return { udid, name, missName, isa, children, childrenNames, path, sourceTree };\n}\n\n/**\n * Image-name generation, with `random.sample`/`random.choice` and\n * `random_word_from_lexicon` replaced by a single seed-derived stream.\n */\nexport function createMissImageName(imageName: string, seed: string): string {\n const random = seededRandom(deriveHash(seed, 'image', imageName));\n const word = () => LEXICON[Math.floor(random() * LEXICON.length)]!;\n const choice = <T,>(items: readonly T[]): T => items[Math.floor(random() * items.length)]!;\n const sampleTwo = (items: string[]): string[] => {\n const copy = items.slice();\n for (let index = 0; index < 2 && index < copy.length - 1; index += 1) {\n const target = index + Math.floor(random() * (copy.length - index));\n [copy[index], copy[target]] = [copy[target]!, copy[index]!];\n }\n return copy.slice(0, 2);\n };\n\n let result = imageName.replace(/-/g, '_');\n let isLine = false;\n if (isContainChinese(result)) {\n const chinese = result.match(/[一-鿿]+/g) ?? [];\n for (const item of chinese) result = result.split(item).join(word());\n }\n if (/\\d/.test(result)) result = result.replace(/\\d/g, '');\n if (result.includes('_')) {\n isLine = true;\n if (result.endsWith('_')) result = result.slice(0, -1);\n const parts = result.split('_');\n result = '';\n for (const item of parts) if (!isNumberString(item)) result += titleCase(item);\n }\n if (result.includes(' ')) {\n if (result.endsWith(' ')) result = result.slice(0, -1);\n const parts = result.split(' ');\n result = '';\n for (const item of parts) if (!isNumberString(item)) result += item;\n }\n let splitSource = result.replace(/[A-Z]/g, match => `==${match}`);\n if (splitSource.startsWith('==')) splitSource = splitSource.slice(2);\n let nameList = splitSource.split('==');\n if (nameList.length >= 2) {\n nameList = sampleTwo(nameList);\n } else {\n const imageCode = choice(['icon', 'img', 'image', 'pic', 'bg', 'name', 'back', 'add', 'left', 'right', word()]);\n nameList.splice(-1, 0, imageCode);\n }\n const last = nameList.length - 1;\n if (!isLine) {\n nameList[last] = titleCase(nameList[last]!);\n nameList[last - 1] = titleCase(nameList[last - 1]!);\n } else {\n nameList[last] = nameList[last]!.toLowerCase();\n nameList[last - 1] = nameList[last - 1]!.toLowerCase();\n }\n nameList.splice(0, 0, word());\n return isLine ? nameList.join('_') : nameList.join('');\n}\n\n/**\n * New image-name generation. Preserves count/language-suffixed variants\n * (counted variants are left unchanged).\n */\nexport function getNewImageName(imageName: string, imageNamePrefix: string, seed: string, algorithm = 0): string {\n if (!imageName) return imageName;\n const imageNameList = imageName.split('_');\n if (imageNameList.length >= 2) {\n const last = imageNameList.at(-1)!;\n if (isDigitString(last)) return imageName;\n if (!isDigitString(last) && LANGUAGE_CODES.includes(last)) return imageName;\n }\n let missImageName: string;\n if (imageNamePrefix && imageNamePrefix.length > 0 && imageNamePrefix.endsWith('#')) {\n return `${imageNamePrefix.split('#')[0]}${imageName}`;\n }\n if (algorithm === 0) missImageName = createMissImageName(imageName, seed);\n else if (algorithm === 3) missImageName = randomStringNoNumber(seed, `img-len:${imageName}`, imageName.length);\n else missImageName = createMissImageName(imageName, seed);\n\n let prefix = imageNamePrefix ?? '';\n if (prefix.length > 0 && imageName.includes('_')) {\n if (!prefix.endsWith('_')) return `${prefix}_${missImageName}`;\n return `${prefix}${missImageName}`;\n }\n let firstCharacter = missImageName.slice(0, 1);\n const surplus = missImageName.slice(1);\n firstCharacter = isLowerAscii(imageName.slice(0, 1)) ? firstCharacter.toLowerCase() : capitalize(firstCharacter);\n return `${prefix}${firstCharacter}${surplus}`;\n}\n\n/**\n * Xib id/destination rename: three hyphen-separated segments,\n * no Chinese, no colon, replaced with same-length alphanumeric segments.\n */\nexport function getIdDestinationMissName(name: string, seed: string): string | null {\n const parts = name.split('-');\n if (parts.length === 2 && name.startsWith('-')) return null;\n if (!name.includes('-')) return null;\n if (parts.length !== 3) return null;\n if (isContainChinese(name)) return null;\n if (name.includes(':')) return null;\n return parts.map((item, index) => randomStringWithNumber(seed, `xib:${name}:${index}`, item.length)).join('-');\n}\n\nexport interface XibKeywordMap {\n map: Map<string, string>;\n keywords: string[];\n}\n\n/**\n * Xib/storyboard id+destination map. Returns the attribute map\n * and the (possibly extended) keyword list used to discover custom attributes.\n */\nexport function getXibStoryboardMap(line: string, keywords: readonly string[], passKeywords: readonly string[]): XibKeywordMap | null {\n let newLine = line.trim();\n if (line.includes('com.apple.InterfaceBuilder')) return null;\n if (newLine.startsWith('<') && !newLine.startsWith('</') && newLine.endsWith('>')) {\n newLine = newLine.slice(newLine.indexOf(' ') + 1, newLine.indexOf('>'));\n } else {\n return null;\n }\n const nextKeywords = keywords.slice();\n const map = new Map<string, string>();\n for (let item of newLine.split(' ')) {\n if (item.endsWith('/')) item = item.slice(0, -1);\n item = item.replace(/\"/g, '');\n const pair = item.split('=');\n if (pair.length === 2 && pair[1]!.includes('-') && pair[1]!.split('-').length === 3 && !nextKeywords.includes(pair[0]!)) {\n if (passKeywords.length !== 0 && !passKeywords.includes(pair[0]!)) nextKeywords.push(pair[0]!);\n }\n if (item.includes('=') && nextKeywords.includes(item.split('=')[0]!)) {\n const kv = item.split('=');\n map.set(kv[0]!, kv[1]!);\n }\n }\n return { map, keywords: nextKeywords };\n}\n\nexport interface XibLineResult {\n line: string;\n keywords: string[];\n missMap: Map<string, string>;\n}\n\n/**\n * Xib/storyboard line transform. Pure per-line transform; the caller\n * threads the `missMap` (xs_miss_map_list) and keyword list between lines.\n */\nexport function replaceXibStoryboardLine(\n line: string,\n keywords: readonly string[],\n passKeywords: readonly string[],\n missMap: Map<string, string>,\n seed: string,\n): XibLineResult {\n const parsed = getXibStoryboardMap(line, keywords, passKeywords);\n if (!parsed) return { line, keywords: keywords.slice(), missMap };\n let missLine = line;\n for (const item of parsed.keywords) {\n const value = parsed.map.get(item);\n if (value === undefined) continue;\n let missValue = missMap.get(value);\n if (missValue === undefined) {\n const generated = getIdDestinationMissName(value, seed);\n if (generated !== null) missMap.set(value, generated);\n missValue = missMap.get(value);\n }\n if (missValue !== undefined) missLine = missLine.split(value).join(missValue);\n }\n return { line: missLine, keywords: parsed.keywords, missMap };\n}\n\n/** Extract keyword matches from a .strings line. */\nexport function getStringKeywords(line: string): string[] | null {\n const matches = [...line.matchAll(/\"([^\"]*-[^\"]*)\"/g)].map(match => match[1]!);\n if (matches.length === 0) return null;\n return matches.map(match => match.split('.')[0]!);\n}\n\n/** Replace keywords in a .strings line. */\nexport function replaceStringKeywords(line: string, missMap: ReadonlyMap<string, string>): string {\n const keywords = getStringKeywords(line);\n if (!keywords || keywords.length === 0) return line;\n let out = line;\n for (const keyword of keywords) {\n const value = missMap.get(keyword);\n if (value !== undefined) out = out.split(keyword).join(value);\n }\n return out;\n}\n\n/** True when `propertyName` is not preceded by an identifier character. */\nfunction judgeFrontSpecialCharacter(line: string, propertyName: string): boolean {\n const allowed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';\n if (line.includes(propertyName)) {\n const index = line.indexOf(propertyName);\n const previous = line.slice(index - 1, index);\n if (previous.length > 0 && allowed.includes(previous)) return false;\n }\n return true;\n}\n\n/**\n * project.pbxproj project-name rewrite, as a per-line pure transform.\n */\nexport function replaceProjectNameInLine(\n line: string,\n original: string,\n replacement: string,\n oldBridge: string,\n newBridge: string,\n): string {\n const skipHeader = line.includes('.h') && !line.includes('Pods-');\n const skipSource = line.includes('.m') && !line.includes('Tests.m') && !line.includes('Pods-');\n const skipPod = line.includes(\"pod '\");\n if (!skipHeader && !skipSource && !skipPod && judgeFrontSpecialCharacter(line, original)) {\n line = line.split(original).join(replacement);\n }\n if (line.includes(oldBridge)) line = line.split(oldBridge).join(newBridge);\n if (line.includes('SWIFT_OBJC_BRIDGING_HEADER') && line.includes(`${original}/`)) {\n line = line.split(`${original}/`).join(`${replacement}/`);\n }\n return line;\n}\n\n/**\n * Image-reference line rewrite. `imageType` is sourceTypeMap[imageName]\n * (e.g. \".png\", \"@2x.png\", or a comma list); pass undefined for plain references.\n */\nexport function replaceImageNameLine(\n line: string,\n imageName: string,\n newImageName: string,\n filePath: string,\n imageType?: string,\n): string {\n let prevWord = '';\n if (filePath.endsWith('.xcodeproj/project.pbxproj')) prevWord = '/* ';\n\n if (filePath.endsWith('.swift')) {\n if (line.includes(`\"${imageName}\"`)) return line.split(`\"${imageName}\"`).join(`\"${newImageName}\"`);\n if (line.includes(`\"${imageName}.`)) return line.split(`\"${imageName}.`).join(`\"${newImageName}.`);\n return line;\n }\n if (filePath.endsWith('.cpp')) {\n if (line.includes(`\"${imageName}\"`)) return line.split(imageName).join(newImageName);\n if (line.includes(`/${imageName}.`)) return line.split(imageName).join(newImageName);\n if (line.includes(`\"${imageName}.`)) return line.split(imageName).join(newImageName);\n return line;\n }\n\n if (imageType !== undefined && !line.includes('imageNamed:@\"') && !line.includes('imageFromBundle:@\"')) {\n if (imageType.includes(',')) {\n for (const type of imageType.split(',')) {\n if (line.includes(`${imageName}${type}`)) {\n line = line.split(`${prevWord}${imageName}${type}`).join(`${prevWord}${newImageName}${type}`);\n line = line.split(`\"${imageName}${type}`).join(`\"${newImageName}${type}`);\n line = line.split(`path = ${imageName}${type}`).join(`path = ${newImageName}${type}`);\n continue;\n }\n if (line.includes(`\"${imageName}.`)) { line = line.split(`\"${imageName}.`).join(`\"${newImageName}.`); continue; }\n if (line.includes(`\"${imageName}\"`)) { line = line.split(`\"${imageName}\"`).join(`\"${newImageName}\"`); continue; }\n if (line.includes(`\"${imageName}@2x\"`)) { line = line.split(`\"${imageName}@2x\"`).join(`\"${newImageName}@2x\"`); continue; }\n if (line.includes(`\"${imageName}@3x\"`)) { line = line.split(`\"${imageName}@3x\"`).join(`\"${newImageName}@3x\"`); }\n }\n return line;\n }\n if (line.includes(`${imageName}${imageType}`)) {\n line = line.split(`${prevWord}${imageName}${imageType}`).join(`${prevWord}${newImageName}${imageType}`);\n line = line.split(`\"${imageName}${imageType}`).join(`\"${newImageName}${imageType}`);\n line = line.split(`path = ${imageName}${imageType}`).join(`path = ${newImageName}${imageType}`);\n return line;\n }\n if (line.includes(`\"${imageName}\"`)) return line.split(`\"${imageName}\"`).join(`\"${newImageName}\"`);\n if (line.includes(`\"${imageName}@2x\"`)) return line.split(`\"${imageName}@2x\"`).join(`\"${newImageName}@2x\"`);\n if (line.includes(`\"${imageName}@3x\"`)) return line.split(`\"${imageName}@3x\"`).join(`\"${newImageName}@3x\"`);\n return line;\n }\n if (line.includes('imageNamed:@\"') && line.includes(imageName)) return line.split(`imageNamed:@\"${imageName}`).join(`imageNamed:@\"${newImageName}`);\n if (line.includes('imageFromBundle:@\"') && line.includes(imageName)) return line.split(`imageFromBundle:@\"${imageName}`).join(`imageFromBundle:@\"${newImageName}`);\n if (line.includes(`\"${imageName}\"`)) return line.split(`\"${imageName}\"`).join(`\"${newImageName}\"`);\n if (line.includes(`\"${imageName}@2x\"`)) return line.split(`\"${imageName}@2x\"`).join(`\"${newImageName}@2x\"`);\n if (line.includes(`\"${imageName}@3x\"`)) return line.split(`\"${imageName}@3x\"`).join(`\"${newImageName}@3x\"`);\n if (line.includes(`/* ${imageName}@`)) return line.split(`/* ${imageName}@`).join(`/* ${newImageName}@`);\n return line;\n}\n\ninterface FolderRow { name: string; missName: string; fatherName: string; fatherMissName: string }\n\n/** Resolve a child's renamed folder from its parent name. */\nfunction getFatherMissName(pbxGroups: ReadonlyMap<string, PbxGroupRow>, fatherName: string, childName: string): string {\n for (const partMap of pbxGroups.values()) {\n if (fatherName === partMap.name && partMap.children.includes(childName)) return partMap.missName;\n }\n return '';\n}\n\nfunction isReadableText(relativePathValue: string): boolean {\n return isTextFile(relativePathValue) || classifyLanguage(relativePathValue) !== undefined;\n}\n\nfunction commonRoot(files: WalkEntry[]): string {\n if (files.length === 0) return process.cwd();\n const parts = files.map(entry => entry.absolute.split(sep));\n let common = parts[0]!.slice();\n for (const next of parts.slice(1)) {\n let index = 0;\n while (index < common.length && index < next.length && common[index] === next[index]) index += 1;\n common = common.slice(0, index);\n }\n return common.join(sep) || sep;\n}\n\n/** True when a directory must not be renamed. */\nfunction ignoreFolderAbs(absolute: string): boolean {\n return IGNORE_FOLDERS.some(item => absolute.endsWith(item) || absolute.includes(item));\n}\n\n/**\n * Path/child map: walk the real directory tree once, recording\n * every child under its parent's \"last two components\" key and collecting the\n * directories that are eligible for renaming (i.e. not ignored).\n */\nfunction buildPathChildMap(root: string): { pathChildMap: Map<string, Set<string>>; renamableDirs: string[] } {\n const pathChildMap = new Map<string, Set<string>>();\n const renamableDirs: string[] = [];\n const add = (keyPath: string, child: string): void => {\n if (!pathChildMap.has(keyPath)) pathChildMap.set(keyPath, new Set());\n pathChildMap.get(keyPath)!.add(child);\n };\n const visit = (directory: string): void => {\n for (const name of readdirSync(directory).sort()) {\n const absolute = join(directory, name);\n const childName = name;\n const parent = dirname(absolute);\n const keyPath = `${basename(dirname(parent))}/${basename(parent)}`;\n if (!childName.includes('.DS_Store') && !childName.includes('Icon\\r') && !childName.includes('.git')) {\n add(keyPath, childName);\n }\n if (!lstatSync(absolute).isDirectory()) continue;\n if (!ignoreFolderAbs(absolute)) {\n renamableDirs.push(absolute);\n visit(absolute);\n }\n }\n };\n visit(root);\n return { pathChildMap, renamableDirs };\n}\n\n/** Project-name folder rename skips these subtrees. */\nfunction skipProjectRenamePath(absolute: string): boolean {\n if (absolute.includes('/.git')) return true;\n if (absolute.includes('.json')) return true;\n if (absolute.includes('/.DS_Store')) return true;\n if (absolute.includes('/Pods') && !absolute.includes('/Pods-')) return true;\n return false;\n}\n\nfunction setEquals(left: ReadonlySet<string>, right: readonly string[]): boolean {\n const rightSet = new Set(right);\n return left.size === rightSet.size && [...left].every(item => rightSet.has(item));\n}\n\n/** Resolve a directory's renamed name from the path map. */\nfunction getThisPathMissName(absolute: string, replacePathMap: ReadonlyMap<string, FolderRow>): string | undefined {\n const thisPathName = basename(absolute);\n const fatherPathName = basename(dirname(absolute));\n for (const [keyName, row] of replacePathMap) {\n if (keyName.includes(`&&${thisPathName}`) && (fatherPathName === row.fatherName || fatherPathName === row.fatherMissName)) {\n return row.missName;\n }\n }\n return undefined;\n}\n\n/** Remap a file path; its final component (the file name) is never remapped. */\nfunction remapAbsolute(absolute: string, dirRenames: ReadonlyMap<string, string>): string {\n const parts = absolute.split(sep);\n const out: string[] = [];\n let originalPrefix = '';\n for (let index = 0; index < parts.length; index += 1) {\n if (index === 0) { out.push(''); continue; }\n const originalAbs = originalPrefix === '' ? `${sep}${parts[index]}` : `${originalPrefix}${sep}${parts[index]}`;\n if (index === parts.length - 1) { out.push(parts[index]!); break; }\n const target = dirRenames.get(originalAbs);\n out.push(target ? basename(target) : parts[index]!);\n originalPrefix = originalAbs;\n }\n return out.join(sep);\n}\n\n/** Remap a directory path, including its final component. */\nfunction remapDirectoryAbsolute(absolute: string, dirRenames: ReadonlyMap<string, string>): string {\n const parts = absolute.split(sep);\n const out: string[] = [];\n let originalPrefix = '';\n for (let index = 0; index < parts.length; index += 1) {\n if (index === 0) { out.push(''); continue; }\n const originalAbs = originalPrefix === '' ? `${sep}${parts[index]}` : `${originalPrefix}${sep}${parts[index]}`;\n const target = dirRenames.get(originalAbs);\n out.push(target ? basename(target) : parts[index]!);\n originalPrefix = originalAbs;\n }\n return out.join(sep);\n}\n\n/**\n * Batch entry point: applies the full project-identity rename pipeline to an\n * already-walked project. Directory and image-file renames are applied on disk;\n * text references are rewritten in place. The same seed yields identical output.\n */\nexport function renameProjectIdentity(files: WalkEntry[], options: ProjectIdentityOptions, seed: string): TransformOutcome {\n const changedPaths: string[] = [];\n if (files.length === 0) return { changed: 0, changedPaths };\n\n const root = commonRoot(files);\n const folderPrefix = options.folderPrefix ?? '';\n const imagePrefix = options.imagePrefix ?? '';\n const imageAlgorithm = options.imageAlgorithm ?? 0;\n\n const originalFiles = files.slice();\n const content = new Map<string, string>();\n for (const entry of files) {\n if (isReadableText(entry.relative) && !isImageFile(entry.relative)) {\n content.set(entry.absolute, readFileSync(entry.absolute, 'utf8'));\n }\n }\n\n const { pathChildMap, renamableDirs } = buildPathChildMap(root);\n const dirRenames = new Map<string, string>();\n const fileRenames = new Map<string, string>();\n\n // 1. Project name.\n const pbxEntry = files.find(entry => entry.relative.endsWith('/project.pbxproj') && !entry.relative.includes('/Pods/'));\n const projectMatch = pbxEntry?.relative.match(/(?:^|\\/)([^/]+)\\.xcodeproj\\/project\\.pbxproj$/);\n const originalProjectName = projectMatch?.[1];\n if (originalProjectName && pbxEntry) {\n const replacement = options.projectName ?? capitalize(deriveHash(seed, 'project-name', originalProjectName).slice(0, 10));\n const oldBridge = `${originalProjectName}-Bridging-Header.h`;\n const newBridge = `${replacement}-Bridging-Header.h`;\n const projectDirs = new Set<string>();\n for (const entry of files) {\n let directory = dirname(entry.absolute);\n while (directory.startsWith(root) && directory !== root) {\n projectDirs.add(directory);\n const parent = dirname(directory);\n if (parent === directory) break;\n directory = parent;\n }\n }\n for (const directory of [...projectDirs].sort((a, b) => b.length - a.length)) {\n if (skipProjectRenamePath(directory)) continue;\n const name = basename(directory);\n if (name.includes(originalProjectName)) {\n dirRenames.set(directory, join(dirname(directory), name.split(originalProjectName).join(replacement)));\n }\n }\n for (const entry of files) {\n if (skipProjectRenamePath(entry.absolute)) continue;\n const name = basename(entry.absolute);\n if (name.includes(originalProjectName) && !name.endsWith('.h') && !name.endsWith('.m')) {\n fileRenames.set(entry.absolute, join(dirname(entry.absolute), name.split(originalProjectName).join(replacement)));\n } else if (name === oldBridge) {\n fileRenames.set(entry.absolute, join(dirname(entry.absolute), newBridge));\n }\n }\n const pbxContent = content.get(pbxEntry.absolute) ?? readFileSync(pbxEntry.absolute, 'utf8');\n content.set(pbxEntry.absolute, pbxContent.split('\\n').map(line => replaceProjectNameInLine(line, originalProjectName, replacement, oldBridge, newBridge)).join('\\n'));\n }\n\n // 2. Folder rename.\n const pbxPath = pbxEntry?.absolute;\n const missPbxprojMap = new Map<string, [string, string]>();\n if (pbxPath && content.has(pbxPath)) {\n const pbxGroups = parsePbxGroups(content.get(pbxPath)!, seed, folderPrefix);\n const replacePathMap = new Map<string, FolderRow>();\n for (const [keyPath, childList] of pathChildMap) {\n for (const [childrenNames, partMap] of pbxGroups) {\n if (!setEquals(childList, childrenNames.split(','))) continue;\n const name = partMap.name;\n const fatherName = keyPath.split('/')[0] ?? '';\n const keyName = `${fatherName}&&${name}`;\n const fatherMissName = getFatherMissName(pbxGroups, fatherName, name);\n replacePathMap.set(keyName, { name, missName: partMap.missName, fatherName, fatherMissName });\n missPbxprojMap.set(partMap.udid, [name, partMap.missName]);\n }\n }\n for (const directory of renamableDirs) {\n const missName = getThisPathMissName(directory, replacePathMap);\n if (missName !== undefined) dirRenames.set(directory, join(dirname(directory), missName));\n }\n content.set(pbxPath, rewritePbxprojForFolders(content.get(pbxPath)!, missPbxprojMap));\n }\n\n // 3. xcassets image rename.\n const sourceMap = new Map<string, string>();\n const sourceTypeMap = new Map<string, string>();\n for (const entry of originalFiles) {\n if (!entry.relative.includes('.imageset/')) continue;\n if (!isImageFile(entry.relative)) continue;\n const parts = entry.absolute.split(sep);\n const imagesetIndex = parts.findIndex(part => part.endsWith('.imageset'));\n if (imagesetIndex === -1) continue;\n const imagesetAbs = parts.slice(0, imagesetIndex + 1).join(sep);\n const fileName = parts.at(-1)!;\n let stem: string;\n let type: string;\n if (fileName.includes('@')) {\n stem = fileName.split('@')[0]!;\n type = `@${fileName.split('@').at(-1)}`;\n } else {\n stem = fileName.split('.')[0]!;\n type = `.${fileName.split('.').at(-1)}`;\n }\n if (!sourceMap.has(stem)) sourceMap.set(stem, getNewImageName(stem, imagePrefix, seed, imageAlgorithm));\n const newStem = sourceMap.get(stem)!;\n const existingType = sourceTypeMap.get(stem);\n sourceTypeMap.set(stem, existingType && existingType !== type ? `${existingType},${type}` : type);\n const nextName = fileName.includes('@') ? `${newStem}@${fileName.split('@').at(-1)}` : `${newStem}.${fileName.split('.').at(-1)}`;\n fileRenames.set(entry.absolute, join(dirname(entry.absolute), nextName));\n\n const contentsAbs = join(imagesetAbs, 'Contents.json');\n const contentsBefore = content.get(contentsAbs) ?? (existsSync(contentsAbs) ? readFileSync(contentsAbs, 'utf8') : '');\n if (contentsBefore) {\n let contentsAfter = contentsBefore;\n for (const [oldName, newName] of sourceMap) contentsAfter = contentsAfter.split(`\"${oldName}.`).join(`\"${newName}.`);\n content.set(contentsAbs, contentsAfter);\n }\n }\n for (const [stem, newStem] of sourceMap) {\n for (const entry of originalFiles) {\n if (!entry.relative.includes('.imageset/')) continue;\n const parts = entry.absolute.split(sep);\n const imagesetIndex = parts.findIndex(part => part.endsWith('.imageset'));\n if (imagesetIndex === -1) continue;\n const imagesetAbs = parts.slice(0, imagesetIndex + 1).join(sep);\n if (basename(imagesetAbs) === `${stem}.imageset`) {\n dirRenames.set(imagesetAbs, join(dirname(imagesetAbs), `${newStem}.imageset`));\n }\n }\n }\n\n // 3b. Rewrite image references in source/code files.\n for (const entry of originalFiles) {\n if (!isReadableText(entry.relative) || isImageFile(entry.relative)) continue;\n const before = content.get(entry.absolute);\n if (before === undefined) continue;\n let after = before;\n for (const [imageName, newImageName] of sourceMap) {\n after = after.split('\\n').map(line => replaceImageNameLine(line, imageName, newImageName, entry.relative, sourceTypeMap.get(imageName))).join('\\n');\n }\n content.set(entry.absolute, after);\n }\n\n // 4. xib/storyboard id + destination and .strings keywords.\n const missMap = new Map<string, string>();\n let keywords: string[] = XIB_KEY_WORDS.slice();\n for (const entry of originalFiles) {\n if (!entry.relative.endsWith('.xib') && !entry.relative.endsWith('.storyboard')) continue;\n const before = content.get(entry.absolute) ?? readFileSync(entry.absolute, 'utf8');\n const after = before.split('\\n').map(line => {\n const result = replaceXibStoryboardLine(line, keywords, XIB_PASS_KEY_WORDS, missMap, seed);\n keywords = result.keywords;\n return result.line;\n }).join('\\n');\n content.set(entry.absolute, after);\n }\n for (const entry of originalFiles) {\n if (!entry.relative.endsWith('.strings')) continue;\n const before = content.get(entry.absolute) ?? readFileSync(entry.absolute, 'utf8');\n content.set(entry.absolute, replaceStringKeywords(before, missMap));\n }\n\n // 5. Apply directory renames (deepest first), then file renames, then content.\n const orderedDirRenames = [...dirRenames.entries()].sort((a, b) => b[0].split(sep).length - a[0].split(sep).length);\n for (const [from, to] of orderedDirRenames) {\n if (from === to || !existsSync(from) || existsSync(to)) continue;\n renameSync(from, to);\n changedPaths.push(to);\n }\n\n for (const [from, to] of fileRenames) {\n const finalFrom = remapAbsolute(from, dirRenames);\n const finalTo = join(remapDirectoryAbsolute(dirname(from), dirRenames), basename(to));\n if (finalFrom === finalTo || !existsSync(finalFrom) || existsSync(finalTo)) continue;\n mkdirSync(dirname(finalTo), { recursive: true });\n renameSync(finalFrom, finalTo);\n changedPaths.push(finalTo);\n }\n\n for (const entry of originalFiles) {\n const rewritten = content.get(entry.absolute);\n if (rewritten === undefined) continue;\n const finalAbs = remapAbsolute(entry.absolute, dirRenames);\n const previous = existsSync(finalAbs) ? readFileSync(finalAbs, 'utf8') : '';\n if (previous !== rewritten) {\n mkdirSync(dirname(finalAbs), { recursive: true });\n writeFileSync(finalAbs, rewritten);\n changedPaths.push(finalAbs);\n }\n }\n\n return { changed: changedPaths.length, changedPaths: [...new Set(changedPaths)].map(absolute => relative(root, absolute).split(sep).join('/')) };\n}\n\n/**\n * PBX folder rename, reduced to the\n * deterministic core: PBXGroup comment/path renames and relay of `path =` lines.\n * Header-path rewriting (pch/plist/bridging) is folded into the caller only when\n * those paths are part of the folder rename; here the relay covers every group.\n */\nfunction rewritePbxprojForFolders(content: string, missMap: ReadonlyMap<string, [string, string]>): string {\n let relay: [string, string] | null = null;\n const out: string[] = [];\n for (const line of content.split('\\n')) {\n if (line.includes('/*') && line.includes('*/')) {\n const lineUdid = line.slice(0, line.indexOf('/*')).split(/\\s+/).join('');\n const lineName = line.slice(line.indexOf('/*') + 2, line.indexOf('*/')).split(/\\s+/).join('');\n let next = line;\n if (missMap.has(lineUdid)) {\n const pair = missMap.get(lineUdid)!;\n next = line.split(lineName).join(pair[1]);\n if (next.includes('*/ = {')) relay = pair;\n }\n out.push(next);\n continue;\n }\n if (line.includes('path =') && relay !== null) {\n out.push(line.split(relay[0]).join(relay[1]));\n continue;\n }\n if (line.includes('};')) relay = null;\n out.push(line);\n }\n return out.join('\\n');\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { escapeRegExp, extension, seededInt, walkProject, writeIfChanged } from "./common.js";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
// src/string-encrypt.ts
|
|
4
|
+
/**
|
|
5
|
+
* Deterministic plaintext string-encryption pass for ObjC/Swift. Every exported
|
|
6
|
+
* transform is pure: identical content +
|
|
7
|
+
* options produce byte-identical output. The XOR key is an integer 0-255 that is
|
|
8
|
+
* either passed explicitly or derived from a seed via `seededInt`; no wall-clock
|
|
9
|
+
* time or `Math.random` is ever consulted.
|
|
10
|
+
*
|
|
11
|
+
* Original control flow (GUI/network/auth discarded):
|
|
12
|
+
* - ObjC: line-based pass turns eligible `@"..."` literals into `NAME("...")`,
|
|
13
|
+
* then a whole-file pass rewrites `NAME("...")`/`NNC_CSTRING("...")` into
|
|
14
|
+
* `NAME(((char []) {<byte^key>, ..., 0}))`. The `.pch` gets a
|
|
15
|
+
* `decryptConstString(char*, size_t)` helper plus `#define NAME(arr) ...`.
|
|
16
|
+
* - Swift: after the first `import` line a `NAME(ucharArr: [UInt8]) -> String`
|
|
17
|
+
* helper is injected; eligible `"..."` literals become
|
|
18
|
+
* `NAME(ucharArr: [<byte^key>, ...])`.
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_OBJC_NAME = "NNC_NSSTRING";
|
|
21
|
+
const DEFAULT_SWIFT_NAME = "OBF_NSSTRING";
|
|
22
|
+
/** Resolve the XOR key: validate an explicit key, otherwise derive it from the seed. */
|
|
23
|
+
function resolveEncryptionNum(seed, encryptionNum) {
|
|
24
|
+
if (encryptionNum !== void 0) {
|
|
25
|
+
if (!Number.isInteger(encryptionNum) || encryptionNum < 0 || encryptionNum > 255) throw new RangeError(`encryptionNum must be an integer between 0 and 255, got ${encryptionNum}`);
|
|
26
|
+
return encryptionNum;
|
|
27
|
+
}
|
|
28
|
+
if (!seed) throw new Error("string encryption requires encryptionNum or a non-empty seed");
|
|
29
|
+
return seededInt(seed, "string-encrypt", 0, 255);
|
|
30
|
+
}
|
|
31
|
+
/** True when any char is in U+4E00..U+9FFF. */
|
|
32
|
+
function isContainChinese(value) {
|
|
33
|
+
for (const char of value) if (char >= "一" && char <= "鿿") return true;
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* First strips the `//` comment (everything after the *last* `//`, matching
|
|
38
|
+
* `split('//')[:-1]`), then returns the index of the last occurrence of
|
|
39
|
+
* `strMark`, or -1.
|
|
40
|
+
*/
|
|
41
|
+
function findLast(strValue, strMark) {
|
|
42
|
+
let value = strValue;
|
|
43
|
+
if (value.includes("//")) {
|
|
44
|
+
const parts = value.split("//");
|
|
45
|
+
parts.pop();
|
|
46
|
+
value = parts.join("//");
|
|
47
|
+
}
|
|
48
|
+
let lastPosition = -1;
|
|
49
|
+
while (true) {
|
|
50
|
+
const position = value.indexOf(strMark, lastPosition + 1);
|
|
51
|
+
if (position === -1) return lastPosition;
|
|
52
|
+
lastPosition = position;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** ObjC decryptor snippet (port of get_pch_code). */
|
|
56
|
+
function objcStringDecryptor(encryptionNum, encryptedName = DEFAULT_OBJC_NAME) {
|
|
57
|
+
return `
|
|
58
|
+
static NSString * decryptConstString(char* string, size_t len){
|
|
59
|
+
NSMutableData *data = [NSMutableData dataWithLength:len];
|
|
60
|
+
for (int i = 0; i < len; i++) {
|
|
61
|
+
char c = string[i] ^ ${`0x${encryptionNum.toString(16)}`};\n [data replaceBytesInRange:NSMakeRange(i, 1) withBytes:&c length:1];
|
|
62
|
+
}
|
|
63
|
+
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
64
|
+
}
|
|
65
|
+
\n#define ${encryptedName}(arr) decryptConstString(arr, sizeof(arr)-1)\n`;
|
|
66
|
+
}
|
|
67
|
+
/** Swift decryptor snippet (port of get_swift_code). */
|
|
68
|
+
function swiftStringDecryptor(encryptionNum, encryptedName = DEFAULT_SWIFT_NAME) {
|
|
69
|
+
return `\npublic func ${encryptedName}(ucharArr: [UInt8]) -> String {\n let decoded = ucharArr.map { $0 ^ ${encryptionNum} }\n return String(decoding: decoded, as: UTF8.self)
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
73
|
+
/** Split preserving the exact line endings so re-joined output is byte-stable. */
|
|
74
|
+
function splitLines(content) {
|
|
75
|
+
const lines = [];
|
|
76
|
+
let start = 0;
|
|
77
|
+
for (let index = 0; index < content.length; index += 1) {
|
|
78
|
+
const char = content[index];
|
|
79
|
+
if (char !== "\n" && char !== "\r") continue;
|
|
80
|
+
if (char === "\r" && content[index + 1] === "\n") {
|
|
81
|
+
lines.push({
|
|
82
|
+
text: content.slice(start, index),
|
|
83
|
+
ending: "\r\n"
|
|
84
|
+
});
|
|
85
|
+
index += 1;
|
|
86
|
+
} else lines.push({
|
|
87
|
+
text: content.slice(start, index),
|
|
88
|
+
ending: char
|
|
89
|
+
});
|
|
90
|
+
start = index + 1;
|
|
91
|
+
}
|
|
92
|
+
lines.push({
|
|
93
|
+
text: content.slice(start),
|
|
94
|
+
ending: ""
|
|
95
|
+
});
|
|
96
|
+
return lines;
|
|
97
|
+
}
|
|
98
|
+
function joinLines(lines) {
|
|
99
|
+
return lines.map((line) => line.text + line.ending).join("");
|
|
100
|
+
}
|
|
101
|
+
/** Insert text right before the first line whose text contains `marker`. */
|
|
102
|
+
function insertBeforeFirstLineContaining(content, marker, insert) {
|
|
103
|
+
const lines = splitLines(content);
|
|
104
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
105
|
+
if (!lines[index].text.includes(marker)) continue;
|
|
106
|
+
const head = joinLines(lines.slice(0, index));
|
|
107
|
+
const tail = joinLines(lines.slice(index));
|
|
108
|
+
return head + insert + tail;
|
|
109
|
+
}
|
|
110
|
+
return content;
|
|
111
|
+
}
|
|
112
|
+
/** Insert text right after the first line whose text contains `marker`. */
|
|
113
|
+
function insertAfterFirstLineContaining(content, marker, insert) {
|
|
114
|
+
const lines = splitLines(content);
|
|
115
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
116
|
+
if (!lines[index].text.includes(marker)) continue;
|
|
117
|
+
const head = joinLines(lines.slice(0, index + 1));
|
|
118
|
+
const tail = joinLines(lines.slice(index + 1));
|
|
119
|
+
return head + insert + tail;
|
|
120
|
+
}
|
|
121
|
+
return content;
|
|
122
|
+
}
|
|
123
|
+
/** Inject the ObjC decryptor into `.pch` content (before the first `#endif`). */
|
|
124
|
+
function injectObjcStringDecryptor(content, encryptionNum, encryptedName = DEFAULT_OBJC_NAME) {
|
|
125
|
+
return insertBeforeFirstLineContaining(content, "#endif", objcStringDecryptor(encryptionNum, encryptedName) + "\n");
|
|
126
|
+
}
|
|
127
|
+
/** Inject the Swift decryptor into content (after the first `import` line). */
|
|
128
|
+
function injectSwiftStringDecryptor(content, encryptionNum, encryptedName = DEFAULT_SWIFT_NAME) {
|
|
129
|
+
return insertAfterFirstLineContaining(content, "import ", swiftStringDecryptor(encryptionNum, encryptedName));
|
|
130
|
+
}
|
|
131
|
+
/** Decide whether an ObjC line must be skipped by string encryption. */
|
|
132
|
+
function isPassObjCEncryptString(line) {
|
|
133
|
+
const noSpaceLine = line.replace(/\s+/g, "");
|
|
134
|
+
if (line.includes("@\"\\\"")) return true;
|
|
135
|
+
if (isContainChinese(line)) return true;
|
|
136
|
+
if (line.includes("NSLog(@\"")) return true;
|
|
137
|
+
if (line.includes("static ")) return true;
|
|
138
|
+
if (line.includes("const ")) return true;
|
|
139
|
+
if (line.includes("\"@\"")) return true;
|
|
140
|
+
if (line.includes("%p")) return true;
|
|
141
|
+
if (line.includes("\"GMT\"") || line.includes("'GMT'")) return true;
|
|
142
|
+
if (line.includes("\"en_US\"") || line.includes("'en_US'")) return true;
|
|
143
|
+
if (line.includes("HH:mm:ss")) return true;
|
|
144
|
+
if (line.includes("yyyy-MM-dd")) return true;
|
|
145
|
+
if (line.includes("\"utf-8\\")) return true;
|
|
146
|
+
if (line.includes("@\";\"")) return true;
|
|
147
|
+
if (line.includes("@\"text/\"")) return true;
|
|
148
|
+
if (line.includes("@\"application/json\"")) return true;
|
|
149
|
+
if (line.includes("@\"application/xml\"")) return true;
|
|
150
|
+
if (line.includes("=%i,")) return true;
|
|
151
|
+
if (line.includes("CFSTR(")) return true;
|
|
152
|
+
if (line.includes("objc_") || line.includes("OBJC_")) return true;
|
|
153
|
+
if (line.startsWith("#define ")) return true;
|
|
154
|
+
if (line.endsWith("\"")) return true;
|
|
155
|
+
if (line.includes(" case ")) return true;
|
|
156
|
+
if (noSpaceLine.startsWith("NSAssert(")) return true;
|
|
157
|
+
if (line.includes("Log(@\"")) return true;
|
|
158
|
+
if (line.includes("LogDebug(")) return true;
|
|
159
|
+
if (line.includes("@\"#@\"")) return true;
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
/** Decide whether an ObjC line is eligible for string encryption. */
|
|
163
|
+
function isCanMissObjCCodeInLine(line) {
|
|
164
|
+
if (line.includes("NSLog")) return false;
|
|
165
|
+
if (line.includes("static")) return false;
|
|
166
|
+
if (line.includes("const")) return false;
|
|
167
|
+
if (line.includes("¥")) return false;
|
|
168
|
+
if (line.includes("$")) return false;
|
|
169
|
+
if (isContainChinese(line)) return false;
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
/** Rewrite `@"..."` -> `NAME("...")`, skipping `%` bodies. */
|
|
173
|
+
function replaceObjCQuotes(input, encryptedName) {
|
|
174
|
+
return input.replace(/@"([^"]*)"/g, (whole, content) => content.includes("%") ? whole : `${encryptedName}("${content}")`);
|
|
175
|
+
}
|
|
176
|
+
const FORMAT_LITERAL_PATTERN = /@"[^"]*%@[^"]*"|@"[^"]*%s[^"]*"/g;
|
|
177
|
+
/**
|
|
178
|
+
* ObjC line transform: protect `%@`/`%s` literals with
|
|
179
|
+
* placeholders, replace remaining `@"..."` literals, restore placeholders, then
|
|
180
|
+
* drop the `@` in front of any pre-existing `NAME(` call.
|
|
181
|
+
*/
|
|
182
|
+
function missObjCCodeInLine(line, encryptedName) {
|
|
183
|
+
const protectedMatches = [];
|
|
184
|
+
let result = line.replace(FORMAT_LITERAL_PATTERN, (match) => {
|
|
185
|
+
const placeholder = `【###@@@###】=【${protectedMatches.length}】`;
|
|
186
|
+
protectedMatches.push(match);
|
|
187
|
+
return placeholder;
|
|
188
|
+
});
|
|
189
|
+
result = replaceObjCQuotes(result, encryptedName);
|
|
190
|
+
protectedMatches.forEach((original, index) => {
|
|
191
|
+
result = result.replace(`【###@@@###】=【${index}】`, original);
|
|
192
|
+
});
|
|
193
|
+
result = result.replaceAll(`@${encryptedName}(`, `${encryptedName}(`);
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
/** Byte-XOR array body for an ObjC `NAME(((char []) {...}))` literal (port of replace_clear_text_string). */
|
|
197
|
+
function getObjcCharArray(content, encryptionNum, isMm = false) {
|
|
198
|
+
const items = [...Buffer.from(content, "utf8")].map((byte) => {
|
|
199
|
+
const value = String(byte ^ encryptionNum);
|
|
200
|
+
return isMm ? `static_cast<char>(${value})` : value;
|
|
201
|
+
});
|
|
202
|
+
items.push(isMm ? "static_cast<char>(0)" : "0");
|
|
203
|
+
return `((char []) {${items.join(", ")}})`;
|
|
204
|
+
}
|
|
205
|
+
/** Whole-file ObjC pass: `NAME("...")`/`NNC_CSTRING("...")` -> `NAME(((char []) {...}))`. */
|
|
206
|
+
function obfuscateObjCStringLiterals(content, encryptionNum, encryptedName = DEFAULT_OBJC_NAME, isMm = false) {
|
|
207
|
+
const pattern = new RegExp(`(${escapeRegExp(encryptedName)}\\(|NNC_CSTRING\\()"(.*?)"(\\))`, "g");
|
|
208
|
+
return content.replace(pattern, (_whole, prefix, body, suffix) => `${prefix}${getObjcCharArray(body, encryptionNum, isMm)}${suffix}`);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Full ObjC `.m`/`.mm` transform: line-based eligibility/skip pass followed by the
|
|
212
|
+
* whole-file `obfuscate` pass, producing the final `NAME(((char []) {...}))` form.
|
|
213
|
+
*/
|
|
214
|
+
function encryptObjcStringLiterals(content, options = {}) {
|
|
215
|
+
const key = resolveEncryptionNum(options.seed ?? "", options.encryptionNum);
|
|
216
|
+
const name = options.encryptedName ?? "NNC_NSSTRING";
|
|
217
|
+
const isMm = options.isMm ?? false;
|
|
218
|
+
let isAddCodeAtM = false;
|
|
219
|
+
const lines = splitLines(content);
|
|
220
|
+
const out = [];
|
|
221
|
+
for (const line of lines) {
|
|
222
|
+
if (line.text.includes("@interface") || line.text.includes("@implementation")) {
|
|
223
|
+
out.push(line);
|
|
224
|
+
isAddCodeAtM = true;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (isPassObjCEncryptString(line.text)) {
|
|
228
|
+
out.push(line);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (!isAddCodeAtM) {
|
|
232
|
+
out.push(line);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (line.text.includes("@\"") && isCanMissObjCCodeInLine(line.text)) {
|
|
236
|
+
out.push({
|
|
237
|
+
text: missObjCCodeInLine(line.text, name),
|
|
238
|
+
ending: line.ending
|
|
239
|
+
});
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
out.push(line);
|
|
243
|
+
}
|
|
244
|
+
return obfuscateObjCStringLiterals(joinLines(out), key, name, isMm);
|
|
245
|
+
}
|
|
246
|
+
/** Decide whether a Swift line is eligible for string encryption. */
|
|
247
|
+
function isCanMissSwiftCodeInLine(line) {
|
|
248
|
+
const stripped = line.trim();
|
|
249
|
+
if (stripped.startsWith("print(")) return false;
|
|
250
|
+
if (stripped.startsWith("debugPrint(")) return false;
|
|
251
|
+
if (stripped.startsWith("#warning")) return false;
|
|
252
|
+
if (stripped.includes("\"\"\"")) return false;
|
|
253
|
+
if (stripped.includes("\\(")) return false;
|
|
254
|
+
if (stripped.includes(" case ")) return false;
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
/** Byte-XOR Swift array string `[b0, b1, ...]` (port of getSwiftEncryptCode). */
|
|
258
|
+
function getSwiftEncryptCode(content, encryptionNum) {
|
|
259
|
+
return `[${[...Buffer.from(content, "utf8")].map((byte) => String(byte ^ encryptionNum)).join(", ")}]`;
|
|
260
|
+
}
|
|
261
|
+
/** Swift line transform: rewrite eligible `"..."` literals. */
|
|
262
|
+
function missSwiftCodeInLine(line, encryptionNum, encryptedName) {
|
|
263
|
+
const specialKey = "^$$$^";
|
|
264
|
+
let result = line.replaceAll("\"\"", specialKey);
|
|
265
|
+
const items = [];
|
|
266
|
+
for (const match of result.matchAll(/"(.+?)"/g)) items.push(match[1]);
|
|
267
|
+
for (const item of items) {
|
|
268
|
+
if (item.startsWith("\\") || item.startsWith("\"") || item.length === 1 || item.startsWith("%")) continue;
|
|
269
|
+
const encrypted = `${encryptedName}(ucharArr: ${getSwiftEncryptCode(item, encryptionNum)})`;
|
|
270
|
+
result = result.split(`"${item}"`).join(encrypted);
|
|
271
|
+
}
|
|
272
|
+
result = result.replaceAll(specialKey, "\"\"");
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
/** Full Swift transform: inject the decryptor after `import`, then encrypt literals. */
|
|
276
|
+
function encryptSwiftStringLiterals(content, options = {}) {
|
|
277
|
+
const key = resolveEncryptionNum(options.seed ?? "", options.encryptionNum);
|
|
278
|
+
const name = options.encryptedName ?? "OBF_NSSTRING";
|
|
279
|
+
let injected = false;
|
|
280
|
+
let out = "";
|
|
281
|
+
for (const line of splitLines(content)) {
|
|
282
|
+
if (!injected && line.text.includes("import ")) {
|
|
283
|
+
out += line.text + line.ending;
|
|
284
|
+
out += swiftStringDecryptor(key, name);
|
|
285
|
+
injected = true;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (line.text.includes("\"") && isCanMissSwiftCodeInLine(line.text)) out += missSwiftCodeInLine(line.text, key, name) + line.ending;
|
|
289
|
+
else out += line.text + line.ending;
|
|
290
|
+
}
|
|
291
|
+
return out;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Batch project transform. `.pch` files receive the ObjC decryptor; `.m`/`.mm`
|
|
295
|
+
* files get their string literals encrypted; `.swift` files get the decryptor
|
|
296
|
+
* injected plus encrypted literals. Returns the uniform TransformOutcome shape.
|
|
297
|
+
*/
|
|
298
|
+
function applyStringEncryption(root, options = {}) {
|
|
299
|
+
const key = resolveEncryptionNum(options.seed ?? "", options.encryptionNum);
|
|
300
|
+
const changedPaths = [];
|
|
301
|
+
let changed = 0;
|
|
302
|
+
for (const entry of walkProject(root).files) {
|
|
303
|
+
const ext = extension(entry.relative);
|
|
304
|
+
const before = readFileSync(entry.absolute, "utf8");
|
|
305
|
+
let after;
|
|
306
|
+
if (ext === "pch") after = injectObjcStringDecryptor(before, key, options.encryptedName);
|
|
307
|
+
else if (ext === "m" || ext === "mm") after = encryptObjcStringLiterals(before, {
|
|
308
|
+
encryptionNum: key,
|
|
309
|
+
encryptedName: options.encryptedName,
|
|
310
|
+
isMm: ext === "mm"
|
|
311
|
+
});
|
|
312
|
+
else if (ext === "swift") after = encryptSwiftStringLiterals(before, {
|
|
313
|
+
encryptionNum: key,
|
|
314
|
+
encryptedName: options.encryptedName
|
|
315
|
+
});
|
|
316
|
+
else continue;
|
|
317
|
+
if (after !== before) {
|
|
318
|
+
writeIfChanged(entry, after);
|
|
319
|
+
changed += 1;
|
|
320
|
+
changedPaths.push(entry.relative);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
changed,
|
|
325
|
+
changedPaths
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
export { DEFAULT_OBJC_NAME, DEFAULT_SWIFT_NAME, applyStringEncryption, encryptObjcStringLiterals, encryptSwiftStringLiterals, findLast, getObjcCharArray, getSwiftEncryptCode, injectObjcStringDecryptor, injectSwiftStringDecryptor, isCanMissObjCCodeInLine, isCanMissSwiftCodeInLine, isContainChinese, isPassObjCEncryptString, missObjCCodeInLine, missSwiftCodeInLine, obfuscateObjCStringLiterals, objcStringDecryptor, replaceObjCQuotes, resolveEncryptionNum, swiftStringDecryptor };
|
|
329
|
+
|
|
330
|
+
//# sourceMappingURL=string-encrypt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string-encrypt.js","names":[],"sources":["../src/string-encrypt.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport type { TransformOutcome } from './common.js';\nimport { escapeRegExp, extension, seededInt, walkProject, writeIfChanged } from './common.js';\n\n/**\n * Deterministic plaintext string-encryption pass for ObjC/Swift. Every exported\n * transform is pure: identical content +\n * options produce byte-identical output. The XOR key is an integer 0-255 that is\n * either passed explicitly or derived from a seed via `seededInt`; no wall-clock\n * time or `Math.random` is ever consulted.\n *\n * Original control flow (GUI/network/auth discarded):\n * - ObjC: line-based pass turns eligible `@\"...\"` literals into `NAME(\"...\")`,\n * then a whole-file pass rewrites `NAME(\"...\")`/`NNC_CSTRING(\"...\")` into\n * `NAME(((char []) {<byte^key>, ..., 0}))`. The `.pch` gets a\n * `decryptConstString(char*, size_t)` helper plus `#define NAME(arr) ...`.\n * - Swift: after the first `import` line a `NAME(ucharArr: [UInt8]) -> String`\n * helper is injected; eligible `\"...\"` literals become\n * `NAME(ucharArr: [<byte^key>, ...])`.\n */\n\nexport const DEFAULT_OBJC_NAME = 'NNC_NSSTRING';\nexport const DEFAULT_SWIFT_NAME = 'OBF_NSSTRING';\n\nexport interface StringEncryptOptions {\n /** Deterministic source of the XOR key when `encryptionNum` is omitted. */\n seed?: string;\n /** XOR key (0-255). Explicit keys win over the seed. */\n encryptionNum?: number;\n /** ObjC macro / Swift function name. Defaults to NNC_NSSTRING / OBF_NSSTRING. */\n encryptedName?: string;\n}\n\n/** Resolve the XOR key: validate an explicit key, otherwise derive it from the seed. */\nexport function resolveEncryptionNum(seed: string, encryptionNum?: number): number {\n if (encryptionNum !== undefined) {\n if (!Number.isInteger(encryptionNum) || encryptionNum < 0 || encryptionNum > 255) {\n throw new RangeError(`encryptionNum must be an integer between 0 and 255, got ${encryptionNum}`);\n }\n return encryptionNum;\n }\n if (!seed) throw new Error('string encryption requires encryptionNum or a non-empty seed');\n return seededInt(seed, 'string-encrypt', 0, 255);\n}\n\n/** True when any char is in U+4E00..U+9FFF. */\nexport function isContainChinese(value: string): boolean {\n for (const char of value) {\n if (char >= '\\u4e00' && char <= '\\u9fff') return true;\n }\n return false;\n}\n\n/**\n * First strips the `//` comment (everything after the *last* `//`, matching\n * `split('//')[:-1]`), then returns the index of the last occurrence of\n * `strMark`, or -1.\n */\nexport function findLast(strValue: string, strMark: string): number {\n let value = strValue;\n if (value.includes('//')) {\n const parts = value.split('//');\n parts.pop();\n value = parts.join('//');\n }\n let lastPosition = -1;\n while (true) {\n const position = value.indexOf(strMark, lastPosition + 1);\n if (position === -1) return lastPosition;\n lastPosition = position;\n }\n}\n\n/** ObjC decryptor snippet (port of get_pch_code). */\nexport function objcStringDecryptor(encryptionNum: number, encryptedName: string = DEFAULT_OBJC_NAME): string {\n const key = `0x${encryptionNum.toString(16)}`;\n return (\n '\\nstatic NSString * decryptConstString(char* string, size_t len){\\n' +\n ' NSMutableData *data = [NSMutableData dataWithLength:len];\\n' +\n ' for (int i = 0; i < len; i++) {\\n' +\n ` char c = string[i] ^ ${key};\\n` +\n ' [data replaceBytesInRange:NSMakeRange(i, 1) withBytes:&c length:1];\\n' +\n ' }\\n' +\n ' return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];\\n' +\n '}\\n' +\n `\\n#define ${encryptedName}(arr) decryptConstString(arr, sizeof(arr)-1)\\n`\n );\n}\n\n/** Swift decryptor snippet (port of get_swift_code). */\nexport function swiftStringDecryptor(encryptionNum: number, encryptedName: string = DEFAULT_SWIFT_NAME): string {\n return (\n `\\npublic func ${encryptedName}(ucharArr: [UInt8]) -> String {\\n` +\n ` let decoded = ucharArr.map { $0 ^ ${encryptionNum} }\\n` +\n ' return String(decoding: decoded, as: UTF8.self)\\n' +\n '}\\n'\n );\n}\n\ninterface Line {\n text: string;\n ending: string;\n}\n\n/** Split preserving the exact line endings so re-joined output is byte-stable. */\nfunction splitLines(content: string): Line[] {\n const lines: Line[] = [];\n let start = 0;\n for (let index = 0; index < content.length; index += 1) {\n const char = content[index];\n if (char !== '\\n' && char !== '\\r') continue;\n if (char === '\\r' && content[index + 1] === '\\n') {\n lines.push({ text: content.slice(start, index), ending: '\\r\\n' });\n index += 1;\n } else {\n lines.push({ text: content.slice(start, index), ending: char });\n }\n start = index + 1;\n }\n lines.push({ text: content.slice(start), ending: '' });\n return lines;\n}\n\nfunction joinLines(lines: Line[]): string {\n return lines.map((line) => line.text + line.ending).join('');\n}\n\n/** Insert text right before the first line whose text contains `marker`. */\nfunction insertBeforeFirstLineContaining(content: string, marker: string, insert: string): string {\n const lines = splitLines(content);\n for (let index = 0; index < lines.length; index += 1) {\n if (!lines[index]!.text.includes(marker)) continue;\n const head = joinLines(lines.slice(0, index));\n const tail = joinLines(lines.slice(index));\n return head + insert + tail;\n }\n return content;\n}\n\n/** Insert text right after the first line whose text contains `marker`. */\nfunction insertAfterFirstLineContaining(content: string, marker: string, insert: string): string {\n const lines = splitLines(content);\n for (let index = 0; index < lines.length; index += 1) {\n if (!lines[index]!.text.includes(marker)) continue;\n const head = joinLines(lines.slice(0, index + 1));\n const tail = joinLines(lines.slice(index + 1));\n return head + insert + tail;\n }\n return content;\n}\n\n/** Inject the ObjC decryptor into `.pch` content (before the first `#endif`). */\nexport function injectObjcStringDecryptor(content: string, encryptionNum: number, encryptedName: string = DEFAULT_OBJC_NAME): string {\n return insertBeforeFirstLineContaining(content, '#endif', objcStringDecryptor(encryptionNum, encryptedName) + '\\n');\n}\n\n/** Inject the Swift decryptor into content (after the first `import` line). */\nexport function injectSwiftStringDecryptor(content: string, encryptionNum: number, encryptedName: string = DEFAULT_SWIFT_NAME): string {\n return insertAfterFirstLineContaining(content, 'import ', swiftStringDecryptor(encryptionNum, encryptedName));\n}\n\n/** Decide whether an ObjC line must be skipped by string encryption. */\nexport function isPassObjCEncryptString(line: string): boolean {\n const noSpaceLine = line.replace(/\\s+/g, '');\n if (line.includes('@\"\\\\\"')) return true;\n if (isContainChinese(line)) return true;\n if (line.includes('NSLog(@\"')) return true;\n if (line.includes('static ')) return true;\n if (line.includes('const ')) return true;\n if (line.includes('\"@\"')) return true;\n if (line.includes('%p')) return true;\n if (line.includes('\"GMT\"') || line.includes(\"'GMT'\")) return true;\n if (line.includes('\"en_US\"') || line.includes(\"'en_US'\")) return true;\n if (line.includes('HH:mm:ss')) return true;\n if (line.includes('yyyy-MM-dd')) return true;\n if (line.includes('\"utf-8\\\\')) return true;\n if (line.includes('@\";\"')) return true;\n if (line.includes('@\"text/\"')) return true;\n if (line.includes('@\"application/json\"')) return true;\n if (line.includes('@\"application/xml\"')) return true;\n if (line.includes('=%i,')) return true;\n if (line.includes('CFSTR(')) return true;\n if (line.includes('objc_') || line.includes('OBJC_')) return true;\n if (line.startsWith('#define ')) return true;\n if (line.endsWith('\"')) return true;\n if (line.includes(' case ')) return true;\n if (noSpaceLine.startsWith('NSAssert(')) return true;\n if (line.includes('Log(@\"')) return true;\n if (line.includes('LogDebug(')) return true;\n if (line.includes('@\"#@\"')) return true;\n return false;\n}\n\n/** Decide whether an ObjC line is eligible for string encryption. */\nexport function isCanMissObjCCodeInLine(line: string): boolean {\n if (line.includes('NSLog')) return false;\n if (line.includes('static')) return false;\n if (line.includes('const')) return false;\n if (line.includes('¥')) return false;\n if (line.includes('$')) return false;\n if (isContainChinese(line)) return false;\n return true;\n}\n\n/** Rewrite `@\"...\"` -> `NAME(\"...\")`, skipping `%` bodies. */\nexport function replaceObjCQuotes(input: string, encryptedName: string): string {\n return input.replace(/@\"([^\"]*)\"/g, (whole, content: string) =>\n content.includes('%') ? whole : `${encryptedName}(\"${content}\")`,\n );\n}\n\nconst FORMAT_LITERAL_PATTERN = /@\"[^\"]*%@[^\"]*\"|@\"[^\"]*%s[^\"]*\"/g;\n\n/**\n * ObjC line transform: protect `%@`/`%s` literals with\n * placeholders, replace remaining `@\"...\"` literals, restore placeholders, then\n * drop the `@` in front of any pre-existing `NAME(` call.\n */\nexport function missObjCCodeInLine(line: string, encryptedName: string): string {\n const protectedMatches: string[] = [];\n let result = line.replace(FORMAT_LITERAL_PATTERN, (match) => {\n const placeholder = `【###@@@###】=【${protectedMatches.length}】`;\n protectedMatches.push(match);\n return placeholder;\n });\n result = replaceObjCQuotes(result, encryptedName);\n protectedMatches.forEach((original, index) => {\n result = result.replace(`【###@@@###】=【${index}】`, original);\n });\n result = result.replaceAll(`@${encryptedName}(`, `${encryptedName}(`);\n return result;\n}\n\n/** Byte-XOR array body for an ObjC `NAME(((char []) {...}))` literal (port of replace_clear_text_string). */\nexport function getObjcCharArray(content: string, encryptionNum: number, isMm = false): string {\n const bytes = Buffer.from(content, 'utf8');\n const items = [...bytes].map((byte) => {\n const value = String(byte ^ encryptionNum);\n return isMm ? `static_cast<char>(${value})` : value;\n });\n items.push(isMm ? 'static_cast<char>(0)' : '0');\n return `((char []) {${items.join(', ')}})`;\n}\n\n/** Whole-file ObjC pass: `NAME(\"...\")`/`NNC_CSTRING(\"...\")` -> `NAME(((char []) {...}))`. */\nexport function obfuscateObjCStringLiterals(\n content: string,\n encryptionNum: number,\n encryptedName: string = DEFAULT_OBJC_NAME,\n isMm = false,\n): string {\n const pattern = new RegExp(`(${escapeRegExp(encryptedName)}\\\\(|NNC_CSTRING\\\\()\"(.*?)\"(\\\\))`, 'g');\n return content.replace(pattern, (_whole, prefix: string, body: string, suffix: string) =>\n `${prefix}${getObjcCharArray(body, encryptionNum, isMm)}${suffix}`,\n );\n}\n\n/**\n * Full ObjC `.m`/`.mm` transform: line-based eligibility/skip pass followed by the\n * whole-file `obfuscate` pass, producing the final `NAME(((char []) {...}))` form.\n */\nexport function encryptObjcStringLiterals(\n content: string,\n options: StringEncryptOptions & { isMm?: boolean } = {},\n): string {\n const key = resolveEncryptionNum(options.seed ?? '', options.encryptionNum);\n const name = options.encryptedName ?? DEFAULT_OBJC_NAME;\n const isMm = options.isMm ?? false;\n let isAddCodeAtM = false;\n const lines = splitLines(content);\n const out: Line[] = [];\n for (const line of lines) {\n if (line.text.includes('@interface') || line.text.includes('@implementation')) {\n out.push(line);\n isAddCodeAtM = true;\n continue;\n }\n if (isPassObjCEncryptString(line.text)) {\n out.push(line);\n continue;\n }\n if (!isAddCodeAtM) {\n out.push(line);\n continue;\n }\n if (line.text.includes('@\"') && isCanMissObjCCodeInLine(line.text)) {\n out.push({ text: missObjCCodeInLine(line.text, name), ending: line.ending });\n continue;\n }\n out.push(line);\n }\n return obfuscateObjCStringLiterals(joinLines(out), key, name, isMm);\n}\n\n/** Decide whether a Swift line is eligible for string encryption. */\nexport function isCanMissSwiftCodeInLine(line: string): boolean {\n const stripped = line.trim();\n if (stripped.startsWith('print(')) return false;\n if (stripped.startsWith('debugPrint(')) return false;\n if (stripped.startsWith('#warning')) return false;\n if (stripped.includes('\"\"\"')) return false;\n if (stripped.includes('\\\\(')) return false;\n if (stripped.includes(' case ')) return false;\n return true;\n}\n\n/** Byte-XOR Swift array string `[b0, b1, ...]` (port of getSwiftEncryptCode). */\nexport function getSwiftEncryptCode(content: string, encryptionNum: number): string {\n const bytes = Buffer.from(content, 'utf8');\n return `[${[...bytes].map((byte) => String(byte ^ encryptionNum)).join(', ')}]`;\n}\n\n/** Swift line transform: rewrite eligible `\"...\"` literals. */\nexport function missSwiftCodeInLine(line: string, encryptionNum: number, encryptedName: string): string {\n const specialKey = '^$$$^';\n let result = line.replaceAll('\"\"', specialKey);\n const items: string[] = [];\n for (const match of result.matchAll(/\"(.+?)\"/g)) items.push(match[1]!);\n for (const item of items) {\n if (item.startsWith('\\\\') || item.startsWith('\"') || item.length === 1 || item.startsWith('%')) continue;\n const encrypted = `${encryptedName}(ucharArr: ${getSwiftEncryptCode(item, encryptionNum)})`;\n result = result.split(`\"${item}\"`).join(encrypted);\n }\n result = result.replaceAll(specialKey, '\"\"');\n return result;\n}\n\n/** Full Swift transform: inject the decryptor after `import`, then encrypt literals. */\nexport function encryptSwiftStringLiterals(\n content: string,\n options: StringEncryptOptions = {},\n): string {\n const key = resolveEncryptionNum(options.seed ?? '', options.encryptionNum);\n const name = options.encryptedName ?? DEFAULT_SWIFT_NAME;\n let injected = false;\n let out = '';\n for (const line of splitLines(content)) {\n if (!injected && line.text.includes('import ')) {\n out += line.text + line.ending;\n out += swiftStringDecryptor(key, name);\n injected = true;\n continue;\n }\n if (line.text.includes('\"') && isCanMissSwiftCodeInLine(line.text)) {\n out += missSwiftCodeInLine(line.text, key, name) + line.ending;\n } else {\n out += line.text + line.ending;\n }\n }\n return out;\n}\n\n/**\n * Batch project transform. `.pch` files receive the ObjC decryptor; `.m`/`.mm`\n * files get their string literals encrypted; `.swift` files get the decryptor\n * injected plus encrypted literals. Returns the uniform TransformOutcome shape.\n */\nexport function applyStringEncryption(root: string, options: StringEncryptOptions = {}): TransformOutcome {\n const key = resolveEncryptionNum(options.seed ?? '', options.encryptionNum);\n const changedPaths: string[] = [];\n let changed = 0;\n for (const entry of walkProject(root).files) {\n const ext = extension(entry.relative);\n const before = readFileSync(entry.absolute, 'utf8');\n let after: string;\n if (ext === 'pch') {\n after = injectObjcStringDecryptor(before, key, options.encryptedName);\n } else if (ext === 'm' || ext === 'mm') {\n after = encryptObjcStringLiterals(before, { encryptionNum: key, encryptedName: options.encryptedName, isMm: ext === 'mm' });\n } else if (ext === 'swift') {\n after = encryptSwiftStringLiterals(before, { encryptionNum: key, encryptedName: options.encryptedName });\n } else {\n continue;\n }\n if (after !== before) {\n writeIfChanged(entry, after);\n changed += 1;\n changedPaths.push(entry.relative);\n }\n }\n return { changed, changedPaths };\n}\n"],"mappings":"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"}
|
|
@@ -4,15 +4,50 @@
|
|
|
4
4
|
"allowLocalExecution": false,
|
|
5
5
|
"allowCloudBuild": false,
|
|
6
6
|
"packageTarget": "aab",
|
|
7
|
-
"
|
|
7
|
+
"obfuscation": {
|
|
8
|
+
"options": {
|
|
9
|
+
"code": {
|
|
10
|
+
"removeComments": true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"executionEnv": [
|
|
15
|
+
"ANDROID_HOME",
|
|
16
|
+
"ANDROID_SERIAL"
|
|
17
|
+
],
|
|
8
18
|
"verification": {
|
|
9
|
-
"prerequisites": [
|
|
10
|
-
|
|
11
|
-
|
|
19
|
+
"prerequisites": [
|
|
20
|
+
[
|
|
21
|
+
"java",
|
|
22
|
+
"-version"
|
|
23
|
+
],
|
|
24
|
+
[
|
|
25
|
+
"adb",
|
|
26
|
+
"version"
|
|
27
|
+
]
|
|
28
|
+
],
|
|
29
|
+
"checks": [
|
|
30
|
+
[
|
|
31
|
+
"./gradlew",
|
|
32
|
+
":app:lintDebug",
|
|
33
|
+
":app:testDebugUnitTest"
|
|
34
|
+
]
|
|
35
|
+
],
|
|
36
|
+
"runtime": [
|
|
37
|
+
[
|
|
38
|
+
"./gradlew",
|
|
39
|
+
":app:connectedDebugAndroidTest"
|
|
40
|
+
]
|
|
41
|
+
]
|
|
12
42
|
},
|
|
13
43
|
"packaging": {
|
|
14
44
|
"provider": "local",
|
|
15
|
-
"commands": [
|
|
45
|
+
"commands": [
|
|
46
|
+
[
|
|
47
|
+
"./gradlew",
|
|
48
|
+
":app:bundleRelease"
|
|
49
|
+
]
|
|
50
|
+
],
|
|
16
51
|
"artifactPath": "app/build/outputs/bundle/release/app-release.aab"
|
|
17
52
|
}
|
|
18
53
|
}
|