mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
Candidate release of source code.
This commit is contained in:
parent
db81e6b3b0
commit
79d8f164f8
12449 changed files with 2800756 additions and 16 deletions
2
GPL/DMG/Module.manifest
Normal file
2
GPL/DMG/Module.manifest
Normal file
|
@ -0,0 +1,2 @@
|
|||
# GPL because we have linked to GPL hsfx.jar
|
||||
MODULE FILE LICENSE: data/lib/dmg.jar GPL 3
|
54
GPL/DMG/build.gradle
Normal file
54
GPL/DMG/build.gradle
Normal file
|
@ -0,0 +1,54 @@
|
|||
apply plugin: 'eclipse'
|
||||
eclipse.project.name = 'GPL DMG'
|
||||
|
||||
/*********************************************************************************
|
||||
*
|
||||
* Define a new source set for dmg source because it is not part of Ghidra, it is
|
||||
* a standalone application that is executed and called from Ghidra.
|
||||
*
|
||||
* see DmgServerProcessManager
|
||||
*
|
||||
*********************************************************************************/
|
||||
sourceSets {
|
||||
dmg {
|
||||
java {
|
||||
srcDir 'src/dmg/java'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eclipse.classpath.plusConfigurations += [configurations.dmgCompile]
|
||||
|
||||
dependencies {
|
||||
dmgCompile ':csframework@jar'
|
||||
dmgCompile ':hfsx@jar'
|
||||
dmgCompile ':hfsx_dmglib@jar'
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
*
|
||||
* Task to create the dmg.jar file
|
||||
*
|
||||
***************************************************************************************/
|
||||
task dmgJar(type: Jar) {
|
||||
from sourceSets.dmg.output
|
||||
destinationDir = file("build/data/lib")
|
||||
baseName = 'dmg'
|
||||
}
|
||||
|
||||
jar {
|
||||
doLast {
|
||||
File f = file("build/libs/DMG.jar")
|
||||
delete "build/libs"
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
*
|
||||
* plugin the jar task into global task for building and zipping contribs
|
||||
*
|
||||
***************************************************************************************/
|
||||
assemble.dependsOn dmgJar
|
||||
|
||||
|
20
GPL/DMG/certification.manifest
Normal file
20
GPL/DMG/certification.manifest
Normal file
|
@ -0,0 +1,20 @@
|
|||
##VERSION: 2.0
|
||||
##MODULE IP: GPL 3
|
||||
##MODULE IP: LGPL 2.1
|
||||
##MODULE IP: Public Domain
|
||||
.classpath||Public Domain||||END|
|
||||
.project||Public Domain||||END|
|
||||
Module.manifest||Public Domain||||END|
|
||||
build.gradle||Public Domain||||END|
|
||||
data/lib/catacombae_csframework.jar||LGPL 2.1||||END|
|
||||
data/lib/catacombae_hfsx.jar||GPL 3||||END|
|
||||
data/lib/catacombae_hfsx_dmglib.jar||GPL 3||||END|
|
||||
data/lib/catacombae_iharder-base64.jar||GPL 3||||END|
|
||||
data/lib/hfsexplorer-0_21-src.zip||GPL 3||||END|
|
||||
data/os/win32/llio_amd64.dll||GPL 3||||END|
|
||||
data/os/win32/llio_i386.dll||GPL 3||||END|
|
||||
data/os/win32/llio_ia64.dll||GPL 3||||END|
|
||||
data/os/win64/llio_amd64.dll||GPL 3||||END|
|
||||
data/os/win64/llio_i386.dll||GPL 3||||END|
|
||||
data/os/win64/llio_ia64.dll||GPL 3||||END|
|
||||
data/server_memory.cfg||Public Domain||||END|
|
BIN
GPL/DMG/data/lib/catacombae_csframework.jar
Normal file
BIN
GPL/DMG/data/lib/catacombae_csframework.jar
Normal file
Binary file not shown.
BIN
GPL/DMG/data/lib/catacombae_hfsx.jar
Normal file
BIN
GPL/DMG/data/lib/catacombae_hfsx.jar
Normal file
Binary file not shown.
BIN
GPL/DMG/data/lib/catacombae_hfsx_dmglib.jar
Normal file
BIN
GPL/DMG/data/lib/catacombae_hfsx_dmglib.jar
Normal file
Binary file not shown.
BIN
GPL/DMG/data/lib/catacombae_iharder-base64.jar
Normal file
BIN
GPL/DMG/data/lib/catacombae_iharder-base64.jar
Normal file
Binary file not shown.
BIN
GPL/DMG/data/lib/hfsexplorer-0_21-src.zip
Normal file
BIN
GPL/DMG/data/lib/hfsexplorer-0_21-src.zip
Normal file
Binary file not shown.
BIN
GPL/DMG/data/os/win32/llio_amd64.dll
Normal file
BIN
GPL/DMG/data/os/win32/llio_amd64.dll
Normal file
Binary file not shown.
BIN
GPL/DMG/data/os/win32/llio_i386.dll
Normal file
BIN
GPL/DMG/data/os/win32/llio_i386.dll
Normal file
Binary file not shown.
BIN
GPL/DMG/data/os/win32/llio_ia64.dll
Normal file
BIN
GPL/DMG/data/os/win32/llio_ia64.dll
Normal file
Binary file not shown.
BIN
GPL/DMG/data/os/win64/llio_amd64.dll
Normal file
BIN
GPL/DMG/data/os/win64/llio_amd64.dll
Normal file
Binary file not shown.
BIN
GPL/DMG/data/os/win64/llio_i386.dll
Normal file
BIN
GPL/DMG/data/os/win64/llio_i386.dll
Normal file
Binary file not shown.
BIN
GPL/DMG/data/os/win64/llio_ia64.dll
Normal file
BIN
GPL/DMG/data/os/win64/llio_ia64.dll
Normal file
Binary file not shown.
1
GPL/DMG/data/server_memory.cfg
Normal file
1
GPL/DMG/data/server_memory.cfg
Normal file
|
@ -0,0 +1 @@
|
|||
2048
|
|
@ -0,0 +1,116 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.btree;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import mobiledevices.dmg.ghidra.GBinaryReader;
|
||||
|
||||
/**
|
||||
* Represents a BTHeaderRec structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/xnu/xnu-792/bsd/hfs/hfs_format.h.auto.html">hfs/hfs_format.h</a>
|
||||
*/
|
||||
public class BTreeHeaderRecord /*implements StructConverter*/ {
|
||||
|
||||
private short treeDepth;
|
||||
private int rootNode;
|
||||
private int leafRecords;
|
||||
private int firstLeafNode;
|
||||
private int lastLeafNode;
|
||||
private short nodeSize;
|
||||
private short maxKeyLength;
|
||||
private int totalNodes;
|
||||
private int freeNodes;
|
||||
private short reserved1;
|
||||
private int clumpSize;
|
||||
private byte btreeType;
|
||||
private byte keyCompareType;
|
||||
private int attributes;
|
||||
private int[] reserved;
|
||||
|
||||
BTreeHeaderRecord(GBinaryReader reader) throws IOException {
|
||||
this.treeDepth = reader.readNextShort();
|
||||
this.rootNode = reader.readNextInt();
|
||||
this.leafRecords = reader.readNextInt();
|
||||
this.firstLeafNode = reader.readNextInt();
|
||||
this.lastLeafNode = reader.readNextInt();
|
||||
this.nodeSize = reader.readNextShort();
|
||||
this.maxKeyLength = reader.readNextShort();
|
||||
this.totalNodes = reader.readNextInt();
|
||||
this.freeNodes = reader.readNextInt();
|
||||
this.reserved1 = reader.readNextShort();
|
||||
this.clumpSize = reader.readNextInt();
|
||||
this.btreeType = reader.readNextByte();
|
||||
this.keyCompareType = reader.readNextByte();
|
||||
this.attributes = reader.readNextInt();
|
||||
this.reserved = reader.readNextIntArray(16);
|
||||
}
|
||||
|
||||
public short getTreeDepth() {
|
||||
return treeDepth;
|
||||
}
|
||||
|
||||
public int getRootNode() {
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
public int getLeafRecords() {
|
||||
return leafRecords;
|
||||
}
|
||||
|
||||
public int getFirstLeafNode() {
|
||||
return firstLeafNode;
|
||||
}
|
||||
|
||||
public int getLastLeafNode() {
|
||||
return lastLeafNode;
|
||||
}
|
||||
|
||||
public short getNodeSize() {
|
||||
return nodeSize;
|
||||
}
|
||||
|
||||
public short getMaxKeyLength() {
|
||||
return maxKeyLength;
|
||||
}
|
||||
|
||||
public int getTotalNodes() {
|
||||
return totalNodes;
|
||||
}
|
||||
|
||||
public int getFreeNodes() {
|
||||
return freeNodes;
|
||||
}
|
||||
|
||||
public short getReserved1() {
|
||||
return reserved1;
|
||||
}
|
||||
|
||||
public int getClumpSize() {
|
||||
return clumpSize;
|
||||
}
|
||||
|
||||
public byte getBtreeType() {
|
||||
return btreeType;
|
||||
}
|
||||
|
||||
public byte getKeyCompareType() {
|
||||
return keyCompareType;
|
||||
}
|
||||
|
||||
public int getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public int[] getReserved() {
|
||||
return reserved;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
// return StructConverterUtil.toDataType( this );
|
||||
// }
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.btree;
|
||||
|
||||
/**
|
||||
* Represents a BTHeaderRec attributes.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/xnu/xnu-792/bsd/hfs/hfs_format.h.auto.html">hfs/hfs_format.h</a>
|
||||
*/
|
||||
public final class BTreeHeaderRecordAttributes {
|
||||
|
||||
public final static int kBTBadCloseMask = 0x00000001;
|
||||
public final static int kBTBigKeysMask = 0x00000002;
|
||||
public final static int kBTVariableIndexKeysMask = 0x00000004;
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.btree;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import mobiledevices.dmg.ghidra.GBinaryReader;
|
||||
|
||||
/**
|
||||
* Represents a Map Record.
|
||||
*
|
||||
* @see <a href="https://developer.apple.com/library/archive/technotes/tn/tn1150.html">Map Record</a>
|
||||
*/
|
||||
public class BTreeMapRecord /*implements StructConverter*/ {
|
||||
|
||||
private byte[] bitmap;
|
||||
|
||||
protected BTreeMapRecord(GBinaryReader reader, BTreeHeaderRecord headerRecord)
|
||||
throws IOException {
|
||||
this.bitmap = reader.readNextByteArray(headerRecord.getNodeSize() - 256);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map record node allocation bitmap.
|
||||
* @return the map record node allocation bitmap
|
||||
*/
|
||||
public byte[] getBitmap() {
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified node index is used.
|
||||
* Returns false if the specified node index is free.
|
||||
* @param nodeIndex the node index
|
||||
* @return true if the specified node index is used, false if free
|
||||
*/
|
||||
public boolean isNodeUsed(int nodeIndex) {
|
||||
int block = bitmap[nodeIndex / 8] & 0xff;
|
||||
return (block & (1 << 7 - (nodeIndex % 8))) != 0;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
// return StructConverterUtil.toDataType( this );
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.btree;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import mobiledevices.dmg.ghidra.GBinaryReader;
|
||||
|
||||
/**
|
||||
* Represents a BTNodeDescriptor structure.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/xnu/xnu-792/bsd/hfs/hfs_format.h.auto.html">hfs/hfs_format.h</a>
|
||||
*/
|
||||
public class BTreeNodeDescriptor /*implements StructConverter*/ {
|
||||
|
||||
private int fLink;
|
||||
private int bLink;
|
||||
private byte kind;
|
||||
private byte height;
|
||||
private short numRecords;
|
||||
private short reserved;
|
||||
|
||||
private List<Short> _recordOffsets = new ArrayList<Short>();
|
||||
private List<BTreeNodeRecord> _records = new ArrayList<BTreeNodeRecord>();
|
||||
|
||||
BTreeNodeDescriptor(GBinaryReader reader) throws IOException {
|
||||
this.fLink = reader.readNextInt();
|
||||
this.bLink = reader.readNextInt();
|
||||
this.kind = reader.readNextByte();
|
||||
this.height = reader.readNextByte();
|
||||
this.numRecords = reader.readNextShort();
|
||||
this.reserved = reader.readNextShort();
|
||||
}
|
||||
|
||||
protected void readRecordOffsets(GBinaryReader reader, long nodeStartIndex,
|
||||
BTreeHeaderRecord header) throws IOException {
|
||||
long position = nodeStartIndex + header.getNodeSize() - 2;
|
||||
while (true) {
|
||||
short recordOffset = reader.readShort(position);
|
||||
if (recordOffset == 0) {
|
||||
break;
|
||||
}
|
||||
_recordOffsets.add(recordOffset);
|
||||
position = position - 2;
|
||||
}
|
||||
}
|
||||
|
||||
protected void readRecords(GBinaryReader reader, long nodeStartIndex) throws IOException {
|
||||
for (int i = 0; i < getNumRecords(); ++i) {
|
||||
|
||||
short offset = getRecordOffsets().get(i);
|
||||
|
||||
long recordIndex = (offset & 0xffff) + nodeStartIndex;
|
||||
reader.setPointerIndex(recordIndex);
|
||||
|
||||
BTreeNodeRecord record = new BTreeNodeRecord(reader, this);
|
||||
_records.add(record);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Short> getRecordOffsets() {
|
||||
return _recordOffsets;
|
||||
}
|
||||
|
||||
public List<BTreeNodeRecord> getRecords() {
|
||||
return _records;
|
||||
}
|
||||
|
||||
/**
|
||||
* The node number of the next node of this type.
|
||||
* Or, zero ( 0 ) if this is the last node.
|
||||
* @return node number of the next node of this type
|
||||
*/
|
||||
public int getFLink() {
|
||||
return fLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* The node number of the previous node of this type.
|
||||
* Or, zero ( 0 ) if this is the first node.
|
||||
* @return node number of the previous node of this type
|
||||
*/
|
||||
public int getBLink() {
|
||||
return bLink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key of this node.
|
||||
* @return the key of this node
|
||||
* @see BTreeNodeKinds
|
||||
*/
|
||||
public byte getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the level, or depth, of this node in the B-tree hierarchy.
|
||||
* @return the level, or depth, of this node in the B-tree hierarchy
|
||||
*/
|
||||
public byte getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of records in this node.
|
||||
* @return the number of records in this node
|
||||
*/
|
||||
public short getNumRecords() {
|
||||
return numRecords;
|
||||
}
|
||||
|
||||
/**
|
||||
* This field is reserved.
|
||||
* @return this field is reserved
|
||||
*/
|
||||
public short getReserved() {
|
||||
return reserved;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
// return StructConverterUtil.toDataType( this );
|
||||
// }
|
||||
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.btree;
|
||||
|
||||
/**
|
||||
* Represents kinds of BTNodeDescriptor.
|
||||
*
|
||||
* @see <a href="https://opensource.apple.com/source/xnu/xnu-792/bsd/hfs/hfs_format.h.auto.html">hfs/hfs_format.h</a>
|
||||
* @see <a href="https://developer.apple.com/library/archive/technotes/tn/tn1150.html">B-Trees</a>
|
||||
*/
|
||||
public final class BTreeNodeKinds {
|
||||
|
||||
public final static byte kBTLeafNode = -1;
|
||||
public final static byte kBTIndexNode = 0;
|
||||
public final static byte kBTHeaderNode = 1;
|
||||
public final static byte kBTMapNode = 2;
|
||||
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.btree;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import mobiledevices.dmg.decmpfs.DecmpfsHeader;
|
||||
import mobiledevices.dmg.ghidra.GBinaryReader;
|
||||
import mobiledevices.dmg.xattr.XattrConstants;
|
||||
|
||||
public class BTreeNodeRecord /*implements StructConverter*/ {
|
||||
|
||||
private int unknown0;
|
||||
private int fileID;
|
||||
private int unknown2;
|
||||
private String type;
|
||||
private int unknown3;
|
||||
private int unknown4;
|
||||
private int unknown5;
|
||||
private int recordLength;
|
||||
|
||||
private short _typeLength;
|
||||
private BTreeNodeDescriptor _descriptor;
|
||||
private DecmpfsHeader _decmpfsHeader;
|
||||
private long _offset;
|
||||
|
||||
BTreeNodeRecord( GBinaryReader reader, BTreeNodeDescriptor descriptor ) throws IOException {
|
||||
_offset = reader.getPointerIndex();
|
||||
|
||||
unknown0 = reader.readNextInt();
|
||||
fileID = reader.readNextInt();
|
||||
unknown2 = reader.readNextInt();
|
||||
|
||||
_typeLength = reader.readNextShort();
|
||||
|
||||
type = readType( reader );
|
||||
unknown3 = reader.readNextInt();
|
||||
|
||||
switch ( descriptor.getKind() ) {
|
||||
case BTreeNodeKinds.kBTHeaderNode: {
|
||||
break;
|
||||
}
|
||||
case BTreeNodeKinds.kBTIndexNode: {
|
||||
break;
|
||||
}
|
||||
case BTreeNodeKinds.kBTLeafNode: {
|
||||
unknown4 = reader.readNextInt();
|
||||
unknown5 = reader.readNextInt();
|
||||
recordLength = reader.readNextInt();
|
||||
break;
|
||||
}
|
||||
case BTreeNodeKinds.kBTMapNode: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_descriptor = descriptor;
|
||||
|
||||
if ( descriptor.getKind() == BTreeNodeKinds.kBTLeafNode ) {
|
||||
if ( getType().equals( XattrConstants.DECMPFS_XATTR_NAME ) ) {
|
||||
_decmpfsHeader = new DecmpfsHeader( reader, getRecordLength() );
|
||||
}
|
||||
else if ( getType().equals( XattrConstants.KAUTH_FILESEC_XATTR_NAME ) ) {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
else if ( descriptor.getKind() == BTreeNodeKinds.kBTIndexNode ) {
|
||||
if ( getType().equals( XattrConstants.DECMPFS_XATTR_NAME ) ) {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String readType( GBinaryReader reader ) throws IOException {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for ( int i = 0 ; i < _typeLength ; ++i ) {
|
||||
reader.readNextByte();//skip it...
|
||||
buffer.append( (char) reader.readNextByte() );
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public int getRecordLength() {
|
||||
return recordLength;
|
||||
}
|
||||
public BTreeNodeDescriptor getDescriptor() {
|
||||
return _descriptor;
|
||||
}
|
||||
public int getUnknown0() {
|
||||
return unknown0;
|
||||
}
|
||||
public int getUnknown2() {
|
||||
return unknown2;
|
||||
}
|
||||
public int getUnknown3() {
|
||||
return unknown3;
|
||||
}
|
||||
public int getUnknown4() {
|
||||
return unknown4;
|
||||
}
|
||||
public int getUnknown5() {
|
||||
return unknown5;
|
||||
}
|
||||
public int getFileID() {
|
||||
return fileID;
|
||||
}
|
||||
public DecmpfsHeader getDecmpfsHeader() {
|
||||
return _decmpfsHeader;
|
||||
}
|
||||
public long getRecordOffset() {
|
||||
return _offset;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
// String name = StructConverterUtil.parseName( BTreeNodeRecord.class );
|
||||
// Structure struct = new StructureDataType( name, 0 );
|
||||
// struct.add( DWORD, "unknown0", null );
|
||||
// struct.add( DWORD, "fileID", null );
|
||||
// struct.add( DWORD, "unknown2", null );
|
||||
// struct.add( WORD, "typeLength", null );
|
||||
// struct.add( UNICODE, _typeLength * 2, "type", null );
|
||||
// struct.add( DWORD, "unknown3", null );
|
||||
// if ( _descriptor.getKind() == BTreeNodeKinds.kBTLeafNode ) {
|
||||
// struct.add( DWORD, "unknown4", null );
|
||||
// struct.add( DWORD, "unknown5", null );
|
||||
// struct.add( DWORD, "recordLength", null );
|
||||
// }
|
||||
// try {
|
||||
// struct.setName( name + '_' + struct.getLength() );
|
||||
// }
|
||||
// catch ( Exception e ) {
|
||||
// }
|
||||
// return struct;
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.btree;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import mobiledevices.dmg.ghidra.GBinaryReader;
|
||||
|
||||
public class BTreeRootNodeDescriptor extends BTreeNodeDescriptor {
|
||||
|
||||
private BTreeHeaderRecord headerRecord;
|
||||
private BTreeUserDataRecord userDataRecord;
|
||||
private BTreeMapRecord mapRecord;
|
||||
private List<BTreeNodeDescriptor> nodes = new ArrayList<BTreeNodeDescriptor>();
|
||||
|
||||
public BTreeRootNodeDescriptor( GBinaryReader reader ) throws IOException {
|
||||
super( reader );
|
||||
|
||||
headerRecord = new BTreeHeaderRecord( reader );
|
||||
userDataRecord = new BTreeUserDataRecord( reader );
|
||||
mapRecord = new BTreeMapRecord( reader, headerRecord );
|
||||
|
||||
nodes.add( this );
|
||||
|
||||
int nodeSize = headerRecord.getNodeSize() & 0xffff;
|
||||
|
||||
for ( int i = nodeSize ; i < reader.length() ; i += nodeSize ) {
|
||||
reader.setPointerIndex( i );
|
||||
BTreeNodeDescriptor node = new BTreeNodeDescriptor( reader );
|
||||
nodes.add( node );
|
||||
node.readRecordOffsets( reader, i, headerRecord );
|
||||
node.readRecords( reader, i );
|
||||
}
|
||||
|
||||
this.readRecordOffsets( reader, 0, headerRecord );
|
||||
}
|
||||
|
||||
public BTreeHeaderRecord getHeaderRecord() {
|
||||
return headerRecord;
|
||||
}
|
||||
|
||||
public BTreeUserDataRecord getUserDataRecord() {
|
||||
return userDataRecord;
|
||||
}
|
||||
|
||||
public BTreeMapRecord getMapRecord() {
|
||||
return mapRecord;
|
||||
}
|
||||
|
||||
public BTreeNodeDescriptor getNode( int index ) {
|
||||
try {
|
||||
return nodes.get( index );
|
||||
}
|
||||
catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public List<BTreeNodeDescriptor> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
// //we want to return the super class structure,
|
||||
// //this class is synthetic
|
||||
// return StructConverterUtil.toDataType( BTreeNodeDescriptor.class );
|
||||
// }
|
||||
}
|
14
GPL/DMG/src/dmg/java/mobiledevices/dmg/btree/BTreeTypes.java
Normal file
14
GPL/DMG/src/dmg/java/mobiledevices/dmg/btree/BTreeTypes.java
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.btree;
|
||||
|
||||
public final class BTreeTypes {
|
||||
|
||||
/** Control file */
|
||||
public final static byte kHFSBTreeType = (byte)0;
|
||||
/** User bTree types start from 128 */
|
||||
public final static byte kUserBTreeType = (byte)128;
|
||||
/** */
|
||||
public final static byte kReservedBTreeType = (byte)255;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.btree;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import mobiledevices.dmg.ghidra.GBinaryReader;
|
||||
|
||||
/**
|
||||
* Represents a User Data Record.
|
||||
*
|
||||
* @see <a href="https://developer.apple.com/library/archive/technotes/tn/tn1150.html">User Data Record</a>
|
||||
*/
|
||||
public class BTreeUserDataRecord /*implements StructConverter*/ {
|
||||
|
||||
private byte[] unused;
|
||||
|
||||
BTreeUserDataRecord(GBinaryReader reader) throws IOException {
|
||||
this.unused = reader.readNextByteArray(128);
|
||||
}
|
||||
|
||||
public byte[] getUnused() {
|
||||
return unused;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
// return StructConverterUtil.toDataType( this );
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.decmpfs;
|
||||
|
||||
public final class DecmpfsCompressionTypes {
|
||||
|
||||
/** Uncompressed data in xattr. */
|
||||
public final static int CMP_Type1 = 1;
|
||||
|
||||
/** Data stored in-line. */
|
||||
public final static int CMP_Type3 = 3;
|
||||
|
||||
/** Resource fork contains compressed data. */
|
||||
public final static int CMP_Type4 = 4;
|
||||
|
||||
/** ???? */
|
||||
public final static int CMP_Type10 = 10;
|
||||
|
||||
public final static int CMP_MAX = 255;
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.decmpfs;
|
||||
|
||||
public final class DecmpfsConstants {
|
||||
|
||||
public final static int MAX_DECMPFS_XATTR_SIZE = 3802;
|
||||
|
||||
public final static byte [] DECMPFS_MAGIC_BYTES = { 'f', 'p', 'm', 'c' };
|
||||
public final static String DECMPFS_MAGIC = new String( DECMPFS_MAGIC_BYTES );
|
||||
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.decmpfs;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import mobiledevices.dmg.ghidra.GBinaryReader;
|
||||
import mobiledevices.dmg.ghidra.GStringUtilities;
|
||||
|
||||
public class DecmpfsHeader /*implements StructConverter*/ {
|
||||
private int compression_magic;
|
||||
private int compression_type;
|
||||
private long uncompressed_size;
|
||||
private byte [] attr_bytes;
|
||||
|
||||
public DecmpfsHeader(GBinaryReader reader, int size) throws IOException {
|
||||
long index = reader.getPointerIndex();
|
||||
|
||||
this.compression_magic = reader.readNextInt();
|
||||
|
||||
boolean originalEndian = reader.isLittleEndian();
|
||||
reader.setLittleEndian( true );
|
||||
|
||||
this.compression_type = reader.readNextInt();
|
||||
this.uncompressed_size = reader.readNextLong();
|
||||
|
||||
reader.setLittleEndian( originalEndian );
|
||||
|
||||
long endIndex = index + size + 1; //TODO always add 1????
|
||||
|
||||
if ( ( endIndex % 2 ) != 0 ) {
|
||||
endIndex = endIndex - 1;
|
||||
}
|
||||
|
||||
long nElements = endIndex - reader.getPointerIndex();
|
||||
|
||||
if ( ( nElements % 2 ) != 0 ) {//TODO
|
||||
++nElements;
|
||||
}
|
||||
else if ( nElements < 0 ) {//TODO
|
||||
System.err.println( "here" );
|
||||
}
|
||||
this.attr_bytes = reader.readNextByteArray( (int)nElements );
|
||||
}
|
||||
|
||||
public String getCompressionMagic() {
|
||||
return GStringUtilities.toString( compression_magic );
|
||||
}
|
||||
|
||||
public int getCompressionType() {
|
||||
return compression_type;
|
||||
}
|
||||
|
||||
public long getUncompressedSize() {
|
||||
return uncompressed_size;
|
||||
}
|
||||
|
||||
public byte [] getAttrBytes() {
|
||||
return attr_bytes;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public DataType toDataType() throws DuplicateNameException, IOException {
|
||||
// String name = StructConverterUtil.parseName( DecmpfsHeader.class );
|
||||
// Structure struct = new StructureDataType( name + "_" + attr_bytes.length, 0 );
|
||||
//
|
||||
// struct.add( STRING, 4, "compression_magic", null );
|
||||
// struct.add( DWORD, "compression_type", null );
|
||||
// struct.add( QWORD, "uncompressed_size", null );
|
||||
//
|
||||
// if ( attr_bytes.length > 0 ) {
|
||||
// ArrayDataType byteArrayDT = new ArrayDataType( BYTE , attr_bytes.length, BYTE.getLength() );
|
||||
// struct.add( byteArrayDT, "attr_bytes", null );
|
||||
// }
|
||||
// return struct;
|
||||
// }
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.decmpfs;
|
||||
|
||||
public final class DecmpfsStates {
|
||||
|
||||
public final static int FILE_TYPE_UNKNOWN = 0;
|
||||
public final static int FILE_IS_NOT_COMPRESSED = 1;
|
||||
public final static int FILE_IS_COMPRESSED = 2;
|
||||
public final static int FILE_IS_CONVERTING = 3; //file is converting from compressed to decompressed...
|
||||
|
||||
}
|
957
GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GBinaryReader.java
Normal file
957
GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GBinaryReader.java
Normal file
|
@ -0,0 +1,957 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.ghidra;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A class for reading data from a
|
||||
* generic byte provider in either big-endian or little-endian.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class GBinaryReader {
|
||||
/**
|
||||
* The size of a BYTE in Java.
|
||||
*/
|
||||
public final static int SIZEOF_BYTE = 1;
|
||||
/**
|
||||
* The size of a SHORT in Java.
|
||||
*/
|
||||
public final static int SIZEOF_SHORT = 2;
|
||||
/**
|
||||
* The size of an INTEGER in Java.
|
||||
*/
|
||||
public final static int SIZEOF_INT = 4;
|
||||
/**
|
||||
* The size of a LONG in Java.
|
||||
*/
|
||||
public final static int SIZEOF_LONG = 8;
|
||||
|
||||
private GByteProvider provider;
|
||||
private GDataConverter converter;
|
||||
private long currentIndex;
|
||||
|
||||
/**
|
||||
* Constructs a reader using the given
|
||||
* file and endian-order.
|
||||
*
|
||||
* If isLittleEndian is true, then all values read
|
||||
* from the file will be done so assuming
|
||||
* little-endian order.
|
||||
*
|
||||
* Otherwise, if isLittleEndian
|
||||
* is false, then all values will be read
|
||||
* assuming big-endian order.
|
||||
*
|
||||
* @param provider the byte provider
|
||||
* @param isLittleEndian the endian-order
|
||||
*/
|
||||
public GBinaryReader(GByteProvider provider, boolean isLittleEndian) {
|
||||
this.provider = provider;
|
||||
setLittleEndian(isLittleEndian);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a clone of this reader positioned at the new index.
|
||||
* @param newIndex the new index
|
||||
* @return a clone of this reader positioned at the new index
|
||||
*/
|
||||
public GBinaryReader clone(int newIndex) {
|
||||
GBinaryReader clone = new GBinaryReader(provider, isLittleEndian());
|
||||
clone.converter = converter;
|
||||
clone.currentIndex = newIndex;
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this reader will extract values in little endian,
|
||||
* otherwise in big endian.
|
||||
* @return true is little endian, false is big endian
|
||||
*/
|
||||
public boolean isLittleEndian() {
|
||||
return converter instanceof GDataConverterLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the endian of this binary reader.
|
||||
* @param isLittleEndian true for little-endian and false for big-endian
|
||||
*/
|
||||
public void setLittleEndian(boolean isLittleEndian) {
|
||||
if (isLittleEndian) {
|
||||
converter = new GDataConverterLE();
|
||||
}
|
||||
else {
|
||||
converter = new GDataConverterBE();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the underlying file.
|
||||
* @return returns the length of the underlying file
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public long length() throws IOException {
|
||||
return provider.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified index into
|
||||
* the underlying byte provider is valid.
|
||||
* @param index the index in the byte provider
|
||||
* @return returns true if the specified index is valid
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public boolean isValidIndex(int index) throws IOException {
|
||||
return provider.isValidIndex(index & GConv.INT_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified index into
|
||||
* the underlying byte provider is valid.
|
||||
* @param index the index in the byte provider
|
||||
* @return returns true if the specified index is valid
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public boolean isValidIndex(long index) throws IOException {
|
||||
return provider.isValidIndex(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aligns the current index on the specified alignment value.
|
||||
* For example, if current index was 123 and align value was
|
||||
* 16, then current index would become 128.
|
||||
* @param alignValue
|
||||
* @return the number of bytes required to align
|
||||
*/
|
||||
public int align(int alignValue) {
|
||||
long align = currentIndex % alignValue;
|
||||
if (align == 0) {
|
||||
return 0;
|
||||
}
|
||||
currentIndex = currentIndex + (alignValue - align);
|
||||
return (int)(alignValue - align);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* A convenience method for setting the index using
|
||||
* an integer.
|
||||
*/
|
||||
public void setPointerIndex(int index) {
|
||||
this.currentIndex = index & GConv.INT_MASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current index to the specified value.
|
||||
* The pointer index will allow the reader
|
||||
* to operate as a psuedo-iterator.
|
||||
*
|
||||
* @param index the byte provider index value
|
||||
*/
|
||||
public void setPointerIndex(long index) {
|
||||
this.currentIndex = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current index value.
|
||||
* @return the current index value
|
||||
*/
|
||||
public long getPointerIndex() {
|
||||
return currentIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Peeks at the next byte without incrementing
|
||||
* the current index.
|
||||
* @return the next byte
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public byte peekNextByte() throws IOException {
|
||||
return readByte(currentIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Peeks at the next short without incrementing
|
||||
* the current index.
|
||||
* @return the next short
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public short peekNextShort() throws IOException {
|
||||
return readShort(currentIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Peeks at the next integer without incrementing
|
||||
* the current index.
|
||||
* @return the next int
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public int peekNextInt() throws IOException {
|
||||
return readInt(currentIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Peeks at the next long without incrementing
|
||||
* the current index.
|
||||
* @return the next long
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public long peekNextLong() throws IOException {
|
||||
return readLong(currentIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the byte at the current index and then increments the current
|
||||
* index by <code>SIZEOF_BYTE</code>.
|
||||
* @return the byte at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public byte readNextByte() throws IOException {
|
||||
byte b = readByte(currentIndex);
|
||||
currentIndex += SIZEOF_BYTE;
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the byte at the current index and then increments the current
|
||||
* index by <code>SIZEOF_BYTE</code>.
|
||||
* @return the byte at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public byte readNextByte(byte minClamp, byte maxClamp, Byte... exceptions) throws IOException {
|
||||
byte b = readByte(currentIndex, minClamp, maxClamp, exceptions);
|
||||
currentIndex += SIZEOF_BYTE;
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the short at the current index and then increments the current
|
||||
* index by <code>SIZEOF_SHORT</code>.
|
||||
* @return the short at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public short readNextShort() throws IOException {
|
||||
short s = readShort(currentIndex);
|
||||
currentIndex+=SIZEOF_SHORT;
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the short at the current index and then increments the current
|
||||
* index by <code>SIZEOF_SHORT</code>.
|
||||
* @return the short at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public short readNextShort(short minClamp, short maxClamp, Short... exceptions) throws IOException {
|
||||
short s = readShort(currentIndex, minClamp, maxClamp, exceptions);
|
||||
currentIndex+=SIZEOF_SHORT;
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the integer at the current index and then increments the current
|
||||
* index by <code>SIZEOF_INT</code>.
|
||||
* @return the integer at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public int readNextInt() throws IOException {
|
||||
int i = readInt(currentIndex);
|
||||
currentIndex+=SIZEOF_INT;
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the integer at the current index and then increments the current
|
||||
* index by <code>SIZEOF_INT</code>.
|
||||
* @return the integer at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public int readNextInt(int minClamp, int maxClamp, Integer... exceptions) throws IOException {
|
||||
int i = readInt(currentIndex, minClamp, maxClamp, exceptions);
|
||||
currentIndex+=SIZEOF_INT;
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the long at the current index and then increments the current
|
||||
* index by <code>SIZEOF_LONG</code>.
|
||||
* @return the long at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public long readNextLong() throws IOException {
|
||||
long l = readLong(currentIndex);
|
||||
currentIndex+=SIZEOF_LONG;
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the long at the current index and then increments the current
|
||||
* index by <code>SIZEOF_LONG</code>.
|
||||
* @return the long at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public long readNextLong(long minClamp, long maxClamp, Long... exceptions) throws IOException {
|
||||
long l = readLong(currentIndex, minClamp, maxClamp, exceptions);
|
||||
currentIndex+=SIZEOF_LONG;
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the Ascii string at the current index and then increments the current
|
||||
* index by the length of the Ascii string that was found. This method
|
||||
* expects the string to be null-terminated.
|
||||
* @return the null-terminated Ascii string at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public String readNextAsciiString() throws IOException {
|
||||
String s = readAsciiString(currentIndex);
|
||||
currentIndex+=(s.length()+1);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an Ascii string of <code>length</code>
|
||||
* characters starting at the current index and then increments the current
|
||||
* index by <code>length</code>.
|
||||
*
|
||||
* @return the Ascii string at the current index
|
||||
*/
|
||||
public String readNextAsciiString(int length) throws IOException {
|
||||
String s = readAsciiString(currentIndex, length);
|
||||
currentIndex+=length;
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the Unicode string at the current index and then increments the current
|
||||
* index by the length of the Unicode string that was found. This method
|
||||
* expects the string to be double null-terminated ('\0\0').
|
||||
* @return the null-terminated Ascii string at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public String readNextUnicodeString() throws IOException {
|
||||
String s = readUnicodeString(currentIndex);
|
||||
currentIndex += ((s.length()+1)*2);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the unicode string at the current index and then increments the current
|
||||
* index by <code>length</code>.
|
||||
* @return the unicode string at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public String readNextUnicodeString(int length) throws IOException {
|
||||
String s = readUnicodeString(currentIndex, length);
|
||||
currentIndex+=(length*2);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a byte array of <code>nElements</code>
|
||||
* starting at the current index and then increments the current
|
||||
* index by <code>SIZEOF_BYTE * nElements</code>.
|
||||
* @return the byte array starting at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public byte [] readNextByteArray(int nElements) throws IOException {
|
||||
byte [] b = readByteArray(currentIndex, nElements);
|
||||
currentIndex+=(SIZEOF_BYTE*nElements);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a byte array of <code>nElements</code>
|
||||
* starting at the current index and then increments the current
|
||||
* index by <code>SIZEOF_BYTE * nElements</code>.
|
||||
* @return the byte array starting at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public byte [] readNextByteArray(int nElements, byte minClamp, byte maxClamp, Byte... exceptions) throws IOException {
|
||||
byte [] b = readByteArray(currentIndex, nElements, minClamp, maxClamp, exceptions);
|
||||
currentIndex+=(SIZEOF_BYTE*nElements);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a short array of <code>nElements</code>
|
||||
* starting at the current index and then increments the current
|
||||
* index by <code>SIZEOF_SHORT * nElements</code>.
|
||||
* @return the short array starting at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public short [] readNextShortArray(int nElements) throws IOException {
|
||||
short [] s = readShortArray(currentIndex, nElements);
|
||||
currentIndex+=(SIZEOF_SHORT*nElements);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a short array of <code>nElements</code>
|
||||
* starting at the current index and then increments the current
|
||||
* index by <code>SIZEOF_SHORT * nElements</code>.
|
||||
* @return the short array starting at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public short [] readNextShortArray(int nElements, short minClamp, short maxClamp, Short... exceptions) throws IOException {
|
||||
short [] s = readShortArray(currentIndex, nElements, minClamp, maxClamp, exceptions);
|
||||
currentIndex+=(SIZEOF_SHORT*nElements);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an integer array of <code>nElements</code>
|
||||
* starting at the current index and then increments the current
|
||||
* index by <code>SIZEOF_INT * nElements</code>.
|
||||
* @return the integer array starting at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public int [] readNextIntArray(int nElements) throws IOException {
|
||||
int [] i = readIntArray(currentIndex, nElements);
|
||||
currentIndex+=(SIZEOF_INT*nElements);
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an integer array of <code>nElements</code>
|
||||
* starting at the current index and then increments the current
|
||||
* index by <code>SIZEOF_INT * nElements</code>.
|
||||
* @return the integer array starting at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public int [] readNextIntArray(int nElements, int minClamp, int maxClamp, Integer... exceptions) throws IOException {
|
||||
int [] i = readIntArray(currentIndex, nElements, minClamp, maxClamp, exceptions);
|
||||
currentIndex+=(SIZEOF_INT*nElements);
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a long array of <code>nElements</code>
|
||||
* starting at the current index and then increments the current
|
||||
* index by <code>SIZEOF_LONG * nElements</code>.
|
||||
* @return the long array starting at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public long [] readNextLongArray(int nElements) throws IOException {
|
||||
long [] l = readLongArray(currentIndex, nElements);
|
||||
currentIndex+=(SIZEOF_LONG*nElements);
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a long array of <code>nElements</code>
|
||||
* starting at the current index and then increments the current
|
||||
* index by <code>SIZEOF_LONG * nElements</code>.
|
||||
* @return the long array starting at the current index
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public long [] readNextLongArray(int nElements, long minClamp, long maxClamp, Long... exceptions) throws IOException {
|
||||
long [] l = readLongArray(currentIndex, nElements, minClamp, maxClamp, exceptions);
|
||||
currentIndex+=(SIZEOF_LONG*nElements);
|
||||
return l;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Returns a null-terminated Ascii string starting
|
||||
* at <code>index</code>. The end of the string
|
||||
* is denoted by a <code>null</code> character.
|
||||
*
|
||||
* @param index the index where the Ascii string begins
|
||||
* @return the Ascii string
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public String readAsciiString(long index) throws IOException {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
while (true) {
|
||||
byte b = provider.readByte(index++);
|
||||
if ((b >= 32) && (b <= 126)) {
|
||||
buffer.append((char)b);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return buffer.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an Ascii string of <code>length</code> bytes
|
||||
* starting at <code>index</code>. This method does not
|
||||
* care about null-terminators.
|
||||
* @param index the index where the Ascii string begins
|
||||
* @param length the length of the Ascii string
|
||||
* @return the Ascii string
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public String readAsciiString(long index, int length) throws IOException {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for (int i = 0 ; i < length ; ++i) {
|
||||
byte b = provider.readByte(index++);
|
||||
buffer.append((char) (b & 0x00FF));
|
||||
}
|
||||
return buffer.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a null-terminated Unicode string starting
|
||||
* at <code>index</code>. The end of the string
|
||||
* is denoted by two-byte Unicode <code>null</code> character.
|
||||
* @param index the index where the Unicode string begins
|
||||
* @return the Unicode string
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public String readUnicodeString(long index) throws IOException {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
char c = 0;
|
||||
int i = 0;
|
||||
while (i < length()) {
|
||||
c = (char)((provider.readByte(index++) & 0xff));
|
||||
c += (char)((provider.readByte(index++) & 0xff) << 8);
|
||||
if (c == 0x0000) {
|
||||
break;
|
||||
}
|
||||
buffer.append(c);
|
||||
i += 2;
|
||||
}
|
||||
return buffer.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Unicode string of <code>length</code> bytes
|
||||
* starting at <code>index</code>. This method does not
|
||||
* care about null-terminators.
|
||||
* @param index the index where the Unicode string begins
|
||||
* @param length the length of the Unicode string
|
||||
* @return the Unicode string
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public String readUnicodeString(long index, int length) throws IOException {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
char c = 0;
|
||||
for (int i = 0 ; i < length*2 ; i+=2) {
|
||||
c = (char)((provider.readByte(index++) & 0xff));
|
||||
c += (char)((provider.readByte(index++) & 0xff) << 8);
|
||||
buffer.append(c);
|
||||
}
|
||||
return buffer.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BYTE at <code>index<code>.
|
||||
* @param index the index where the BYTE begins
|
||||
* @return the BYTE
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public byte readByte(long index) throws IOException {
|
||||
return provider.readByte(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BYTE at <code>index</code>.
|
||||
* @param index the index where the BYTE begins
|
||||
* @return the BYTE
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public byte readByte(long index, byte minClamp, byte maxClamp, Byte... exceptions) throws IOException {
|
||||
byte b = readByte(index);
|
||||
return clampByte(b, minClamp, maxClamp, exceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SHORT at <code>index</code>.
|
||||
* @param index the index where the SHORT begins
|
||||
* @return the SHORT
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public short readShort(long index) throws IOException {
|
||||
byte [] bytes = provider.readBytes(index, SIZEOF_SHORT);
|
||||
return converter.getShort(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SHORT at <code>index</code>.
|
||||
* @param index the index where the SHORT begins
|
||||
* @return the SHORT
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public short readShort(long index, short minClamp, short maxClamp, Short... exceptions) throws IOException {
|
||||
short s = readShort(index);
|
||||
return clampShort(s, minClamp, maxClamp, exceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the INTEGER at <code>index</code>.
|
||||
* @param index the index where the INTEGER begins
|
||||
* @return the INTEGER
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public int readInt(long index) throws IOException {
|
||||
byte [] bytes = provider.readBytes(index, SIZEOF_INT);
|
||||
return converter.getInt(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the INTEGER at <code>index</code>.
|
||||
* @param index the index where the INTEGER begins
|
||||
* @return the INTEGER
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public int readInt(long index, int minClamp, int maxClamp, Integer... exceptions) throws IOException {
|
||||
int i = readInt(index);
|
||||
return clampInt(i, minClamp, maxClamp, exceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LONG at <code>index</code>.
|
||||
* @param index the index where the LONG begins
|
||||
* @return the LONG
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public long readLong(long index) throws IOException {
|
||||
byte [] bytes = provider.readBytes(index, SIZEOF_LONG);
|
||||
return converter.getLong(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LONG at <code>index</code>.
|
||||
* @param index the index where the LONG begins
|
||||
* @return the LONG
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public long readLong(long index, long minClamp, long maxClamp, Long... exceptions) throws IOException {
|
||||
long l = readLong(index);
|
||||
return clampLong(l, minClamp, maxClamp, exceptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BYTE array of <code>nElements</code>
|
||||
* starting at <code>index</code>.
|
||||
* @param index the index where the BYTE begins
|
||||
* @param nElements the number of array elements
|
||||
* @return the BYTE array
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public byte [] readByteArray(long index, int nElements) throws IOException {
|
||||
if (nElements < 0) {
|
||||
throw new IOException("Invalid number of elements specified: "+nElements);
|
||||
}
|
||||
return provider.readBytes(index, nElements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BYTE array of <code>nElements</code>
|
||||
* starting at <code>index</code>.
|
||||
* @param index the index where the BYTE begins
|
||||
* @param nElements the number of array elements
|
||||
* @return the BYTE array
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public byte [] readByteArray(long index, int nElements, byte minClamp, byte maxClamp, Byte... exceptions) throws IOException {
|
||||
byte[] array = readByteArray(index, nElements);
|
||||
for (int ii = 0; ii < array.length; ++ii) {
|
||||
array[ii] = clampByte(array[ii], minClamp, maxClamp, exceptions);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SHORT array of <code>nElements</code>
|
||||
* starting at <code>index</code>.
|
||||
* @param index the index where the SHORT begins
|
||||
* @param nElements the number of array elements
|
||||
* @return the SHORT array
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public short [] readShortArray(long index, int nElements) throws IOException {
|
||||
if (nElements < 0) {
|
||||
throw new IOException("Invalid number of elements specified: "+nElements);
|
||||
}
|
||||
short [] arr = new short[nElements];
|
||||
for (int i = 0 ; i < nElements ; ++i) {
|
||||
arr[i] = readShort(index);
|
||||
index += SIZEOF_SHORT;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SHORT array of <code>nElements</code>
|
||||
* starting at <code>index</code>.
|
||||
* @param index the index where the SHORT begins
|
||||
* @param nElements the number of array elements
|
||||
* @return the SHORT array
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public short [] readShortArray(long index, int nElements, short minClamp, short maxClamp, Short... exceptions) throws IOException {
|
||||
short[] array = readShortArray(index, nElements);
|
||||
for (int ii = 0; ii < array.length; ++ii) {
|
||||
array[ii] = clampShort(array[ii], minClamp, maxClamp, exceptions);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the INTEGER array of <code>nElements</code>
|
||||
* starting at <code>index</code>.
|
||||
* @param index the index where the INTEGER begins
|
||||
* @param nElements the number of array elements
|
||||
* @return the INTEGER array
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public int [] readIntArray(long index, int nElements) throws IOException {
|
||||
if (nElements < 0) {
|
||||
throw new IOException("Invalid number of elements specified: "+nElements);
|
||||
}
|
||||
int [] arr = new int[nElements];
|
||||
for (int i = 0 ; i < nElements ; ++i) {
|
||||
arr[i] = readInt(index);
|
||||
index += SIZEOF_INT;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the INTEGER array of <code>nElements</code>
|
||||
* starting at <code>index</code>.
|
||||
* @param index the index where the INTEGER begins
|
||||
* @param nElements the number of array elements
|
||||
* @return the INTEGER array
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public int [] readIntArray(long index, int nElements, int minClamp, int maxClamp, Integer... exceptions) throws IOException {
|
||||
int[] array = readIntArray(index, nElements);
|
||||
for (int ii = 0; ii < array.length; ++ii) {
|
||||
array[ii] = clampInt(array[ii], minClamp, maxClamp, exceptions);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LONG array of <code>nElements</code>
|
||||
* starting at <code>index</code>.
|
||||
* @param index the index where the LONG begins
|
||||
* @param nElements the number of array elements
|
||||
* @return the LONG array
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public long [] readLongArray(long index, int nElements) throws IOException {
|
||||
if (nElements < 0) {
|
||||
throw new IOException("Invalid number of elements specified: "+nElements);
|
||||
}
|
||||
long [] arr = new long[nElements];
|
||||
for (int i = 0 ; i < nElements ; ++i) {
|
||||
arr[i] = readLong(index);
|
||||
index += SIZEOF_LONG;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LONG array of <code>nElements</code>
|
||||
* starting at <code>index</code>.
|
||||
* @param index the index where the LONG begins
|
||||
* @param nElements the number of array elements
|
||||
* @return the LONG array
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public long [] readLongArray(long index, int nElements, long minClamp, long maxClamp, Long... exceptions) throws IOException {
|
||||
long[] array = readLongArray(index, nElements);
|
||||
for (int ii = 0; ii < array.length; ++ii) {
|
||||
array[ii] = clampLong(array[ii], minClamp, maxClamp, exceptions);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Ascii string array of <code>nElements</code>
|
||||
* starting at <code>index</code>
|
||||
* @param index the index where the Ascii Strings begin
|
||||
* @param nElements the number of array elements
|
||||
* @return the Ascii String array
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public String [] readAsciiStringArray(long index, int nElements) throws IOException {
|
||||
if (nElements < 0) {
|
||||
throw new IOException("Invalid number of elements specified: "+nElements);
|
||||
}
|
||||
String [] arr = new String[nElements];
|
||||
for (int i = 0 ; i < nElements ; ++i) {
|
||||
String tmp = readAsciiString(index);
|
||||
arr[i] = tmp;
|
||||
index += (tmp == null ? 1 : tmp.length());
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified byte at the specified index.
|
||||
* @param index the index where the byte should be written
|
||||
* @param value the byte value to be written
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public void writeByte(long index, byte value) throws IOException {
|
||||
provider.writeByte(index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified short at the specified index.
|
||||
* @param index the index where the short should be written
|
||||
* @param value the short value to be written
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public void writeShort(long index, short value) throws IOException {
|
||||
byte [] bytes = converter.getBytes(value);
|
||||
provider.writeBytes(index, bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified int at the specified index.
|
||||
* @param index the index where the int should be written
|
||||
* @param value the int value to be written
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public void writeInt(long index, int value) throws IOException {
|
||||
byte [] bytes = converter.getBytes(value);
|
||||
provider.writeBytes(index, bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified long at the specified index.
|
||||
* @param index the index where the long should be written
|
||||
* @param value the long value to be written
|
||||
* @exception IOException if an I/O error occurs
|
||||
*/
|
||||
public void writeLong(long index, long value) throws IOException {
|
||||
byte [] bytes = converter.getBytes(value);
|
||||
provider.writeBytes(index, bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the underlying byte provider.
|
||||
* @return the underlying byte provider
|
||||
*/
|
||||
public GByteProvider getByteProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
protected byte clampByte(byte b, byte minClamp, byte maxClamp, Byte... exceptions) {
|
||||
if (maxClamp < minClamp) {
|
||||
throw new IllegalArgumentException("maxClamp < minClamp not allowed");
|
||||
}
|
||||
if (exceptions != null) {
|
||||
if (exceptions.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("exceptions must be pairs of (flag, replacement) bytes");
|
||||
}
|
||||
}
|
||||
boolean clamp = true;
|
||||
if (exceptions != null) {
|
||||
for (int ii = 0; ii < exceptions.length; ii += 2) {
|
||||
if (b == exceptions[ii]) {
|
||||
b = exceptions[ii+1];
|
||||
clamp = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clamp) {
|
||||
if (b < minClamp) {
|
||||
b = minClamp;
|
||||
} else if (b > maxClamp) {
|
||||
b = maxClamp;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
protected short clampShort(short s, short minClamp, short maxClamp, Short... exceptions) {
|
||||
if (maxClamp < minClamp) {
|
||||
throw new IllegalArgumentException("maxClamp < minClamp not allowed");
|
||||
}
|
||||
if (exceptions != null) {
|
||||
if (exceptions.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("exceptions must be pairs of (flag, replacement) shorts");
|
||||
}
|
||||
}
|
||||
boolean clamp = true;
|
||||
if (exceptions != null) {
|
||||
for (int ii = 0; ii < exceptions.length; ii += 2) {
|
||||
if (s == exceptions[ii]) {
|
||||
s = exceptions[ii+1];
|
||||
clamp = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clamp) {
|
||||
if (s < minClamp) {
|
||||
s = minClamp;
|
||||
} else if (s > maxClamp) {
|
||||
s = maxClamp;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
protected int clampInt(int i, int minClamp, int maxClamp, Integer... exceptions) {
|
||||
if (maxClamp < minClamp) {
|
||||
throw new IllegalArgumentException("maxClamp < minClamp not allowed");
|
||||
}
|
||||
if (exceptions != null) {
|
||||
if (exceptions.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("exceptions must be pairs of (flag, replacement) ints");
|
||||
}
|
||||
}
|
||||
boolean clamp = true;
|
||||
if (exceptions != null) {
|
||||
for (int ii = 0; ii < exceptions.length; ii += 2) {
|
||||
if (i == exceptions[ii]) {
|
||||
i = exceptions[ii+1];
|
||||
clamp = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clamp) {
|
||||
if (i < minClamp) {
|
||||
i = minClamp;
|
||||
} else if (i > maxClamp) {
|
||||
i = maxClamp;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
protected long clampLong(long l, long minClamp, long maxClamp, Long... exceptions) {
|
||||
if (maxClamp < minClamp) {
|
||||
throw new IllegalArgumentException("maxClamp < minClamp not allowed");
|
||||
}
|
||||
if (exceptions != null) {
|
||||
if (exceptions.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("exceptions must be pairs of (flag, replacement) longs");
|
||||
}
|
||||
}
|
||||
boolean clamp = true;
|
||||
if (exceptions != null) {
|
||||
for (int ii = 0; ii < exceptions.length; ii += 2) {
|
||||
if (l == exceptions[ii]) {
|
||||
l = exceptions[ii+1];
|
||||
clamp = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clamp) {
|
||||
if (l < minClamp) {
|
||||
l = minClamp;
|
||||
} else if (l > maxClamp) {
|
||||
l = maxClamp;
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
}
|
126
GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GByteProvider.java
Normal file
126
GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GByteProvider.java
Normal file
|
@ -0,0 +1,126 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.ghidra;
|
||||
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* An implementation of ByteProvider where the underlying
|
||||
* bytes are supplied by a random access file.
|
||||
*/
|
||||
public class GByteProvider implements Closeable {
|
||||
private File file;
|
||||
private GRandomAccessFile randomAccessFile;
|
||||
|
||||
/**
|
||||
* Constructs a byte provider using the specified file
|
||||
* @param file the file to open for random access
|
||||
* @throws FileNotFoundException if the file does not exist
|
||||
*/
|
||||
public GByteProvider(File file) throws IOException {
|
||||
this.file = file;
|
||||
this.randomAccessFile = new GRandomAccessFile(file, "r");
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a byte provider using the specified file and permissions string
|
||||
* @param file the file to open for random access
|
||||
* @param string indicating permissions used for open
|
||||
* @throws FileNotFoundException if the file does not exist
|
||||
*/
|
||||
public GByteProvider(File file, String permissions) throws IOException {
|
||||
this.file = file;
|
||||
this.randomAccessFile = new GRandomAccessFile(file, permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GByteProvider.app.util.bin.ByteProvider#getFile()
|
||||
*/
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GByteProvider.app.util.bin.ByteProvider#getName()
|
||||
*/
|
||||
public String getName() {
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
public String getAbsolutePath() {
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GByteProvider.app.util.bin.ByteProvider#getInputStream(long)
|
||||
*/
|
||||
public InputStream getInputStream(long index) throws IOException {
|
||||
FileInputStream is = new FileInputStream(file);
|
||||
is.skip(index);
|
||||
return is;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying random-access file.
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
randomAccessFile.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GByteProvider.app.util.bin.ByteProvider#length()
|
||||
*/
|
||||
public long length() throws IOException {
|
||||
return randomAccessFile.length();
|
||||
}
|
||||
|
||||
public boolean isValidIndex(long index) {
|
||||
try {
|
||||
return index >= 0 && index < randomAccessFile.length();
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GByteProvider.app.util.bin.ByteProvider#readByte(long)
|
||||
*/
|
||||
public byte readByte(long index) throws IOException {
|
||||
randomAccessFile.seek(index);
|
||||
return randomAccessFile.readByte();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GByteProvider.app.util.bin.ByteProvider#readBytes(long, long)
|
||||
*/
|
||||
public byte [] readBytes(long index, long length) throws IOException {
|
||||
randomAccessFile.seek(index);
|
||||
byte [] b = new byte[(int)length];
|
||||
int nRead = randomAccessFile.read(b);
|
||||
if (nRead != length) {
|
||||
throw new IOException("Unable to read "+length+" bytes");
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GByteProvider.app.util.bin.ByteProvider#writeByte(long, byte)
|
||||
*/
|
||||
public void writeByte(long index, byte value) throws IOException {
|
||||
randomAccessFile.seek(index);
|
||||
randomAccessFile.write(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GByteProvider.app.util.bin.ByteProvider#writeBytes(long, byte[])
|
||||
*/
|
||||
public void writeBytes(long index, byte [] values) throws IOException {
|
||||
randomAccessFile.seek(index);
|
||||
randomAccessFile.write(values);
|
||||
}
|
||||
}
|
136
GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GConv.java
Normal file
136
GPL/DMG/src/dmg/java/mobiledevices/dmg/ghidra/GConv.java
Normal file
|
@ -0,0 +1,136 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.ghidra;
|
||||
|
||||
/**
|
||||
* Helper methods for converting between
|
||||
* number data types without negative
|
||||
* promotion.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class GConv {
|
||||
|
||||
private GConv() {
|
||||
}
|
||||
|
||||
/**
|
||||
* A byte mask.
|
||||
*/
|
||||
public static final int BYTE_MASK = 0xff;
|
||||
/**
|
||||
* A short mask.
|
||||
*/
|
||||
public static final int SHORT_MASK = 0xffff;
|
||||
/**
|
||||
* An integer mask.
|
||||
*/
|
||||
public static final long INT_MASK = 0x00000000ffffffffL;
|
||||
/**
|
||||
* Converts a byte to a short.
|
||||
* @param b the byte
|
||||
* @return the short equivalent of the byte
|
||||
*/
|
||||
public static short byteToShort(byte b) {
|
||||
return (short)(b & BYTE_MASK);
|
||||
}
|
||||
/**
|
||||
* Converts a byte to an integer.
|
||||
* @param b the byte
|
||||
* @return the integer equivalent of the byte
|
||||
*/
|
||||
public static int byteToInt(byte b) {
|
||||
return (b & BYTE_MASK);
|
||||
}
|
||||
/**
|
||||
* Converts a byte to a long.
|
||||
* @param b the byte
|
||||
* @return the long equivalent of the byte
|
||||
*/
|
||||
public static long byteToLong(byte b) {
|
||||
return intToLong(b & BYTE_MASK);
|
||||
}
|
||||
/**
|
||||
* Converts a short to an integer.
|
||||
* @param s the short
|
||||
* @return the integer equivalent of the short
|
||||
*/
|
||||
public static int shortToInt(short s) {
|
||||
return (s & SHORT_MASK);
|
||||
}
|
||||
/**
|
||||
* Converts a short to a long.
|
||||
* @param s the short
|
||||
* @return the long eqivalent of the short
|
||||
*/
|
||||
public static long shortToLong(short s) {
|
||||
return intToLong(s & SHORT_MASK);
|
||||
}
|
||||
/**
|
||||
* Converts an integer to a long.
|
||||
* @param i the integer
|
||||
* @return the long equivalent of the long
|
||||
*/
|
||||
public static long intToLong(int i) {
|
||||
return (i & INT_MASK);
|
||||
}
|
||||
|
||||
public static String toString(byte [] array) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for (byte b : array) {
|
||||
buffer.append((char)b);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
/**
|
||||
* Converts a byte into a padded hex string.
|
||||
* @param b the byte
|
||||
* @return the padded hex string
|
||||
*/
|
||||
public static String toHexString(byte b) {
|
||||
return zeropad(Integer.toHexString(byteToInt(b)), 2);
|
||||
}
|
||||
/**
|
||||
* Converts a short into a padded hex string.
|
||||
* @param s the short
|
||||
* @return the padded hex string
|
||||
*/
|
||||
public static String toHexString(short s) {
|
||||
return zeropad(Integer.toHexString(shortToInt(s)), 4);
|
||||
}
|
||||
/**
|
||||
* Converts an integer into a padded hex string.
|
||||
* @param i the integer
|
||||
* @return the padded hex string
|
||||
*/
|
||||
public static String toHexString(int i) {
|
||||
return zeropad(Integer.toHexString(i), 8);
|
||||
}
|
||||
/**
|
||||
* Converts a long into a padded hex string.
|
||||
* @param l the long
|
||||
* @return the padded hex string
|
||||
*/
|
||||
public static String toHexString(long l) {
|
||||
return zeropad(Long.toHexString(l), 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string that is extended to
|
||||
* length len with zeroes.
|
||||
* @param s The string to pad
|
||||
* @param len The length of the return string
|
||||
* @return A string that has been padded to be of legnth len
|
||||
*/
|
||||
public static String zeropad(String s, int len) {
|
||||
if (s == null) s = "";
|
||||
StringBuffer buffer = new StringBuffer(s);
|
||||
int zerosNeeded = len - s.length();
|
||||
for (int i = 0 ; i < zerosNeeded ; ++i) {
|
||||
buffer.insert(0, '0');
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.ghidra;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
*
|
||||
* Defines methods to convert byte arrays to a specific primitive Java types,
|
||||
* and to populate byte arrays from primitive Java types.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public interface GDataConverter extends Serializable {
|
||||
|
||||
/**
|
||||
* Get the short value from the given byte array.
|
||||
* @param b array containing bytes
|
||||
* @throws IndexOutOfBoundsException if byte array size is
|
||||
* less than 2.
|
||||
*/
|
||||
public short getShort(byte[] b);
|
||||
|
||||
/**
|
||||
* Get the short value from the given byte array.
|
||||
* @param b array containing bytes
|
||||
* @param offset offset into byte array for getting the short
|
||||
* @throws IndexOutOfBoundsException if byte array size is
|
||||
* less than offset+2.
|
||||
*/
|
||||
public short getShort(byte[] b, int offset);
|
||||
|
||||
/**
|
||||
* Get the int value from the given byte array.
|
||||
* @param b array containing bytes
|
||||
* @throws IndexOutOfBoundsException if byte array size is
|
||||
* less than 4.
|
||||
*/
|
||||
public int getInt(byte[] b);
|
||||
|
||||
/**
|
||||
* Get the int value from the given byte array.
|
||||
* @param b array containing bytes
|
||||
* @param offset offset into byte array for getting the int
|
||||
* @throws IndexOutOfBoundsException if byte array size is
|
||||
* less than offset+4.
|
||||
*/
|
||||
public int getInt(byte[] b, int offset);
|
||||
|
||||
/**
|
||||
* Get the long value from the given byte array.
|
||||
* @param b array containing bytes
|
||||
* @throws IndexOutOfBoundsException if byte array size is
|
||||
* less than 8.
|
||||
*/
|
||||
public long getLong(byte[] b);
|
||||
|
||||
/**
|
||||
* Get the long value from the given byte array.
|
||||
* @param b array containing bytes
|
||||
* @param offset offset into byte array for getting the long
|
||||
* @throws IndexOutOfBoundsException if byte array size is
|
||||
* less than offset+8.
|
||||
*/
|
||||
public long getLong(byte[] b, int offset);
|
||||
|
||||
/**
|
||||
* Get the value from the given byte array using the specified size.
|
||||
* @param b array containing bytes
|
||||
* @param size number of bytes to use from array at offset 0
|
||||
* @throws IndexOutOfBoundsException if byte array size is
|
||||
* less than size.
|
||||
*/
|
||||
public long getValue(byte[] b, int size);
|
||||
|
||||
/**
|
||||
* Get the value from the given byte array using the specified size.
|
||||
* @param b array containing bytes
|
||||
* @param size number of bytes to use from array
|
||||
* @param offset offset into byte array for getting the long
|
||||
* @throws IndexOutOfBoundsException if byte array size is
|
||||
* less than offset+size or size is greater than 8 (sizeof long).
|
||||
*/
|
||||
public long getValue(byte[] b, int offset, int size);
|
||||
|
||||
/**
|
||||
* Converts the given value to bytes.
|
||||
* @param value value to convert to bytes
|
||||
* @param b byte array to store bytes
|
||||
* @throws IndexOutOfBoundsException if b.length is not at least
|
||||
* 2.
|
||||
*/
|
||||
public void getBytes(short value, byte[] b);
|
||||
|
||||
/**
|
||||
* Converts the given value to bytes.
|
||||
* @param value value to convert to bytes
|
||||
* @param b byte array to store bytes
|
||||
* @param offset offset into byte array to put the bytes
|
||||
* @throws IndexOutOfBoundsException if (offset+2)>b.length
|
||||
*/
|
||||
public void getBytes(short value, byte[] b, int offset);
|
||||
|
||||
/**
|
||||
* Converts the given value to bytes.
|
||||
* @param value value to convert to bytes
|
||||
* @param b byte array to store bytes
|
||||
* @throws IndexOutOfBoundsException if b.length is not at least
|
||||
* 4.
|
||||
*/
|
||||
public void getBytes(int value, byte[] b);
|
||||
|
||||
/**
|
||||
* Converts the given value to bytes.
|
||||
* @param value value to convert to bytes
|
||||
* @param b byte array to store bytes
|
||||
* @param offset offset into byte array to put the bytes
|
||||
* @throws IndexOutOfBoundsException if (offset+4)>b.length
|
||||
*/
|
||||
public void getBytes(int value, byte[] b, int offset);
|
||||
|
||||
/**
|
||||
* Converts the given value to bytes.
|
||||
* @param value value to convert to bytes
|
||||
* @param b byte array to store bytes
|
||||
* @throws IndexOutOfBoundsException if b.length is not at least
|
||||
* 8.
|
||||
*/
|
||||
public void getBytes(long value, byte[] b);
|
||||
|
||||
/**
|
||||
* Converts the given value to bytes.
|
||||
* @param value value to convert to bytes
|
||||
* @param b byte array to store bytes
|
||||
* @param offset offset into byte array to put the bytes
|
||||
* @throws IndexOutOfBoundsException if (offset+8)>b.length
|
||||
*/
|
||||
public void getBytes(long value, byte[] b, int offset);
|
||||
|
||||
/**
|
||||
* Converts the given value to bytes using the number of least significant bytes
|
||||
* specified by size.
|
||||
* @param value value to convert to bytes
|
||||
* @param size number of least significant bytes of value to be written to the byte array
|
||||
* @param b byte array to store bytes
|
||||
* @param offset offset into byte array to put the bytes
|
||||
* @throws IndexOutOfBoundsException if (offset+size)>b.length.
|
||||
*/
|
||||
public void getBytes(long value, int size, byte[] b, int offset);
|
||||
|
||||
/**
|
||||
* Converts the short value to an array of bytes.
|
||||
* @param value short value to be converted
|
||||
* @return array of bytes
|
||||
*/
|
||||
public byte[] getBytes(short value);
|
||||
|
||||
/**
|
||||
* Converts the int value to an array of bytes.
|
||||
* @param value int value to be converted
|
||||
* @return array of bytes
|
||||
*/
|
||||
public byte[] getBytes(int value);
|
||||
|
||||
/**
|
||||
* Converts the long value to an array of bytes.
|
||||
* @param value long value to be converted
|
||||
* @return array of bytes
|
||||
*/
|
||||
public byte[] getBytes(long value);
|
||||
|
||||
/**
|
||||
* Writes a short value into a byte array.
|
||||
* @param b array to contain the bytes;
|
||||
* @param value the short value
|
||||
*/
|
||||
public void putShort(byte[] b, short value);
|
||||
|
||||
/**
|
||||
* Writes a short value into the byte array at the given offset
|
||||
* @param b array to contain the bytes;
|
||||
* @param offset the offset into the byte array to store the value.
|
||||
* @param value the short value
|
||||
*/
|
||||
public void putShort(byte[] b, int offset, short value);
|
||||
|
||||
/**
|
||||
* Writes a int value into a byte array.
|
||||
* @param b array to contain the bytes;
|
||||
* @param value the int value
|
||||
*/
|
||||
public void putInt(byte[] b, int value);
|
||||
|
||||
/**
|
||||
* Writes a int value into the byte array at the given offset
|
||||
* @param b array to contain the bytes;
|
||||
* @param offset the offset into the byte array to store the value.
|
||||
* @param value the int value
|
||||
*/
|
||||
public void putInt(byte[] b, int offset, int value);
|
||||
|
||||
/**
|
||||
* Writes a long value into a byte array.
|
||||
* @param b array to contain the bytes;
|
||||
* @param value the long value
|
||||
*/
|
||||
public void putLong(byte[] b, long value);
|
||||
|
||||
/**
|
||||
* Writes a long value into the byte array at the given offset
|
||||
* @param b array to contain the bytes;
|
||||
* @param offset the offset into the byte array to store the value.
|
||||
* @param value the long value
|
||||
*/
|
||||
public void putLong(byte[] b, int offset, long value);
|
||||
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.ghidra;
|
||||
|
||||
/**
|
||||
* Helper class to convert a byte array to Java primitives and primitives to a
|
||||
* byte array in Big endian.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class GDataConverterBE implements GDataConverter {
|
||||
public static final GDataConverterBE INSTANCE = new GDataConverterBE();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructor for BigEndianDataConverter.
|
||||
*/
|
||||
public GDataConverterBE() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getShort(byte[])
|
||||
*/
|
||||
public final short getShort(byte[] b) {
|
||||
return getShort(b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getShort(byte[], int)
|
||||
*/
|
||||
public short getShort(byte[] b, int offset) {
|
||||
return (short) (((b[offset] & 0xff) << 8) | (b[offset + 1] & 0xff));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getInt(byte[])
|
||||
*/
|
||||
public final int getInt(byte[] b) {
|
||||
return getInt(b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getInt(byte[], int)
|
||||
*/
|
||||
public int getInt(byte[] b, int offset) {
|
||||
int v = b[offset];
|
||||
for (int i = 1; i < 4; i++) {
|
||||
v = (v << 8) | (b[offset + i] & 0xff);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getLong(byte[])
|
||||
*/
|
||||
public final long getLong(byte[] b) {
|
||||
return getLong(b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getLong(byte[], int)
|
||||
*/
|
||||
public long getLong(byte[] b, int offset) {
|
||||
long v = b[offset];
|
||||
for (int i = 1; i < 8; i++) {
|
||||
v = (v << 8) | (b[offset + i] & 0xff);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter.util.DataConverter#getValue(byte[], int)
|
||||
*/
|
||||
public long getValue(byte[] b, int size) {
|
||||
return getValue(b, 0, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter.util.DataConverter#getValue(byte[], int, int)
|
||||
*/
|
||||
public long getValue(byte[] b, int offset, int size) {
|
||||
if (size > 8) {
|
||||
throw new IndexOutOfBoundsException("size exceeds sizeof long: " + size);
|
||||
}
|
||||
long val = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
val = (val << 8) | (b[offset + i] & 0xff);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getBytes(short, byte[])
|
||||
*/
|
||||
public final void getBytes(short value, byte[] b) {
|
||||
getBytes(value, b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getBytes(short, byte[], int)
|
||||
*/
|
||||
public void getBytes(short value, byte[] b, int offset) {
|
||||
b[offset] = (byte) (value >> 8);
|
||||
b[offset + 1] = (byte) (value & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getBytes(int, byte[])
|
||||
*/
|
||||
public final void getBytes(int value, byte[] b) {
|
||||
getBytes(value, b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getBytes(int, byte[], int)
|
||||
*/
|
||||
public void getBytes(int value, byte[] b, int offset) {
|
||||
b[offset + 3] = (byte) (value);
|
||||
for (int i = 2; i >= 0; i--) {
|
||||
value >>= 8;
|
||||
b[offset + i] = (byte) (value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getBytes(long, byte[])
|
||||
*/
|
||||
public final void getBytes(long value, byte[] b) {
|
||||
getBytes(value, 8, b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getBytes(long, byte[], int)
|
||||
*/
|
||||
public void getBytes(long value, byte[] b, int offset) {
|
||||
getBytes(value, 8, b, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter.util.DataConverter#getBytes(long, int, byte[], int)
|
||||
*/
|
||||
public void getBytes(long value, int size, byte[] b, int offset) {
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
b[offset + i] = (byte) value;
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter.util.DataConverter#putInt(byte[], int, int)
|
||||
*/
|
||||
public final void putInt(byte[] b, int offset, int value) {
|
||||
getBytes(value, b, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter.util.DataConverter#putInt(byte[], int)
|
||||
*/
|
||||
public final void putInt(byte[] b, int value) {
|
||||
getBytes(value, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter.util.DataConverter#putLong(byte[], int, long)
|
||||
*/
|
||||
public final void putLong(byte[] b, int offset, long value) {
|
||||
getBytes(value, b, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter.util.DataConverter#putLong(byte[], long)
|
||||
*/
|
||||
public final void putLong(byte[] b, long value) {
|
||||
getBytes(value, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter.util.DataConverter#putShort(byte[], int, short)
|
||||
*/
|
||||
public final void putShort(byte[] b, int offset, short value) {
|
||||
getBytes(value, b, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter.util.DataConverter#putShort(byte[], short)
|
||||
*/
|
||||
public final void putShort(byte[] b, short value) {
|
||||
getBytes(value, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter.util.DataConverter#getBytes(int)
|
||||
*/
|
||||
public byte[] getBytes(int value) {
|
||||
byte[] bytes = new byte[4];
|
||||
getBytes(value, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter.util.DataConverter#getBytes(long)
|
||||
*/
|
||||
public byte[] getBytes(long value) {
|
||||
byte[] bytes = new byte[8];
|
||||
getBytes(value, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter.util.DataConverter#getBytes(short)
|
||||
*/
|
||||
public byte[] getBytes(short value) {
|
||||
byte[] bytes = new byte[2];
|
||||
getBytes(value, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.ghidra;
|
||||
|
||||
/**
|
||||
*
|
||||
* Helper class to convert a byte array to a Java primitive in Little endian
|
||||
* order, and to convert a primitive to a byte array.
|
||||
*/
|
||||
|
||||
public class GDataConverterLE implements GDataConverter {
|
||||
public static GDataConverterLE INSTANCE = new GDataConverterLE();
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructor for BigEndianDataConverter.
|
||||
*/
|
||||
public GDataConverterLE() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getShort(byte[])
|
||||
*/
|
||||
public final short getShort(byte[] b) {
|
||||
return getShort(b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getShort(byte[], int)
|
||||
*/
|
||||
public short getShort(byte[] b, int offset) {
|
||||
return (short) (((b[offset + 1] & 0xff) << 8) | (b[offset] & 0xff));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getInt(byte[])
|
||||
*/
|
||||
public final int getInt(byte[] b) {
|
||||
return getInt(b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getInt(byte[], int)
|
||||
*/
|
||||
public int getInt(byte[] b, int offset) {
|
||||
int v = b[offset + 3];
|
||||
for (int i = 2; i >= 0; i--) {
|
||||
v = (v << 8) | (b[offset + i] & 0xff);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getLong(byte[])
|
||||
*/
|
||||
public final long getLong(byte[] b) {
|
||||
return getLong(b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getLong(byte[], int)
|
||||
*/
|
||||
public long getLong(byte[] b, int offset) {
|
||||
long v = b[offset + 7];
|
||||
for (int i = 6; i >= 0; i--) {
|
||||
v = (v << 8) | (b[offset + i] & 0xff);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.GDataConverter#getValue(byte[], int)
|
||||
*/
|
||||
public long getValue(byte[] b, int size) {
|
||||
return getValue(b, 0, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.GDataConverter#getValue(byte[], int, int)
|
||||
*/
|
||||
public long getValue(byte[] b, int offset, int size) {
|
||||
if (size > 8) {
|
||||
throw new IndexOutOfBoundsException("size exceeds sizeof long: " + size);
|
||||
}
|
||||
long val = 0;
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
val = (val << 8) | (b[offset + i] & 0xff);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getBytes(short, byte[])
|
||||
*/
|
||||
public final void getBytes(short value, byte[] b) {
|
||||
getBytes(value, b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getBytes(short, byte[], int)
|
||||
*/
|
||||
public void getBytes(short value, byte[] b, int offset) {
|
||||
b[offset + 1] = (byte) (value >> 8);
|
||||
b[offset] = (byte) (value & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getBytes(int, byte[])
|
||||
*/
|
||||
public final void getBytes(int value, byte[] b) {
|
||||
getBytes(value, b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getBytes(int, byte[], int)
|
||||
*/
|
||||
public void getBytes(int value, byte[] b, int offset) {
|
||||
b[offset] = (byte) (value);
|
||||
for (int i = 1; i < 4; i++) {
|
||||
value >>= 8;
|
||||
b[offset + i] = (byte) (value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getBytes(long, byte[])
|
||||
*/
|
||||
public final void getBytes(long value, byte[] b) {
|
||||
getBytes(value, 8, b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see GDataConverter#getBytes(long, byte[], int)
|
||||
*/
|
||||
public void getBytes(long value, byte[] b, int offset) {
|
||||
getBytes(value, 8, b, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.GDataConverter#getBytes(long, int, byte[], int)
|
||||
*/
|
||||
public void getBytes(long value, int size, byte[] b, int offset) {
|
||||
for (int i = 0; i < size; i++) {
|
||||
b[offset + i] = (byte) value;
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.GDataConverter#putInt(byte[], int, int)
|
||||
*/
|
||||
public final void putInt(byte[] b, int offset, int value) {
|
||||
getBytes(value, b, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.GDataConverter#putInt(byte[], int)
|
||||
*/
|
||||
public final void putInt(byte[] b, int value) {
|
||||
getBytes(value, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.GDataConverter#putLong(byte[], int, long)
|
||||
*/
|
||||
public final void putLong(byte[] b, int offset, long value) {
|
||||
getBytes(value, b, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.GDataConverter#putLong(byte[], long)
|
||||
*/
|
||||
public final void putLong(byte[] b, long value) {
|
||||
getBytes(value, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.GDataConverter#putShort(byte[], int, short)
|
||||
*/
|
||||
public final void putShort(byte[] b, int offset, short value) {
|
||||
getBytes(value, b, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.GDataConverter#putShort(byte[], short)
|
||||
*/
|
||||
public final void putShort(byte[] b, short value) {
|
||||
getBytes(value, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.GDataConverter#getBytes(int)
|
||||
*/
|
||||
public byte[] getBytes(int value) {
|
||||
byte[] bytes = new byte[4];
|
||||
getBytes(value, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.GDataConverter#getBytes(long)
|
||||
*/
|
||||
public byte[] getBytes(long value) {
|
||||
byte[] bytes = new byte[8];
|
||||
getBytes(value, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.GDataConverter#getBytes(short)
|
||||
*/
|
||||
public byte[] getBytes(short value) {
|
||||
byte[] bytes = new byte[2];
|
||||
getBytes(value, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.ghidra;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
|
||||
public final class GFileUtilityMethods {
|
||||
|
||||
private static final String GHIDRA_FILE_SYSTEM_PREFIX = "ghidra_file_system_";
|
||||
private static final String GHIDRA_FILE_SYSTEM_SUFFIX = ".tmp";
|
||||
|
||||
public final static File writeTemporaryFile( InputStream inputStream ) throws IOException {
|
||||
return writeTemporaryFile( inputStream , Integer.MAX_VALUE );
|
||||
}
|
||||
|
||||
public final static File writeTemporaryFile( InputStream inputStream, int maxBytesToWrite ) throws IOException {
|
||||
File tempOutputFile = File.createTempFile( GHIDRA_FILE_SYSTEM_PREFIX, GHIDRA_FILE_SYSTEM_SUFFIX );
|
||||
tempOutputFile.deleteOnExit();
|
||||
OutputStream outputStream = new FileOutputStream( tempOutputFile );
|
||||
try {
|
||||
int nWritten = 0;
|
||||
byte [] buffer = new byte[ 8192 ];
|
||||
while ( true ) {
|
||||
int nRead = inputStream.read( buffer );
|
||||
if ( nRead == -1 ) {
|
||||
break;
|
||||
}
|
||||
outputStream.write( buffer, 0, nRead );
|
||||
nWritten += nRead;
|
||||
if ( nWritten >= maxBytesToWrite ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
outputStream.close();
|
||||
}
|
||||
return tempOutputFile;
|
||||
}
|
||||
|
||||
public final static File writeTemporaryFile( byte [] bytes, String prefix ) throws IOException {
|
||||
if ( prefix == null ) {
|
||||
prefix = GHIDRA_FILE_SYSTEM_PREFIX;
|
||||
}
|
||||
if ( prefix.length() < 3 ) {//temp file prefix must be at least 3 chars in length
|
||||
for ( int i = prefix.length() ; i < 3 ; ++i ) {
|
||||
prefix = prefix + '_';
|
||||
}
|
||||
}
|
||||
File tempFile = File.createTempFile( prefix , GHIDRA_FILE_SYSTEM_SUFFIX );
|
||||
tempFile.deleteOnExit();
|
||||
OutputStream tempFileOut = new FileOutputStream( tempFile );
|
||||
try {
|
||||
tempFileOut.write( bytes );
|
||||
}
|
||||
finally {
|
||||
tempFileOut.close();
|
||||
}
|
||||
return tempFile;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.ghidra;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Instances of this class support both reading and writing to a
|
||||
* random access file. A random access file behaves like a large
|
||||
* array of bytes stored in the file system. There is a kind of cursor,
|
||||
* or index into the implied array, called the <em>file pointer</em>.
|
||||
* This implementation relies on java.net.RandomAccessFile,
|
||||
* but adds buffering to limit the amount.
|
||||
*/
|
||||
public class GRandomAccessFile {
|
||||
private static final byte[] EMPTY = new byte[0];
|
||||
private static final int BUFFER_SIZE = 0x100000;
|
||||
|
||||
private File file;
|
||||
private RandomAccessFile randomAccessFile;
|
||||
private byte[] buffer = EMPTY;
|
||||
private long bufferOffset = 0;
|
||||
private long bufferFileStartIndex = 0;
|
||||
private byte[] lastbuffer = EMPTY;
|
||||
private long lastbufferOffset = 0;
|
||||
private long lastbufferFileStartIndex = 0;
|
||||
private boolean open = false;
|
||||
|
||||
private void checkOpen() throws IOException {
|
||||
if (!open) {
|
||||
throw new IOException("GhidraRandomAccessFile is closed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a random access file stream to read from, and optionally to
|
||||
* write to, the file specified by the {@link File} argument. A new {@link
|
||||
* FileDescriptor} object is created to represent this file connection.
|
||||
*
|
||||
* <p>
|
||||
* This implementation relies on java.net.RandomAccessFile,
|
||||
* but adds buffering to limit the amount.
|
||||
* <p>
|
||||
*
|
||||
* <a name="mode"><p> The <tt>mode</tt> argument specifies the access mode
|
||||
* in which the file is to be opened. The permitted values and their
|
||||
* meanings are:
|
||||
*
|
||||
* <blockquote><table summary="Access mode permitted values and meanings">
|
||||
* <tr><th><p align="left">Value</p></th><th><p align="left">Meaning</p></th></tr>
|
||||
* <tr><td valign="top"><tt>"r"</tt></td>
|
||||
* <td> Open for reading only. Invoking any of the <tt>write</tt>
|
||||
* methods of the resulting object will cause an {@link
|
||||
* java.io.IOException} to be thrown. </td></tr>
|
||||
* <tr><td valign="top"><tt>"rw"</tt></td>
|
||||
* <td> Open for reading and writing. If the file does not already
|
||||
* exist then an attempt will be made to create it. </td></tr>
|
||||
* <tr><td valign="top"><tt>"rws"</tt></td>
|
||||
* <td> Open for reading and writing, as with <tt>"rw"</tt>, and also
|
||||
* require that every update to the file's content or metadata be
|
||||
* written synchronously to the underlying storage device. </td></tr>
|
||||
* <tr><td valign="top"><tt>"rwd" </tt></td>
|
||||
* <td> Open for reading and writing, as with <tt>"rw"</tt>, and also
|
||||
* require that every update to the file's content be written
|
||||
* synchronously to the underlying storage device. </td></tr>
|
||||
* </table></blockquote>
|
||||
*
|
||||
* @param file the file object
|
||||
* @param mode the access mode, as described
|
||||
* <a href="#mode">above</a>
|
||||
* @exception IllegalArgumentException if the mode argument is not equal
|
||||
* to one of <tt>"r"</tt>, <tt>"rw"</tt>, <tt>"rws"</tt>, or
|
||||
* <tt>"rwd"</tt>
|
||||
* @exception FileNotFoundException
|
||||
* that name cannot be created, or if some other error occurs
|
||||
* while opening or creating the file
|
||||
*/
|
||||
public GRandomAccessFile(File file, String mode) throws IOException {
|
||||
this.file = file;
|
||||
randomAccessFile = new RandomAccessFile(file, mode);
|
||||
this.open = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
if (open) {
|
||||
//TODO Msg.warn(this, "FAIL TO CLOSE " + file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this random access file stream and releases any system
|
||||
* resources associated with the stream. A closed random access
|
||||
* file cannot perform input or output operations and cannot be
|
||||
* reopened.
|
||||
* <p>
|
||||
* If this file has an associated channel then the channel is closed as well.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
checkOpen();
|
||||
open = false;
|
||||
randomAccessFile.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of this file.
|
||||
* @return the length of this file, measured in bytes.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public long length() throws IOException {
|
||||
checkOpen();
|
||||
return randomAccessFile.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file-pointer offset, measured from the beginning of this
|
||||
* file, at which the next read or write occurs. The offset may be
|
||||
* set beyond the end of the file. Setting the offset beyond the end
|
||||
* of the file does not change the file length. The file length will
|
||||
* change only by writing after the offset has been set beyond the end
|
||||
* of the file.
|
||||
* @param pos the offset position, measured in bytes from the
|
||||
* beginning of the file, at which to set the file
|
||||
* pointer.
|
||||
* @throws IOException
|
||||
* @exception IOException if <code>pos</code> is less than
|
||||
* <code>0</code> or if an I/O error occurs.
|
||||
*/
|
||||
public void seek(long pos) throws IOException {
|
||||
checkOpen();
|
||||
|
||||
if (pos < 0) {
|
||||
throw new IOException("pos cannot be less than zero");
|
||||
}
|
||||
|
||||
if (pos < bufferFileStartIndex || pos >= bufferFileStartIndex + BUFFER_SIZE) {
|
||||
// check if the last buffer contained it, and swap in if necessary
|
||||
swapInLast();
|
||||
if (pos < bufferFileStartIndex || pos >= bufferFileStartIndex + BUFFER_SIZE) {
|
||||
// not in either, gotta get a new one
|
||||
buffer = EMPTY;
|
||||
bufferOffset = 0;
|
||||
bufferFileStartIndex = pos;
|
||||
}
|
||||
}
|
||||
bufferOffset = pos - bufferFileStartIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method reads a byte from the file, starting from the current file pointer.
|
||||
* <p>
|
||||
* This method blocks until the byte is read, the end of the stream
|
||||
* is detected, or an exception is thrown.
|
||||
*
|
||||
* @return the next byte of this file as a signed eight-bit
|
||||
* <code>byte</code>.
|
||||
* @exception EOFException if this file has reached the end.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public byte readByte() throws IOException {
|
||||
checkOpen();
|
||||
ensure(1);
|
||||
return buffer[(int) bufferOffset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to <code>b.length</code> bytes of data from this file
|
||||
* into an array of bytes. This method blocks until at least one byte
|
||||
* of input is available.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @return the total number of bytes read into the buffer, or
|
||||
* <code>-1</code> if there is no more data because the end of
|
||||
* this file has been reached.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public int read(byte[] b) throws IOException {
|
||||
checkOpen();
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to <code>len</code> bytes of data from this file into an
|
||||
* array of bytes. This method blocks until at least one byte of input
|
||||
* is available.
|
||||
*
|
||||
* @param b the buffer into which the data is read.
|
||||
* @param off the start offset of the data.
|
||||
* @param len the maximum number of bytes read.
|
||||
* @return the total number of bytes read into the buffer, or
|
||||
* <code>-1</code> if there is no more data because the end of
|
||||
* the file has been reached.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public int read(byte[] b, int offset, int length) throws IOException {
|
||||
checkOpen();
|
||||
int readLen = length;
|
||||
do {
|
||||
int blocklength = readLen;
|
||||
if (readLen > (BUFFER_SIZE - bufferOffset)) {
|
||||
blocklength = (BUFFER_SIZE - (int) bufferOffset);
|
||||
if (blocklength <= 0) {
|
||||
blocklength = BUFFER_SIZE;
|
||||
}
|
||||
}
|
||||
ensure(blocklength);
|
||||
System.arraycopy(buffer, (int) bufferOffset, b, offset, blocklength);
|
||||
readLen -= blocklength;
|
||||
offset += blocklength;
|
||||
if (readLen > 0) {
|
||||
seek(this.bufferFileStartIndex + bufferOffset + blocklength);
|
||||
}
|
||||
}
|
||||
while (readLen > 0);
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a byte to this file, starting at the current file pointer.
|
||||
* @param b the data.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public void write(byte b) throws IOException {
|
||||
checkOpen();
|
||||
write(new byte[] { b }, 0, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes <code>b.length</code> bytes from the specified byte array
|
||||
* to this file, starting at the current file pointer.
|
||||
* @param b the data.
|
||||
* @exception IOException if an I/O error occurs.
|
||||
*/
|
||||
public void write(byte[] b) throws IOException {
|
||||
checkOpen();
|
||||
write(b, 0, b.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a sub array as a sequence of bytes.
|
||||
* @param b the data to be written
|
||||
* @param offset the start offset in the data
|
||||
* @param length the number of bytes that are written
|
||||
* @exception IOException If an I/O error has occurred.
|
||||
*/
|
||||
public void write(byte[] b, int offset, int length) throws IOException {
|
||||
checkOpen();
|
||||
randomAccessFile.write(b, offset, length);
|
||||
buffer = EMPTY;
|
||||
bufferOffset = 0;
|
||||
lastbuffer = EMPTY;
|
||||
lastbufferOffset = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that enough bytes are cached to
|
||||
* satisfy the next request to read.
|
||||
*/
|
||||
private void ensure(int bytesNeeded) throws IOException {
|
||||
checkOpen();
|
||||
long oldFileStartIndex = bufferFileStartIndex;
|
||||
long oldBufferOffset = bufferOffset;
|
||||
long oldSeekPos = oldFileStartIndex + oldBufferOffset;
|
||||
|
||||
if (bufferOffset + bytesNeeded > buffer.length) {
|
||||
// check if the last buffer contained it, and swap in if necessary
|
||||
swapInLast();
|
||||
// must ensure that current read pos is in old buffer, and enough bytes
|
||||
long newBufferOffset = (oldSeekPos - bufferFileStartIndex);
|
||||
if (oldSeekPos < bufferFileStartIndex ||
|
||||
oldSeekPos >= bufferFileStartIndex + BUFFER_SIZE ||
|
||||
(newBufferOffset + bytesNeeded > buffer.length)) {
|
||||
bufferFileStartIndex = oldFileStartIndex + oldBufferOffset;
|
||||
|
||||
buffer = new byte[BUFFER_SIZE];
|
||||
randomAccessFile.seek(bufferFileStartIndex);
|
||||
randomAccessFile.read(buffer);
|
||||
bufferOffset = 0;
|
||||
}
|
||||
else {
|
||||
bufferOffset = newBufferOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void swapInLast() throws IOException {
|
||||
checkOpen();
|
||||
if (buffer == EMPTY) {
|
||||
return;
|
||||
}
|
||||
// swap em and return
|
||||
byte[] swapbuffer = buffer;
|
||||
long swapbufferOffset = bufferOffset;
|
||||
long swapbufferFileStartIndex = bufferFileStartIndex;
|
||||
|
||||
buffer = lastbuffer;
|
||||
bufferOffset = lastbufferOffset;
|
||||
bufferFileStartIndex = lastbufferFileStartIndex;
|
||||
|
||||
lastbuffer = swapbuffer;
|
||||
lastbufferOffset = swapbufferOffset;
|
||||
lastbufferFileStartIndex = swapbufferFileStartIndex;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.ghidra;
|
||||
|
||||
|
||||
/**
|
||||
* Class with static methods that deal with string manipulation.
|
||||
*/
|
||||
public class GStringUtilities {
|
||||
|
||||
/**
|
||||
* Converts an integer into a string.
|
||||
* For example, given an integer 0x41424344,
|
||||
* the returned string would be "ABCD".
|
||||
* @param value the integer value
|
||||
* @return the converted string
|
||||
*/
|
||||
public static String toString(int value) {
|
||||
byte[] bytes = new byte[4];
|
||||
int byteIndex = bytes.length - 1;
|
||||
while (value != 0) {
|
||||
bytes[byteIndex] = (byte) value;
|
||||
value = value >> 8;
|
||||
--byteIndex;
|
||||
}
|
||||
return new String(bytes);
|
||||
}
|
||||
|
||||
public static String convertBytesToString( byte [] bytes, int length ) {
|
||||
StringBuffer buf = new StringBuffer( length * 2);
|
||||
for ( int i = 0 ; i < length ; ++i ) {
|
||||
String bs = Integer.toHexString( bytes[ i ] & 0xff );
|
||||
if ( bs.length() == 1 ) {
|
||||
buf.append( "0" );
|
||||
}
|
||||
buf.append( bs );
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static byte[] convertStringToBytes(String hexstr) {
|
||||
try {
|
||||
byte[] bytes = new byte[hexstr.length() / 2];
|
||||
for (int i = 0; i < hexstr.length(); i += 2) {
|
||||
String bs = hexstr.substring(i, i + 2);
|
||||
bytes[i / 2] = (byte) Integer.parseInt(bs, 16);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
catch (Exception e) {
|
||||
// tried, but failed
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.ghidra;
|
||||
|
||||
|
||||
public class GSystemUtilities {
|
||||
|
||||
public static boolean isEqual(Object o1, Object o2) {
|
||||
if (o1 == null) {
|
||||
return (o2 == null);
|
||||
}
|
||||
return o1.equals(o2);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.hfsplus;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.catacombae.hfsexplorer.fs.ImplHFSXFileSystemView;
|
||||
import org.catacombae.hfsexplorer.fs.NullProgressMonitor;
|
||||
import org.catacombae.hfsexplorer.types.hfscommon.CommonHFSCatalogFile;
|
||||
import org.catacombae.hfsexplorer.types.hfscommon.CommonHFSForkData;
|
||||
import org.catacombae.hfsexplorer.types.hfsplus.*;
|
||||
import org.catacombae.jparted.lib.fs.FSFile;
|
||||
import org.catacombae.jparted.lib.fs.hfscommon.HFSCommonFSFile;
|
||||
import org.catacombae.jparted.lib.fs.hfsx.HFSXFileSystemHandler;
|
||||
|
||||
import mobiledevices.dmg.btree.*;
|
||||
import mobiledevices.dmg.decmpfs.DecmpfsHeader;
|
||||
import mobiledevices.dmg.ghidra.GBinaryReader;
|
||||
import mobiledevices.dmg.ghidra.GByteProvider;
|
||||
|
||||
/**
|
||||
* This code will extract the attributes file from the HFS+ file system,
|
||||
* which contains the B-tree for traversing the DECOMPFS files.
|
||||
*/
|
||||
public class AttributesFileParser {
|
||||
|
||||
private Map<FSFile, DecmpfsHeader> map = new HashMap<FSFile, DecmpfsHeader>();
|
||||
private GByteProvider provider;
|
||||
private BTreeRootNodeDescriptor root;
|
||||
|
||||
public AttributesFileParser( HFSXFileSystemHandler handler, String prefix ) throws IOException {
|
||||
|
||||
ImplHFSXFileSystemView hfsxFileSystemView = (ImplHFSXFileSystemView) handler.getFSView();
|
||||
HFSPlusVolumeHeader volumeHeader = hfsxFileSystemView.getHFSPlusVolumeHeader();
|
||||
|
||||
HFSPlusForkData attributes = volumeHeader.getAttributesFile();
|
||||
|
||||
File attributesFile = writeVolumeHeaderFile( hfsxFileSystemView, attributes, prefix + "_" + "attributesFile" );
|
||||
|
||||
provider = new GByteProvider( attributesFile );
|
||||
|
||||
if ( attributesFile.length() == 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
GBinaryReader reader = new GBinaryReader( provider, false );
|
||||
|
||||
root = new BTreeRootNodeDescriptor( reader );
|
||||
}
|
||||
|
||||
public void dispose() throws IOException {
|
||||
map.clear();
|
||||
provider.close();
|
||||
}
|
||||
|
||||
private int getFileID(FSFile file) {
|
||||
try {
|
||||
HFSCommonFSFile hfsFile = (HFSCommonFSFile)file;
|
||||
CommonHFSCatalogFile catalogFile = hfsFile.getInternalCatalogFile();
|
||||
CommonHFSCatalogFile.HFSPlusImplementation hfsPlusCatalogFile = (CommonHFSCatalogFile.HFSPlusImplementation)catalogFile;
|
||||
HFSPlusCatalogFile underlying = hfsPlusCatalogFile.getUnderlying();
|
||||
HFSCatalogNodeID fileID = underlying.getFileID();
|
||||
return fileID.toInt();
|
||||
}
|
||||
catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private File writeVolumeHeaderFile( ImplHFSXFileSystemView hfsxFileSystemView,
|
||||
HFSPlusForkData volumeHeaderFile,
|
||||
String volumeHeaderFileName ) throws IOException {
|
||||
|
||||
if (volumeHeaderFile == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File file = File.createTempFile( "Ghidra_" + volumeHeaderFileName + "_", ".tmp" );
|
||||
file.deleteOnExit();
|
||||
OutputStream out = new FileOutputStream( file );
|
||||
try {
|
||||
CommonHFSForkData fork = CommonHFSForkData.create( volumeHeaderFile );
|
||||
hfsxFileSystemView.extractForkToStream( fork, fork.getBasicExtents(), out, new NullProgressMonitor() {} );
|
||||
}
|
||||
finally {
|
||||
out.close();
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
public DecmpfsHeader getDecmpfsHeader(FSFile file) throws IOException {
|
||||
|
||||
if ( root == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( map.get( file ) != null ) {
|
||||
return map.get( file );
|
||||
}
|
||||
|
||||
int fileID = getFileID( file );
|
||||
|
||||
if ( fileID == -1 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for ( BTreeNodeDescriptor node : root.getNodes() ) {
|
||||
for ( BTreeNodeRecord record : node.getRecords() ) {
|
||||
if ( record.getFileID() == fileID ) {
|
||||
DecmpfsHeader header = record.getDecmpfsHeader();
|
||||
if ( header != null ) {
|
||||
map.put( file, header );
|
||||
return header;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
344
GPL/DMG/src/dmg/java/mobiledevices/dmg/reader/DmgFileReader.java
Normal file
344
GPL/DMG/src/dmg/java/mobiledevices/dmg/reader/DmgFileReader.java
Normal file
|
@ -0,0 +1,344 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.reader;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.catacombae.dmgextractor.encodings.encrypted.ReadableCEncryptedEncodingStream;
|
||||
import org.catacombae.hfsexplorer.FileSystemRecognizer;
|
||||
import org.catacombae.hfsexplorer.PartitionSystemRecognizer;
|
||||
import org.catacombae.hfsexplorer.UDIFRecognizer;
|
||||
import org.catacombae.hfsexplorer.partitioning.Partition;
|
||||
import org.catacombae.hfsexplorer.partitioning.PartitionSystem;
|
||||
import org.catacombae.hfsexplorer.win32.WindowsLowLevelIO;
|
||||
import org.catacombae.io.*;
|
||||
import org.catacombae.jparted.lib.DataLocator;
|
||||
import org.catacombae.jparted.lib.ReadableStreamDataLocator;
|
||||
import org.catacombae.jparted.lib.fs.*;
|
||||
import org.catacombae.jparted.lib.fs.FileSystemHandlerFactory.StandardAttribute;
|
||||
import org.catacombae.jparted.lib.fs.hfsx.HFSXFileSystemHandler;
|
||||
import org.catacombae.udif.UDIFFile;
|
||||
import org.catacombae.udif.UDIFRandomAccessStream;
|
||||
|
||||
import mobiledevices.dmg.decmpfs.DecmpfsCompressionTypes;
|
||||
import mobiledevices.dmg.decmpfs.DecmpfsHeader;
|
||||
import mobiledevices.dmg.ghidra.*;
|
||||
import mobiledevices.dmg.hfsplus.AttributesFileParser;
|
||||
import mobiledevices.dmg.zlib.ZLIB;
|
||||
|
||||
public class DmgFileReader implements Closeable {
|
||||
private final static GDataConverter ledc = new GDataConverterLE();
|
||||
private final static GDataConverter bedc = new GDataConverterBE();
|
||||
|
||||
private GByteProvider provider;
|
||||
private AttributesFileParser parser;
|
||||
private ReadableRandomAccessStream rras;
|
||||
private List<FSFolder> rootFolders = new ArrayList<FSFolder>();
|
||||
private List<FileSystemHandler> fileSystemHandlers = new ArrayList<FileSystemHandler>();
|
||||
|
||||
public DmgFileReader( GByteProvider provider ) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public void open() throws IOException {
|
||||
|
||||
File file = provider.getFile();
|
||||
|
||||
if (WindowsLowLevelIO.isSystemSupported()) {
|
||||
rras = new WindowsLowLevelIO(file.getAbsolutePath());
|
||||
}
|
||||
else {
|
||||
rras = new ReadableFileStream(file.getAbsolutePath());
|
||||
}
|
||||
|
||||
if (ReadableCEncryptedEncodingStream.isCEncryptedEncoding(rras)) {
|
||||
//TODO use our decryption instead??
|
||||
}
|
||||
|
||||
System.err.println("Trying to detect UDIF structure...");
|
||||
if (UDIFRecognizer.isUDIF(rras)) {
|
||||
System.err.println("UDIF structure found! Creating filter stream...");
|
||||
|
||||
UDIFFile udifFile = new UDIFFile(new ReadableFileStream(file.getAbsolutePath()));
|
||||
debug(udifFile.getView().getPlistData(), "dmg-xml");
|
||||
|
||||
UDIFRandomAccessStream stream = new UDIFRandomAccessStream(rras);
|
||||
rras = stream;
|
||||
}
|
||||
else {
|
||||
System.err.println("UDIF structure not found. Proceeding...");
|
||||
}
|
||||
|
||||
PartitionSystemRecognizer partitionSystemRecognizer = new PartitionSystemRecognizer(rras);
|
||||
PartitionSystem partitionSystem = partitionSystemRecognizer.getPartitionSystem();
|
||||
|
||||
if (partitionSystem == null) {
|
||||
throw new IOException("No system partitions found. Perhaps the decryption failed?");
|
||||
}
|
||||
|
||||
Partition[] partitions = partitionSystem.getUsedPartitionEntries();
|
||||
for (Partition partition : partitions) {
|
||||
openPartition(partition);
|
||||
}
|
||||
}
|
||||
|
||||
private void debug( byte [] plistData, String fileName ) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
private void openPartition( Partition selectedPartition ) throws IOException {
|
||||
long fsOffset = selectedPartition.getStartOffset();//getPmPyPartStart()+selectedPartition.getPmLgDataStart())*blockSize;
|
||||
long fsLength = selectedPartition.getLength();//getPmDataCnt()*blockSize;
|
||||
|
||||
FileSystemRecognizer fsr = new FileSystemRecognizer( rras, fsOffset );
|
||||
FileSystemRecognizer.FileSystemType fsType = fsr.detectFileSystem();
|
||||
|
||||
if ( fsType == FileSystemRecognizer.FileSystemType.HFS_PLUS ||
|
||||
fsType == FileSystemRecognizer.FileSystemType.HFSX ||
|
||||
fsType == FileSystemRecognizer.FileSystemType.HFS ) {
|
||||
|
||||
final FileSystemMajorType fsMajorType;
|
||||
switch ( fsType ) {
|
||||
case HFS:
|
||||
fsMajorType = FileSystemMajorType.APPLE_HFS;
|
||||
break;
|
||||
case HFS_PLUS:
|
||||
fsMajorType = FileSystemMajorType.APPLE_HFS_PLUS;
|
||||
break;
|
||||
case HFSX:
|
||||
fsMajorType = FileSystemMajorType.APPLE_HFSX;
|
||||
break;
|
||||
default:
|
||||
fsMajorType = null;
|
||||
break;
|
||||
}
|
||||
|
||||
FileSystemHandlerFactory factory = fsMajorType.createDefaultHandlerFactory();
|
||||
if ( factory.isSupported( StandardAttribute.CACHING_ENABLED ) ) {
|
||||
factory.getCreateAttributes().
|
||||
setBooleanAttribute(StandardAttribute.CACHING_ENABLED,
|
||||
true);
|
||||
}
|
||||
|
||||
ReadableRandomAccessStream stage1;
|
||||
|
||||
if (fsLength > 0) {
|
||||
stage1 = new ReadableConcatenatedStream(rras, fsOffset, fsLength);
|
||||
}
|
||||
else {
|
||||
stage1 = rras;
|
||||
}
|
||||
|
||||
DataLocator dataLocator = new ReadableStreamDataLocator(stage1);
|
||||
|
||||
FileSystemHandler fileSystemHandler = factory.createHandler(dataLocator);
|
||||
fileSystemHandlers.add( fileSystemHandler );
|
||||
|
||||
rootFolders.add( fileSystemHandler.getRoot() );
|
||||
|
||||
if ( fileSystemHandler instanceof HFSXFileSystemHandler ) {
|
||||
parser = new AttributesFileParser( (HFSXFileSystemHandler)fileSystemHandler, fileSystemHandler.getRoot( ).getName( ) );
|
||||
}
|
||||
} else {
|
||||
System.err.println("UNKNOWN file system type. Can't Open filesystem. Suspect this is an APFS.\n");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
rras.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
//ignore
|
||||
}
|
||||
if ( parser != null ) {
|
||||
parser.dispose();
|
||||
parser = null;
|
||||
}
|
||||
fileSystemHandlers.clear();
|
||||
rootFolders.clear();
|
||||
}
|
||||
|
||||
public InputStream getData( FSEntry entry ) throws IOException {
|
||||
if ( entry != null && entry.isFile() ) {
|
||||
FSFile fsFile = (FSFile)entry;
|
||||
FSFork mainFork = fsFile.getMainFork();
|
||||
if ( mainFork.getLength() > 0 ) {
|
||||
ReadableRandomAccessStream mainForkStream = mainFork.getReadableRandomAccessStream();
|
||||
if ( mainForkStream.length() != 0 ) {
|
||||
return new DmgInputStream( mainForkStream );
|
||||
}
|
||||
}
|
||||
else if ( mainFork.getLength() == 0 ) {
|
||||
|
||||
FSFork resourceFork = fsFile.getForkByType( FSForkType.MACOS_RESOURCE );
|
||||
ReadableRandomAccessStream resourceForkStream = resourceFork.getReadableRandomAccessStream();
|
||||
|
||||
if ( parser == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DecmpfsHeader decmpfsHeader = parser.getDecmpfsHeader( fsFile );
|
||||
|
||||
if ( decmpfsHeader == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( decmpfsHeader.getCompressionType() == DecmpfsCompressionTypes.CMP_Type3 ) {
|
||||
|
||||
if ( decmpfsHeader.getAttrBytes()[ 0 ] == -1 ) {
|
||||
return new ByteArrayInputStream( decmpfsHeader.getAttrBytes(), 1, decmpfsHeader.getAttrBytes().length - 1 );
|
||||
}
|
||||
|
||||
ZLIB zlib = new ZLIB();
|
||||
|
||||
InputStream inputStream =
|
||||
new ByteArrayInputStream(decmpfsHeader.getAttrBytes());
|
||||
|
||||
ByteArrayOutputStream uncompressedBytes =
|
||||
zlib.decompress(inputStream, (int) decmpfsHeader.getUncompressedSize());
|
||||
|
||||
File tempDecompressedFile = GFileUtilityMethods.writeTemporaryFile(
|
||||
uncompressedBytes.toByteArray(), entry.getName());
|
||||
return new FileInputStream(tempDecompressedFile);
|
||||
}
|
||||
else if ( decmpfsHeader.getCompressionType() == DecmpfsCompressionTypes.CMP_Type4 ) {
|
||||
|
||||
return decompressResourceFork( entry, resourceForkStream, (int)decmpfsHeader.getUncompressedSize() );
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private InputStream decompressResourceFork( FSEntry entry,
|
||||
ReadableRandomAccessStream resourceForkStream,
|
||||
int expectedLength ) throws IOException {
|
||||
|
||||
File tempFile = GFileUtilityMethods.writeTemporaryFile( new DmgInputStream( resourceForkStream ) );
|
||||
System.err.println(
|
||||
"dmg resource fork for " + entry.getName() + ": " + tempFile.getAbsolutePath());
|
||||
|
||||
InputStream input = new FileInputStream( tempFile );
|
||||
|
||||
for ( int i = 0 ; i < 0x100 ; ++i ) {
|
||||
input.read();
|
||||
}
|
||||
|
||||
byte [] sizeBytes = new byte[ 4 ];
|
||||
input.read( sizeBytes );
|
||||
int size = sizeBytes[ 0 ] == 0 ?
|
||||
bedc.getInt( sizeBytes ) :
|
||||
ledc.getInt( sizeBytes );
|
||||
|
||||
byte [] flagsBytes = new byte[ 4 ];
|
||||
input.read( flagsBytes );
|
||||
|
||||
byte [] startDistanceBytes = new byte[ 4 ];
|
||||
input.read( startDistanceBytes );
|
||||
int startDistance = ledc.getInt( startDistanceBytes );
|
||||
|
||||
input.skip( startDistance - 8 );//skip to the start of the zlib compressed file
|
||||
|
||||
File tempCompressedFile = GFileUtilityMethods.writeTemporaryFile( input, size - startDistance );
|
||||
InputStream inputStream = new FileInputStream( tempCompressedFile );
|
||||
|
||||
ZLIB zlib = new ZLIB( );
|
||||
ByteArrayOutputStream uncompressedByteStream = zlib.decompress( inputStream, expectedLength );
|
||||
|
||||
return new ByteArrayInputStream( uncompressedByteStream.toByteArray() );
|
||||
}
|
||||
|
||||
public List<String> getInfo( String path ) {
|
||||
if ( path != null ) {
|
||||
DmgInfoGenerator info = new DmgInfoGenerator( this, path, parser );
|
||||
return info.getInformation( );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<FSEntry> getListing( String path ) {
|
||||
List<FSEntry> list = new ArrayList<FSEntry>();
|
||||
if ( path == null || path.equals( "/" ) ) {
|
||||
for ( FileSystemHandler handler : fileSystemHandlers ) {
|
||||
list.add( handler.getRoot() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
FSEntry fileByPath = getFileByPath( path );
|
||||
if ( fileByPath != null ) {
|
||||
if ( fileByPath.isFolder() ) {
|
||||
FSEntry [] listEntries = fileByPath.asFolder().listEntries();
|
||||
for ( FSEntry entry : listEntries ) {
|
||||
list.add( entry );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the given file system entry.
|
||||
* If the entry is actually a directory, then -1 is returned.
|
||||
*/
|
||||
public long getLength( FSEntry entry ) {
|
||||
if ( entry != null & entry.isFile() ) {
|
||||
FSFork mainFork = entry.asFile().getMainFork();
|
||||
if ( mainFork.getLength() > 0 ) {
|
||||
return mainFork.getLength();
|
||||
}
|
||||
try {
|
||||
if (parser != null) {
|
||||
DecmpfsHeader header = parser.getDecmpfsHeader(entry.asFile());
|
||||
if (header != null) {
|
||||
return header.getUncompressedSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
return 1;//TODO lookup valid length in DECMPFS
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert path to string array.
|
||||
*
|
||||
* For example, "/a/b/c.txt" will be converted to [ "a", "b", "c.txt" ].
|
||||
*
|
||||
* Note: the "a" will be stripped because it corresponds to the file system handler.
|
||||
*/
|
||||
public String [] convertPathToArrayAndStripFileSystemName( String path ) {
|
||||
String [] splitPath = path.split( "/" );
|
||||
if ( splitPath.length <= 2 ) {
|
||||
return new String[ 0 ];
|
||||
}
|
||||
String [] temp = new String[ splitPath.length - 2 ];
|
||||
System.arraycopy( splitPath, 2, temp, 0, splitPath.length - 2 );
|
||||
return temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DMG file object for the corresponding path.
|
||||
* Path should contain the file system handler name.
|
||||
*/
|
||||
public FSEntry getFileByPath( String path ) {
|
||||
if ( path == null || path.equals( "/" ) ) {//ROOT
|
||||
return null;
|
||||
}
|
||||
for ( FileSystemHandler handler : fileSystemHandlers ) {
|
||||
FSEntry entry = handler.getEntry( convertPathToArrayAndStripFileSystemName( path ) );
|
||||
if ( entry != null ) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.catacombae.hfsexplorer.ObjectContainer;
|
||||
import org.catacombae.hfsexplorer.types.hfscommon.CommonHFSCatalogFile;
|
||||
import org.catacombae.hfsexplorer.types.hfsplus.HFSCatalogNodeID;
|
||||
import org.catacombae.hfsexplorer.types.hfsplus.HFSPlusCatalogFile;
|
||||
import org.catacombae.jparted.lib.fs.*;
|
||||
import org.catacombae.jparted.lib.fs.FSAttributes.POSIXFileAttributes;
|
||||
import org.catacombae.jparted.lib.fs.hfscommon.HFSCommonFSFile;
|
||||
|
||||
import mobiledevices.dmg.decmpfs.DecmpfsHeader;
|
||||
import mobiledevices.dmg.hfsplus.AttributesFileParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* @see org.catacombae.hfsexplorer.gui.FSEntrySummaryPanel
|
||||
*
|
||||
*/
|
||||
class DmgInfoGenerator {
|
||||
private DmgFileReader fileSystem;
|
||||
private String filePath;
|
||||
private AttributesFileParser parser;
|
||||
private FSEntry entry;
|
||||
private DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);
|
||||
|
||||
DmgInfoGenerator(DmgFileReader fileSystem, String filePath, AttributesFileParser parser) {
|
||||
this.fileSystem = fileSystem;
|
||||
this.filePath = filePath;
|
||||
this.parser = parser;
|
||||
this.entry = fileSystem.getFileByPath(filePath);
|
||||
}
|
||||
|
||||
List<String> getInformation() {
|
||||
List<String> infoList = new ArrayList<String>();
|
||||
|
||||
if (entry == null) {
|
||||
infoList.add("<< no information available >>");
|
||||
return infoList;
|
||||
}
|
||||
|
||||
infoList.add("Name: " + entry.getName());
|
||||
|
||||
if (entry instanceof FSFile) {
|
||||
FSFile file = (FSFile) entry;
|
||||
infoList.add("Type: " + "File");
|
||||
infoList.add("Total Size: " + getSizeString(file.getCombinedLength()));
|
||||
FSFork[] allForks = file.getAllForks();
|
||||
for (FSFork fork : allForks) {
|
||||
infoList.add(
|
||||
" " + fork.getForkIdentifier() + ": " + getSizeString(fork.getLength()));
|
||||
}
|
||||
appendFileID(infoList, file);
|
||||
|
||||
if (parser != null) {
|
||||
try {
|
||||
DecmpfsHeader decmpfsHeader = parser.getDecmpfsHeader(file);
|
||||
if (decmpfsHeader != null) {
|
||||
infoList.add(
|
||||
"Decmpfs Size: " + getSizeString(decmpfsHeader.getUncompressedSize()));
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (entry instanceof FSFolder) {
|
||||
FSFolder folder = (FSFolder) entry;
|
||||
infoList.add("Type: " + "Folder");
|
||||
infoList.add("Size: " + startFolderSizeCalculation(folder));
|
||||
}
|
||||
else if (entry instanceof FSLink) {
|
||||
FSLink link = (FSLink) entry;
|
||||
|
||||
FSEntry linkTarget =
|
||||
link.getLinkTarget(fileSystem.convertPathToArrayAndStripFileSystemName(filePath));
|
||||
if (linkTarget == null) {
|
||||
infoList.add("Type: " + "Symbolic link (broken)");
|
||||
infoList.add("Size: " + "- (broken link)");
|
||||
}
|
||||
else if (linkTarget instanceof FSFile) {
|
||||
FSFile file = (FSFile) linkTarget;
|
||||
infoList.add("Type: " + "Symbolic link (file)");
|
||||
infoList.add("Size: " + getSizeString(file.getMainFork().getLength()));
|
||||
FSFork[] allForks = file.getAllForks();
|
||||
for (FSFork fork : allForks) {
|
||||
infoList.add(
|
||||
" " + fork.getForkIdentifier() + ": " + getSizeString(fork.getLength()));
|
||||
}
|
||||
}
|
||||
else if (linkTarget instanceof FSFolder) {
|
||||
FSFolder folder = (FSFolder) linkTarget;
|
||||
infoList.add("Type: " + "Symbolic link (folder)");
|
||||
infoList.add("Size: " + startFolderSizeCalculation(folder));
|
||||
}
|
||||
else {
|
||||
infoList.add("Type: " + "Symbolic link (unknown [" + linkTarget.getClass() + "])");
|
||||
infoList.add("Size: " + "- (unknown type)");
|
||||
}
|
||||
infoList.add("Link Target: " + link.getLinkTargetString());
|
||||
}
|
||||
else {
|
||||
infoList.add("Type: " + "Unknown [" + entry.getClass() + "]");
|
||||
infoList.add("Size: " + "- (unknown size)");
|
||||
}
|
||||
|
||||
FSAttributes attrs = entry.getAttributes();
|
||||
|
||||
appendDateInformation(attrs, infoList);
|
||||
appendPosixInformation(attrs, infoList);
|
||||
appendWindowsInformation(attrs, infoList);
|
||||
|
||||
return infoList;
|
||||
}
|
||||
|
||||
private void appendFileID(List<String> infoList, FSFile file) {
|
||||
try {
|
||||
HFSCommonFSFile hfsFile = (HFSCommonFSFile) file;
|
||||
CommonHFSCatalogFile catalogFile = hfsFile.getInternalCatalogFile();
|
||||
CommonHFSCatalogFile.HFSPlusImplementation hfsPlusCatalogFile =
|
||||
(CommonHFSCatalogFile.HFSPlusImplementation) catalogFile;
|
||||
HFSPlusCatalogFile underlying = hfsPlusCatalogFile.getUnderlying();
|
||||
HFSCatalogNodeID fileID = underlying.getFileID();
|
||||
infoList.add("File ID: 0x" + Integer.toHexString(fileID.toInt()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
infoList.add("Unable to obtain file ID. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void appendPosixInformation(FSAttributes attrs, List<String> infoList) {
|
||||
if (attrs.hasPOSIXFileAttributes()) {
|
||||
POSIXFileAttributes posixAttrs = attrs.getPOSIXFileAttributes();
|
||||
infoList.add("Permissions: " + posixAttrs.getPermissionString());
|
||||
infoList.add("User ID: " + posixAttrs.getUserID());
|
||||
infoList.add("Group ID: " + posixAttrs.getGroupID());
|
||||
}
|
||||
}
|
||||
|
||||
private void appendWindowsInformation(FSAttributes attrs, List<String> infoList) {
|
||||
if (attrs.hasWindowsFileAttributes()) {
|
||||
WindowsFileAttributes windowsFileAttributes = attrs.getWindowsFileAttributes();
|
||||
infoList.add("Archive: " + windowsFileAttributes.isArchive());
|
||||
infoList.add("Compressed: " + windowsFileAttributes.isCompressed());
|
||||
infoList.add("Directory: " + windowsFileAttributes.isDirectory());
|
||||
infoList.add("Encrypted: " + windowsFileAttributes.isEncrypted());
|
||||
infoList.add("Hidden: " + windowsFileAttributes.isHidden());
|
||||
infoList.add("Normal: " + windowsFileAttributes.isNormal());
|
||||
infoList.add("Off-line: " + windowsFileAttributes.isOffline());
|
||||
infoList.add("Read-only: " + windowsFileAttributes.isReadOnly());
|
||||
infoList.add("Reparse: " + windowsFileAttributes.isReparsePoint());
|
||||
infoList.add("Sparse: " + windowsFileAttributes.isSparseFile());
|
||||
infoList.add("System: " + windowsFileAttributes.isSystem());
|
||||
infoList.add("Temp: " + windowsFileAttributes.isTemporary());
|
||||
infoList.add("Virtual: " + windowsFileAttributes.isVirtual());
|
||||
}
|
||||
}
|
||||
|
||||
private void appendDateInformation(FSAttributes attributes, List<String> infoList) {
|
||||
if (attributes.hasCreateDate()) {
|
||||
infoList.add("Created: " + df.format(attributes.getCreateDate()));
|
||||
}
|
||||
if (attributes.hasModifyDate()) {
|
||||
infoList.add("Contents Modified: " + df.format(attributes.getModifyDate()));
|
||||
}
|
||||
if (attributes.hasAttributeModifyDate()) {
|
||||
infoList.add("Attributes Modified: " + df.format(attributes.getAttributeModifyDate()));
|
||||
}
|
||||
if (attributes.hasAccessDate()) {
|
||||
infoList.add("Last Accessed: " + df.format(attributes.getAccessDate()));
|
||||
}
|
||||
if (attributes.hasBackupDate()) {
|
||||
infoList.add("Last Backup: " + df.format(attributes.getBackupDate()));
|
||||
}
|
||||
}
|
||||
|
||||
private String getSizeString(long result) {
|
||||
String baseString = Long.toString(result);
|
||||
return baseString + " bytes";
|
||||
}
|
||||
|
||||
private String startFolderSizeCalculation(FSFolder folder) {
|
||||
String resultString;
|
||||
try {
|
||||
ObjectContainer<Long> result = new ObjectContainer<Long>((long) 0);
|
||||
calculateFolderSize(folder, result);
|
||||
resultString = getSizeString(result.o);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
resultString = "Exception while calculating! See debug console for info...";
|
||||
}
|
||||
return resultString;
|
||||
}
|
||||
|
||||
private void calculateFolderSize(FSFolder folder, ObjectContainer<Long> result) {
|
||||
for (FSEntry entry : folder.listEntries()) {
|
||||
if (entry instanceof FSFile) {
|
||||
Long value = result.o;
|
||||
value += ((FSFile) entry).getMainFork().getLength();
|
||||
result.o = value;
|
||||
}
|
||||
else if (entry instanceof FSFolder) {
|
||||
calculateFolderSize((FSFolder) entry, result);
|
||||
}
|
||||
else if (entry instanceof FSLink) {
|
||||
/* Do nothing. Symbolic link targets aren't part of the folder. */
|
||||
}
|
||||
else {
|
||||
System.err.println("FSEntrySummaryPanel.calculateFolderSize():" +
|
||||
" unexpected type " + entry.getClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.reader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.catacombae.io.ReadableRandomAccessStream;
|
||||
|
||||
/**
|
||||
* A class to wrap a ReadableRandomAccessStream
|
||||
* so it may be used as a conventional
|
||||
* input stream.
|
||||
*/
|
||||
public class DmgInputStream extends InputStream {
|
||||
private ReadableRandomAccessStream stream;
|
||||
|
||||
DmgInputStream(ReadableRandomAccessStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
public long getLength() {
|
||||
return stream.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return stream.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte [] b) throws IOException {
|
||||
return stream.read(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte [] b, int off, int len) throws IOException {
|
||||
return stream.read(b, off, len);
|
||||
}
|
||||
|
||||
|
||||
}
|
193
GPL/DMG/src/dmg/java/mobiledevices/dmg/server/DmgServer.java
Normal file
193
GPL/DMG/src/dmg/java/mobiledevices/dmg/server/DmgServer.java
Normal file
|
@ -0,0 +1,193 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.server;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
|
||||
import org.catacombae.jparted.lib.fs.*;
|
||||
|
||||
import mobiledevices.dmg.ghidra.GByteProvider;
|
||||
import mobiledevices.dmg.ghidra.GFileUtilityMethods;
|
||||
import mobiledevices.dmg.reader.DmgFileReader;
|
||||
|
||||
public class DmgServer {
|
||||
|
||||
private static void writeln(String s) {
|
||||
|
||||
StringBuilder encoded = new StringBuilder();
|
||||
char[] charArray = s.toCharArray();
|
||||
for (char c : charArray) {
|
||||
if (c == 11) {
|
||||
encoded.append(c);// tab
|
||||
}
|
||||
|
||||
if (c <= 31 || c == 127) {
|
||||
continue;// control characters
|
||||
}
|
||||
|
||||
encoded.append(c);
|
||||
}
|
||||
|
||||
System.out.println(encoded.toString());
|
||||
}
|
||||
|
||||
public static void sendResponse(String s) {
|
||||
System.out.println(s);
|
||||
System.out.flush();
|
||||
}
|
||||
|
||||
public static void sendResponses(String... responseStrs) {
|
||||
for (String s : responseStrs) {
|
||||
System.out.println(s);
|
||||
}
|
||||
System.out.flush();
|
||||
}
|
||||
|
||||
public static void log(String... logstrs) {
|
||||
for (String s : logstrs) {
|
||||
System.err.println(s);
|
||||
}
|
||||
System.err.flush();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
log("Waiting for client to connect to DMG server...");
|
||||
|
||||
BufferedReader inputReader = new BufferedReader(new InputStreamReader(System.in));
|
||||
|
||||
try {
|
||||
String openLine = inputReader.readLine();
|
||||
if (openLine == null) {
|
||||
return;
|
||||
}
|
||||
if (!openLine.startsWith("open ")) {
|
||||
return;//TODO handle invalid initial command???
|
||||
}
|
||||
String openPath = parseLine(openLine);
|
||||
|
||||
File openFile = new File(openPath);
|
||||
if (!openFile.exists()) {//TODO handle files that do not exist
|
||||
|
||||
}
|
||||
|
||||
try (GByteProvider provider = new GByteProvider(openFile);
|
||||
DmgFileReader dmgFileReader = new DmgFileReader(provider);) {
|
||||
dmgFileReader.open();
|
||||
while (true) {
|
||||
String line = inputReader.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
String[] parts = line.split(" ", 2);
|
||||
if (parts.length < 1)
|
||||
continue;
|
||||
String cmd = parts[0];
|
||||
switch (cmd) {
|
||||
case "close":
|
||||
log("Exiting DMG server process: close cmd");
|
||||
return;
|
||||
case "get_listing": {
|
||||
String path = parseLine(line);
|
||||
List<FSEntry> listing = dmgFileReader.getListing(path);
|
||||
sendResponse("" + listing.size());//write total number of children
|
||||
for (FSEntry childEntry : listing) {
|
||||
// send 3 responses: name, isfolder boolean, file length
|
||||
writeln(childEntry.getName());//write name of each child
|
||||
sendResponses("" + childEntry.isFolder(),
|
||||
"" + dmgFileReader.getLength(childEntry));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "get_info": {
|
||||
String path = parseLine(line);
|
||||
List<String> infoList = dmgFileReader.getInfo(path);
|
||||
sendResponse("" + infoList.size());//write total number of info lines
|
||||
for (String info : infoList) {
|
||||
sendResponse(info);//write each info line
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "get_data": {
|
||||
String path = parseLine(line);
|
||||
|
||||
FSFile dmgFile = toFile(dmgFileReader, path);
|
||||
|
||||
if (dmgFile == null) {//TODO not a valid file...
|
||||
sendResponse("");
|
||||
}
|
||||
else {
|
||||
long expectedFileLength = dmgFileReader.getLength(dmgFile);
|
||||
|
||||
try (InputStream inputStream = dmgFileReader.getData(dmgFile)) {
|
||||
if (inputStream != null) {
|
||||
File temporaryFile =
|
||||
GFileUtilityMethods.writeTemporaryFile(inputStream);
|
||||
|
||||
sendResponse(temporaryFile.getAbsolutePath());
|
||||
|
||||
if (expectedFileLength != temporaryFile.length()) {
|
||||
log("file sizes do not match!");
|
||||
}
|
||||
}
|
||||
else {
|
||||
sendResponse("");// TODO: is this correct way to respond when error cond?
|
||||
log("No data stream for get_data for " + path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
log("IOException error in DMGServer command processing: " + e.getMessage());
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
finally {
|
||||
log("DMG server has terminated.");
|
||||
}
|
||||
}
|
||||
|
||||
private static FSFile toFile(DmgFileReader dmgFileReader, String path) {
|
||||
FSEntry entry = dmgFileReader.getFileByPath(path);
|
||||
|
||||
if (entry == null) {
|
||||
//System.err.println("Bad path for toFile: " + path);
|
||||
return null;
|
||||
}
|
||||
if (entry.isFile()) {
|
||||
return entry.asFile();
|
||||
}
|
||||
else if (entry instanceof FSLink) {
|
||||
int limit = 0;
|
||||
while (limit++ < 10) {
|
||||
FSLink link = (FSLink) entry;
|
||||
|
||||
FSEntry linkTarget = link.getLinkTarget(
|
||||
dmgFileReader.convertPathToArrayAndStripFileSystemName(path));
|
||||
|
||||
if (linkTarget instanceof FSFile) {
|
||||
return linkTarget.asFile();
|
||||
}
|
||||
else if (linkTarget instanceof FSLink) {
|
||||
entry = linkTarget;
|
||||
}
|
||||
else {//anything else just return
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String parseLine(String openLine) {
|
||||
int space = openLine.indexOf(' ');
|
||||
String path = openLine.substring(space + 1).trim();
|
||||
return path;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.xattr;
|
||||
|
||||
public final class XattrConstants {
|
||||
|
||||
public final static String RESOURCE_XATTR_NAME = "com.apple.ResourceFork";
|
||||
public final static String DECMPFS_XATTR_NAME = "com.apple.decmpfs";
|
||||
public final static String KAUTH_FILESEC_XATTR_NAME = "com.apple.system.Security";
|
||||
public final static String KAUTH_SCOPE_PROCESS = "com.apple.kauth.process";
|
||||
|
||||
}
|
120
GPL/DMG/src/dmg/java/mobiledevices/dmg/zlib/ZLIB.java
Normal file
120
GPL/DMG/src/dmg/java/mobiledevices/dmg/zlib/ZLIB.java
Normal file
|
@ -0,0 +1,120 @@
|
|||
/* ###
|
||||
* IP: Public Domain
|
||||
*/
|
||||
package mobiledevices.dmg.zlib;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO make this more memory efficient!!
|
||||
*
|
||||
*/
|
||||
public class ZLIB {
|
||||
|
||||
public ZLIB() {
|
||||
}
|
||||
|
||||
public ByteArrayOutputStream decompress( InputStream compressedIn, int expectedDecompressedLength ) throws IOException {
|
||||
return decompress( compressedIn, expectedDecompressedLength, false );
|
||||
}
|
||||
|
||||
public ByteArrayOutputStream decompress( InputStream compressedIn, int expectedDecompressedLength, boolean noWrap ) throws IOException {
|
||||
|
||||
byte [] compressedBytes = convertInputStreamToByteArray( compressedIn );
|
||||
|
||||
ByteArrayOutputStream decompressedBOS = new ByteArrayOutputStream();
|
||||
|
||||
byte [] tempDecompressedBytes = new byte[ 0x10000 ];
|
||||
|
||||
int totalDecompressed = 0;
|
||||
int offset = 0;
|
||||
|
||||
try {
|
||||
while ( offset < compressedBytes.length && totalDecompressed < expectedDecompressedLength ) {
|
||||
|
||||
if ( !noWrap && compressedBytes [ offset ] != 0x78 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
Inflater inflater = new Inflater( noWrap );
|
||||
|
||||
inflater.setInput( compressedBytes, offset, compressedBytes.length - offset );
|
||||
|
||||
int nDecompressed = inflater.inflate( tempDecompressedBytes );
|
||||
|
||||
if ( nDecompressed == 0 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
totalDecompressed += nDecompressed;
|
||||
|
||||
decompressedBOS.write( tempDecompressedBytes, 0, nDecompressed );
|
||||
|
||||
offset += inflater.getTotalIn();//increment total compressed bytes consumed
|
||||
}
|
||||
}
|
||||
catch ( DataFormatException e ) {
|
||||
throw new IOException( e.getMessage() );
|
||||
}
|
||||
|
||||
return decompressedBOS;
|
||||
}
|
||||
|
||||
public ByteArrayOutputStream compress(byte[] decompressedBytes) {
|
||||
return compress( false, decompressedBytes );
|
||||
}
|
||||
|
||||
public ByteArrayOutputStream compress(boolean noWrap, byte[] decompressedBytes) {
|
||||
ByteArrayOutputStream compressedBOS = new ByteArrayOutputStream();
|
||||
|
||||
byte [] tempBuffer = new byte[ 0x10000 ];
|
||||
|
||||
int offset = 0;
|
||||
|
||||
while ( offset < decompressedBytes.length ) {
|
||||
|
||||
Deflater deflater = new Deflater( 0, noWrap );
|
||||
|
||||
deflater.setInput( decompressedBytes, offset, decompressedBytes.length - offset );
|
||||
|
||||
deflater.finish();
|
||||
|
||||
if ( deflater.needsInput() ) {
|
||||
System.err.println( "needs input??" );
|
||||
}
|
||||
|
||||
int nDeflated = deflater.deflate( tempBuffer );
|
||||
|
||||
if ( nDeflated == 0 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
compressedBOS.write( tempBuffer, 0, nDeflated );
|
||||
|
||||
offset += deflater.getTotalIn();
|
||||
}
|
||||
|
||||
return compressedBOS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the contents of an input stream to a byte array
|
||||
* @param compressedIn
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private byte [] convertInputStreamToByteArray( InputStream compressedIn ) throws IOException {
|
||||
byte [] bytes = new byte[ 8096 ];
|
||||
ByteArrayOutputStream compressedBOS = new ByteArrayOutputStream();
|
||||
while ( true ) {
|
||||
int nRead = compressedIn.read( bytes );
|
||||
if ( nRead == -1 ) {
|
||||
break;
|
||||
}
|
||||
compressedBOS.write( bytes, 0, nRead );
|
||||
}
|
||||
return compressedBOS.toByteArray();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue