James Henderson

16 papers A* 3C 1Journal 11Unranked 1
YearRankTypeTitle / Venue / Authors
2026 J jnl
CoRR
Dina El Zein, James Henderson
2026 J jnl
CoRR
Dina El Zein, Shashi Kumar, James Henderson
2025 A* conf
EMNLP
Andrei Catalin Coman, Ionut-Teodor Sorodoc, Leonardo F. R. Ribeiro, Bill Byrne, James Henderson, Adrià de Gispert
2025 J jnl
CoRR
Andrei C. Coman, Ionut-Teodor Sorodoc, Leonardo F. R. Ribeiro, Bill Byrne, James Henderson, Adrià de Gispert
2024 J jnl
CoRR
Yuta Nagano, Andrew G. T. Pyo, Martina Milighetti, James Henderson, John Shawe-Taylor, Benny Chain, Andreas Tiffeau-Mayer
2024 J jnl
CoRR
Andrew G. T. Pyo, Yuta Nagano, Martina Milighetti, James Henderson, Curtis G. Callan Jr., Benny Chain, Ned S. Wingreen, Andreas Tiffeau-Mayer
2024 conf
EACL (2)
Yingqiang Gao, Nianlong Gu, Jessica Lam, James Henderson, Richard H. R. Hahnloser
2024 J jnl
CoRR
James Henderson, Yuta Nagano, Martina Milighetti, Andreas Tiffeau-Mayer
2024 A* conf
USENIX Security Symposium
Tingfeng Yu, James Henderson, Alwen Tiu, Thomas Haines
2023 J jnl
CoRR
Quan Hoang Nguyen, James Henderson, Egor Lysenko
2022 C conf
ICCE
Megan Wiedbusch, Nathan A. Sonnenfeld, James Henderson
2022 J jnl
CoRR
Tingfeng Yu, James Henderson, Alwen Tiu, Thomas Haines
2022 J jnl
Symmetry
Edda Gschwendtner, Konstantin Lotov, Patric Muggli, Matthew Wing, Riccardo Agnello, Claudia Christina Ahdida, Maria Carolina Amoedo Goncalves, Yanis Andrebe, Oznur Apsimon, Robert Apsimon, Jordan Matias Arnesano, Anna-Maria Bachmann, Diego Barrientos, Fabian Batsch, Vittorio Bencini, Michele Bergamaschi, Patrick Blanchard, Philip Nicholas Burrows, Birger Buttenschön, Allen Caldwell, James Chappell, Eric Chevallay, Moses Chung, David Andrew Cooke, Heiko Damerau, Can Davut, Gábor Demeter, Amos Christopher Dexter, Steffen Doebert, Francesa Ann Elverson, John Farmer, Ambrogio Fasoli, Valentin Fedosseev, Ricardo Fonseca, Ivo Furno, Spencer Gessner, Aleksandr Gorn, Eduardo Granados, Marcel Granetzny, Tim Graubner, Olaf Grulke, Eloise Daria Guran, Vasyl Hafych, Anthony Hartin, James Henderson, Mathias Hüther, Miklos Kedves, Fearghus Keeble, Vadim Khudiakov, Seong-Yeol Kim, Florian Kraus, Michel Krupa, Thibaut Lefevre, Linbo Liang, Shengli Liu, Nelson Lopes, Miguel Martinez Calderon, Stefano Mazzoni, David Medina Godoy, Joshua Moody, Kookjin Moon, Pablo Israel Morales Guzmán, Mariana Moreira, Tatiana Nechaeva, Elzbieta Nowak, Collette Pakuza, Harsha Panuganti, Ans Pardons, Kevin Pepitone, Aravinda Perera, Jan Pucek, Alexander Pukhov, Rebecca Louise Ramjiawan, Stephane Rey, Adam Scaachi, Oliver Schmitz, Eugenio Senes, J. Fernando A. da Silva, Luis Silva, Christine Stollberg, Alban Sublet, Catherine Swain, Athanasios Topaloudis, Nuno Torrado, Petr Tuev, Marlene Turner, Francesco Velotti, Livio Verra, Victor Verzilov, Jorge Vieira, Helmut Vincke, Martin Weidl, Carsten P. Welsch, Manfred Wendt, Peerawan Wiwattananon, Joseph Wolfenden, Benjamin Woolley, Samuel Wyler, Guoxing Xia, Vlada Yarygova, Michael Zepp, Giovanni Zevi Della Porta
2022 A* conf
CCS
Chris McMahon Stone, Sam L. Thomas, Mathy Vanhoef, James Henderson, Nicolas Bailluet, Tom Chothia
2021 J jnl
CoRR
Chris McMahon Stone, Sam L. Thomas, Mathy Vanhoef, James Henderson, Nicolas Bailluet, Tom Chothia
1994 J jnl
Comput. Sci. Educ.
Hossein Saiedian, James Henderson
redb/extractors/decompiler/_archive/GhidraDecompilerScript-v2.java
← Index redb/extractors/decompiler/_archive/GhidraDecompilerScript-v2.java java
import ghidra.app.script.GhidraScript;
import ghidra.program.model.listing.*;
import ghidra.app.decompiler.*;
import ghidra.program.model.block.*;
import ghidra.program.model.symbol.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.address.*;
import org.json.JSONObject;
import org.json.JSONArray;
import java.security.MessageDigest;
import java.nio.charset.StandardCharsets;

