const { utils: { book_new, json_to_sheet }, writeFileXLSX } = require("xlsx"); /* start decompiler */ const DecompInterface = JavaHelper.getClass('ghidra.app.decompiler.DecompInterface'); const decompiler = new DecompInterface(); decompiler.openProgram(currentProgram); /* get decompiled C source */ const src = (() => { const fname = '_TSTCellToCellStorage'; /* find address to function */ const fsymbs = currentProgram.getSymbolTable().getGlobalSymbols(fname); if(!fsymbs) throw new Error(`Global Symbol ${fname} cannot be found`); /* find function */ const fn = currentProgram.getFunctionManager().getFunctionAt(fsymbs[0].getAddress()); if(!fn) throw new Error(`Function ${fname} cannot be found`); /* decompile function */ const decomp = decompiler.decompileFunction(fn, 10000, null); if (decomp.isTimedOut() || !decomp.decompileCompleted()) throw new Error(`Function ${fname} at ${fn} could not be decompiled`); /* get and return generated C code */ const src = decomp.getDecompiledFunction().getC(); if(!src) throw new Error(`Function ${fname} at ${fn} decompilation`); return src; })(); /* offset[n] will be the name of the field at bit `n` (mask `1 << n`) */ let offset = []; /* combine split lines and lazily extract data from C source */ let n = -1; src.split(/[\r\n]+/).reduce((acc,row,i) => { if(i <= 9) acc.rows.push(row); else if(row.match(/[{};]$/)) { if(acc.buf) { row = acc.buf + row.trim(); acc.buf = ""; } acc.rows.push(row); } else acc.buf += acc.buf ? row.trim() : row.replace(/[\r\n]+$/,""); return acc; }, {rows:[], buf:""}).rows.forEach(line => { if(line.match(/^ if.*(char|short| >> | & )/)) { n = -1; if(!line.match(/Var/)) return; if(line.match(/char/)) n = 7; else if(line.match(/short/)) n = 15; else if(line.match(/>>/)) n = parseInt(line.match(/>> ([1-9]\d*|0x[0-9a-f]+)/)[1]); else if(line.match(/&/)) n = Math.round(Math.log2(parseInt(line.match(/& ([1-9]\d*|0x[0-9a-f]+)/)[1]))); } else if(line.match(/PTR__objc_msgSend/) && n >= 0) { if(line.match(/AssertionHandler|NSString|stringWithUTF8String|Failure/)) return; if(!line.match(/,/)) return; // suppress noop const ptr = line.match(/PTR_s_\w+/)[0]; offset[+n] = ptr; n = -1; } }); /* construct objects with "Mask" (hex string) and "Internal Name" */ const aoo = offset.map((name, idx) => ({ Mask: "0x" + (1 << idx).toString(16), "Internal Name": name.replace(/^PTR_s_|_[0-9a-f]*$/g,"").replace(/[A-Z]/g, " $&").toLowerCase().replace(/ i d/, " ID") })); /* create worksheet */ const ws = json_to_sheet(aoo); /* write workbook */ const wb = book_new(ws, "Offsets"); /* write to XLSX */ writeFileXLSX(wb, "SheetJSGhidraTSTCell.xlsx");