ghidra/Ghidra/Features/FileFormats/ghidra_scripts/BTreeAnnotationScript.java

215 lines
7.6 KiB
Java

/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Annotates an HFS+ attributes b-Tree file.
//@category iOS
import ghidra.app.script.GhidraScript;
import ghidra.app.util.bin.*;
import ghidra.docking.settings.SettingsDefinition;
import ghidra.file.formats.ios.btree.*;
import ghidra.file.formats.ios.decmpfs.DecmpfsHeader;
import ghidra.file.formats.ios.xattr.XattrConstants;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.EndianSettingsDefinition;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Program;
public class BTreeAnnotationScript extends GhidraScript {
@Override
public void run() throws Exception {
Address address = currentProgram.getMinAddress();
ByteProvider provider = new MemoryByteProvider(currentProgram.getMemory(), address);
BinaryReader reader = new BinaryReader(provider, false);
BTreeRootNodeDescriptor root = new BTreeRootNodeDescriptor(reader);
markupRecordOffsets(currentProgram, root, 0, root);
Data headerNodeData = createBTreeNode(currentProgram, root, 0);
Data headerRecordData =
createBTreeHeaderRecord(currentProgram, root.getHeaderRecord(),
headerNodeData.getLength());
Data userDataRecordData =
createUserDataRecord(currentProgram, root.getUserDataRecord(),
headerNodeData.getLength() + headerRecordData.getLength());
Data mapRecordData =
createMapRecord(currentProgram, root.getMapRecord(), headerNodeData.getLength() +
headerRecordData.getLength() + userDataRecordData.getLength());
if (mapRecordData == null) {
printerr("mapRecordData == null ????");
}
processNodes(currentProgram, root);
}
@Override
public AnalysisMode getScriptAnalysisMode() {
return AnalysisMode.DISABLED;
}
private int processNodes(Program program, BTreeRootNodeDescriptor root) throws Exception {
int nodeIndex = 1;
int min = (int) program.getMinAddress().getOffset();
int max = (int) program.getMaxAddress().getOffset();
monitor.setMaximum(max - min);
monitor.setMessage("Applying node descriptors...");
int nodeSize = root.getHeaderRecord().getNodeSize() & 0xffff;
for (int i = nodeSize; i < program.getMemory().getSize(); i += nodeSize) {
if (monitor.isCancelled()) {
break;
}
monitor.setProgress(min + i);
BTreeNodeDescriptor nodeI = root.getNode(nodeIndex);
createBTreeNode(program, nodeI, i);
StringBuffer buffer = new StringBuffer();
buffer.append("Index: 0x" + Integer.toHexString(nodeIndex) + '\n');
buffer.append("flink: 0x" + Integer.toHexString(nodeI.getFLink()) + '\n');
buffer.append("blink: 0x" + Integer.toHexString(nodeI.getBLink()) + '\n');
buffer.append("kind: " + nodeI.getKind() + '\n');
buffer.append("Records: 0x" + Integer.toHexString(nodeI.getNumRecords()) + '\n');
setPlateComment(toAddr(i), buffer.toString());
markupBTreeNodeData(program, nodeI);
markupRecordOffsets(program, root, i, nodeI);
++nodeIndex;
}
return nodeIndex;
}
private void markupRecordOffsets(Program program, BTreeRootNodeDescriptor root, int offset,
BTreeNodeDescriptor nodeI) throws Exception {
/* TODO
int pos = 2;
Address startAddress = toAddr( program, offset + root.getHeaderRecord().getNodeSize() - pos );
Address currentAddress = startAddress;
for ( int i = 0 ; i < nodeI.getRecordOffsets().size() ; ++i ) {
createData( program , currentAddress, new WordDataType() );
pos += 2;
currentAddress = currentAddress.subtract( 2 );
}
if ( currentAddress.compareTo( startAddress ) < 0 ) {
createFragment( program , "RecordOffset", currentAddress.add( 2 ), startAddress );
}
*/
}
private void markupBTreeNodeData(Program program, BTreeNodeDescriptor descriptor)
throws Exception {
for (BTreeNodeRecord record : descriptor.getRecords()) {
Address address = toAddr(record.getRecordOffset());
DataType recordDataType = record.toDataType();
Data recordData = createData(address, recordDataType);
createFragment(record.getType(), recordData.getMinAddress(), recordData.getLength());
setPlateComment(address,
record.getType() + " 0x" + Integer.toHexString(record.getFileID()));
if (descriptor.getKind() == BTreeNodeKinds.kBTLeafNode) {
if (record.getType().equals(XattrConstants.DECMPFS_XATTR_NAME)) {
markupDecmpfs(program, descriptor, record, recordData.getMaxAddress().add(1));
}
else if (record.getType().equals(XattrConstants.KAUTH_FILESEC_XATTR_NAME)) {
//TODO
}
}
}
}
private void markupDecmpfs(Program program, BTreeNodeDescriptor descriptor,
BTreeNodeRecord record, Address address) throws Exception {
DecmpfsHeader header = record.getDecmpfsHeader();
DataType headerDataType = header.toDataType();
Data headerData = createData(address, headerDataType);
changeEndianSettings(headerData);
createFragment(header.getCompressionMagic(), headerData.getMinAddress(),
headerData.getLength());
StringBuffer buffer = new StringBuffer();
buffer.append(header.getCompressionMagic());
buffer.append('\n');
buffer.append("CompressionType: 0x" + Integer.toHexString(header.getCompressionType()));
buffer.append('\n');
buffer.append("UncompressedSize: 0x" + Long.toHexString(header.getUncompressedSize()));
buffer.append('\n');
setPlateComment(address, buffer.toString());
}
private Data createMapRecord(Program program, BTreeMapRecord mapRecord, int offset)
throws Exception {
Address address = toAddr(offset);
DataType dataType = mapRecord.toDataType();
Data data = createData(address, dataType);
createFragment(dataType.getName(), data.getMinAddress(), data.getLength());
return data;
}
private Data createUserDataRecord(Program program, BTreeUserDataRecord userDataRecord,
int offset) throws Exception {
Address address = toAddr(offset);
DataType dataType = userDataRecord.toDataType();
Data data = createData(address, dataType);
createFragment(dataType.getName(), data.getMinAddress(), data.getLength());
return data;
}
private Data createBTreeHeaderRecord(Program program, BTreeHeaderRecord headerRecord, int offset)
throws Exception {
Address address = toAddr(offset);
DataType dataType = headerRecord.toDataType();
Data data = createData(address, dataType);
createFragment(dataType.getName(), data.getMinAddress(), data.getLength());
return data;
}
private Data createBTreeNode(Program program, BTreeNodeDescriptor node, int offset)
throws Exception {
Address address = toAddr(offset);
DataType dataType = node.toDataType();
Data data = createData(address, dataType);
createFragment(dataType.getName(), data.getMinAddress(), data.getLength());
return data;
}
private void changeEndianSettings(Data data) throws Exception {
for (int i = 0; i < data.getNumComponents(); ++i) {
Data component = data.getComponent(i);
SettingsDefinition[] settings = component.getDataType().getSettingsDefinitions();
for (int j = 0; j < settings.length; ++j) {
if (settings[j] instanceof EndianSettingsDefinition) {
EndianSettingsDefinition setting = (EndianSettingsDefinition) settings[j];
setting.setBigEndian(component, false);
}
}
}
}
}