public class GhidraDecompilerScript extends GhidraScript {
    private DecompInterface decompInterface;
    private BasicBlockModel basicBlockModel;

    @Override
    public void run() throws Exception {
        System.err.println("{\"debug\": \"Script starting\"}");

        // Get binary hash and filepath from arguments
        String[] args = getScriptArgs();
        if (args.length < 2) {
            System.err.println("{\"error\": \"Both SHA256 and filepath arguments are required\"}");
            return;
        }
        String sha256 = args[0];
        String filepath = args[1];

        // Add debug output after setup
        System.err.println("{\"debug\": \"Processing file: " + args[1] + "\"}");

        // Initialize analysis components
        setupDecompiler();
        basicBlockModel = new BasicBlockModel(currentProgram);

        // Create the main JSON object for output
        JSONObject output = new JSONObject();
        output.put("sha256", sha256);
        output.put("decompiled", new JSONArray());
        output.put("disassembled", new JSONArray());
        output.put("cfg", new JSONArray());

        // Process all functions
        FunctionIterator functions = currentProgram.getFunctionManager().getFunctions(true);
        for (Function function : functions) {
            processFunction(function, output);
        }

        System.err.println("{\"debug\": \"Preparing final output\"}");
        // Output the final JSON to stdout
        System.out.println(output.toString());
    }

    private void setupDecompiler() {
        decompInterface = new DecompInterface();
        DecompileOptions options = new DecompileOptions();
        decompInterface.setOptions(options);
        decompInterface.openProgram(currentProgram);
    }

    private void processFunction(Function function, JSONObject output) {
        try {
            Address entry = function.getEntryPoint();
            String functionName = function.getName();
            String functionAddress = entry.toString();

            // Process each analysis type independently
            boolean hasAnyResults = false;

            try {
                if (processDecompiledCode(function, output.getJSONArray("decompiled"),
                                        functionName, functionAddress)) {
                    hasAnyResults = true;
                }
            } catch (Exception e) {
                System.err.println(String.format(
                    "{\"error\": \"Decompilation failed for function %s: %s\"}",
                    functionName, e.getMessage().replace("\"", "'")));
            }

            try {
                if (processDisassembledCode(function, output.getJSONArray("disassembled"),
                                        functionName, functionAddress)) {
                    hasAnyResults = true;
                }
            } catch (Exception e) {
                System.err.println(String.format(
                    "{\"error\": \"Disassembly failed for function %s: %s\"}",
                    functionName, e.getMessage().replace("\"", "'")));
            }

            try {
                if (processCFG(function, output.getJSONArray("cfg"), functionAddress)) {
                    hasAnyResults = true;
                }
            } catch (Exception e) {
                System.err.println(String.format(
                    "{\"error\": \"CFG extraction failed for function %s: %s\"}",
                    functionName, e.getMessage().replace("\"", "'")));
            }

            if (!hasAnyResults) {
                System.err.println(String.format(
                    "{\"warning\": \"No results obtained for function %s\"}",
                    functionName));
            }

        } catch (Exception e) {
            System.err.println(String.format(
                "{\"error\": \"Failed to process function: %s\"}",
                e.getMessage().replace("\"", "'")));
        }
    }

