/* ### * 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. */ import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import javax.swing.SwingUtilities; import db.*; import db.buffers.BufferFile; import ghidra.app.script.GhidraScript; import ghidra.app.script.ImproperUseException; import ghidra.framework.data.GhidraFile; import ghidra.framework.data.GhidraFileData; import ghidra.framework.main.DataTreeDialog; import ghidra.framework.model.DomainFile; import ghidra.framework.store.FolderItem; import ghidra.framework.store.local.LocalDatabaseItem; import ghidra.program.model.lang.LanguageDescription; import ghidra.program.model.lang.LanguageID; import ghidra.program.model.listing.Program; import ghidra.program.util.DefaultLanguageService; import ghidra.util.Msg; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitorAdapter; public class FixLangId extends GhidraScript { private static final String LANGUAGE_ID = "Language ID"; private static final String LANGUAGE_VERSION = "Language Version"; private static final String TABLE_NAME = "Program"; @Override public void run() throws Exception { DomainFile df = askProgramFile("Select Program File"); if (df == null) { return; } if (df.isVersioned()) { Msg.showError(getClass(), null, "Script Error", "Selected project file must not be under version control!"); return; } GhidraFile gf = (GhidraFile) df; Method method = GhidraFile.class.getDeclaredMethod("getFileData", new Class[0]); method.setAccessible(true); GhidraFileData fileData = (GhidraFileData) method.invoke(gf, new Object[0]); FolderItem item = (FolderItem) getInstanceField("folderItem", fileData); if (!(item instanceof LocalDatabaseItem)) { Msg.showError(getClass(), null, "Script Error", "Unsupported file type!"); return; } LocalDatabaseItem dbItem = (LocalDatabaseItem) item; BufferFile bf = dbItem.openForUpdate(-1); DBHandle dbh = new DBHandle(bf); if (!modifyLanguage(df, dbh)) { dbh.close(); return; } dbh.save("Set Language", null, TaskMonitorAdapter.DUMMY_MONITOR); dbh.close(); } private boolean modifyLanguage(DomainFile df, DBHandle dbh) throws IOException, ImproperUseException { // TODO: Check for address map and overlay entries which could break from // changing the memory model !! Table table = dbh.getTable(TABLE_NAME); if (table == null) { Msg.showError(getClass(), null, "Script Error", "Bad program database!!"); return false; } Record record = table.getRecord(new StringField(LANGUAGE_ID)); if (record == null) { // must be in old style combined language/compiler spec format Msg.showError(getClass(), null, "Script Error", "Old program file! Language fix is not appropriate."); return false; } String langId = record.getString(0); LanguageDescription desc = null; List descriptions = DefaultLanguageService.getLanguageService().getLanguageDescriptions(true); List choices = new ArrayList<>(descriptions.size()); for (int i = 0; i < choices.size(); i++) { choices.add(descriptions.get(i).getLanguageID().getIdAsString()); } try { langId = askChoice("Select New Language", "Language ID:", choices, null); if (langId != null) { Msg.warn(this, "Changing language ID from '" + record.getString(0) + "' to '" + langId + "' for program: " + df.getName()); desc = DefaultLanguageService.getLanguageService().getLanguageDescription( new LanguageID(langId)); long txId = dbh.startTransaction(); try { record.setString(0, langId); table.putRecord(record); record = table.getSchema().createRecord(new StringField(LANGUAGE_VERSION)); record.setString(0, desc.getVersion() + "." + desc.getMinorVersion()); table.putRecord(record); } finally { dbh.endTransaction(txId, true); } return true; } } catch (CancelledException e) { // just return false } return false; } public DomainFile askProgramFile(String title) { final DomainFile[] domainFile = new DomainFile[] { null }; final DataTreeDialog dtd = new DataTreeDialog(null, title, DataTreeDialog.OPEN); dtd.addOkActionListener(e -> { dtd.close(); domainFile[0] = dtd.getDomainFile(); }); try { SwingUtilities.invokeAndWait(() -> dtd.showComponent()); } catch (Exception e) { return null; } if (domainFile[0] != null && !Program.class.isAssignableFrom(domainFile[0].getDomainObjectClass())) { Msg.showError(getClass(), null, "Script Error", "Selected project file is not a program file!"); return null; } return domainFile[0]; } public static Object getInstanceField(String fieldName, Object ownerInstance) throws RuntimeException { if (ownerInstance == null) { throw new NullPointerException("Owner of instance field cannot be null"); } Class objectClass = (ownerInstance instanceof Class) ? (Class) ownerInstance : ownerInstance.getClass(); Object result = null; try { // get the field from the class object Field field = locateFieldObjectOnClass(fieldName, objectClass); // open up the field so that we have access field.setAccessible(true); // get the field from the object instance that we were provided result = field.get(ownerInstance); } catch (Exception e) { throw new RuntimeException("Unable to use reflection to obtain " + "field: " + fieldName + " from class: " + objectClass, e); } return result; } public static Field locateFieldObjectOnClass(String fieldName, Class containingClass) { Field field = null; try { field = containingClass.getDeclaredField(fieldName); } catch (NoSuchFieldException nsfe) { // O.K., the field may be located on a parent class. So, we // will call this method on the parent class Class parentClass = containingClass.getSuperclass(); if (parentClass != null) { field = locateFieldObjectOnClass(fieldName, parentClass); } } return field; } }