yea so basically you have an environment and a goal. for example, you can have many objects with "predicates" (basically properties) and you want to know if some predicate can be true. for example, every byte in memory can be an object and can have the predicate "is user controlled". you then have actions, which change the predicates of bytes. for example, if i wanted to represent a "gets()" on the heap as an action, i would mark every byte following the gets address as "is user controlled".
you feed your actions, goals, and objects into a pddl solver, and it'll spit out a series of actions to make the goal true. for example, maybe your goal is to set "is user controlled" on the bytes of __free_hook or something, and the pddl solver will figure out what series of heap allocations and frees and whatnot would make that true. it'd be cool if we had action definitions for four function heap, but as i mentioned before, at a certain level of specificity the problem is equivalent to symbolic execution. at that point, you might as well have angr try to find unconstrained states lol









