
虽然在开车的时候,多数时间不会停留在carplay的桌面。然而,当第一次切进来的时候显示的那个桌面背景图片,着实不怎么喜欢。于是就想着能够换掉这个壁纸。
网上搜了一下,基本都是一年前的文章,或者说最新的文章都是一年前的。这就比较尴尬了。而至于实现工具和方法,到处都是抄来抄去的文章,第一步基本都是安装巨魔助手,Troll app,https://trollstore.app这是一个越狱的应用商店。通过这个越狱的商店安装AirWall,在air wall里面设置壁纸。https://onejailbreak.com/blog/airwall/
这一切看起来似乎完美,但是,这个troll store app 最高支持到ios 17,我现在的18没有越狱,也不想越狱。那么又该怎么搞呢?

自然是自签名,目前爱思助手之类的貌似不支持普通的appstore账号签名安装了。不过可以通过下面的工具,签名应用然后通过爱思助手安装。

Esign iPA download and install using Sideloaly App.Download Esign IPA:First, download the Esign IPA file for your PC.Download Esign iPA file.Install Sideloadly:Sideloadly is the tool we’ll use to install Esign on your iOS device.If you don’t have Sideloadly yet, download and install Sideloadly app on your PC(Windows or Mac).Connect Your Device:Connect your iPhone or iPad to your computer using a USB cable.Open Sideloadly:Launch the Sideloadly application on your computer.Select the IPA File:In Sideloadly, click on the IPA icon to select the Esign IPA file you downloaded earlier.Sign the IPA File:Enter your Apple ID and password when prompted. This step is necessary for signing the IPA file.Start Installation:Click the Start button in Sideloadly to begin the installation process.Sideloadly will sign the Esign IPA file and install it on your iOS device.Check Your Home Screen:Once the process is complete, you’ll find the Esign app icon on your home screen.Then go to Settings App → General → Profiles & Device Management → Find the Esign app profile and trust it.You can now use Esign to sign and install other IPA files directly on your device.
然而,签名安装之后却发现了另外一个问题,那就是卡在了加载界面。一直在获取目录,后面就进行不下去了。

上网搜了一下,好无进展,都是说什么连接carplay之后在设置,然而,这就是句废话。连上了也没什么用。不过,这个app体积不大,直接拉出来。扔到hopper里面看下实现逻辑,也并不复杂。直接通过目录来获取的当前壁纸,同样替换壁纸也是直接写入文件实现的。
看F5之后的代码就更直观了。

导出代码,直接扔给cursor,让cursor根据f5代码拆分重构代码。

