package the.bytecode.club.bytecodeviewer.malwarescanner;

import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.decompilers.bytecode.InstructionPrinter;
import the.bytecode.club.bytecodeviewer.malwarescanner.util.SearchableString;

/***************************************************************************
 * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite        *
 * Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com  *
 *                                                                         *
 * This program 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, either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. *
 ***************************************************************************/

/**
 * The base class for the malware code scanners
 *
 * @author Konloch
 * @since 6/27/2021
 */
public abstract class MalwareCodeScanner implements CodeScanner
{
	private final InstructionPrinter instructionPrinter = new InstructionPrinter(null, null);
	public MalwareScanModule module;
	
	public abstract void scanFieldString(MalwareScan scan, ClassNode cn, FieldNode field, SearchableString string);
	
	public abstract void scanMethodString(MalwareScan scan, ClassNode cn, MethodNode method, SearchableString string);
	
	public abstract void scanMethodInstruction(MalwareScan scan, ClassNode cn, MethodNode method, AbstractInsnNode instruction);
	
	@Override
	public void scanningClass(MalwareScan scan, ClassNode cn)
	{
		scanFields(scan, cn, cn.fields.toArray(new FieldNode[0]));
		scanMethods(scan, cn, cn.methods.toArray(new MethodNode[0]));
	}
	
	@Override
	public void scanFields(MalwareScan scan, ClassNode cn, FieldNode[] fields)
	{
		for (FieldNode field : fields)
		{
			Object fieldValue = field.value;
			
			//scan strings
			if (fieldValue instanceof String)
				scanFieldString(scan, cn, field, new SearchableString((String) fieldValue));
			
			//scan string array
			else if (fieldValue instanceof String[])
				for(String s : (String[]) fieldValue)
					scanFieldString(scan, cn, field, new SearchableString(s));
		}
	}
	
	@Override
	public void scanMethods(MalwareScan scan, ClassNode cn, MethodNode[] methods)
	{
		for (MethodNode method : methods)
		{
			InsnList instructionList = method.instructions;
			
			//scan each instruction
			for (AbstractInsnNode instruction : instructionList.toArray())
			{
				scanMethodInstruction(scan, cn, method, instruction);
				
				if (instruction instanceof LdcInsnNode)
				{
					if (((LdcInsnNode) instruction).cst instanceof String)
					{
						final String string = (String) ((LdcInsnNode) instruction).cst;
						scanMethodString(scan, cn, method, new SearchableString(string));
					}
				}
			}
		}
	}
	
	public String fieldToString(ClassNode cn, FieldNode field)
	{
		return cn.name + "." + field.name + "(" + field.desc + ")";
	}
	
	public String methodToString(ClassNode cn, MethodNode method)
	{
		return cn.name + "." + method.name + "(" + method.desc + ")";
	}
	
	public String instructionToString(AbstractInsnNode instruction)
	{
		return instructionPrinter.printInstruction(instruction).trim();
	}
	
	public String header()
	{
		String header = String.format("%30s", (module.getReadableName() + " ->\t"));
		
		//TODO display the resource container for this specific ClassNode
		if(BytecodeViewer.viewer.showFileInTabTitle.isSelected())
			header += "{fileContainerGoesHere}\t";
		
		return header;
	}
	
	public void foundLDC(MalwareScan scan, String ldc, String foundAt)
	{
		scan.sb.append(header()).append(" Found LDC \"").append(ldc).append("\" ").append(foundAt);
	}
	
	public void foundMethod(MalwareScan scan, String foundAt)
	{
		scan.sb.append(header()).append(" Found Method call to ").append(foundAt);
	}
	
	public void found(MalwareScan scan, String found)
	{
		scan.sb.append(header()).append(" Found ").append(found);
	}
}
