GP-1286 DWARF external debug files

This commit is contained in:
dev747368 2021-11-15 13:44:59 -05:00
parent 1a7b45e656
commit f023b8ad3f
21 changed files with 1339 additions and 31 deletions

View file

@ -0,0 +1,119 @@
/* ###
* 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.
*/
// Bulk extracts ELF external debug files from distro container files (rpm, ddeb, etc) so that
// the DWARF analyzer can find them using the "Edit | DWARF External Debug Config" location.
//
// When using this script, do not co-mingle different architectures or versions of debug files in
// the same directory as debug file names may conflict.
//
// Known issues: symlinks between different locations inside the debug package are not extracted,
// so some layout schemes where a single debug binary file is present in both the .build-id directory
// and the /usr/lib/, /usr/bin/, etc directory will not fully work.
//
// @category DWARF
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import org.apache.commons.io.FilenameUtils;
import ghidra.app.script.GhidraScript;
import ghidra.app.util.bin.ByteProvider;
import ghidra.formats.gfilesystem.*;
import ghidra.util.exception.CancelledException;
import utilities.util.FileUtilities;
public class ExtractELFDebugFilesScript extends GhidraScript {
private static final Set<String> containerExts =
Set.of("ddeb", "tar", "xz", "rpm", "tgz", "deb", "srpm", "cpio");
private FileSystemService fsService;
private int fileCount;
@Override
protected void run() throws Exception {
fsService = FileSystemService.getInstance();
File baseDir = askDirectory("Debug Packages Directory", "Select");
if (baseDir == null) {
return;
}
File destDir = askDirectory("Extract Destination Directory", "Select");
if (destDir == null) {
return;
}
println("Source directory: " + baseDir);
println("Destination directory: " + destDir);
try (FileSystemRef fsRef =
fsService.probeFileForFilesystem(fsService.getLocalFSRL(baseDir), monitor, null)) {
processDir(fsRef.getFilesystem().lookup(null), destDir);
}
println("Extracted: " + fileCount);
}
void processDir(GFile dir, File destDir) throws IOException, CancelledException {
List<GFile> listing = dir.getListing();
for (GFile file : listing) {
monitor.checkCanceled();
if (file.isDirectory()) {
continue;
}
String extension = FilenameUtils.getExtension(file.getName()).toLowerCase();
if (containerExts.contains(extension)) {
try (FileSystemRef fsRef =
fsService.probeFileForFilesystem(file.getFSRL(), monitor, null)) {
if (fsRef == null) {
continue;
}
processDir(fsRef.getFilesystem().lookup(null), destDir);
}
}
else if ("debug".equalsIgnoreCase(extension)) {
extractDebugFileToDestDir(file, destDir);
}
}
for (GFile file : listing) {
monitor.checkCanceled();
if (file.isDirectory()) {
processDir(file, destDir);
}
}
}
private void extractDebugFileToDestDir(GFile file, File destDir)
throws CancelledException, IOException {
File destFile = new File(destDir, file.getPath()).getCanonicalFile();
if (!FileUtilities.isPathContainedWithin(destDir, destFile)) {
throw new IOException("Bad path / filename: " + file);
}
if (destFile.exists()) {
printerr("Duplicate debug file: " + file.getFSRL() + ", " + destFile);
return;
}
try (ByteProvider bp = file.getFilesystem().getByteProvider(file, monitor)) {
FileUtilities.checkedMkdirs(destFile.getParentFile());
FSUtilities.copyByteProviderToFile(bp, destFile, monitor);
fileCount++;
}
catch (IOException e) {
printerr("Error extracting file: " + file + ", " + e.getMessage());
}
}
}