| 
									
										
										
										
											2024-10-14 04:45:51 +00:00
										 |  |  | 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") | 
					
						
							| 
									
										
										
										
											2025-04-18 04:04:01 +00:00
										 |  |  | })).filter(x => x); | 
					
						
							| 
									
										
										
										
											2024-10-14 04:45:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* create worksheet */ | 
					
						
							|  |  |  | const ws = json_to_sheet(aoo); | 
					
						
							|  |  |  | /* write workbook */ | 
					
						
							|  |  |  | const wb = book_new(ws, "Offsets"); | 
					
						
							|  |  |  | /* write to XLSX */  | 
					
						
							|  |  |  | writeFileXLSX(wb, "SheetJSGhidraTSTCell.xlsx"); |