src/Finder.js
var ut = require("./Utils.js");
const CLASS = require("./CoreClass.js");
const FILE = require("./File.js");
const Logger = require("./Logger.js")();
var CONST = require("./CoreConst.js");
var MemoryDb = require("../connectors/inmemory/InMemoryDb.js");
var AppCmp = require("./AndroidAppComponents.js");
const Accessor = require("./AccessFlags");
const ModelPackage = require('./ModelPackage');
var DataModel = {
package: new ModelPackage("stub"),
class: new CLASS.Class(),
field: new CLASS.Field(),
method: new CLASS.Method(),
call: new CLASS.Call(),
modifier: new Accessor.AccessFlags(),
objectType: new CLASS.ObjectType(),
basicType: new CLASS.BasicType(),
value: new CLASS.ValueConst(),
string: new CLASS.StringValue(),
syscall: new CLASS.Syscall(),
missing: new CLASS.MissingReference(),
file: new FILE.File(),
datablock: new CLASS.DataBlock(),
activity: new AppCmp.Activity(),
receiver: new AppCmp.Receiver(),
provider: new AppCmp.Provider(),
service: new AppCmp.Service(),
permission: new AppCmp.Permission(),
};
function PreparedRequest(name){
this.name = name;
this.actions = [];
//this._run = null;
return this;
}
PreparedRequest.prototype.pushSelect = function(name, params){
this.actions.push({ fn:name, param:params });
return this;
};
PreparedRequest.prototype.pushAction = function(name, params){
this.actions.push({ fn:name, param:params });
return this;
};
PreparedRequest.prototype.exec = function(ctx){
let i = 0;
let res = ctx.find;
while(i<this.actions.length){
res = res[this.actions[0]]
// if(this.actions instanceof )
i++;
}
}
class SearchPattern
{
constructor(cfg){
this.fn = null;
this.pattern = null;
this.field = null;
this.isModifier = false;
this.isStructField = false;
this.isDeepSearch = false;
// init
if(cfg!==undefined)
for(let i in cfg) this[i] = cfg[i];
}
serialize(){
let o = new Object();
o.isModifier = this.isModifier;
o.isStructField = this.isStructField;
o.isDeepSearch = this.isDeepSearch;
o.field = this.field;
o.pattern = this.pattern;
return o;
}
}
class FinderJoin
{
constructor(rootData,joinData,finder){
this.rootData = rootData;
this.joinData = joinData
this._finder = finder;
}
// do rootData[]-joinData[]
sub(){
let res=new MemoryDb.Index(), x=0;
this.rootData.map((k,v)=>{
// element ignored if joinData contains
if(this.joinData.hasEntry(v))
return;
else
res.insert(v);
});
return new FinderResult(res,this._finder);
}
on(pattern){
let prop = pattern.split(".");
let lp = prop.length, pref=null, res=new MemoryDb.Index(), x=0;
this.joinData.map((k,v)=>{
x=0;
pref = v;
do{ pref = pref[prop[x++]] }while(x<lp);
this.rootData.map((i,j)=>{
let y=0, xref = j
do{ xref = xref[prop[y++]] }while(y<lp);
if(xref==pref)
res.insert(this.rootData[j]);
});
});
return new FinderResult(res,this._finder);
};
}
function FinderResult(data,finder){
this.data = data;
this._finder = finder;
this.foreach = function(fn){
this.data.map(fn);
//for(let i in this.data) fn(this.data[i]);
};
return this;
}
FinderResult.prototype.getData = function(){
return this.data.getAll();
}
/**
* To get reference to the method calling each object
*/
FinderResult.prototype.caller = function(){
let meth = new MemoryDb.Index(), obj=null;
this.data.map((k,v)=>{
obj=this.data[i];
if(v instanceof CLASS.StringValue){
meth.insert(v.src);
}
else if(v instanceof CLASS.ValueConst){
console.error("[!] Not implemented : [ValueConst].method() ")
}
else if(v instanceof CLASS.Field
|| v instanceof CLASS.Method
|| v instanceof CLASS.Class){
for(let k=0; k<v._callers.length; k++ ){
meth.insert(v._callers[k]);
}
}
else if(v instanceof CLASS.Call){
meth.insert(v.caller)
}
});/*
for(let i in this.data){
obj=this.data[i];
if(obj instanceof CLASS.StringValue){
meth.push(obj.src);
}
else if(obj instanceof CLASS.ValueConst){
console.error("[!] Not implemented : [ValueConst].method() ")
}
else if(obj instanceof CLASS.Field
|| obj instanceof CLASS.Method
|| obj instanceof CLASS.Class){
for(let k=0; k<obj._callers.length; k++ ){
meth.push(obj._callers[k]);
}
}
else if(obj instanceof CLASS.Call){
meth.push(obj.caller)
}
}
*/
return new FinderResult(meth,this._finder);
};
/**
* Perform lzing filtering
* @param {*} pattern
*/
FinderResult.prototype.filter = function(pattern, caseSensitive=true){
// perform search with lazy mode
// not detects fails
return this._finder._find(this.data, null, pattern, caseSensitive, true);
};
FinderResult.prototype.ifilter = function(pattern){
// perform search with lazy mode
// not detects fails
return this.filter(pattern, false);
};
/**
* To merge two results sets, object already present are ignored
* @param {FinderResult} data Another result set or list of object
* @returns {FinderResult}
*/
FinderResult.prototype.union = function(resultSet){
if(typeof resultSet === 'string' || resultSet instanceof String){
let res = this._finder._find(resultSet);
res.data.map((k,v)=>{
if(!this.contains(v))
this.data.insert(v);
});
}else{
resultSet.data.map((k,v)=>{
if(!this.contains(v))
this.data.insert(v);
});
}
return this;
};
/**
* To get the list of callers of each object contained into the current result set
* @returns {FinderResult} A list of instructions using a reference to this object
*/
FinderResult.prototype.callers = function(){
let rset = new FinderResult();
this.data.map((k,v) => {
for(let i in v._callers){
rset.data.insert(v._callers[i]);
}
});
/*
for(let i in this.data){
for(let k in this.data[i]._callers){
rset.data.push(this.data[i]._callers[k]);
}
}*/
return rset;
};
/**
* To get the count of object into the result set
* @return {int} The count of object into the result set
*/
FinderResult.prototype.count = function(){
return this.data.size();
};
FinderResult.prototype.get = function(offset){
return this.data.getEntry(offset);
};
FinderResult.prototype.select = function(member){
let data = new MemoryDb.Index();
this.data.map((k,v)=>{
if(v[member] !==undefined) data.insert(v[member]);
});
return new FinderResult(data,this._finder);
};
FinderResult.prototype.toString = function(){
let out = "";
this.data.map((k,x)=>{
out += x._hashcode+"\n";
});
return out;
}
FinderResult.prototype.dump = function(){
console.log(this.toString());
};
FinderResult.prototype.toJsonObject = function(fields){
let data=[], stub={};
this.data.map((k,v)=>{
if(v.toJsonObject == undefined){
console.log("ERROR : toJsonObject() not found");
}else if(! (v instanceof CLASS.MissingReference)){
data.push(v.toJsonObject(fields));
}else{
stub = {};
for(let k in fields) stub[fields[k]] = "[MissingReference] Object";
data.push(stub);
}
});
/*
for(let i in this.data){
if(this.data[i].toJsonObject == undefined){
console.log("ERROR : toJsonObject() not found");
}else if(! (this.data[i] instanceof CLASS.MissingReference)){
data.push(this.data[i].toJsonObject(fields));
}else{
stub = {};
for(let k in fields) stub[fields[k]] = "[MissingReference]";
data.push(stub);
}
}*/
return data;
};
/**
* To search references to the given objects
*/
FinderResult.prototype.xref = function(){
let data=new MemoryDb.Index();
this.data.map((k,v)=>{
data.insert(new CLASS.XRef(v,v._callers));
});
return new FinderResult(data,this._finder);
};
FinderResult.prototype.help = function(){
let t="+-------------------- HELP --------------------+";
t += "\n\t.foreach(<fn>)\t\tExecute the function <fn> for each row of the result set";
t += "\n\t.caller()\t\tSearch the xref for each row of the result set";
t += "\n\t.filter(<pattern>)\tvFilter the result set by searching a pattern (same format than .find(<pattern>) )";
t += "\n\t.ifilter(<pattern>)\t\tSame than .filter() but case insensitive";
t += "\n\t.contains(<object>)\tvCheck the result set contains the given <object>";
t += "\n\t.union(<FinderResult>)\tvPerform an union between two result sets";
t += "\n\t.exclude(<pattern>)\tvExclude a subset of objects matching the pattern <pattern>";
t += "\n\t.show()\t\tDisplay the result data with a formatted style";
t += "\n\t.sshow()\t\tSame than .show() but with lesser data. (Small Show)";
t += "\n\t.ssshow(<length>)\t\tSame than .sshow() but with truncate data at <length> char. (Small Small Show)";
t += "\n\t.get(<id>)\t\tGet the <id> matching object from the result set.";
t += "\n\t.using(<pattern>)\t\tFilter by class/field/method used by the subject";
t += "\n\t.count()\t\tGet the count of matching object contained in the result set";
t += "\n\t.help()\t\tThis help";
t += "\n";
console.log(t)
};
FinderResult.prototype.using = function(pattern){
let data = new MemoryDb.Index();
if(pattern == null) return this;
data = this._finder._find(this.data, null, "_useClass."+pattern, caseSensitive, true);
data.union(this._finder._find(this.data, null, "_useMethod."+pattern, caseSensitive, true));
data.union(this._finder._find(this.data, null, "_useField."+pattern, caseSensitive, true));
return new FinderResult(data,this._finder);
};
FinderResult.prototype.exclude = function(pattern){
let res = new MemoryDb.Index();
let arg = this._finder.cache[this._finder.cache.length-1];
let result = this._finder._find(arg.index, arg.model, pattern, arg.case, arg.lazy);
return new FinderJoin(this.data,result,this._finder);
};
FinderResult.prototype.intersect = function(property,pattern){
let res = new MemoryDb.Index();
let arg = this._finder.cache[this._finder.cache.length-1];
let result = this._finder._find(arg.index, arg.model, pattern, arg.case, arg.lazy);
return (new FinderJoin(this.data,result,this._finder)).sub();
};
/**
* To check is a result set contains an object
* @param {*} obj Should has a field _hashcode containing the unique identifier of the object
*/
FinderResult.prototype.contains = function(obj){
let f=0;
this.data.map((k,v)=>{
if(obj._hashcode===v._hashcode) f++;
});
// TODO : remove
// console.log("[DBG] "+obj._hashcode+" not contained");
return (f>0);
};
/**
* To display data with formatting
* Short Show
*/
FinderResult.prototype.sshow = function(){
let sub = [];
this.data.map((k,x)=>{
if(x instanceof CLASS.Method){
sub.push({
Class: x.enclosingClass.package+"."+x.enclosingClass.simpleName,
Method: x.name,
});
}
else if(x instanceof CLASS.Class){
sub.push({
Class: x.name
});
}
else if(x instanceof CLASS.Field){
sub.push({
Class: x.enclosingClass.package+"."+x.enclosingClass.simpleName,
Field: x.name
});
}
else if(x instanceof CLASS.StringValue){
sub.push({
Value: x.value
});
}
else if(x instanceof CLASS.Call){
sub.push({
Type: CONST.INSTR_TYPE_LABEL[x.instr.opcode.type],
Calleed: x.calleed.signature()
});
}
else if(x instanceof FILE.File){
sub.push({
Name: x.name,
Extension: (x.type!=null)? x.type.ext : "[NULL]"
});
}
else if(x instanceof CLASS.XRef){
sub.push({ Subject: x.calleed.signature() });
if(x.empty)
sub.push({ Subject: "\t No reference found" });
else
for(let k in x.xref) sub.push({ Subject: "\t "+x.xref[k].signature() });
}
});
console.log(ut.makeTable(sub));
sub = null;
};
/**
* To display data with formatting
*/
FinderResult.prototype.show = function(){
let sub = [];
this.data.map((k,x)=>{
if(x instanceof CLASS.Method){
sub.push({
Class: x._hashcode.substr(0,x._hashcode.indexOf('|')),
Modifiers: x.modifiers.sprint(),
Method: x.name,
});
}
else if(x instanceof CLASS.Class){
sub.push({
Package: x.package,
SimpleName: x.simpleName
});
}
else if(x instanceof CLASS.Field){
sub.push({
Class: x.enclosingClass.package+"."+x.enclosingClass.simpleName,
Field: x.name,
Modifiers: x.modifiers.sprint()
});
}
else if(x instanceof CLASS.StringValue){
sub.push({
Value: x.value,
Caller: x.src.signature()
});
}
else if(x instanceof CLASS.Call){
sub.push({
Type: CONST.INSTR_TYPE_LABEL[x.instr.opcode.type],
Caller: x.caller.signature(),
Calleed: x.calleed.signature()
});
}
else if(x instanceof CLASS.XRef){
sub.push({ Subject: x.calleed.signature() });
if(x.empty)
sub.push({ Subject: "\t No reference found" });
else
for(let k in x.xref) sub.push({ Subject: "\t "+x.xref[k].signature() });
}
else if(x instanceof FILE.File){
sub.push({
Name: x.path,
Extension: (x.type!=null)? x.type.ext : "[NULL]"
});
}
else if(x instanceof CLASS.Syscall){
sub.push({
"num": x.sysnum.join(","),
"Function": x.func_name,
"Syscall": x.sys_name,
"Params": x.args.join(","),
"Return Type": x.ret
});
}
else{
sub.push({ Value: x.toString() });
}
});
console.log(ut.makeTable(sub));
sub = null;
};
class Finder
{
constructor(pDatabase){
this.__DB = pDatabase;
this.cache = [];
this._test = {
/**
* Check if the <data> object has the <pattern> modifier
*
*/
hasModifier: function(request,data){
//console.log(request.field, data.modifiers[request.field])
return data.modifiers[request.field]; //(data.modifiers[request.field]!==0);
},
/**
* Check if the <data> object is tagged with <pattern>
*
*/
hasTag: function(request,data){
if(data.tags === undefined)
console.error("Object "+data+" has not 'tags' field");
//console.log(data.tags.indexOf(request.pattern), data.tags, request.pattern);
return data.tags.indexOf(request.pattern)>-1;
},
/**
* Mock
*/
NO_TEST: true
};
if(pDatabase != null){
Logger.info("[SEARCH] Finder inittialized");
}else{
Logger.info("[SEARCH] Finder DB is empty");
}
}
updateDB(pDatabase){
this.__DB = pDatabase;
}
/**
* To parse a pattern like [native:]*ssl*.
- wildcard : replace any char
- case sensitive
- add unicode
* @param {*} dataModel
* @param String pattern
* @param Boolean caseSensitive
* @param Boolean lazy If FALSE, verify if the field exists
* @returns {SearchPattern} The parsed search pattern, ready to be used
*/
_getTestFn(dataModel, pattern, caseSensitive, lazy=false){
//if(lazy===true) console.debug("LAZY mode detected !");
if(pattern==undefined || pattern.length==0){
console.log("[!] find : Pattern cannot be null");
return null;
}
let token = "name", lex=-1, isDeepSearch=false, rx=null, fn=null, flags="";
// test si le motif s'applique sur un champs particulier
// parse pattern
if(pattern.substr(0,3)=="is."){
if((lex=pattern.indexOf(":"))>-1){
token = pattern.substr(3,lex-3);
pattern = pattern.substr(lex+1,pattern.length-lex-1);
}else{
token = pattern.substr(3, pattern.length-3);
pattern = "";
}
//console.debug("Modifier search ... "+token+"."+pattern+" == true");
if(lazy === false){
if(DataModel.modifier[token] !== undefined)
return new SearchPattern({
pattern:pattern,
field:token,
isModifier:true,
fn:this._test.hasModifier
});
else{
console.log("[!] The modifier '"+token+"' not exists for these objects");
return null;
}
}else{
//console.debug("LAZY filtering ...");
return new SearchPattern({
pattern:pattern,
field:token,
isModifier:true,
fn:this._test.hasModifier
});
}
}
else if(pattern.substr(0,4)=="has."){
//console.debug("Tag-based request detected");
return new SearchPattern({
pattern: pattern.substr(4),
isModifier: false,
hasTag: true,
fn:this._test.hasTag
});
}
/*
// exact match is not a RegExp-based search
else if(pattern.substr(0,7)=="#exact#"){
//console.debug("Tag-based request detected");
return new SearchPattern({
pattern: pattern.substr(4),
isModifier: false,
hasTag: true,
fn:this._test.hasExactToken
});
}*/
if((lex=pattern.indexOf(":"))>-1){
token = pattern.substr(0,lex);
pattern = pattern.substr(lex+1);
}else{
// DEFAULT field must be parameterized
token = "name";
pattern = "";
}
// check if it is a deep search
if(token.indexOf(".")>-1){
token = token.split(".");
isDeepSearch = true;
//console.debug("Deep search detected !");
}
if(lazy === false && isDeepSearch === false && dataModel[token] === undefined){
console.log("[!] The property '"+token+"' not exists for these objects");
return null;
}
// make corresponding regexp
flags += (caseSensitive?"":"i");
rx = new RegExp(pattern,flags);
if(rx != null){
fn = function(x){
return rx.test(x)
} ;
}else{
fn = this._test.NO_TEST
}
let struct = false;
if(lazy === false && isDeepSearch===false)
struct = (dataModel[token] instanceof Array)||(dataModel[token] instanceof Object);
return new SearchPattern({
pattern: pattern,
field: token,
isStructField: struct,
isDeepSearch: isDeepSearch,
fn: fn,
});
}
_findObject(index, search_pattern, includeMissing=false){
let matches= new MemoryDb.Index(), k=0, field=undefined;
//console.log(search_pattern);
index.map((k,v)=>{
if(!includeMissing && (v instanceof CLASS.MissingReference))
return;
if((v instanceof CLASS.Method)
&& (v.modifiers === undefined || v.modifiers === null))
return;
field = v[search_pattern.field];
if(field!==undefined && search_pattern.fn(field))
matches.insert(v);
});
/*
for(let i in index){
if(!includeMissing && index[i] instanceof CLASS.MissingReference)
continue;
if(index[i] instanceof CLASS.Method
&& (index[i].modifiers === undefined || index[i].modifiers === null))
continue;
field = index[i][search_pattern.field];
if(field!==undefined && search_pattern.fn(field))
matches.push(index[i]);
}
//console.log("[*] "+matches.length+" items found");*/
return matches;
};
/**
* To search an object by applying the condition on nested fields.
* The depth level is ignored, the field is searched recursively by following the path
* give by the search argument.
*
* @param {*} object
* @param {SearchPattern} search A search pattern containing the full path to the field to compare
* @returns {Boolean} Return the check result
*/
__checkDeepField(object,search, offset=0){
let ref=object, i=offset;
if(object == null) return false;
if(ref[search.field[i]]!==undefined && ref[search.field[i]]!==null){
// if nested ppt is an array - such ars method.args
/*if(ref[search.field[i]] instanceof Array){
for(let k=0; k<ref[search.field[i]].length; k++){
if(ref[search.field[i]][k] instanceof CLASS.ObjectType){
console.log(search.field[i], ref[search.field[i]][k].name, search.field[i+1]);
return this.__checkDeepField( this.__DB.classes.getEntry(ref[search.field[i]][k].name), search, i+1);
}
else if(ref[search.field[i]][k] instanceof CLASS.BasicType){
console.log(search.field[i], ref[search.field[i]][k].name, search.field[i+1]);
return this.__checkDeepField( this.__DB.classes.getEntry(ref[search.field[i]][k].name), search, i+1);
}else{
console.log(search.field[i],ref[search.field[i]]);
return this.__checkDeepField(ref[search.field[i]][k], search, i+1);
}
}
}*/
if(i<search.field.length-1){
if(ref[search.field[i]] instanceof Array){
for(let k=0; k<ref[search.field[i]].length; k++){
if(ref[search.field[i]][k] instanceof CLASS.ObjectType){
//console.log(search.field[i], ref[search.field[i]][k].name, search.field[i+1]);
return this.__checkDeepField( this.__DB.classes.getEntry(ref[search.field[i]][k].name), search, i+1);
}
else if(ref[search.field[i]][k] instanceof CLASS.BasicType){
// terminal node (ignore array tag)
return false;
}else{
//console.log(search.field[i],ref[search.field[i]]);
return this.__checkDeepField(ref[search.field[i]][k], search, i+1);
}
}
}else{
if(ref[search.field[i]] instanceof CLASS.ObjectType){
return this.__checkDeepField( this.__DB.classes.getEntry(ref[search.field[i]].name), search, i+1);
}
else if(ref[search.field[i]] instanceof CLASS.BasicType){
return false;
}else{
return this.__checkDeepField(ref[search.field[i]], search, i+1);
}
return this.__checkDeepField(ref[search.field[i]], search, i+1);
}
}else{
ref = ref[search.field[i]];
}
}
if(ref != null){
return search.fn(ref);
}else
return false;
/*
do{
if(ref[search.field[i]]!==undefined && ref[search.field[i]]!==null){
// if nested ppt is an array - such ars method.args
if(ref[search.field[i]] instanceof Array){
for(let k=0; k<ref[search.field[i]].length; k++){
if(ref[search.field[i]][k] instanceof CLASS.ObjectType){
console.log(ref[search.field[i]][k].name, search.field[i+1]);
return this.__checkDeepField( this.__DB.classes.getEntry(ref[search.field[i]][k].name), search, i+1);
}
console.log(search.field[i],ref[search.field[i]]);
return this.__checkDeepField(ref[search.field[i]][k], search, i+1);
}
}
// else if
else{
ref = ref[search.field[i]];
}
}else{
// don't treat intermediate noed
ref = null;
}
i++;
}while(i<search.field.length);*/
//console.log(ref);
};
_findDeepObject(index, search_pattern){
let matches=new MemoryDb.Index(), k=0, field=undefined;
index.map((k,v)=>{
if(this.__checkDeepField(v, search_pattern))
matches.insert(v);
});
/*
for(let i in index){
if(this.__checkDeepField(index[i], search_pattern))
matches.push(index[i]);
}*/
return matches;
};
// TODO : Factoriser tous les finds
_findObjectByTag(index, search_pattern){
let matches=new MemoryDb.Index();
index.map((k,v)=>{
if(search_pattern.fn(search_pattern,v))
matches.insert(v);
});
/*
for(let i in index){
if(search_pattern.fn(search_pattern,index[i]))
matches.push(index[i]);
}*/
//console.log("[*] "+matches.length+" items found");
return matches;
};
_findObjectByModifier(index, search_pattern){
let matches=new MemoryDb.Index(), k=0, field=undefined;
index.map((k,v)=>{
if(v.modifiers === undefined || v.modifiers === null)
return;
if(search_pattern.fn(search_pattern,v))
matches.insert(v);
});
/*
for(let i in index){
if(index[i].modifiers === undefined || index[i].modifiers === null)
continue;
if(search_pattern.fn(search_pattern,index[i]))
matches.push(index[i]);
}*/
//console.log("[*] "+matches.length+" items found");
return matches;
};
_listObject(obj_type){
return db[obj_type].getAll();
};
_find(index, model, pattern, caseSensitive, lazy=false, includeMissing=false){
if(pattern === null || pattern === undefined) return new FinderResult(index,this);
this.cache.push({ index:index, model:model, case:caseSensitive, lazy:lazy });
let spatt = this._getTestFn(model, pattern, caseSensitive, lazy);
if(spatt!=null){
if(spatt.isModifier)
return new FinderResult(this._findObjectByModifier(index, spatt), this);
if(spatt.hasTag)
return new FinderResult(this._findObjectByTag(index, spatt), this);
else if(spatt.isDeepSearch){
console.debug("Running deep search ...")
//return new FinderResult(this._findDeepObject(index, spatt), this);
return new FinderResult(this._findDeepObject(index, spatt), this);
}
else
return new FinderResult(this._findObject(index, spatt, includeMissing), this);
}else{
return new FinderResult(new MemoryDb.Index(), this);
}
}
}
/**
* A specialization of the searchAPI for searching missing object
* and around
* @param {SearchAPI} searchAPI
* @constructor
*/
function MissingObjectAPI(searchAPI){
this._search = searchAPI;
}
/**
* To get statistics about missing reference by type
* @returns {Object} A array of number of unique missing reference per type
* @function
*/
MissingObjectAPI.prototype.stats = function(){
let stats = {};
stats.field = this.search("_log_tag:FIELD").count();
stats.type = this.search("_log_tag:TYPE").count();
stats.method = this.search("_log_tag:METHOD").count();
stats.class = this.search("_log_tag:CLASS").count();
return stats;
}
/**
* To search object into the list of missing reference
* @param {String} pattern The search pattern following the same syntax than the SearchAPI
* @returns {FinderResult} A set of occurences
* @function
*/
MissingObjectAPI.prototype.search = function(pattern){
return this._search._finder._find(this._search._db.missing, DataModel.missing, pattern, this._search._caseSensitive, true, true);
}
MissingObjectAPI.prototype.method = function(pattern){
let db = this.search("_log_tag:METHOD");
return this._search._finder._find(db.data, DataModel.method, pattern, this._search._caseSensitive, true, true);
}
MissingObjectAPI.prototype.field = function(pattern){
let db = this.search("_log_tag:FIELD");
return this._search._finder._find(db.data, DataModel.field, pattern, this._search._caseSensitive, true, true);
}
MissingObjectAPI.prototype.type = function(pattern){
let db = this.search("_log_tag:TYPE");
return this._search._finder._find(db.data, DataModel.type, pattern, this._search._caseSensitive, true, true);
}
/**
* The SearchAPI. Allow the user to perform search into the object
* database.
* @param {Object} data The database of objects
* @constructor
*/
function SearchAPI(data){
// AnalyzerDatabase (specialize InMemoryDb)
var _db = this._db = data;
this._queryCache = [];
// set default case sensitivity for all search
this._caseSensitive = true;
var finder = this._finder = new Finder(this._db);
this.help = "Usage: .help()";
this.help = function(){
ut.msgBox("HELP : Search API",[
"class(<pattern>)\t\tSearch a class by any properties",
"field(<pattern>)\t\tSearch a field by any properties",
"method(<pattern>)\t\tSearch a method by any properties",
"string(<pattern>)\t\tSearch a defined string by any properties",
"call(<pattern>)\t\tSearch a call by caller/calleed/instructions properties",
"invoke(<pattern>|<method>)\t\tSearch a call to a given method (called) by a pattern or a method object",
"getter(<pattern>|<field>)\t\tSearch for getter of given field by a pattern or a field object",
"setter(<pattern>|<field>)\t\tSearch for setter of given field by a pattern or a field object",
"new(<pattern>|<class>)\t\tSearch for new instance of a given class object or class properties ",
"array(<pattern>|<*>)\t\tSearch for static array by array properties",
"nocase()\t\tSwitch ON/OFF the case sensitive flag of the following search"
]);
};
/**
* Switch case sensitive On/Off of following search
*/
this.nocase = function(){
this._caseSensitive = false;
return this;
};
this.class = function(pattern){ return finder._find(_db.classes, DataModel.class, pattern, this._caseSensitive); };
this.package = function(pattern){ return finder._find(_db.packages, DataModel.package, pattern, this._caseSensitive); };
this.method = function(pattern){ return finder._find(_db.methods, DataModel.method, pattern, this._caseSensitive); };
this.field = function(pattern){ return finder._find(_db.fields, DataModel.field, pattern, this._caseSensitive); };
this.file = function(pattern){ return finder._find(_db.files, DataModel.file, pattern, this._caseSensitive); };
this.array = function(pattern){ return finder._find(_db.datablock, DataModel.datablock, pattern, this._caseSensitive); };
this.activity = function(pattern){ return finder._find(_db.activities, DataModel.activity, pattern, this._caseSensitive); };
this.service = function(pattern){ return finder._find(_db.services, DataModel.service, pattern, this._caseSensitive); };
this.receiver = function(pattern){ return finder._find(_db.receivers, DataModel.receiver, pattern, this._caseSensitive); };
this.provider = function(pattern){ return finder._find(_db.providers, DataModel.provider, pattern, this._caseSensitive); };
this.permission = function(pattern){ return finder._find(_db.permissions, DataModel.permission, pattern, this._caseSensitive); };
this.call = function(pattern){
return finder._find(_db.call, DataModel.call, pattern, this._caseSensitive);
};
this.string = function(pattern){
let rf = finder._find(_db.strings, DataModel.string, pattern, this._caseSensitive);
return rf;
};
this.syscall = function(pattern){ return finder._find(_db.syscalls, DataModel.syscall, pattern, this._caseSensitive); };
//this.syscall = function(pattern){ return finder._find(_db.syscalls, DataModel.syscall, pattern, this._caseSensitive); };
this.syscallnum = function(id){
return this.syscall("sysnum:^"+id+"$");
};
this.missing = new MissingObjectAPI(this);
this.get = {
package: function(id){ return _db.packages.getEntry(id) },
class: function(id){ return _db.classes.getEntry(id) },
method: function(id){ return _db.methods.getEntry(id) },
field: function(id){ return _db.fields.getEntry(id) },
syscalls: function(id){ return _db.syscalls.getEntry(id) },
activity: function(id){ return _db.activities.getEntry(id) },
provider: function(id){ return _db.providers.getEntry(id) },
receiver: function(id){ return _db.receivers.getEntry(id) },
service: function(id){ return _db.services.getEntry(id) },
permission: function(id){ return _db.permissions.getEntry(id) }
/*datablock: function(id){
}*/
};
/**
* To seach only method call
* @param {*} pattern
*/
this.invoke = function(pattern){
let res = finder._find(
_db.call, DataModel.call,
"instr.opcode.instr:invoke", false, true);
if(pattern === null)
return res;
if(typeof pattern === 'string' || pattern instanceof String)
return res.filter(pattern);
else if(pattern instanceof CLASS.Method)
return res.filter("calleed.__signature__:"+pattern.__signature__);
else
return res;
};
/**
* @param {String} pattern Search pattern
*/
this.setter = function(pattern=null){
let res = null;
if(pattern != null){
res = finder._find(
_db.call, DataModel.call,
"calleed."+pattern, false, true);
res = res.filter("instr.opcode.type:"+CONST.INSTR_TYPE.SETTER);
}
else{
res = finder._find(
_db.call, DataModel.call,
"instr.opcode.type:"+CONST.INSTR_TYPE.SETTER, false, true);
}
return res;
}
/**
*
* @param {String} pattern Field signature
*/
this.settersOf = function(signature){
return this.setter("__signature__:"+signature);
}
/**
* @param {String} pattern Field signature
*/
this.getter = function(pattern=null){
let res = null;
if(pattern != null){
res = finder._find(
_db.call, DataModel.call,
"calleed."+pattern, false, true);
res = res.filter("instr.opcode.type:"+CONST.INSTR_TYPE.GETTER);
}
else{
res = finder._find(
_db.call, DataModel.call,
"instr.opcode.type:"+CONST.INSTR_TYPE.GETTER, false, true);
}
return res;
}
/**
* @param {String} pattern Field signature
*/
this.gettersOf = function(signature){
let f = new FinderResult([this.get.field(signature)], this);
return finder._find(
_db.call, DataModel.call,
"instr.opcode.type:"+CONST.INSTR_TYPE.GETTER, false, true);
return this.getter("__signature__:"+signature);
}
this.calls = {
setter: function(pattern=null){
let res = finder._find(
_db.call, DataModel.call,
"instr.opcode.type:"+CONST.INSTR_TYPE.SETTER, false, true);
//console.log(res);
if(pattern !== null)
return res.filter(pattern);
else
return res;
},
getter: function(pattern=null){
let res = finder._find(
_db.call, DataModel.call,
"instr.opcode.type:"+CONST.INSTR_TYPE.GETTER, false, true);
if(pattern !== null)
return res.filter(pattern);
else
return res;
},
print: function(){
_db.call.map((k,v) => { v.print() });;
},
raw: function(){
_db.call.map((k,v) => { console.log("\t"+v.instr._raw) });;
},
//find: function(pattern){ return finder.instr(pattern,true); }
};
this.updateDB = function(data){
_db = this._db = data;
this._finder.updateDB(data);
};
/**
* Set the flag "preparing" which means the research should not be executed.
* Create a new PreparedRequest instance.
*/
this.beginPrepare = function(name){
this.preparing = true;
this.preparingReq = new PreparedRequest(name);
return this;
}
/**
*
*/
this.endPrepare = function(){
this.preparing = false;
return this.preparingReq;
}
/**
* To execute a prepared request. Almost time it is when
* an inspector is running.
* @param {*} preparedRequest
*/
this.exec = function(preparedRequest){
}
}
module.exports = {
SearchAPI: SearchAPI,
SearchPattern: SearchPattern,
Finder: Finder,
FinderResult: FinderResult
};