    private boolean processDecompiledCode(Function function, JSONArray decompArray,
                                        String functionName, String functionAddress) {
        try {
            DecompileResults results = decompInterface.decompileFunction(function, 30, monitor);
            if (results == null || !results.decompileCompleted()) {
                System.err.println(String.format(
                    "{\"warning\": \"Decompilation incomplete for function %s\"}",
                    functionName));
                return false;
            }

            String decompiledCode = results.getDecompiledFunction().getC();
            if (decompiledCode == null || decompiledCode.trim().isEmpty()) {
                System.err.println(String.format(
                    "{\"warning\": \"Empty decompilation result for function %s\"}",
                    functionName));
                return false;
            }

            String contentHash = calculateHash(decompiledCode);

            JSONObject functionObj = new JSONObject();
            functionObj.put("decompiled_content_hash", contentHash);
            functionObj.put("decompiled_function_name", functionName);
            functionObj.put("decompiled_function_address", functionAddress);
            functionObj.put("decompiled_function", decompiledCode);

            decompArray.put(functionObj);
            return true;

        } catch (Exception e) {
            throw new RuntimeException("Decompilation error: " + e.getMessage(), e);
        }
    }

    private boolean processDisassembledCode(Function function, JSONArray disasmArray,
                                        String functionName, String functionAddress) {
        try {
            StringBuilder disassembly = new StringBuilder();
            StringBuilder normalized = new StringBuilder();
            int instructionCount = 0;
            boolean hasValidInstructions = false;

            Listing listing = currentProgram.getListing();
            AddressSetView functionBody = function.getBody();
            InstructionIterator instructions = listing.getInstructions(functionBody, true);

            while (instructions.hasNext()) {
                try {
                    Instruction instr = instructions.next();
                    if (instr != null) {
                        String disasmLine = instr.toString();
                        if (disasmLine != null && !disasmLine.trim().isEmpty()) {
                            disassembly.append(disasmLine).append("\n");
                            normalized.append(normalizeInstruction(disasmLine)).append("\n");
                            instructionCount++;
                            hasValidInstructions = true;
                        }
                    }
                } catch (Exception e) {
                    System.err.println(String.format(
                        "{\"warning\": \"Skipped invalid instruction in %s: %s\"}",
                        functionName, e.getMessage().replace("\"", "'")));
                }
            }

            if (!hasValidInstructions) {
                System.err.println(String.format(
                    "{\"warning\": \"No valid instructions found in function %s\"}",
                    functionName));
                return false;
            }

            String disassembledCode = disassembly.toString();
            String contentHash = calculateHash(disassembledCode);

            JSONObject functionObj = new JSONObject();
            functionObj.put("disassembled_content_hash", contentHash);
            functionObj.put("disassembled_function_name", functionName);
            functionObj.put("disassembled_function_address", functionAddress);
            functionObj.put("disassembled_function", disassembledCode);
            functionObj.put("normalized_disassembly", normalized.toString());
            functionObj.put("instruction_count", instructionCount);

            // Initialize similarity fields as null
            functionObj.put("minhash_signature", JSONObject.NULL);
            functionObj.put("opcode_frequency_vector", JSONObject.NULL);
            functionObj.put("api_calls_vector", JSONObject.NULL);
            functionObj.put("instruction_embedding", JSONObject.NULL);

            disasmArray.put(functionObj);
            return true;

        } catch (Exception e) {
            throw new RuntimeException("Disassembly error: " + e.getMessage(), e);
        }
    }

