@eyeauras/insight-cli 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cli-package-build.json +1 -0
- package/LICENSE +21 -0
- package/README.md +60 -2
- package/SKILL.md +58 -0
- package/dist/src/administration-results.d.ts +1 -0
- package/dist/src/administration-results.js +84 -0
- package/dist/src/administration.d.ts +14 -0
- package/dist/src/administration.js +134 -0
- package/dist/src/archive-contract.d.ts +6 -0
- package/dist/src/archive-contract.js +108 -0
- package/dist/src/archive.d.ts +12 -0
- package/dist/src/archive.js +116 -0
- package/dist/src/arguments.d.ts +14 -0
- package/dist/src/arguments.js +62 -0
- package/dist/src/artifacts.d.ts +17 -0
- package/dist/src/artifacts.js +115 -0
- package/dist/src/bin.d.ts +2 -0
- package/dist/src/bin.js +4 -0
- package/dist/src/cli.d.ts +10 -0
- package/dist/src/cli.js +419 -0
- package/dist/src/comment-effects.d.ts +3 -0
- package/dist/src/comment-effects.js +55 -0
- package/dist/src/contexts.d.ts +115 -0
- package/dist/src/contexts.js +1078 -0
- package/dist/src/coordinator.d.ts +41 -0
- package/dist/src/coordinator.js +160 -0
- package/dist/src/cross-results.d.ts +14 -0
- package/dist/src/cross-results.js +71 -0
- package/dist/src/cross.d.ts +3 -0
- package/dist/src/cross.js +43 -0
- package/dist/src/data-effects.d.ts +3 -0
- package/dist/src/data-effects.js +103 -0
- package/dist/src/debugger-results.d.ts +4 -0
- package/dist/src/debugger-results.js +151 -0
- package/dist/src/debugger-supervisor.d.ts +1 -0
- package/dist/src/debugger-supervisor.js +136 -0
- package/dist/src/debugger.d.ts +21 -0
- package/dist/src/debugger.js +267 -0
- package/dist/src/documentation-effects.d.ts +5 -0
- package/dist/src/documentation-effects.js +56 -0
- package/dist/src/extensions.d.ts +64 -0
- package/dist/src/extensions.js +289 -0
- package/dist/src/function-effects.d.ts +4 -0
- package/dist/src/function-effects.js +112 -0
- package/dist/src/ghidra.d.ts +50 -0
- package/dist/src/ghidra.js +468 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +1 -0
- package/dist/src/initial-model.d.ts +6 -0
- package/dist/src/initial-model.js +85 -0
- package/dist/src/json.d.ts +2 -0
- package/dist/src/json.js +8 -0
- package/dist/src/lifecycle.d.ts +49 -0
- package/dist/src/lifecycle.js +154 -0
- package/dist/src/memory-inputs.d.ts +8 -0
- package/dist/src/memory-inputs.js +56 -0
- package/dist/src/merge.d.ts +29 -0
- package/dist/src/merge.js +66 -0
- package/dist/src/model.d.ts +74 -0
- package/dist/src/model.js +67 -0
- package/dist/src/mutation-effects.d.ts +4 -0
- package/dist/src/mutation-effects.js +321 -0
- package/dist/src/mutation-inputs.d.ts +2 -0
- package/dist/src/mutation-inputs.js +71 -0
- package/dist/src/mutations.d.ts +4 -0
- package/dist/src/mutations.js +53 -0
- package/dist/src/native-inputs.d.ts +28 -0
- package/dist/src/native-inputs.js +182 -0
- package/dist/src/native-queries.d.ts +2 -0
- package/dist/src/native-queries.js +29 -0
- package/dist/src/projects.d.ts +31 -0
- package/dist/src/projects.js +301 -0
- package/dist/src/prototype-cli.d.ts +8 -0
- package/dist/src/prototype-cli.js +155 -0
- package/dist/src/queries.d.ts +5 -0
- package/dist/src/queries.js +84 -0
- package/dist/src/query-policy.d.ts +6 -0
- package/dist/src/query-policy.js +72 -0
- package/dist/src/query-results.d.ts +2 -0
- package/dist/src/query-results.js +224 -0
- package/dist/src/research.d.ts +184 -0
- package/dist/src/research.js +808 -0
- package/dist/src/runtime-archives.d.ts +5 -0
- package/dist/src/runtime-archives.js +127 -0
- package/dist/src/runtime-catalog.d.ts +18 -0
- package/dist/src/runtime-catalog.js +27 -0
- package/dist/src/runtime-manager.d.ts +101 -0
- package/dist/src/runtime-manager.js +444 -0
- package/dist/src/runtime-selection.d.ts +13 -0
- package/dist/src/runtime-selection.js +25 -0
- package/dist/src/saved-materials.d.ts +12 -0
- package/dist/src/saved-materials.js +196 -0
- package/dist/src/scripts.d.ts +19 -0
- package/dist/src/scripts.js +79 -0
- package/dist/src/sdk-contracts.d.ts +13 -0
- package/dist/src/sdk-contracts.js +37 -0
- package/dist/src/sdk-files.d.ts +29 -0
- package/dist/src/sdk-files.js +249 -0
- package/dist/src/sdk.d.ts +4 -0
- package/dist/src/sdk.js +125 -0
- package/dist/src/server.d.ts +46 -0
- package/dist/src/server.js +460 -0
- package/dist/src/store.d.ts +33 -0
- package/dist/src/store.js +178 -0
- package/dist/src/sync-policy.d.ts +22 -0
- package/dist/src/sync-policy.js +123 -0
- package/dist/src/transport.d.ts +23 -0
- package/dist/src/transport.js +135 -0
- package/dist/src/ui-results.d.ts +2 -0
- package/dist/src/ui-results.js +80 -0
- package/dist/src/ui.d.ts +17 -0
- package/dist/src/ui.js +88 -0
- package/dist/src/utility.d.ts +14 -0
- package/dist/src/utility.js +43 -0
- package/dist/src/version-tracking.d.ts +57 -0
- package/dist/src/version-tracking.js +200 -0
- package/dist/src/workspace.d.ts +199 -0
- package/dist/src/workspace.js +564 -0
- package/package.json +47 -4
- package/references/InsightRepositoryProbe.java +55 -0
- package/references/administration.md +34 -0
- package/references/archive.md +32 -0
- package/references/artifacts.md +47 -0
- package/references/debugger.md +40 -0
- package/references/extensions-and-sync.md +124 -0
- package/references/managed-runtime.md +116 -0
- package/references/mutations.json +1424 -0
- package/references/native/InsightApplyDocumentation.java +48 -0
- package/references/native/InsightArchivePayloads.java +36 -0
- package/references/native/InsightBlockTransfer.java +57 -0
- package/references/native/InsightEmulation.java +93 -0
- package/references/native/InsightExtensions.java +36 -0
- package/references/native/InsightFunctionDatabases.java +98 -0
- package/references/native/InsightGuiMain.java +32 -0
- package/references/native/InsightHeadlessMain.java +10 -0
- package/references/native/InsightInitialImport.java +42 -0
- package/references/native/InsightInitialJournal.java +48 -0
- package/references/native/InsightInitialPublication.java +142 -0
- package/references/native/InsightKeytool.java +16 -0
- package/references/native/InsightNativeReceipt.java +31 -0
- package/references/native/InsightPlugins.java +34 -0
- package/references/native/InsightProjectFiles.java +92 -0
- package/references/native/InsightPublicationRecovery.java +63 -0
- package/references/native/InsightPythonWheels.java +31 -0
- package/references/native/InsightResearchQueries.java +188 -0
- package/references/native/InsightRuntimeDoctor.java +33 -0
- package/references/native/InsightSdkFiles.java +142 -0
- package/references/native/InsightSdkScript.java +31 -0
- package/references/native/InsightSdkTools.java +338 -0
- package/references/native/InsightSdkTypes.java +132 -0
- package/references/native/InsightServerAdministration.java +82 -0
- package/references/native/InsightServerInit.java +62 -0
- package/references/native/InsightServerMain.java +43 -0
- package/references/native/InsightServerProbe.java +29 -0
- package/references/native/InsightSnapshotFields.java +68 -0
- package/references/native/InsightSnapshotInspect.java +189 -0
- package/references/native/InsightSnapshotPublisher.java +123 -0
- package/references/native/InsightStartDebugger.java +28 -0
- package/references/native/InsightStopDebugger.java +20 -0
- package/references/native/InsightTraceInspect.java +24 -0
- package/references/native/InsightTraceState.java +130 -0
- package/references/native/InsightTraceTools.java +114 -0
- package/references/native/InsightVersionTracking.java +91 -0
- package/references/native-workflows.md +112 -0
- package/references/queries.json +1761 -0
- package/references/query-semantics.md +96 -0
- package/references/recovery.md +53 -0
- package/references/research.md +101 -0
- package/references/setup.md +49 -0
- package/runtime/GhidraMCP-7.0.0.jar +0 -0
- package/runtime/GhidraMCP-LICENSE +202 -0
- package/runtime/GhidraMCP-NOTICE +10 -0
- package/runtime/provenance.json +11 -0
- package/skills/insight-re/SKILL.md +108 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
package ghidra.server;
|
|
2
|
+
|
|
3
|
+
import com.google.gson.Gson;
|
|
4
|
+
import generic.hash.HashUtilities;
|
|
5
|
+
import ghidra.GhidraApplicationLayout;
|
|
6
|
+
import ghidra.framework.Application;
|
|
7
|
+
import ghidra.framework.HeadlessGhidraApplicationConfiguration;
|
|
8
|
+
import ghidra.framework.remote.User;
|
|
9
|
+
import java.nio.channels.FileChannel;
|
|
10
|
+
import java.nio.file.*;
|
|
11
|
+
import java.util.*;
|
|
12
|
+
|
|
13
|
+
/** Initialize only the facade's owned local storage, before any network listener exists. */
|
|
14
|
+
class InsightServerInit {
|
|
15
|
+
public static void main(String[] args) {
|
|
16
|
+
var output = System.out; System.setOut(System.err);
|
|
17
|
+
try {
|
|
18
|
+
if(args.length!=4) throw new IllegalArgumentException();
|
|
19
|
+
Path root=Path.of(args[0]).toRealPath();
|
|
20
|
+
String repository=args[1], publisher=args[2], reader=args[3];
|
|
21
|
+
if(publisher.equals(reader) || !repository.startsWith("insight_")) throw new IllegalArgumentException();
|
|
22
|
+
try(var channel=FileChannel.open(root.resolveSibling("server.lock"),StandardOpenOption.CREATE,StandardOpenOption.WRITE);
|
|
23
|
+
var lock=channel.tryLock()) {
|
|
24
|
+
if(lock==null) throw new IllegalStateException("Owned server is already running");
|
|
25
|
+
Application.initializeApplication(new GhidraApplicationLayout(),new HeadlessGhidraApplicationConfiguration());
|
|
26
|
+
RepositoryManager manager=new RepositoryManager(root.toFile(),true,0,false);
|
|
27
|
+
try {
|
|
28
|
+
UserManager users=manager.getUserManager();
|
|
29
|
+
for(String role:List.of("PUBLISHER","READER")) {
|
|
30
|
+
String user=role.equals("PUBLISHER")?publisher:reader;
|
|
31
|
+
String secret=System.getenv("INSIGHT_"+role+"_PASSWORD");
|
|
32
|
+
if(secret==null || secret.length()<32) throw new SecurityException();
|
|
33
|
+
char[] password=secret.toCharArray();
|
|
34
|
+
try {
|
|
35
|
+
if(!users.isValidUser(user)) {
|
|
36
|
+
char[] hash=HashUtilities.getSaltedHash(HashUtilities.SHA256_ALGORITHM,password);
|
|
37
|
+
try { users.addUser(user,hash.clone()); users.setPassword(user,hash.clone(),false); }
|
|
38
|
+
finally { Arrays.fill(hash,'\0'); }
|
|
39
|
+
}
|
|
40
|
+
users.authenticateUser(user,password);
|
|
41
|
+
} finally { Arrays.fill(password,'\0'); }
|
|
42
|
+
}
|
|
43
|
+
Repository model=manager.getRepository(publisher,repository);
|
|
44
|
+
if(model==null) {
|
|
45
|
+
model=manager.createRepository(publisher,repository);
|
|
46
|
+
model.setUserList(publisher,new User[]{new User(publisher,User.ADMIN),new User(reader,User.READ_ONLY)},false);
|
|
47
|
+
}
|
|
48
|
+
var permissions=model.getUserList(publisher);
|
|
49
|
+
if(permissions.length!=2 || model.anonymousAccessAllowed() ||
|
|
50
|
+
Arrays.stream(permissions).noneMatch(user->user.getName().equals(reader)&&user.isReadOnly()) ||
|
|
51
|
+
Arrays.stream(permissions).noneMatch(user->user.getName().equals(publisher)&&user.isAdmin()))
|
|
52
|
+
throw new SecurityException("Owned repository authority changed");
|
|
53
|
+
} finally { manager.dispose(); }
|
|
54
|
+
}
|
|
55
|
+
output.println(new Gson().toJson(Map.of("state","ready","repository",repository,"readerReadOnly",true,"publisherAdmin",true)));
|
|
56
|
+
System.exit(0);
|
|
57
|
+
} catch(Throwable failure) {
|
|
58
|
+
failure.printStackTrace(System.err);
|
|
59
|
+
output.println("{\"state\":\"failed\",\"error\":\"owned_server_initialization_failed\"}"); System.exit(1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import com.google.gson.Gson;
|
|
2
|
+
import com.sun.net.httpserver.HttpServer;
|
|
3
|
+
import ghidra.net.ApplicationKeyManagerFactory;
|
|
4
|
+
import ghidra.server.remote.GhidraServer;
|
|
5
|
+
import java.net.InetSocketAddress;
|
|
6
|
+
import java.nio.channels.FileChannel;
|
|
7
|
+
import java.nio.charset.StandardCharsets;
|
|
8
|
+
import java.nio.file.*;
|
|
9
|
+
import java.security.MessageDigest;
|
|
10
|
+
import java.util.*;
|
|
11
|
+
|
|
12
|
+
/** Owned loopback server with an authenticated identity probe, not an administration API. */
|
|
13
|
+
class InsightServerMain {
|
|
14
|
+
public static void main(String[] args) throws Exception {
|
|
15
|
+
if(args.length!=5) throw new IllegalArgumentException();
|
|
16
|
+
Path root=Path.of(args[0]).toRealPath();
|
|
17
|
+
int port=Integer.parseInt(args[1]), healthPort=Integer.parseInt(args[2]);
|
|
18
|
+
String instance=args[3], keystore=args[4];
|
|
19
|
+
String token=System.getenv("INSIGHT_SERVER_TOKEN"), password=System.getenv("INSIGHT_KEYSTORE_PASSWORD");
|
|
20
|
+
if(token==null || token.length()<32 || password==null || password.length()<32) throw new SecurityException();
|
|
21
|
+
FileChannel channel=FileChannel.open(root.resolveSibling("server.lock"),StandardOpenOption.CREATE,StandardOpenOption.WRITE);
|
|
22
|
+
var lock=channel.tryLock(); if(lock==null) throw new IllegalStateException("Owned server already running");
|
|
23
|
+
System.setProperty("ghidra.keystore",keystore);
|
|
24
|
+
ApplicationKeyManagerFactory.setKeyStorePasswordProvider((path,retry)->password.toCharArray());
|
|
25
|
+
GhidraServer.main(new String[]{"-i","127.0.0.1","-ip","127.0.0.1","-p"+port,"-a0","-e0","-u",root.toString()});
|
|
26
|
+
HttpServer health=HttpServer.create(new InetSocketAddress("127.0.0.1",healthPort),4);
|
|
27
|
+
byte[] authorization=("Bearer "+token).getBytes(StandardCharsets.UTF_8);
|
|
28
|
+
health.createContext("/identity",exchange->{
|
|
29
|
+
String supplied=exchange.getRequestHeaders().getFirst("Authorization");
|
|
30
|
+
if(!exchange.getRequestMethod().equals("GET") || !exchange.getRequestURI().toString().equals("/identity") ||
|
|
31
|
+
supplied==null || !MessageDigest.isEqual(authorization,supplied.getBytes(StandardCharsets.UTF_8))) {
|
|
32
|
+
exchange.sendResponseHeaders(403,-1); exchange.close(); return;
|
|
33
|
+
}
|
|
34
|
+
byte[] response=new Gson().toJson(Map.of("pid",ProcessHandle.current().pid(),"instance",instance,"port",port)).getBytes(StandardCharsets.UTF_8);
|
|
35
|
+
exchange.getResponseHeaders().set("Content-Type","application/json");
|
|
36
|
+
exchange.sendResponseHeaders(200,response.length); exchange.getResponseBody().write(response); exchange.close();
|
|
37
|
+
});
|
|
38
|
+
health.start();
|
|
39
|
+
Runtime.getRuntime().addShutdownHook(new Thread(()->{
|
|
40
|
+
health.stop(0); try {lock.release();channel.close();} catch(Exception ignored){}
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import com.google.gson.Gson;
|
|
2
|
+
import com.xebyte.headless.GhidraServerManager;
|
|
3
|
+
import ghidra.GhidraApplicationLayout;
|
|
4
|
+
import ghidra.framework.Application;
|
|
5
|
+
import ghidra.framework.HeadlessGhidraApplicationConfiguration;
|
|
6
|
+
import java.util.*;
|
|
7
|
+
|
|
8
|
+
/** Verify actual repository credentials and permissions, without creating or changing anything. */
|
|
9
|
+
class InsightServerProbe {
|
|
10
|
+
public static void main(String[] args) {
|
|
11
|
+
var output=System.out; System.setOut(System.err);
|
|
12
|
+
try {
|
|
13
|
+
if(args.length!=2 || !Set.of("reader","publisher").contains(args[1])) throw new IllegalArgumentException();
|
|
14
|
+
Application.initializeApplication(new GhidraApplicationLayout(),new HeadlessGhidraApplicationConfiguration());
|
|
15
|
+
GhidraServerManager manager=new GhidraServerManager(); manager.connect();
|
|
16
|
+
var repository=manager.server().getRepository(args[0]); repository.connect();
|
|
17
|
+
var user=repository.getUser();
|
|
18
|
+
if(user==null || !user.getName().equals(System.getenv("GHIDRA_SERVER_USER")) ||
|
|
19
|
+
(args[1].equals("reader")?!user.isReadOnly():!user.hasWritePermission())) throw new SecurityException();
|
|
20
|
+
int permission=user.getPermissionType();
|
|
21
|
+
repository.disconnect(); manager.server().disconnect();
|
|
22
|
+
output.println(new Gson().toJson(Map.of("repository",args[0],"role",args[1],"permission",permission)));
|
|
23
|
+
System.exit(0);
|
|
24
|
+
} catch(Throwable failure) {
|
|
25
|
+
failure.printStackTrace(System.err);
|
|
26
|
+
output.println("{\"error\":\"repository_authority_not_verified\"}"); System.exit(1);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import com.google.gson.*;
|
|
2
|
+
import db.DBHandle;
|
|
3
|
+
import ghidra.GhidraApplicationLayout;
|
|
4
|
+
import ghidra.framework.Application;
|
|
5
|
+
import ghidra.framework.HeadlessGhidraApplicationConfiguration;
|
|
6
|
+
import ghidra.framework.data.OpenMode;
|
|
7
|
+
import ghidra.framework.store.db.PackedDatabase;
|
|
8
|
+
import ghidra.program.database.ProgramDB;
|
|
9
|
+
import ghidra.program.model.listing.CodeUnit;
|
|
10
|
+
import ghidra.util.task.TaskMonitor;
|
|
11
|
+
import java.nio.file.*;
|
|
12
|
+
import java.nio.charset.StandardCharsets;
|
|
13
|
+
import java.security.MessageDigest;
|
|
14
|
+
import java.util.*;
|
|
15
|
+
|
|
16
|
+
/** Read scalar values and conservative dependencies from an immutable saved program. */
|
|
17
|
+
class InsightSnapshotFields {
|
|
18
|
+
static final Map<String,Integer> kinds=Map.of("comment.eol",CodeUnit.EOL_COMMENT,"comment.pre",CodeUnit.PRE_COMMENT,
|
|
19
|
+
"comment.post",CodeUnit.POST_COMMENT,"comment.plate",CodeUnit.PLATE_COMMENT,"comment.repeatable",CodeUnit.REPEATABLE_COMMENT);
|
|
20
|
+
public static void main(String[] args) {
|
|
21
|
+
var output=System.out;System.setOut(System.err);
|
|
22
|
+
try {
|
|
23
|
+
if(args.length!=2)throw new IllegalArgumentException();
|
|
24
|
+
String input=Files.readString(Path.of(args[1]));if(input.length()>1048576)throw new IllegalArgumentException();
|
|
25
|
+
JsonArray requests=JsonParser.parseString(input).getAsJsonArray();if(requests.size()>5000)throw new IllegalArgumentException();
|
|
26
|
+
Application.initializeApplication(new GhidraApplicationLayout(),new HeadlessGhidraApplicationConfiguration());
|
|
27
|
+
DBHandle db=PackedDatabase.getPackedDatabase(Path.of(args[0]).toFile(),true,TaskMonitor.DUMMY).open(TaskMonitor.DUMMY);
|
|
28
|
+
Object consumer=new Object();ProgramDB program=new ProgramDB(db,OpenMode.IMMUTABLE,TaskMonitor.DUMMY,consumer);
|
|
29
|
+
Map<String,Object> values=new LinkedHashMap<>();
|
|
30
|
+
try {
|
|
31
|
+
for(JsonElement item:requests) {
|
|
32
|
+
JsonObject request=item.getAsJsonObject();String address=request.get("address").getAsString(),field=request.get("field").getAsString();
|
|
33
|
+
if(!field.equals("function.name")&&!kinds.containsKey(field))throw new IllegalArgumentException();
|
|
34
|
+
var at=program.getAddressFactory().getAddress(address);
|
|
35
|
+
var block=at==null?null:program.getMemory().getBlock(at);
|
|
36
|
+
var function=at==null?null:field.equals("function.name")?program.getFunctionManager().getFunctionAt(at):program.getFunctionManager().getFunctionContaining(at);
|
|
37
|
+
boolean exists=block!=null&&(!field.equals("function.name")||function!=null);
|
|
38
|
+
String value=field.equals("function.name")?(function==null?null:function.getName()):
|
|
39
|
+
(at==null?null:program.getListing().getComment(kinds.get(field),at));
|
|
40
|
+
Map<String,Object> dependency=new LinkedHashMap<>();
|
|
41
|
+
if(exists) {
|
|
42
|
+
dependency.put("space",at.getAddressSpace().getName());
|
|
43
|
+
dependency.put("blockStart",block.getStart().toString());dependency.put("blockEnd",block.getEnd().toString());
|
|
44
|
+
dependency.put("blockType",block.getType().toString());
|
|
45
|
+
byte[] bytes=new byte[16];int read=0;
|
|
46
|
+
if(block.isInitialized()) {
|
|
47
|
+
long available=block.getEnd().subtract(at)+1;
|
|
48
|
+
read=program.getMemory().getBytes(at,bytes,0,(int)Math.min(16,available));
|
|
49
|
+
}
|
|
50
|
+
dependency.put("bytes",HexFormat.of().formatHex(Arrays.copyOf(bytes,read)));
|
|
51
|
+
if(function!=null) {
|
|
52
|
+
dependency.put("functionId",function.getID());dependency.put("body",function.getBody().toString());
|
|
53
|
+
dependency.put("namespace",function.getParentNamespace().getName(true));
|
|
54
|
+
dependency.put("returnType",function.getReturnType().getPathName());
|
|
55
|
+
dependency.put("callingConvention",Objects.toString(function.getCallingConventionName(),""));
|
|
56
|
+
dependency.put("parameters",Arrays.stream(function.getParameters()).map(parameter->parameter.getDataType().getPathName()).toList());
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
String anchor=HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(new Gson().toJson(dependency).getBytes(StandardCharsets.UTF_8)));
|
|
60
|
+
Map<String,Object> observed=new LinkedHashMap<>();observed.put("value",value);observed.put("exists",exists);observed.put("dependency",anchor);
|
|
61
|
+
values.put(address+"@"+field,observed);
|
|
62
|
+
}
|
|
63
|
+
}finally{program.release(consumer);}
|
|
64
|
+
output.println(new GsonBuilder().serializeNulls().create().toJson(Map.of("fields",values)));
|
|
65
|
+
System.exit(0);
|
|
66
|
+
}catch(Throwable failure){failure.printStackTrace(System.err);output.println("{\"error\":\"snapshot_inspection_failed\"}");System.exit(1);}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import com.google.gson.*;
|
|
2
|
+
import com.xebyte.core.ServiceUtils;
|
|
3
|
+
import db.DBHandle;
|
|
4
|
+
import ghidra.GhidraApplicationLayout;
|
|
5
|
+
import ghidra.framework.Application;
|
|
6
|
+
import ghidra.framework.HeadlessGhidraApplicationConfiguration;
|
|
7
|
+
import ghidra.framework.data.OpenMode;
|
|
8
|
+
import ghidra.framework.store.db.PackedDatabase;
|
|
9
|
+
import ghidra.program.database.ProgramDB;
|
|
10
|
+
import ghidra.program.model.data.*;
|
|
11
|
+
import ghidra.util.task.TaskMonitor;
|
|
12
|
+
import java.nio.file.*;
|
|
13
|
+
import java.util.*;
|
|
14
|
+
|
|
15
|
+
/** Inspect the committed private snapshot, rather than trusting a mutation acknowledgment. */
|
|
16
|
+
class InsightSnapshotInspect {
|
|
17
|
+
static String string(JsonObject request,String name) {return request.has(name)?request.get(name).getAsString():"";}
|
|
18
|
+
static Map<String,Object> type(DataType value) {
|
|
19
|
+
Map<String,Object> out=new LinkedHashMap<>();out.put("name",value.getName());out.put("path",value.getPathName());
|
|
20
|
+
out.put("size",value.getLength());out.put("category",value.getCategoryPath().getPath());out.put("kind",value.getClass().getSimpleName());
|
|
21
|
+
if(value instanceof Composite composite) {
|
|
22
|
+
List<Object> fields=new ArrayList<>();
|
|
23
|
+
for(var field:composite.getDefinedComponents()) {Map<String,Object> f=new LinkedHashMap<>();
|
|
24
|
+
f.put("name",field.getFieldName());f.put("type",field.getDataType().getName());f.put("offset",field.getOffset());f.put("size",field.getLength());fields.add(f);}
|
|
25
|
+
out.put("fields",fields);
|
|
26
|
+
}
|
|
27
|
+
if(value instanceof ghidra.program.model.data.Enum enumeration) {
|
|
28
|
+
Map<String,Object> members=new LinkedHashMap<>();for(String name:enumeration.getNames())members.put(name,Long.toString(enumeration.getValue(name)));
|
|
29
|
+
out.put("values",members);
|
|
30
|
+
}
|
|
31
|
+
if(value instanceof FunctionDefinition function) {
|
|
32
|
+
out.put("returnType",function.getReturnType().getName());
|
|
33
|
+
out.put("arguments",Arrays.stream(function.getArguments()).map(arg->arg.getDataType().getName()).toList());
|
|
34
|
+
}
|
|
35
|
+
if(value instanceof Array array){out.put("elementType",array.getDataType().getName());out.put("elementCount",array.getNumElements());}
|
|
36
|
+
if(value instanceof Pointer pointer)out.put("pointerBase",pointer.getDataType()==null?"void":pointer.getDataType().getName());
|
|
37
|
+
if(value instanceof TypeDef alias)out.put("typedefBase",alias.getBaseDataType().getName());
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
static Object inspect(ProgramDB program,JsonObject request) throws Exception {
|
|
41
|
+
String kind=string(request,"kind"),name=string(request,"name"),address=string(request,"address");
|
|
42
|
+
if(kind.equals("sdk"))return insight.InsightSdkTools.verify(program,string(request,"operation"),request.getAsJsonObject("parameters"),TaskMonitor.DUMMY);
|
|
43
|
+
var at=address.isEmpty()?null:program.getAddressFactory().getAddress(address);
|
|
44
|
+
if(!address.isEmpty()&&at==null)throw new IllegalArgumentException("Invalid inspection address");
|
|
45
|
+
if(kind.equals("debugger_bookmarks")){
|
|
46
|
+
List<String> rows=new ArrayList<>();var gson=new Gson();
|
|
47
|
+
for(String type:List.of("BreakpointEnabled","BreakpointDisabled")){
|
|
48
|
+
var bookmarks=program.getBookmarkManager().getBookmarksIterator(type);
|
|
49
|
+
while(bookmarks.hasNext()){if(rows.size()>=100000)throw new IllegalArgumentException("Debugger bookmark inventory too large");var bookmark=bookmarks.next();
|
|
50
|
+
rows.add(gson.toJson(Arrays.asList(bookmark.getAddress().toString(),type,bookmark.getCategory(),bookmark.getComment())));}
|
|
51
|
+
}
|
|
52
|
+
Collections.sort(rows);byte[] hash=java.security.MessageDigest.getInstance("SHA-256").digest(gson.toJson(rows).getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
|
53
|
+
return Map.of("count",rows.size(),"fingerprint",HexFormat.of().formatHex(hash));
|
|
54
|
+
}
|
|
55
|
+
if(kind.equals("type")||kind.equals("resolve_type")) {
|
|
56
|
+
DataType value=kind.equals("resolve_type")?ServiceUtils.resolveDataType(program.getDataTypeManager(),name):
|
|
57
|
+
ServiceUtils.findDataTypeByNameInAllCategories(program.getDataTypeManager(),name);
|
|
58
|
+
return value==null?Map.of("exists",false):Map.of("exists",true,"type",type(value));
|
|
59
|
+
}
|
|
60
|
+
if(kind.equals("data")) {
|
|
61
|
+
if(at==null)throw new IllegalArgumentException();var data=program.getListing().getDefinedDataAt(at);
|
|
62
|
+
return data==null?Map.of("exists",false):Map.of("exists",true,"type",data.getDataType().getName(),"size",data.getLength(),"details",type(data.getDataType()),"isString",data.hasStringValue());
|
|
63
|
+
}
|
|
64
|
+
if(kind.equals("import_check")) {
|
|
65
|
+
try(var expected=new StandAloneDataTypeManager("ExpectedImport",program.getDataTypeManager().getDataOrganization())) {
|
|
66
|
+
int transaction=expected.startTransaction("Parse expected types");
|
|
67
|
+
var parser=new ghidra.app.util.cparser.C.CParser(expected,true,new DataTypeManager[]{program.getDataTypeManager()});
|
|
68
|
+
try{parser.parse(string(request,"source"));}finally{expected.endTransaction(transaction,true);}
|
|
69
|
+
if(!parser.didParseSucceed())throw new IllegalArgumentException("Expected import did not parse");
|
|
70
|
+
List<Object> types=new ArrayList<>();var iter=expected.getAllDataTypes();
|
|
71
|
+
while(iter.hasNext()){if(types.size()>10000)throw new IllegalArgumentException("Import too large");var wanted=iter.next();
|
|
72
|
+
var actual=program.getDataTypeManager().getDataType(wanted.getPathName());
|
|
73
|
+
types.add(Map.of("name",wanted.getName(),"path",wanted.getPathName(),"matches",actual!=null&&wanted.isEquivalent(actual),"expected",type(wanted),"actual",actual==null?Map.of():type(actual)));}
|
|
74
|
+
return Map.of("types",types,"count",types.size());
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if(kind.equals("category"))return Map.of("exists",program.getDataTypeManager().getCategory(new CategoryPath(name))!=null);
|
|
78
|
+
if(kind.equals("memory")) {
|
|
79
|
+
var memory=program.getMemory();var block=name.isEmpty()?memory.getBlock(at):memory.getBlock(name);
|
|
80
|
+
if(block==null)return Map.of("exists",false);
|
|
81
|
+
Map<String,Object> out=new LinkedHashMap<>();out.put("exists",true);out.put("name",block.getName());out.put("start",block.getStart().toString());
|
|
82
|
+
out.put("size",block.getSize());out.put("read",block.isRead());out.put("write",block.isWrite());out.put("execute",block.isExecute());
|
|
83
|
+
out.put("volatile",block.isVolatile());out.put("initialized",block.isInitialized());out.put("overlay",block.isOverlay());out.put("comment",block.getComment());
|
|
84
|
+
out.put("physicalStart",block.getStart().getPhysicalAddress().toString());
|
|
85
|
+
if(request.has("digest_bytes")&&request.get("digest_bytes").getAsBoolean()) {
|
|
86
|
+
if(block.getSize()>16777216L)throw new IllegalArgumentException("Memory digest exceeds 16 MiB verification bound");
|
|
87
|
+
String digest=null;if(block.isInitialized()) {var hash=java.security.MessageDigest.getInstance("SHA-256");long offset=0;
|
|
88
|
+
while(offset<block.getSize()){int count=(int)Math.min(65536,block.getSize()-offset);byte[] bytes=new byte[count];int read=memory.getBytes(block.getStart().add(offset),bytes);if(read!=count)throw new IllegalStateException("Incomplete saved memory read");hash.update(bytes);offset+=count;}
|
|
89
|
+
digest=java.util.HexFormat.of().formatHex(hash.digest());}out.put("sha256",digest);
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
if(kind.equals("metadata"))return Map.of("imageBase",program.getImageBase().toString(),"sha256",program.getExecutableSHA256(),
|
|
94
|
+
"analyzed",ghidra.program.util.GhidraProgramUtilities.isAnalyzed(program),"functionCount",program.getFunctionManager().getFunctionCount());
|
|
95
|
+
if(kind.equals("function_tags")) {
|
|
96
|
+
return Map.of("tags",program.getFunctionManager().getFunctionTagManager().getAllFunctionTags().stream()
|
|
97
|
+
.map(tag->Map.of("name",tag.getName(),"comment",Objects.toString(tag.getComment(),""))).toList());
|
|
98
|
+
}
|
|
99
|
+
if(kind.equals("function")) {
|
|
100
|
+
var functions=program.getFunctionManager();var function=at==null?null:functions.getFunctionAt(at);
|
|
101
|
+
if(function==null&&!name.isEmpty()){var iter=functions.getFunctions(true);while(iter.hasNext()){var candidate=iter.next();
|
|
102
|
+
if(candidate.getName().equals(name)||candidate.getName(true).equals(name)){if(function!=null)throw new IllegalArgumentException("Ambiguous function");function=candidate;}}}
|
|
103
|
+
if(function==null)return Map.of("exists",false);
|
|
104
|
+
Map<String,Object> out=new LinkedHashMap<>();out.put("exists",true);out.put("name",function.getName());out.put("address",function.getEntryPoint().toString());
|
|
105
|
+
out.put("returnType",function.getReturnType().getName());out.put("signature",function.getSignature().getPrototypeString());
|
|
106
|
+
out.put("defaultCallingConvention",program.getCompilerSpec().getDefaultCallingConvention().getName());
|
|
107
|
+
out.put("namespace",function.getParentNamespace().getName(true));out.put("noReturn",function.hasNoReturn());out.put("callingConvention",function.getCallingConventionName());
|
|
108
|
+
List<Object> variables=new ArrayList<>();for(var variable:function.getAllVariables())variables.add(Map.of("name",variable.getName(),"type",variable.getDataType().getName(),"storage",variable.getVariableStorage().toString(),"parameter",variable instanceof ghidra.program.model.listing.Parameter));
|
|
109
|
+
out.put("variables",variables);out.put("parameters",Arrays.stream(function.getParameters()).map(param->Map.of("name",param.getName(),"type",param.getDataType().getName(),"storage",param.getVariableStorage().toString(),"auto",param.isAutoParameter())).toList());
|
|
110
|
+
out.put("tags",function.getTags().stream().map(tag->Map.of("name",tag.getName(),"comment",Objects.toString(tag.getComment(),""))).toList());
|
|
111
|
+
String prototype=string(request,"prototype");if(!prototype.isEmpty()){
|
|
112
|
+
String[] normalized=com.xebyte.core.FunctionService.extractCallingConvention(prototype);
|
|
113
|
+
var expected=new ghidra.app.util.parser.FunctionSignatureParser(program.getDataTypeManager(),null).parse(function.getSignature(),normalized[1]);
|
|
114
|
+
out.put("expectedPrototype",Map.of("name",expected.getName(),"returnType",expected.getReturnType().getName(),
|
|
115
|
+
"inlineCallingConvention",normalized[0],"parameters",Arrays.stream(expected.getArguments()).map(param->Map.of("name",Objects.toString(param.getName(),""),"type",param.getDataType().getName())).toList()));}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
if(kind.equals("instructions")) {
|
|
119
|
+
if(at==null)throw new IllegalArgumentException();var instruction=program.getListing().getInstructionAt(at);
|
|
120
|
+
return instruction==null?Map.of("exists",false):Map.of("exists",true,"flowOverride",instruction.getFlowOverride().toString(),"length",instruction.getLength(),"mnemonic",instruction.getMnemonicString());
|
|
121
|
+
}
|
|
122
|
+
if(kind.equals("instruction_range")) {
|
|
123
|
+
if(at==null)throw new IllegalArgumentException();var end=program.getAddressFactory().getAddress(string(request,"end_address"));
|
|
124
|
+
if(end==null||!at.getAddressSpace().equals(end.getAddressSpace())||end.compareTo(at)<=0)throw new IllegalArgumentException("Invalid instruction seed");
|
|
125
|
+
long count=0;var iter=program.getListing().getInstructions(new ghidra.program.model.address.AddressSet(at,end.subtract(1)),true);
|
|
126
|
+
while(iter.hasNext()){iter.next();if(++count>1000000)throw new IllegalArgumentException("Instruction seed too large");}
|
|
127
|
+
return Map.of("count",count);
|
|
128
|
+
}
|
|
129
|
+
if(kind.equals("symbols")) {
|
|
130
|
+
List<Object> symbols=new ArrayList<>();
|
|
131
|
+
if(at!=null)for(var symbol:program.getSymbolTable().getSymbols(at))symbols.add(Map.of("name",symbol.getName(),"namespace",symbol.getParentNamespace().getName(true),"primary",symbol.isPrimary(),"kind",symbol.getSymbolType().toString()));
|
|
132
|
+
else {var iter=program.getSymbolTable().getSymbols(name);while(iter.hasNext()){var s=iter.next();symbols.add(Map.of("name",s.getName(),"address",s.getAddress().toString(),"primary",s.isPrimary()));}}
|
|
133
|
+
return Map.of("symbols",symbols);
|
|
134
|
+
}
|
|
135
|
+
if(kind.equals("references")) {
|
|
136
|
+
if(at==null)throw new IllegalArgumentException();List<Object> refs=new ArrayList<>();
|
|
137
|
+
for(var ref:program.getReferenceManager().getReferencesFrom(at))refs.add(Map.of("to",ref.getToAddress().toString(),"type",ref.getReferenceType().toString(),"operand",ref.getOperandIndex(),"source",ref.getSource().toString()));
|
|
138
|
+
return Map.of("references",refs);
|
|
139
|
+
}
|
|
140
|
+
if(kind.equals("property")) {
|
|
141
|
+
var map=program.getUsrPropertyManager().getPropertyMap(name);
|
|
142
|
+
if(map==null)return Map.of("exists",false);
|
|
143
|
+
Map<String,Object> out=new LinkedHashMap<>();out.put("exists",true);out.put("type",map.getClass().getSimpleName());
|
|
144
|
+
if(at!=null) {boolean has=map.hasProperty(at);out.put("hasValue",has);
|
|
145
|
+
if(has){Object value=map instanceof ghidra.program.model.util.StringPropertyMap m?m.getString(at):
|
|
146
|
+
map instanceof ghidra.program.model.util.IntPropertyMap m?Integer.toString(m.getInt(at)):
|
|
147
|
+
map instanceof ghidra.program.model.util.LongPropertyMap m?Long.toString(m.getLong(at)):null;out.put("value",value);}}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
if(kind.equals("option")) {
|
|
151
|
+
var options=program.getOptions(string(request,"group"));boolean exists=options.contains(name);
|
|
152
|
+
Map<String,Object> out=new LinkedHashMap<>();out.put("exists",exists);if(exists){out.put("type",options.getType(name).toString());out.put("value",Objects.toString(options.getObject(name,null),null));}return out;
|
|
153
|
+
}
|
|
154
|
+
if(kind.equals("bookmarks")) {
|
|
155
|
+
if(at==null)throw new IllegalArgumentException();List<Object> entries=new ArrayList<>();
|
|
156
|
+
for(var mark:program.getBookmarkManager().getBookmarks(at))entries.add(Map.of("category",mark.getCategory(),"comment",mark.getComment(),"type",mark.getTypeString()));
|
|
157
|
+
return Map.of("bookmarks",entries);
|
|
158
|
+
}
|
|
159
|
+
if(kind.equals("comments")) {
|
|
160
|
+
if(at==null)throw new IllegalArgumentException();Map<String,Object> result=new LinkedHashMap<>();
|
|
161
|
+
result.put("eol",program.getListing().getComment(ghidra.program.model.listing.CodeUnit.EOL_COMMENT,at));
|
|
162
|
+
result.put("pre",program.getListing().getComment(ghidra.program.model.listing.CodeUnit.PRE_COMMENT,at));
|
|
163
|
+
result.put("plate",program.getListing().getComment(ghidra.program.model.listing.CodeUnit.PLATE_COMMENT,at));return result;
|
|
164
|
+
}
|
|
165
|
+
if(kind.equals("function_comments")) {
|
|
166
|
+
var function=at==null?null:program.getFunctionManager().getFunctionAt(at);
|
|
167
|
+
if(function==null&&!name.isEmpty()){var iter=program.getFunctionManager().getFunctions(true);while(iter.hasNext()){var candidate=iter.next();
|
|
168
|
+
if(candidate.getName().equals(name)){if(function!=null)throw new IllegalArgumentException("Ambiguous function");function=candidate;}}}
|
|
169
|
+
if(function==null)throw new IllegalArgumentException("Function missing");
|
|
170
|
+
List<Object> comments=new ArrayList<>();var iter=program.getListing().getInstructions(function.getBody(),true);int count=0;
|
|
171
|
+
while(iter.hasNext()){if(++count>100000)throw new IllegalArgumentException("Function too large for comment inspection");var instruction=iter.next();
|
|
172
|
+
for(int type:new int[]{ghidra.program.model.listing.CodeUnit.PRE_COMMENT,ghidra.program.model.listing.CodeUnit.EOL_COMMENT}){
|
|
173
|
+
String comment=program.getListing().getComment(type,instruction.getAddress());if(comment!=null)comments.add(Map.of("kind",type==ghidra.program.model.listing.CodeUnit.EOL_COMMENT?"eol":"pre","address",instruction.getAddress().toString(),"text",comment));}}
|
|
174
|
+
Map<String,Object> result=new LinkedHashMap<>();result.put("plate",function.getComment());result.put("comments",comments);return result;
|
|
175
|
+
}
|
|
176
|
+
throw new IllegalArgumentException("Unsupported saved-state inspection");
|
|
177
|
+
}
|
|
178
|
+
public static void main(String[] args) {
|
|
179
|
+
var output=System.out;System.setOut(System.err);
|
|
180
|
+
try {
|
|
181
|
+
if(args.length!=2)throw new IllegalArgumentException();String encoded=Files.readString(Path.of(args[1]));if(encoded.length()>65536)throw new IllegalArgumentException();
|
|
182
|
+
Application.initializeApplication(new GhidraApplicationLayout(),new HeadlessGhidraApplicationConfiguration());
|
|
183
|
+
DBHandle db=PackedDatabase.getPackedDatabase(Path.of(args[0]).toFile(),true,TaskMonitor.DUMMY).open(TaskMonitor.DUMMY);
|
|
184
|
+
Object consumer=new Object();ProgramDB program=new ProgramDB(db,OpenMode.IMMUTABLE,TaskMonitor.DUMMY,consumer);
|
|
185
|
+
try {output.println(new GsonBuilder().serializeNulls().create().toJson(Map.of("value",inspect(program,JsonParser.parseString(encoded).getAsJsonObject()))));}
|
|
186
|
+
finally {program.release(consumer);}System.exit(0);
|
|
187
|
+
}catch(Throwable failure){failure.printStackTrace(System.err);output.println("{\"error\":\"saved_inspection_failed\"}");System.exit(1);}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
package db;
|
|
2
|
+
|
|
3
|
+
import com.google.gson.Gson;
|
|
4
|
+
import com.xebyte.headless.GhidraServerManager;
|
|
5
|
+
import db.buffers.ManagedBufferFile;
|
|
6
|
+
import db.buffers.LocalBufferFile;
|
|
7
|
+
import db.buffers.InsightBlockTransfer;
|
|
8
|
+
import ghidra.GhidraApplicationLayout;
|
|
9
|
+
import ghidra.framework.Application;
|
|
10
|
+
import ghidra.framework.HeadlessGhidraApplicationConfiguration;
|
|
11
|
+
import ghidra.framework.data.OpenMode;
|
|
12
|
+
import ghidra.framework.store.CheckoutType;
|
|
13
|
+
import ghidra.framework.store.db.PackedDatabase;
|
|
14
|
+
import ghidra.program.database.ProgramDB;
|
|
15
|
+
import ghidra.program.model.listing.ProgramChangeSet;
|
|
16
|
+
import ghidra.util.task.TaskMonitor;
|
|
17
|
+
import java.io.*;
|
|
18
|
+
import java.nio.file.*;
|
|
19
|
+
import java.util.*;
|
|
20
|
+
|
|
21
|
+
/** Publish one verified saved private program through the pinned native DB transfer protocol.
|
|
22
|
+
* Package db grants access to the pinned DBHandle saved source buffers without reflection.
|
|
23
|
+
* The caller owns this repository and must journal intent before invoking this helper. */
|
|
24
|
+
class InsightSnapshotPublisher {
|
|
25
|
+
static void receipt(Path file, Map<String,Object> state) throws Exception {
|
|
26
|
+
Path temporary = file.resolveSibling(file.getFileName() + ".new");
|
|
27
|
+
try (FileOutputStream stream = new FileOutputStream(temporary.toFile())) {
|
|
28
|
+
stream.write(new Gson().toJson(state).getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
|
29
|
+
stream.getFD().sync();
|
|
30
|
+
}
|
|
31
|
+
Files.move(temporary, file, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
|
32
|
+
}
|
|
33
|
+
public static void main(String[] args) {
|
|
34
|
+
PrintStream output = System.out;
|
|
35
|
+
System.setOut(System.err);
|
|
36
|
+
Map<String,Object> state = new LinkedHashMap<>();
|
|
37
|
+
try {
|
|
38
|
+
if (args.length != 7) throw new IllegalArgumentException("Expected seven publication arguments");
|
|
39
|
+
String repositoryName=args[0], path=args[1], marker=args[4];
|
|
40
|
+
int expected=Integer.parseInt(args[3]); Path journal=Path.of(args[5]);
|
|
41
|
+
if (!path.startsWith("/") || path.indexOf('/',1)>=0 || !marker.matches("insight merge q_[a-f0-9-]{36}")) throw new IllegalArgumentException();
|
|
42
|
+
Application.initializeApplication(new GhidraApplicationLayout(), new HeadlessGhidraApplicationConfiguration());
|
|
43
|
+
GhidraServerManager manager=new GhidraServerManager(); manager.connect();
|
|
44
|
+
var repository=manager.server().getRepository(repositoryName); repository.connect();
|
|
45
|
+
var user=repository.getUser();
|
|
46
|
+
if (user==null || !user.hasWritePermission() || !user.getName().equals(System.getenv("GHIDRA_SERVER_USER"))) throw new SecurityException();
|
|
47
|
+
String folder="/", name=path.substring(1);
|
|
48
|
+
state.put("repository",repositoryName);state.put("path",path);state.put("marker",marker);state.put("baseline",expected);
|
|
49
|
+
if (repository.getItem(folder,name).getVersion()!=expected) {
|
|
50
|
+
output.println("{\"state\":\"conflict\",\"applied\":0}"); System.exit(2);
|
|
51
|
+
}
|
|
52
|
+
long databaseId;String expectedFingerprint;
|
|
53
|
+
var checkout=repository.checkout(folder,name,CheckoutType.EXCLUSIVE,journal.getParent().toString());
|
|
54
|
+
if (checkout==null) throw new IllegalStateException("Exclusive checkout denied");
|
|
55
|
+
state.put("checkoutId",checkout.getCheckoutId()); state.put("baseline",expected); state.put("phase","checkout"); receipt(journal,state);
|
|
56
|
+
if (checkout.getCheckoutVersion()!=expected || repository.getItem(folder,name).getVersion()!=expected) throw new IllegalStateException("Baseline changed before exclusive ownership");
|
|
57
|
+
ManagedBufferFile remote=repository.openDatabase(folder,name,checkout.getCheckoutId());
|
|
58
|
+
boolean completed=false;
|
|
59
|
+
try {
|
|
60
|
+
state.put("phase","commit_intent"); receipt(journal,state);
|
|
61
|
+
PackedDatabase packed=PackedDatabase.getPackedDatabase(new File(args[2]),true,TaskMonitor.DUMMY);
|
|
62
|
+
DBHandle snapshot=packed.open(TaskMonitor.DUMMY);
|
|
63
|
+
databaseId=snapshot.getDatabaseId();
|
|
64
|
+
expectedFingerprint=InsightBlockTransfer.fingerprint(snapshot.getBufferMgr().getSourceFile());
|
|
65
|
+
Object snapshotConsumer=new Object();
|
|
66
|
+
ProgramDB snapshotProgram=new ProgramDB(snapshot,OpenMode.IMMUTABLE,TaskMonitor.DUMMY,snapshotConsumer);
|
|
67
|
+
if (!args[6].equals(snapshotProgram.getExecutableSHA256())) throw new SecurityException("Snapshot source identity changed");
|
|
68
|
+
ProgramChangeSet changes=(ProgramChangeSet)snapshotProgram.getChangeSet();
|
|
69
|
+
snapshotProgram.getChangeSet().startTransaction();
|
|
70
|
+
try {
|
|
71
|
+
changes.add(snapshotProgram.getMemory());
|
|
72
|
+
var dataTypes=snapshotProgram.getDataTypeManager().getAllDataTypes();
|
|
73
|
+
while(dataTypes.hasNext()) changes.dataTypeChanged(snapshotProgram.getDataTypeManager().getID(dataTypes.next()));
|
|
74
|
+
var symbols=snapshotProgram.getSymbolTable().getAllSymbols(true);
|
|
75
|
+
while(symbols.hasNext()) changes.symbolChanged(symbols.next().getID());
|
|
76
|
+
} finally { snapshotProgram.getChangeSet().endTransaction(true); }
|
|
77
|
+
ManagedBufferFile save=remote.getSaveFile();
|
|
78
|
+
if (save==null) throw new IllegalStateException("Save buffer denied");
|
|
79
|
+
save.setVersionComment(marker);
|
|
80
|
+
try {
|
|
81
|
+
InsightBlockTransfer.copy((LocalBufferFile)snapshot.getBufferMgr().getSourceFile(),save);
|
|
82
|
+
var changeFile=remote.getSaveChangeDataFile();
|
|
83
|
+
if(changeFile==null) throw new IllegalStateException("Native change-set buffer missing");
|
|
84
|
+
DBHandle changeDb=new DBHandle();
|
|
85
|
+
try {
|
|
86
|
+
snapshotProgram.getChangeSet().write(changeDb,false);
|
|
87
|
+
changeDb.saveAs(changeFile,false,TaskMonitor.DUMMY);
|
|
88
|
+
} finally { changeDb.close(); changeFile.dispose(); }
|
|
89
|
+
} finally { snapshotProgram.release(snapshotConsumer); }
|
|
90
|
+
remote.saveCompleted(true); completed=true;
|
|
91
|
+
save.dispose();
|
|
92
|
+
state.put("phase","committed"); receipt(journal,state);
|
|
93
|
+
} finally {
|
|
94
|
+
remote.dispose();
|
|
95
|
+
if (!completed) state.put("phase","unknown");
|
|
96
|
+
}
|
|
97
|
+
var versions=repository.getVersions(folder,name);
|
|
98
|
+
long matches=Arrays.stream(versions).filter(v->marker.equals(v.getComment())).count();
|
|
99
|
+
int actual=repository.getItem(folder,name).getVersion();
|
|
100
|
+
if (matches!=1 || actual!=expected+1) throw new IllegalStateException("Publication history does not establish outcome");
|
|
101
|
+
DBHandle readback=new DBHandle(repository.openDatabase(folder,name,actual,-1));
|
|
102
|
+
try {
|
|
103
|
+
if (readback.getDatabaseId()!=databaseId) throw new IllegalStateException("Saved snapshot database ID changed");
|
|
104
|
+
if(!expectedFingerprint.equals(InsightBlockTransfer.fingerprint(readback.getBufferMgr().getSourceFile())))
|
|
105
|
+
throw new IllegalStateException("Published database buffers differ from the verified saved snapshot");
|
|
106
|
+
Object consumer=new Object();
|
|
107
|
+
ProgramDB program=new ProgramDB(readback,OpenMode.IMMUTABLE,TaskMonitor.DUMMY,consumer);
|
|
108
|
+
program.release(consumer);
|
|
109
|
+
} finally { readback.close(); }
|
|
110
|
+
repository.terminateCheckout(folder,name,checkout.getCheckoutId(),false);
|
|
111
|
+
if (Arrays.stream(repository.getCheckouts(folder,name)).anyMatch(c->c.getCheckoutId()==checkout.getCheckoutId())) throw new IllegalStateException("Owned checkout not released");
|
|
112
|
+
state.put("phase","released"); state.put("version",actual); receipt(journal,state);
|
|
113
|
+
repository.disconnect(); manager.server().disconnect();
|
|
114
|
+
output.println(new Gson().toJson(Map.of("state","published","version",actual,"snapshotDatabaseIdPreserved",true,"snapshotBuffersVerified",true,"ownedCheckoutReleased",true)));
|
|
115
|
+
System.exit(0);
|
|
116
|
+
} catch (Throwable failure) {
|
|
117
|
+
System.err.println(failure.getClass().getName()+": "+failure.getMessage());
|
|
118
|
+
failure.printStackTrace(System.err);
|
|
119
|
+
output.println(new Gson().toJson(Map.of("state","failed","phase",state.getOrDefault("phase","setup"))));
|
|
120
|
+
System.exit(1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import ghidra.app.script.GhidraScript;
|
|
2
|
+
import ghidra.app.services.DebuggerTraceManagerService;
|
|
3
|
+
import ghidra.app.services.ProgramManager;
|
|
4
|
+
import ghidra.framework.plugintool.PluginTool;
|
|
5
|
+
import javax.swing.SwingUtilities;
|
|
6
|
+
import java.util.concurrent.atomic.AtomicReference;
|
|
7
|
+
|
|
8
|
+
/** Idempotent resource startup in this script's owned project; does not launch a debuggee. */
|
|
9
|
+
public class InsightStartDebugger extends GhidraScript {
|
|
10
|
+
void prepare(PluginTool tool)throws Exception{
|
|
11
|
+
tool.getService(DebuggerTraceManagerService.class).setAutoCloseOnTerminate(false);
|
|
12
|
+
var programs=tool.getService(ProgramManager.class);if(programs==null||currentProgram==null)return;
|
|
13
|
+
Runnable open=()->{if(!java.util.Arrays.asList(programs.getAllOpenPrograms()).contains(currentProgram))programs.openProgram(currentProgram);};
|
|
14
|
+
if(SwingUtilities.isEventDispatchThread())open.run();else SwingUtilities.invokeAndWait(open);
|
|
15
|
+
}
|
|
16
|
+
@Override public void run()throws Exception{
|
|
17
|
+
var project=state.getProject();if(project==null)throw new IllegalStateException("Owned project missing");
|
|
18
|
+
var manager=project.getToolManager();
|
|
19
|
+
for(PluginTool tool:manager.getRunningTools())if(tool.getService(DebuggerTraceManagerService.class)!=null){prepare(tool);println("INSIGHT_DEBUGGER_READY");return;}
|
|
20
|
+
var template=project.getLocalToolChest().getToolTemplate("Debugger");if(template==null)throw new IllegalStateException("Debugger tool template missing");
|
|
21
|
+
AtomicReference<PluginTool> started=new AtomicReference<>();
|
|
22
|
+
Runnable start=()->started.set(manager.getActiveWorkspace().runTool(template));
|
|
23
|
+
if(SwingUtilities.isEventDispatchThread())start.run();else SwingUtilities.invokeAndWait(start);
|
|
24
|
+
if(started.get()==null||started.get().getService(DebuggerTraceManagerService.class)==null)throw new IllegalStateException("Debugger services unavailable");
|
|
25
|
+
prepare(started.get());
|
|
26
|
+
println("INSIGHT_DEBUGGER_READY");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import ghidra.app.script.GhidraScript;
|
|
2
|
+
import ghidra.app.services.DebuggerTraceManagerService;
|
|
3
|
+
import ghidra.app.services.DebuggerTargetService;
|
|
4
|
+
import ghidra.framework.plugintool.PluginTool;
|
|
5
|
+
import java.util.concurrent.TimeUnit;
|
|
6
|
+
|
|
7
|
+
/** Close only target objects reached through this owned project's debugger services. */
|
|
8
|
+
public class InsightStopDebugger extends GhidraScript {
|
|
9
|
+
@Override public void run()throws Exception{
|
|
10
|
+
var project=state.getProject();if(project==null)throw new IllegalStateException("Owned project missing");
|
|
11
|
+
for(PluginTool tool:project.getToolManager().getRunningTools()){
|
|
12
|
+
var traces=tool.getService(DebuggerTraceManagerService.class);var targets=tool.getService(DebuggerTargetService.class);
|
|
13
|
+
if(traces==null||targets==null)continue;
|
|
14
|
+
for(var trace:traces.getOpenTraces()){
|
|
15
|
+
var target=targets.getTarget(trace);if(target!=null&&target.isValid())target.forceTerminateAsync().get(15,TimeUnit.SECONDS);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
println("INSIGHT_DEBUGGER_STOPPED");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import com.google.gson.Gson;
|
|
2
|
+
import db.DBHandle;
|
|
3
|
+
import ghidra.GhidraApplicationLayout;
|
|
4
|
+
import ghidra.framework.Application;
|
|
5
|
+
import ghidra.framework.HeadlessGhidraApplicationConfiguration;
|
|
6
|
+
import ghidra.framework.data.OpenMode;
|
|
7
|
+
import ghidra.framework.store.db.PackedDatabase;
|
|
8
|
+
import ghidra.trace.database.DBTrace;
|
|
9
|
+
import ghidra.util.task.TaskMonitor;
|
|
10
|
+
import java.nio.file.Path;
|
|
11
|
+
import java.util.*;
|
|
12
|
+
|
|
13
|
+
/** Independent immutable readback of a packed trace, including its logical owner binding. */
|
|
14
|
+
class InsightTraceInspect {
|
|
15
|
+
public static void main(String[] args){var output=System.out;System.setOut(System.err);try{
|
|
16
|
+
Application.initializeApplication(new GhidraApplicationLayout(),new HeadlessGhidraApplicationConfiguration());
|
|
17
|
+
DBHandle db=PackedDatabase.getPackedDatabase(Path.of(args[0]).toFile(),true,TaskMonitor.DUMMY).open(TaskMonitor.DUMMY);
|
|
18
|
+
Object consumer=new Object();DBTrace trace=new DBTrace(db,OpenMode.IMMUTABLE,TaskMonitor.DUMMY,consumer);
|
|
19
|
+
try{var options=trace.getOptions("Insight");
|
|
20
|
+
output.println(new Gson().toJson(Map.of("id",options.getString("Trace ID",""),"target",options.getString("Target",""),"context",options.getString("Context",""),
|
|
21
|
+
"name",trace.getName(),"modules",trace.getModuleManager().getAllModules().size(),"threads",trace.getThreadManager().getAllThreads().size())));
|
|
22
|
+
}finally{trace.release(consumer);}System.exit(0);
|
|
23
|
+
}catch(Throwable error){error.printStackTrace(System.err);output.println("{\"error\":\"trace_readback_failed\"}");System.exit(1);}}
|
|
24
|
+
}
|