/*
* <Manal project is an eclipse plugin for the automation of malware analysis.>
* Copyright (C) <2014> <Nikolay Akatyev, Hojun Son>
* This file is part of Manal project.
*
* Manal project is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* Manal project is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Manal project. If not, see <http://www.gnu.org/licenses/>.
*
* Contact information of contributors:
* - Nikolay Akatyev: nikolay.akatyev@gmail.com
* - Hojun Son: smuoon4680@gmail.com
*/
package com.dforensic.plugin.manal.model;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.IPackageBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodInvocation;
import soot.SootClass;
import soot.SootMethod;
import soot.SootMethodRef;
import soot.Type;
import soot.Value;
import soot.jimple.InvokeExpr;
import soot.jimple.Stmt;
import soot.jimple.infoflow.InfoflowResults.SinkInfo;
import soot.jimple.infoflow.InfoflowResults.SourceInfo;
import soot.tagkit.LineNumberTag;
/**
* <p>
* The class describes suspicious APIs to be searched.
* </p>
* <p>
* This descriptor describes both APIs extracted from
* an xml and API found in a source of a project.<br/>
* TODO this class needs to understand {@link IMethod}
* in order to use isSimilar() for comparison.
* </p>
*
* @author Zeoo
*
*/
public class ApiDescriptor {
private CompilationUnit mCompilationUnit = null;
private int mLineNum = -1;
public enum MethodType {CONSTRUCTOR, NORMAL};
public class ParameterDescriptor {
private String mType = null;
/** The value is extracted from a source code. */
private String mValue = null;
/**
* The name is extracted from an xml or
* the description of an API.
*/
private String mName = null;
/**
* An xml describes not all parameters but
* only important ones. A position of the
* described parameter is specified.
*/
private int mPos = -1;
/** The constructor for initialization from an xml. */
public ParameterDescriptor(String name, String type, int pos) {
mName = name;
mType = type;
mPos = pos;
}
/** The constructor for initialization from a source code. */
public ParameterDescriptor(String value, String type) {
mValue = value;
mType = type;
}
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
public String getValue() {
return mValue;
}
public void setValue(String value) {
mValue = value;
}
public String getType() {
return mType;
}
public void setType(String type) {
mType = type;
}
public int getPos() {
return mPos;
}
public void setPos(int pos) {
mPos = pos;
}
public ParameterDescriptor clone() {
ParameterDescriptor pd = new ParameterDescriptor(null, null);
String copyName = null;
String copyType = null;
String copyValue = null;
if (mName != null) {
copyName = new String(mName);
}
if (mValue != null) {
copyValue = new String(mValue);
}
if (mType != null) {
copyType = new String(mType);
}
pd.setName(copyName);
pd.setType(copyType);
pd.setValue(copyValue);
pd.setPos(mPos);
return pd;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (mType != null) {
sb.append(mType).append(" ");
}
if (mPos != -1) {
sb.append(mPos).append(":");
}
if (mName != null) {
sb.append(mName).append(" ");
}
if (mValue != null) {
sb.append("[").append(mValue).append("]");
}
return sb.toString();
}
};
/* <p>Methods to add information about the method
* in the eclipse project.</p>
* <p>CompilationUnit is used to obtain JavaElement
* which can be opened in a java editor.</p> */
public void setCompilationUnit(CompilationUnit cu) {
mCompilationUnit = cu;
}
public CompilationUnit getCompilatioinUnit() {
return mCompilationUnit;
}
public void setLineNumber(int ln) {
mLineNum = ln;
}
public int getLineNumber() {
return mLineNum;
}
private String mReturnType = null;
private String mReturnValue = null;
private List<ParameterDescriptor> mParams = null;
private String mMethodName = null;
private String mSignature = null;
private String mPackageName = null;
private String mClassName = null;
/** Constructor or a normal method. */
private MethodType mMethodType = MethodType.NORMAL;
private List<ApiDescriptor> mDependencyList = null;
private SinkInfo mRootSink = null;
private SourceInfo mRootSource = null;
private SootMethod mSootMethod = null;
public ApiDescriptor() {
}
public ApiDescriptor(SinkInfo sinkInfo) {
mRootSink = sinkInfo;
}
public ApiDescriptor(SourceInfo sourceInfo) {
mRootSource = sourceInfo;
}
public void addDependency(ApiDescriptor method) {
if (mDependencyList == null) {
mDependencyList = new ArrayList<ApiDescriptor>();
}
mDependencyList.add(method);
}
public List<ApiDescriptor> getDependencyList() {
return mDependencyList;
}
public boolean isSink() {
if (mRootSink != null) {
return true;
}
return false;
}
public SinkInfo getSinkInfo() {
return mRootSink;
}
public SourceInfo getSourceInfo() {
return mRootSource;
}
public boolean isSource() {
if (mRootSource != null) {
return true;
}
return false;
}
public void setSootMethod(SootMethod method) {
mSootMethod = method;
}
public String getClassNameFromSoot() {
if (mSootMethod != null) {
SootClass sootClass = mSootMethod.getDeclaringClass();
if (sootClass != null) {
String className = sootClass.getJavaStyleName();
if (className != null) {
// get only name without package.
String packageName = getPackageNameFromSoot();
if (packageName != null) {
if (className.contains(packageName)) {
return className.substring(packageName.length(),
className.length());
}
}
System.out.println("Problems to find package in class name.");
int ind = className.lastIndexOf('.');
if (ind != -1) {
return className.substring(ind,
className.length());
} else {
return className;
}
} else {
System.err.println("Can't get class name. It is not initialized in " +
"SootClass.");
return null;
}
} else {
System.err.println("Can't get class name. SootClass is not initialized.");
return null;
}
} else {
System.err.println("Can't get class name. SootMethod is not initialized.");
return null;
}
}
public String getPackageNameFromSoot() {
if (mSootMethod != null) {
SootClass sootClass = mSootMethod.getDeclaringClass();
if (sootClass != null) {
return sootClass.getJavaPackageName();
} else {
System.err.println("Can't get package name. SootClass is not initialized.");
return null;
}
} else {
System.err.println("Can't get package name. SootMethod is not initialized.");
return null;
}
}
public String getMethodNameFromSoot() {
Value sink = mRootSink.getSink();
if (sink instanceof InvokeExpr) {
InvokeExpr expr = (InvokeExpr) sink;
SootMethodRef exprRef = expr.getMethodRef();
if (exprRef != null)
{
//System.out.println("function name: " + exprRef.name());
exprRef.parameterTypes();
exprRef.returnType();
return exprRef.name().trim();
}
}
System.err.println("Can't get method name. A problem to extract it from InvokeExpr.");
return null;
}
public int getLineNumFromSoot() {
Stmt context = null;
if (mRootSink != null) {
context = mRootSink.getContext();
} else if (mRootSource != null) {
context = mRootSource.getContext();
}
if (context == null) {
System.err.println("Context was not obtained neither from Sink nor from Source.");
return -1;
}
if (context.hasTag("LineNumberTag")) {
return ((LineNumberTag)context.getTag("LineNumberTag")).getLineNumber();
}
return -1;
}
public ApiDescriptor(MethodInvocation method) {
IMethodBinding methodBinding = method.resolveMethodBinding();
if (methodBinding != null) {
mMethodName = methodBinding.getName();
ITypeBinding[] argMethods = methodBinding.getParameterTypes();
ITypeBinding retMethod = methodBinding.getReturnType();
boolean isConstructor = methodBinding.isConstructor();
ITypeBinding declaringClass = methodBinding.getDeclaringClass();
MethodType methodType = MethodType.NORMAL;
if (isConstructor) {
methodType = MethodType.CONSTRUCTOR;
}
mMethodType = methodType;
if (declaringClass != null) {
mClassName = declaringClass.getName();
IPackageBinding pckg = declaringClass.getPackage();
if (pckg != null) {
mPackageName = pckg.getName();
} else {
System.out.println(">>inf: package is not specified.");
}
} else {
System.out.println(">>inf: class is not specified.");
}
if (retMethod != null) {
mReturnType = retMethod.getName();
} else {
System.out.println(">>inf: return type is not specified.");
}
if (argMethods != null) {
mParams = new ArrayList<ParameterDescriptor>(argMethods.length);
int pos = 1;
for (ITypeBinding param : argMethods) {
if (param != null) {
String paramName = param.getName();
ITypeBinding paramType = param.getTypeDeclaration();
String paramTypeName = null;
if (paramType != null) {
paramTypeName = paramType.getName();
}
mParams.add(new ParameterDescriptor(paramName,
paramTypeName, pos));
pos++;
} else {
System.out.println(">>error: parameter is NULL.");
}
}
} else {
System.out.println(">>inf: method parameters are not specified.");
}
} else {
System.out.println(">>warning: method binding can't be resolved.");
}
}
public String getMethodName() {
return mMethodName;
}
public void setMethodName(String methodName) {
mMethodName = methodName;
}
public String getSignature() {
return mSignature;
}
public void setSignature(String signature) {
mSignature = signature;
}
public String getReturnType() {
return mReturnType;
}
public void setReturnType(String returnType) {
mReturnType = returnType;
}
public String getReturnValue() {
return mReturnValue;
}
public void setReturnValue(String returnValue) {
mReturnValue = returnValue;
}
public List<ParameterDescriptor> getParams() {
return mParams;
}
/**
* Assign the entire set as a list of parameters.
*
* @param params
*/
public void setParams(List<ParameterDescriptor> params) {
mParams = params;
}
/**
* Add one more parameter to the existing ones.
*
* @param param
*/
public void addParam(ParameterDescriptor param) {
if (mParams == null) {
mParams = new ArrayList<ParameterDescriptor>();
}
mParams.add(param);
}
public String getPackageName() {
return mPackageName;
}
public void setPackageName(String packageName) {
mPackageName = packageName;
}
public String getClassName() {
return mClassName;
}
public void setClassName(String className) {
mClassName = className;
}
public MethodType getMethodType() {
return mMethodType;
}
public void setMethodType(MethodType type) {
mMethodType = type;
}
public boolean isSimilar(MethodInvocation method) {
List<String> argTypes = new ArrayList<String>();
// List<Expression> argsMethod = method.arguments();
IMethodBinding methodBinding = method.resolveMethodBinding();
if (methodBinding != null) {
String name = methodBinding.getName();
ITypeBinding[] argMethods = methodBinding.getParameterTypes();
ITypeBinding retMethod = methodBinding.getReturnType();
boolean isConstructor = methodBinding.isConstructor();
ITypeBinding declaringClass = methodBinding.getDeclaringClass();
return compareAttributes(name, declaringClass, argMethods, retMethod, isConstructor);
} else {
System.out.println(">>warning: method binding can't be resolved.");
}
return false;
}
private boolean compareAttributes(String name, ITypeBinding declaringClass, ITypeBinding[] args,
ITypeBinding ret, boolean isConstructor) {
boolean matched = false;
if (mMethodName != null) {
if (mMethodName.equals(name)) {
matched = true;
} else {
System.out.println(">>inf: method names are not matched.");
return false;
}
} else {
System.out.println(">>inf: method name is not specified.");
}
MethodType methodType = MethodType.NORMAL;
if (isConstructor) {
methodType = MethodType.CONSTRUCTOR;
}
if (mMethodType.equals(methodType)) {
matched = true;
} else {
System.out.println(">>inf: method type is not matched.");
return false;
}
if ((mClassName != null) && declaringClass != null) {
if (mClassName.equals(declaringClass.getName())) {
matched = true;
} else {
System.out.println(">>inf: class names are not matched.");
return false;
}
IPackageBinding pckg = declaringClass.getPackage();
if ((mPackageName != null) && pckg != null) {
if (mPackageName.equals(pckg.getName())) {
matched = true;
} else {
System.out.println(">>inf: package names are not matched.");
return false;
}
} else {
System.out.println(">>inf: package is not specified.");
}
} else {
System.out.println(">>inf: class is not specified.");
}
if ((mReturnType != null) && (ret != null)) {
if (mReturnType.equals(ret.getName())) {
matched = true;
} else {
System.out.println(">>inf: return types are not matched.");
return false;
}
} else {
System.out.println(">>inf: return type is not specified.");
}
if ((mParams != null) && (args != null)) {
for (ParameterDescriptor param : mParams) {
if ((param != null) && (param.getPos() >= 0) &&
(param.getPos() < args.length)) {
String paramName = param.getName();
if ((paramName != null) &&
paramName.equals(args[param.getPos()].getName())) {
matched = true;
} else {
System.out.println(">>inf: parameters are not matched.");
return false;
}
} else {
System.out.println(">>error: bad configuration of parameters.");
}
}
} else {
System.out.println(">>inf: method parameters are not specified.");
}
return matched;
}
public ApiDescriptor clone(MethodInvocation method) {
// TODO when process details, extract later values of arguments
// and return here and put it into the clone.
ApiDescriptor desc = new ApiDescriptor();
String copyClassName = null;
String copyMethodName = null;
String copyPackageName = null;
if (mClassName != null) {
copyClassName = new String(mClassName);
}
if (mMethodName != null) {
copyMethodName = new String(mMethodName);
}
if (mPackageName != null) {
copyPackageName = new String(mPackageName);
}
desc.setClassName(copyClassName);
desc.setMethodName(copyMethodName);
desc.setMethodType(mMethodType);
desc.setPackageName(copyPackageName);
if (mParams != null) {
for (ParameterDescriptor param : mParams) {
ParameterDescriptor newParam = param.clone();
desc.addParam(newParam);
}
}
return desc;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
// String totalValue;
if ((mRootSink != null) || (mRootSource != null)) {
sb.append(getMethodSignatureFromSoot());
// String temp = mRootSink.toString();
// //System.out.println("temp : " + temp);
// ArrayList<String> getParaList = new ArrayList<String>();
// //System.out.println("function name : "+ getMethodNameFromSoot());
// String func = getMethodNameFromSoot();
// //System.out.println("return type : " + getReturnType(temp, func));
// for (ApiDescriptor apiD : getElements()) {
// System.out.println("function name : "
// + apiD.getMethodNameFromSoot());
// String func = apiD.getMethodNameFromSoot();
// System.out.println("return type : " + getReturnType(apiD, func));
// getParaList = getParameters(apiD);
//
// for(int i=0; i < getParaList.size(); i++)
// {
// System.out.println("parameter: " + getParaList.get(i));
// }
// }
// totalValue = getReturnType(temp, func)+ " " + func + getParameters(temp, func);
// System.out.println("totalValue : " + totalValue);
// sb.append(/*mRootSink.toString()*/totalValue);
} else {
if (mMethodType.equals(MethodType.CONSTRUCTOR)) {
sb.append("constructor\n");
}
if (mReturnType != null) {
sb.append(mReturnType).append(" ");
}
if (mReturnValue != null) {
sb.append("[").append(mReturnValue).append("]")
.append(" ");
}
if (mPackageName != null) {
sb.append(mPackageName).append(".");
}
if (mClassName != null) {
sb.append(mClassName).append(".");
}
if (mMethodName != null) {
sb.append(mMethodName).append("(\n");
}
if (mParams != null) {
for (ParameterDescriptor param : mParams) {
sb.append("\t").append(param.toString()).append("\n");
}
}
if (mMethodName != null) {
sb.append(")\n");
}
}
return sb.toString();
}
public String getMethodSignatureFromSoot() {
Value sink = null;
if (mRootSink != null) {
sink = mRootSink.getSink();
} else if (mRootSource != null) {
sink = mRootSource.getSource();
}
if ((sink != null) && (sink instanceof InvokeExpr)) {
InvokeExpr expr = (InvokeExpr) sink;
SootMethodRef exprRef = expr.getMethodRef();
if (exprRef != null)
{
//System.out.println("function name: " + exprRef.name());
StringBuilder methSign = new StringBuilder();
Type ret = exprRef.returnType();
String retStr = null;
if (ret != null) {
retStr = ret.toString().trim();
}
if ((retStr == null) || (retStr.isEmpty())) {
retStr = new String("void");
}
methSign.append(retStr).append(" ");
methSign.append(exprRef.name().trim());
List<Type> params = exprRef.parameterTypes();
if ((params != null) && !params.isEmpty()) {
methSign.append("(");
for (Type param : params) {
String paramStr = null;
if (param != null) {
paramStr = param.toString().trim();
}
if ((paramStr == null) || (paramStr.isEmpty())) {
paramStr = new String("void");
}
methSign.append(paramStr).append(", ");
}
methSign.replace(methSign.length() - 2, methSign.length(), "");
methSign.append(")");
} else {
methSign.append("()");
}
return methSign.toString();
}
}
System.err.println("Can't get method name. A problem to extract it from InvokeExpr.");
return null;
}
private String getParameters(String apiD, String funcName) {
//System.out.println(apiD.split(funcName)[1]);
System.out.println(apiD.split(funcName)[1].split(">")[0]);
/*try {
StringTokenizer st = new StringTokenizer(apiD.toString(), "(");
String temp = null;
ArrayList<String> paraList = new ArrayList<String>();
while (st.hasMoreTokens()) {
if (st.hasMoreTokens() == false) {
temp = st.toString();
System.out.println("in parameter : " + temp);
}
st.nextToken();
}
StringTokenizer st_para = new StringTokenizer(temp, ",");
while (st_para.hasMoreTokens()) {
paraList.add(st_para.nextToken());
}
String lastPara = paraList.get(paraList.size() - 1);
paraList.remove(paraList.size() - 1);
StringTokenizer checkPara = new StringTokenizer(lastPara, ")");
String[] revisedPara = lastPara.split(")");
paraList.add(revisedPara[0]);
return paraList;
} catch (Exception exc) {
exc.printStackTrace();
}*/
return apiD.split(funcName)[1].split(">")[0];
}
private String getReturnType(String apiD, String funcName) {
//System.out.println(apiD.split(funcName)[0]);
String[] temp = apiD.split(funcName)[0].split(" ");
//System.out.println(temp[temp.length -1]);
/*StringTokenizer st = new StringTokenizer(apiD, funcName.trim());
System.out.println("funcName trim: " + funcName.trim());
String tempToken;
ArrayList<String> TokenList = new ArrayList<String>();
int idx = 0;
try
{
while (st.hasMoreTokens()) {
tempToken = st.nextToken();
TokenList.add(tempToken);
System.out.println(tempToken);
if (tempToken.equals(funcName)) // if find function name
{
System.out
.println("return type: " + TokenList.get(idx - 1));
return TokenList.get(idx - 1);
// StringTokenizer st_func = new StringTokenizer(tempToken,
}
idx++;
}
} catch (Exception exc) {
exc.printStackTrace();
}
*/
return temp[temp.length -1];
}
}