    private boolean processCFG(Function function, JSONArray cfgArray, String functionAddress) {
        try {
            CodeBlockIterator blocks = basicBlockModel.getCodeBlocksContaining(
                function.getBody(), monitor);

            boolean hasValidBlocks = false;

            while (blocks.hasNext()) {
                try {
                    CodeBlock block = blocks.next();
                    String blockInstructions = getBlockInstructions(block);

                    if (blockInstructions == null || blockInstructions.trim().isEmpty()) {
                        continue;
                    }

                    String blockId = calculateHash(blockInstructions);

                    JSONObject blockObj = new JSONObject();
                    blockObj.put("block_id", blockId);
                    blockObj.put("function_address", functionAddress);
                    blockObj.put("block_instructions", blockInstructions);

                    // Process successors with error handling
                    JSONArray successorAddresses = new JSONArray();
                    try {
                        CodeBlockReferenceIterator successors = block.getDestinations(monitor);
                        while (successors.hasNext()) {
                            try {
                                CodeBlockReference ref = successors.next();
                                if (ref != null && ref.getDestinationAddress() != null) {
                                    successorAddresses.put(ref.getDestinationAddress().toString());
                                }
                            } catch (Exception e) {
                                System.err.println(String.format(
                                    "{\"warning\": \"Skipped invalid successor in block %s: %s\"}",
                                    blockId, e.getMessage().replace("\"", "'")));
                            }
                        }
                    } catch (Exception e) {
                        System.err.println(String.format(
                            "{\"warning\": \"Error processing successors for block %s: %s\"}",
                            blockId, e.getMessage().replace("\"", "'")));
                    }

                    blockObj.put("successor_blocks", successorAddresses);
                    cfgArray.put(blockObj);
                    hasValidBlocks = true;

                } catch (Exception e) {
                    System.err.println(String.format(
                        "{\"warning\": \"Skipped invalid block in function %s: %s\"}",
                        functionAddress, e.getMessage().replace("\"", "'")));
                }
            }

            return hasValidBlocks;

        } catch (Exception e) {
            throw new RuntimeException("CFG extraction error: " + e.getMessage(), e);
        }
    }

    private String getBlockInstructions(CodeBlock block) {
        StringBuilder instructions = new StringBuilder();
        try {
            AddressIterator addresses = block.getAddresses(true);
            while (addresses.hasNext()) {
                try {
                    Address addr = addresses.next();
                    if (addr != null) {
                        Instruction instr = currentProgram.getListing().getInstructionAt(addr);
                        if (instr != null) {
                            instructions.append(instr.toString()).append("\n");
                        }
                    }
                } catch (Exception e) {
                    System.err.println(String.format(
                        "{\"warning\": \"Skipped invalid instruction at address %s: %s\"}",
                        addresses.next(), e.getMessage().replace("\"", "'")));
                }
            }
        } catch (Exception e) {
            System.err.println(String.format(
                "{\"warning\": \"Error getting block instructions: %s\"}",
                e.getMessage().replace("\"", "'")));
        }
        return instructions.toString();
    }

    private String normalizeInstruction(String instruction) {
        try {
            if (instruction == null || instruction.trim().isEmpty()) {
                return "";
            }
            return instruction.replaceAll("0x[0-9a-fA-F]+", "IMM")
                            .replaceAll("\\b\\d+\\b", "NUM")
                            .replaceAll("[\\[\\]\\+\\-\\*/%&|^]+", "_OP_");
        } catch (Exception e) {
            System.err.println(String.format(
                "{\"warning\": \"Error normalizing instruction: %s\"}",
                e.getMessage().replace("\"", "'")));
            return instruction;
        }
    }

    private String calculateHash(String content) {
        try {
            if (content == null || content.trim().isEmpty()) {
                return "";
            }
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(content.getBytes(StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                hexString.append(String.format("%02x", b));
            }
            return hexString.toString();
        } catch (Exception e) {
            System.err.println(String.format(
                "{\"error\": \"Error calculating hash: %s\"}",
                e.getMessage().replace("\"", "'")));
            return "";
        }
    }
}