Cocoa资源文件嵌入是本文要介绍的内容,主要是俩学习Cocoa/iPhone App/静态库 嵌入资源文件 rtb v0.1发布,软件开发中,可能需要把用到的资源文件嵌入到二进制执行文件中,例如生成单个执行文件、防止机密或版权信息被PE工具查看或修改、嵌入图片资源到静态库中等等。
在Mac OSX Cocoa 或iOS开发中,编译生成的Product.app是一个APP包,其实就是个文件夹,右键Show Package Contents或者去掉,app 扩展后双击打开就可以查看包内容,修改包里面的任何资源文件都不会影响程序正常运行,要提交到App Store的程序在修改资源文件后运行下codesign(安装Xcode时已经装了这个dev tool)重签名就可以了:
- $ codesign -fvs "Your Identity" path/to/appfile.app
我之前写的 ElfCodeSigner 就是基于codesign的。
所以嵌入二进制资源文件也算是一种有效的保护手段。
结合我以前Windows开发中用到的措施,我想到了三种方案:
Base64编码。
N久前在CSDN上给一个网友提供的方法,当时他的需求是对一些中英文字符串数据的编解码。我当时给出的Base64方案算是比较简便而且行之有效的。Base64方法同样适用于今天要讨论的嵌入资源问题。
NSString 和 NSData可以互转,UIImage也提供了initWithData:的创建方法,所以将image data 进行base64编码后保存在一个NSString常量中,在用到时进行base64解码再[UIImage initWithData:]。
此方法在处理很小的数据时比较有用,网上也有比较成熟的NSData+Base64类别类供使用。
PNG等资源的压缩加密
经过加密处理的PNG图像用普通看图软件查看时只能看到一片透明或空白图带点斑点这样的无效图,但在程序运行时图像显示正常,这个加密方法在j2me、iOS等游戏和应用开发被大量使用。网上现成的加密工具很多,但可以被简单的逆向还原原图。
转换成bytes数组。
大多数资源嵌入都采用这种方案,原理很简单,把资源文件的每位字符转换成十六进制bytes[],几乎所有语言都提供了bytes[]到data的直接转换。
大多数十六进制编辑器可以将hex结果保存为文件。被解密或修改比较困难,需要知道图形基本格式、熟练UE等十六进制编辑器、猜测、运气等。
最近公司项目中需要把一些图片资源嵌入到静态库中,我考虑了下还是用bytes的方法比较好,Cocoa原生支持其不会留下被修改的余地。一些简单的图标用CG画上去。这样公开出去的库只有一个.a文件和几个必要的.h文件。
下午闲着没事,就操起Xcode在新配的MacBook上写下了我这第一个跑在Mac OS上的C程序。
Release notes
rtb(Resource To Bytes)是一个命令行小工具,将二进制资源文件转换为bytes数组,方便在程序中使用嵌入资源。
由于时间仓促,rtb仅在Mac OS SL上测试过,对应应用在Mac OS桌面程序和iOS App中测试过,以后有时间再改成跨平台的。目前没发现什么bug。
使用方法:打开Terminal,cd到rtb所在目录,运行
- $ ./rtb image.png
将生成
- unsigned char image_png[] = { ..... };
- unsigned int image_png_len = 16045;
变量名称根据资源文件名而来,数字开头的会加前缀 "__" ,文件名中非英文和数字的字符转换为下划线"_"(使用了isalnum()测试函数),在数组变量名称添加"_len"后缀作为数组长度变量名称。
例如"123te的 st5.png"将生成变量名
- unsigned char __123te____st5_png[] 和 unsigned int __123te____st5_png_len
Example
首先执行rtb生成.h文件:
- $ ./rtb test.png > test.png.h
新建一个Window Base的iPhone项目,添加test.png.h文件到项目中,在
- - (BOOL)application: didFinishLaunchingWithOptions:
方法中创建一个UIImage并把它添加到一个ImageView中:
- #import "test.png.h"
- //............
- - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
- {
- [self.window makeKeyAndVisible];
- 8 unsigned char *imgBytes = test_png;
- NSUInteger imgLenght = test_png_len;
- NSData *imgData = [NSData dataWithBytesNoCopy:imgBytes length:imgLenght freeWhenDone:NO];11
- // UIImage *image = [UIImage imageWithData:imgData];
- // or
- UIImage *image = [[UIImage alloc] initWithData:imgData];
- 15 UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
- imageView.frame = self.window.frame;
- imageView.contentMode = UIViewContentModeCenter;
- [self.window addSubview:imageView];
- [imageView release];
- [image release];
- return YES;
- }
因为在程序加载时已经将test.png.h中的数组加载到内存中,所以使用NSData的dataWithBytesNoCopy方法即可,不需要再Copy一份,转换成NSData后也不需要释放它,所以freeWhenDonw参数值NO.
Code Review
我C语言很烂,就不贴代码了。注释、空行加起来80多行,核心功能代码就是fopen这个资源文件:
- if ((fp = fopen(argv[1], "r")) != NULL)
- 从文件头取到(getc(fp))到EOF,fprintf(stdout,"0x%02x",ch):
- for (p = 0; (length < 0 || p < length) && (ch = getc(fp)) != EOF; p++)
- {
- char *c = p ? ",\n " : " ";
- fprintf(fpo, "%s0x%02x", (p % COLS) ? ", " : c, ch);
- }
Download
已收录至Cocoa-Utilities: https://github.com/Sundae/Cocoa-Utilities
小结:详解Cocoa资源文件嵌入的内容介绍完了,希望通过本文的学习能对你有所帮助!