现在代码逻辑就明朗了,更换壁纸的方法主要在AirawWallpaper.m中:
#pragma mark - Helper Methods
- (void)checkingPath { // 检查壁纸路径 NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *basePath = @"/var/mobile/Containers/Data/Application/";
// 检查是否有权限访问
if (![fileManager isWritableFileAtPath:basePath]) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"权限错误"
message:@"无法访问系统目录,请确保设备已越狱并授予了正确的权限。"
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
return;
}
NSError *error = nil;
NSArray *contents = [fileManager contentsOfDirectoryAtPath:basePath error:&error];
if (error) {
NSLog(@"Error reading directory: %@", error);
return;
}
NSMutableArray *validPaths = [NSMutableArray array];
for (NSString *path in contents) {
if ([path containsString:@"com.apple.CarPlayApp.wallpaper-images"]) {
NSString *fullPath = [basePath stringByAppendingPathComponent:path];
BOOL isDirectory;
if ([fileManager fileExistsAtPath:fullPath isDirectory:&isDirectory] && isDirectory) {
[validPaths addObject:path];
}
}
}
if (validPaths.count > 0) {
self.FullCache = [basePath stringByAppendingPathComponent:validPaths[0]];
[self.tableView reloadData];
} else {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"错误"
message:@"未找到 CarPlay 壁纸目录,请确保已正确安装 CarPlay 应用。"
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
}
}
当然,上面这段代码的错误提示是我让cursor加上的。原来的并没有这段,这个是f5的代码:
int ___30-[AirawWallpaper checkingPath]_block_invoke(int arg0) {
r31 = r31 - 0xc0;
saved_fp = r29;
stack[-8] = r30;
var_80 = arg0;
var_18 = [[NSFileManager defaultManager] retain];
r0 = [var_18 enumeratorAtPath:@"/var/mobile/Containers/Data/Application/"];
r29 = &saved_fp;
var_20 = [r0 retain];
var_28 = [@"" retain];
do {
r0 = [var_20 nextObject];
r29 = r29;
r0 = [r0 retain];
r8 = var_28;
var_28 = r0;
[r8 release];
if (r0 == 0x0) {
break;
}
if ([var_28 rangeOfString:@"com.apple.CarPlayApp.wallpaper-images"] == 0x7fffffffffffffff) {
continue;
}
r0 = [@"/var/mobile/Containers/Data/Application/" stringByAppendingPathComponent:var_28];
r29 = r29;
[var_18 fileExistsAtPath:[r0 retain] isDirectory:r29 - 0x29];
if ((var_29 & 0x1) != 0x0) {
[*(var_80 + 0x20) addObject:var_28];
}
objc_storeStrong(r29 - 0x48, 0x0);
} while (true);
r11 = *(var_80 + 0x28);
*(&var_78 + 0x10) = 0x100007ae0;
*(&var_78 + 0x18) = 0x100014200;
*(&var_78 + 0x20) = [*(var_80 + 0x30) retain];
*(&var_78 + 0x28) = [*(var_80 + 0x20) retain];
dispatch_async(r11, &var_78);
objc_storeStrong(&var_78 + 0x28, 0x0);
objc_storeStrong(&var_78 + 0x20, 0x0);
objc_storeStrong(r29 - 0x28, 0x0);
objc_storeStrong(r29 - 0x20, 0x0);
r0 = objc_storeStrong(r29 - 0x18, 0x0);
return r0;
}
不过现在,也能看出问题出在什么地方了。/var/mobile/Containers/Data/Application/这个目录,普通的app是没有足够的全项访问的。需要申请特殊的权限,直接让cursor创建权限申请的Entitlements:
既然添加了自定义的权限,那么使用这些签名工具就没办法写入权限文件了,需要自己来实现签名,具体的实现方法可以参考:iOS 签名杂谈(一):https://h4ck.org.cn/2020/06/7112iOS 签名杂谈(二): https://h4ck.org.cn/2020/06/7130
虽然之前做个一个签名的图形界面工具,但是引入了一个库不支持arm架构,也懒得再更新了。现在就只能创建签名脚本签名了,来回修改多次之后,让cursor创建了一个自签名脚本,sign_with_args.sh。通过这个东西就可以快速签名文件了。
签名脚本代码:
#!/bin/bash
变量声明(无默认值)
MOBILEPROVISION=""
APP_PATH=""
CERTIFICATE=""
BUNDLE_ID=""
TEAM_ID=""
DEVICE_UDID=""
ENTITLEMENTS_FILE="" # 新增:自定义 Entitlements 文件路径
显示帮助信息
show_help() {
echo "用法: $0 [选项]"
echo "选项:"
echo " -p, --provision mobileprovision文件路径 (必需)"
echo " -a, --app-path 应用路径 (必需)"
echo " -c, --certificate 证书名称 (必需)"
echo " -b, --bundle-id Bundle ID (必需)"
echo " -t, --team-id Team ID (必需)"
echo " -d, --device-udid 设备UDID (必需)"
echo " -e, --entitlements Entitlements文件路径 (可选,默认从mobileprovision提取)"
echo " -h, --help 显示此帮助信息"
exit 0
}
检查必需参数
check_required_params() {
local missing=0
if [ -z "APP_PATH" ]; then
echo "错误: 缺少应用路径 (-a)"
missing=1
fi
if [ -z "BUNDLE_ID" ]; then
echo "错误: 缺少 Bundle ID (-b)"
missing=1
fi
if [ -z "DEVICE_UDID" ]; then
echo "错误: 缺少设备 UDID (-d)"
missing=1
fi
if [ $missing -eq 1 ]; then
echo "请使用 -h 或 --help 查看帮助信息"
exit 1
fi
}
解析命令行参数
while [[ 1 in
-p|--provision)
MOBILEPROVISION="2"
shift 2
;;
-c|--certificate)
CERTIFICATE="2"
shift 2
;;
-t|--team-id)
TEAM_ID="2"
shift 2
;;
-e|--entitlements)
ENTITLEMENTS_FILE="1"
show_help
;;
esac
done
检查必需参数
check_required_params
清理函数
cleanup() {
echo "清理临时文件..."
rm -f temp_entitlements.plist
}
错误处理
handle_error() {
echo "错误: $1"
cleanup
exit 1
}
检查文件是否存在
if [ ! -f "MOBILEPROVISION"
fi
检查目录是否存在
if [ ! -d "APP_PATH"
fi
如果提供了自定义 Entitlements 文件,检查其是否存在
if [ ! -z "ENTITLEMENTS_FILE" ]; then
handle_error "Entitlements 文件不存在: $ENTITLEMENTS_FILE"
fi
验证配置文件
echo "验证配置文件..."
PROFILE_INFO=MOBILEPROVISION")
if [ $? -ne 0 ]; then
handle_error "配置文件无效"
fi
提取信息
echo "正在从 mobileprovision 提取信息..."
PROFILE_TEAM_ID=PROFILE_INFO" | plutil -extract TeamIdentifier.0 raw -)
PROFILE_APP_ID=PROFILE_INFO" | plutil -extract Entitlements.application-identifier raw -)
PROFILE_EXPIRATION=PROFILE_INFO" | plutil -extract ExpirationDate raw -)
echo "Profile Team ID: PROFILE_APP_ID"
echo "Profile Expiration: $PROFILE_EXPIRATION"
验证 Team ID
if [ "TEAM_ID" ]; then
echo "警告: Team ID 不匹配"
echo "Profile中的: TEAM_ID"
handle_error "请确保使用正确的 mobileprovision 文件"
fi
验证设备 UDID
echo "验证设备 UDID..."
if ! security cms -D -i "DEVICE_UDID"; then
echo "错误: 设备 UDID MOBILEPROVISION" | grep -A 20 ProvisionedDevices
handle_error "设备未授权"
fi
echo "设备 UDID 验证通过: $DEVICE_UDID"
设置完整的 Bundle ID
FULL_BUNDLE_ID="FULL_BUNDLE_ID"
清理旧的签名
echo "清理旧的签名..."
rm -rf "$APP_PATH/_CodeSignature" 2>/dev/null
复制 mobileprovision 并设置权限
echo "复制 mobileprovision..."
cp "APP_PATH/embedded.mobileprovision"
chmod 644 "$APP_PATH/embedded.mobileprovision"
处理 Entitlements
if [ ! -z "ENTITLEMENTS_FILE"
cp "MOBILEPROVISION" | plutil -extract Entitlements xml1 -o - - > temp_entitlements.plist
fi
显示 entitlements 内容
echo "Entitlements 内容:"
plutil -p temp_entitlements.plist
设置正确的文件权限
echo "设置文件权限..."
chmod -R 755 "APP_PATH" -type f -exec chmod 644 {} \;
find "APP_PATH/AirWall" ] && chmod 755 "APP_PATH/AirWallHelper" ] && chmod 755 "APP_PATH/trollstorehelper" ] && chmod 755 "$APP_PATH/trollstorehelper"
移除空文件(特别是0字节的PNG文件)
echo "移除空文件..."
find "$APP_PATH" -type f -size 0 -delete
echo "已移除空文件"
修正Info.plist中的Bundle ID
echo "修正Info.plist中的Bundle ID..."
plutil -replace CFBundleIdentifier -string "APP_PATH/Info.plist"
echo "Bundle ID已设置为: APP_PATH/Info.plist")"
对可执行文件进行签名
echo "对可执行文件进行签名..."
if [ -f "CERTIFICATE" --entitlements temp_entitlements.plist "$APP_PATH/AirWallHelper" || handle_error "AirWallHelper 签名失败"
fi
if [ -f "CERTIFICATE" --entitlements temp_entitlements.plist "$APP_PATH/trollstorehelper" || handle_error "trollstorehelper 签名失败"
fi
对所有的 frameworks 和 dylibs 进行签名
echo "签名 frameworks 和 dylibs..."
find "file"
codesign -f -s "file" || handle_error "Framework/dylib 签名失败: $file"
done
对整个应用进行签名
echo "对整个应用进行签名..."
codesign -f -s "APP_PATH" || handle_error "应用签名失败"
验证签名
echo "验证签名..."
codesign -vv -d "$APP_PATH"
检查_CodeSignature目录
echo "检查_CodeSignature目录..."
if [ -d "APP_PATH/_CodeSignature/"
else
echo " _CodeSignature 目录不存在,这可能导致安装失败"
fi

显示更多签名信息
echo "显示详细签名信息..."
codesign -d --entitlements :- "$APP_PATH"
验证所有签名
echo "验证所有签名..."
if codesign --verify --deep --strict --verbose=4 "$APP_PATH"; then
echo "✓ 严格验证通过"
else
echo " 严格验证失败,但基本签名可能仍然有效"
fi

清理临时文件
cleanup
重新打包
echo "重新打包..."
rm -f signed_AirWall.ipa
zip -qry signed_AirWall.ipa Payload
echo "签名完成!"
然而,在测试的时候发现com.apple.private私有权限,签名之后安装全部被拒绝了。那么,也就是说目前是没办法在非越狱系统访问这个文件的,自然也就没法更改壁纸。
原来是想把代码开源的,结果上传之后把自己的证书也放进去了。所以就没发开园啦,主要原理也说了,结论就是,目前不越狱是没办法修改carplay的壁纸的,所以可以不用尝试了。
附视频链接: