Elvadult pillanatomban a Mach-O file formátumának leírását kerestem. Nem kellett sokáig kutakodnom, mert nyílt és az Apple szépen leírja az elképzeléseit és bárki számára elérhető itt.
Ami felkeltette az érdeklődésemet éppen a dokumentum végén található: Universal Binaries. Az unverzális bináris állományok lényege, hogy több architektúrára fordított program egy file-ban helyezkedik el és az operációs rendszer a neki megfelelőt tölti be és futtatja.
Az univerzális állományok 0xCAFEBABE - small endian - intereg szignaturával kezdődnek és a következő integer a különböző architekturális bináris képállományok számát adja meg.
{
uint32_t magic;
uint32_t nfat_arch;
};
Ez után szerepel nfat_arch számú fat_arch struktúra.
{
cpu_type_t cputype;
cpu_subtype_t cpusubtype;
uint32_t offset;
uint32_t size;
uint32_t align;
};
Most a CPU típus az érdekes csupán, az altípus lényegtelen. A típusleíró intereg első 24 bitjére kell koncentrálnunk; ez fölött (a 24. bit) jelzi, hogy 64 bites. A következő értékek fordulhatnak elő:
- 7: x86
- 12: ARM (iPhone!!!)
- 18: PPC
Az offset és size a megfelelő állománykép helyét adja meg a file-ban.
Ezen a ponton gondolkoztam el: ha Intel (én spec. AMD) processzoron futtatom a Leopard-ot, akkor szükségem van a PPC-s programrészletekre? Nem. Tehát ha a belső képállományt kiemelném, akkor működőképes maradna a program? A Mach-O szabványleírás szerint igen.
Egy próbától senkinek sem eshet baja. Erre dobtam össze egy egyszerű programot Java-ban.
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class TheBiggestLoser {
private final static String ParIntel = "Intel";
private final static String ParPPC = "PPC";
private final static String ParARM = "ARM";
private final static int aIntel = 7;
private final static int aARM = 12;
private final static int aPPC = 18;
public static void main(String[] args) {
if (args.length != 2 && !(ParIntel.equals(args[0]) || ParPPC.equals(args[0]) || ParARM.equals(args[0]))) {
System.out.println("Usage:\n java -cp ... org.hackstock.tbl.TheBiggestLoser <Intel | PPC | ARM> <dir to scan>");
}else {
int arch = 0;
if (ParIntel.equals(args[0])) {
arch = aIntel;
}else if (ParPPC.equals(args[0])) {
arch = aPPC;
}else {
arch = aARM;
}
File file = new File(args[1]);
if (file.exists()) {
new TheBiggestLoser(arch,file);
}else {
System.out.println("The file or directory doesn't exist.");
}
}
}
private TheBiggestLoser(int arch,File dir) {
if (dir.isFile()) {
processFile(arch, dir);
}else {
System.out.println("Scanning: "+dir.getAbsolutePath());
File[] child = dir.listFiles();
for (int i = 0; i < child.length; ++i) {
if (child[i].isDirectory()) {
new TheBiggestLoser(arch,child[i]);
}else if (child[i].isFile()) {
processFile(arch,child[i]);
}
}
}
}
private void processFile(int arch,File file) {
try {
RandomAccessFile raf = new RandomAccessFile(file,"rw");
try {
if (raf.length() > 8) {
int fat = raf.readInt();
if (fat == 0xcafebabe) {
System.out.print("\tProcessing: "+file.getName()+" ... ");
System.out.print("size: "+(raf.length()+4)+" - ");
int nfatarchs = raf.readInt();
int offset, size;
offset = size = -1;
for (int i = 0; i < nfatarchs; ++i) {
if ((raf.readInt() & 0xFFFFFF) == arch) {
raf.readInt();
offset = raf.readInt();
size = raf.readInt();
raf.readInt();
}else {
raf.readInt();
raf.readInt();
raf.readInt();
raf.readInt();
}
}
if (offset != -1 && size != -1) {
System.out.print("Size of content: "+size);
byte[] buffer = new byte[size];
raf.seek(offset);
raf.read(buffer, 0, size);
raf.seek(0);
raf.write(buffer, 0, size);
raf.setLength(size);
}else {
System.out.print("Specified content not found!");
}
System.out.println();
}
}
} catch (IOException e1) {
e1.printStackTrace();
}
try {
raf.close();
} catch (IOException e) {
}
} catch (FileNotFoundException e) {
}
}
}
A futtatható JAR file-t innen lehet letölteni.
A használatával legyünk óvatosak. Az iTunes-on, Microsoft Messenger-en próbáltam ki és tökéletesen működik. A viszonyleg kevés ellenőrzés miatt lehetőleg ne akadjon össze java file-okkal, mert azokat tönkre teheti.
Működését tekintve egy file-t vagy egy könyvtárat fésül át és minden univerzális binárisból a paraméterben meghatározott speciális binárist emeli ki.
Vigyázat: az eredeti állomány az új tartalommal íródik felül!
Indítása parancssorból a következőképpen: