/*
Copyright (C) 2006 Evan Teran
                   eteran@alum.rit.edu

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 2 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, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "DialogOpcodes.h"
#include "DebuggerCoreInterface.h"
#include "Debugger.h"
#include "MemoryRegions.h"
#include "ByteShiftArray.h"

#include <QHeaderView>
#include <QMessageBox>
#include <QVector>

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
DialogOpcodes::DialogOpcodes(QWidget *parent) : QDialog(parent) {
	ui.setupUi(this);
	ui.tableWidget->verticalHeader()->hide();
	ui.tableWidget->horizontalHeader()->setStretchLastSection(true);
}

//------------------------------------------------------------------------------
// Name: on_listWidget_itemDoubleClicked(QListWidgetItem *item)
// Desc: follows the found item in the data view
//------------------------------------------------------------------------------
void DialogOpcodes::on_listWidget_itemDoubleClicked(QListWidgetItem *item) {
	bool ok;
	const QString s = item->text();
	// TODO: remove hard coded 8
	const BaseTypes::address_t addr = s.left(8).toUInt(&ok, 16);
	if(ok) {
		Debugger::jumpToAddress(addr);
	}
}


//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::showEvent(QShowEvent *) {
	if(Debugger::memoryRegions != 0) {
		Debugger::memoryRegions->sync();
		QVector<MemoryRegions::Region> r = Debugger::memoryRegions->regions();

		ui.tableWidget->setSortingEnabled(false);
		ui.tableWidget->setRowCount(0);
		for(QVector<MemoryRegions::Region>::const_iterator i = r.begin(); i != r.end(); ++i) {
			int row = ui.tableWidget->rowCount();
			ui.tableWidget->insertRow(row);
			ui.tableWidget->setItem(row, 0, new QTableWidgetItem(QString().sprintf("%08x", i->start)));
			ui.tableWidget->setItem(row, 1, new QTableWidgetItem(QString().sprintf("%08x", i->end)));
			ui.tableWidget->setItem(row, 2, new QTableWidgetItem(QString().sprintf("%c%c%c", i->readable() ? 'r' : '-', i->writeable() ? 'w' : '-', i->executable() ? 'x' : '-' )));
			ui.tableWidget->setItem(row, 3, new QTableWidgetItem(i->name));
		}
		ui.tableWidget->setSortingEnabled(true);
	} else {
		ui.tableWidget->setRowCount(0);
	}
	ui.progressBar->setValue(0);
	ui.listWidget->clear();
}

#define OPCODE_CASE(value, str, address) case (value): ui.listWidget->addItem(QString().sprintf((str), (address))); break

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::testEax(const DialogOpcodes::OpcodeData &data, BaseTypes::address_t start_address) {
	switch(data.word) {
	OPCODE_CASE(0xe0ff, "%08x: jump eax", start_address);
	OPCODE_CASE(0xd0ff, "%08x: call eax", start_address);
	OPCODE_CASE(0xc350, "%08x: push eax; ret", start_address);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::testEbx(const DialogOpcodes::OpcodeData &data, BaseTypes::address_t start_address) {
	switch(data.word) {
	OPCODE_CASE(0xe3ff, "%08x: jump ebx", start_address);
	OPCODE_CASE(0xd3ff, "%08x: call ebx", start_address);
	OPCODE_CASE(0xc353, "%08x: push ebx; ret", start_address);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::testEcx(const DialogOpcodes::OpcodeData &data, BaseTypes::address_t start_address) {
	switch(data.word) {
	OPCODE_CASE(0xe1ff, "%08x: jump ecx", start_address);
	OPCODE_CASE(0xd1ff, "%08x: call ecx", start_address);
	OPCODE_CASE(0xc351, "%08x: push ecx; ret", start_address);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::testEdx(const DialogOpcodes::OpcodeData &data, BaseTypes::address_t start_address) {
	switch(data.word) {
	OPCODE_CASE(0xe2ff, "%08x: jump edx", start_address);
	OPCODE_CASE(0xd2ff, "%08x: call edx", start_address);
	OPCODE_CASE(0xc352, "%08x: push edx; ret", start_address);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::testEbp(const DialogOpcodes::OpcodeData &data, BaseTypes::address_t start_address) {
	switch(data.word) {
	OPCODE_CASE(0xe5ff, "%08x: jump ebp", start_address);
	OPCODE_CASE(0xd5ff, "%08x: call ebp", start_address);
	OPCODE_CASE(0xc355, "%08x: push ebp; ret", start_address);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::testEsp(const DialogOpcodes::OpcodeData &data, BaseTypes::address_t start_address) {
	switch(data.word) {
	OPCODE_CASE(0xe4ff, "%08x: jump esp", start_address);
	OPCODE_CASE(0xd4ff, "%08x: call esp", start_address);
	OPCODE_CASE(0xc354, "%08x: push esp; ret", start_address);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::testEsi(const DialogOpcodes::OpcodeData &data, BaseTypes::address_t start_address) {
	switch(data.word) {
	OPCODE_CASE(0xe6ff, "%08x: jump esi", start_address);
	OPCODE_CASE(0xd6ff, "%08x: call esi", start_address);
	OPCODE_CASE(0xc356, "%08x: push esi; ret", start_address);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::testEdi(const DialogOpcodes::OpcodeData &data, BaseTypes::address_t start_address) {
	switch(data.word) {
	OPCODE_CASE(0xe7ff, "%08x: jump edi", start_address);
	OPCODE_CASE(0xd7ff, "%08x: call edi", start_address);
	OPCODE_CASE(0xc357, "%08x: push edi; ret", start_address);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::testESP_ADD_0(const OpcodeData &data, BaseTypes::address_t start_address) {
	switch(data.byte) {
	OPCODE_CASE(0xc3, "%08x: ret", start_address);
	OPCODE_CASE(0xc2, "%08x: ret N", start_address);
	}
	
	const uint32_t instr24 = data.dword & 0x00ffffff;
	switch(instr24) {
	OPCODE_CASE(0x00d0ff58, "%08x: pop eax; call eax", start_address);
	OPCODE_CASE(0x00d1ff59, "%08x: pop ecx; call ecx", start_address);
	OPCODE_CASE(0x00d2ff5a, "%08x: pop edx; call edx", start_address);
	OPCODE_CASE(0x00d3ff5b, "%08x: pop ebx; call ebx", start_address);
	OPCODE_CASE(0x00d4ff5c, "%08x: pop esp; call esp", start_address);
	OPCODE_CASE(0x00d5ff5d, "%08x: pop ebp; call ebp", start_address);
	OPCODE_CASE(0x00d6ff5e, "%08x: pop esi; call esi", start_address);
	OPCODE_CASE(0x00d7ff5f, "%08x: pop edi; call edi", start_address);
	OPCODE_CASE(0x00e0ff58, "%08x: pop eax; jmp eax", start_address);
	OPCODE_CASE(0x00e1ff59, "%08x: pop ecx; jmp ecx", start_address);
	OPCODE_CASE(0x00e2ff5a, "%08x: pop edx; jmp edx", start_address);
	OPCODE_CASE(0x00e3ff5b, "%08x: pop ebx; jmp ebx", start_address);
	OPCODE_CASE(0x00e4ff5c, "%08x: pop esp; jmp esp", start_address);
	OPCODE_CASE(0x00e5ff5d, "%08x: pop ebp; jmp ebp", start_address);
	OPCODE_CASE(0x00e6ff5e, "%08x: pop esi; jmp esi", start_address);
	OPCODE_CASE(0x00e7ff5f, "%08x: pop edi; jmp edi", start_address);
	OPCODE_CASE(0x002424ff, "%08x: jmp [esp]", start_address);
	OPCODE_CASE(0x002414ff, "%08x: call [esp]", start_address);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::testESP_ADD_4(const OpcodeData &data, BaseTypes::address_t start_address) {
	switch(data.word) {
	OPCODE_CASE(0xc358, "%08x: pop eax; ret", start_address);
	OPCODE_CASE(0xc35b, "%08x: pop ebx; ret", start_address);
	OPCODE_CASE(0xc359, "%08x: pop ecx; ret", start_address);
	OPCODE_CASE(0xc35a, "%08x: pop edx; ret", start_address);
	OPCODE_CASE(0xc35e, "%08x: pop esi; ret", start_address);
	OPCODE_CASE(0xc35f, "%08x: pop edi; ret", start_address);
	OPCODE_CASE(0xc35d, "%08x: pop ebp; ret", start_address);
	//OPCODE_CASE(0xc35c, "%08x: pop esp; ret", start_address);
	}
	
	switch(data.dword) {
	OPCODE_CASE(0x042464ff, "%08x: jmp [esp + 4]", start_address);
	OPCODE_CASE(0x042454ff, "%08x: call [esp + 4]", start_address);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::testESP_ADD_8(const OpcodeData &data, BaseTypes::address_t start_address) {

	
	const uint32_t instr24 = data.dword & 0x00ffffff;
	switch(instr24) {
	OPCODE_CASE(0x00c35858, "%08x: pop eax; pop eax; ret", start_address);
	OPCODE_CASE(0x00c35958, "%08x: pop eax; pop ecx; ret", start_address);
	OPCODE_CASE(0x00c35a58, "%08x: pop eax; pop edx; ret", start_address);
	OPCODE_CASE(0x00c35b58, "%08x: pop eax; pop ebx; ret", start_address);
//	OPCODE_CASE(0x00c35c58, "%08x: pop eax; pop esp; ret", start_address);
	OPCODE_CASE(0x00c35d58, "%08x: pop eax; pop ebp; ret", start_address);
	OPCODE_CASE(0x00c35e58, "%08x: pop eax; pop esi; ret", start_address);
	OPCODE_CASE(0x00c35f58, "%08x: pop eax; pop edi; ret", start_address);
	
	OPCODE_CASE(0x00c35859, "%08x: pop ecx; pop eax; ret", start_address);
	OPCODE_CASE(0x00c35959, "%08x: pop ecx; pop ecx; ret", start_address);
	OPCODE_CASE(0x00c35a59, "%08x: pop ecx; pop edx; ret", start_address);
	OPCODE_CASE(0x00c35b59, "%08x: pop ecx; pop ebx; ret", start_address);
//	OPCODE_CASE(0x00c35c59, "%08x: pop ecx; pop esp; ret", start_address);
	OPCODE_CASE(0x00c35d59, "%08x: pop ecx; pop ebp; ret", start_address);
	OPCODE_CASE(0x00c35e59, "%08x: pop ecx; pop esi; ret", start_address);
	OPCODE_CASE(0x00c35f59, "%08x: pop ecx; pop edi; ret", start_address);
	
	OPCODE_CASE(0x00c3585a, "%08x: pop edx; pop eax; ret", start_address);
	OPCODE_CASE(0x00c3595a, "%08x: pop edx; pop ecx; ret", start_address);
	OPCODE_CASE(0x00c35a5a, "%08x: pop edx; pop edx; ret", start_address);
	OPCODE_CASE(0x00c35b5a, "%08x: pop edx; pop ebx; ret", start_address);
//	OPCODE_CASE(0x00c35c5a, "%08x: pop edx; pop esp; ret", start_address);
	OPCODE_CASE(0x00c35d5a, "%08x: pop edx; pop ebp; ret", start_address);
	OPCODE_CASE(0x00c35e5a, "%08x: pop edx; pop esi; ret", start_address);
	OPCODE_CASE(0x00c35f5a, "%08x: pop edx; pop edi; ret", start_address);
	
	OPCODE_CASE(0x00c3585b, "%08x: pop ebx; pop eax; ret", start_address);
	OPCODE_CASE(0x00c3595b, "%08x: pop ebx; pop ecx; ret", start_address);
	OPCODE_CASE(0x00c35a5b, "%08x: pop ebx; pop edx; ret", start_address);
	OPCODE_CASE(0x00c35b5b, "%08x: pop ebx; pop ebx; ret", start_address);
//	OPCODE_CASE(0x00c35c5b, "%08x: pop ebx; pop esp; ret", start_address);
	OPCODE_CASE(0x00c35d5b, "%08x: pop ebx; pop ebp; ret", start_address);
	OPCODE_CASE(0x00c35e5b, "%08x: pop ebx; pop esi; ret", start_address);
	OPCODE_CASE(0x00c35f5b, "%08x: pop ebx; pop edi; ret", start_address);
	
//	OPCODE_CASE(0x00c3585c, "%08x: pop esp; pop eax; ret", start_address);
//	OPCODE_CASE(0x00c3595c, "%08x: pop esp; pop ecx; ret", start_address);
//	OPCODE_CASE(0x00c35a5c, "%08x: pop esp; pop edx; ret", start_address);
//	OPCODE_CASE(0x00c35b5c, "%08x: pop esp; pop ebx; ret", start_address);
//	OPCODE_CASE(0x00c35c5c, "%08x: pop esp; pop esp; ret", start_address);
//	OPCODE_CASE(0x00c35d5c, "%08x: pop esp; pop ebp; ret", start_address);
//	OPCODE_CASE(0x00c35e5c, "%08x: pop esp; pop esi; ret", start_address);
//	OPCODE_CASE(0x00c35f5c, "%08x: pop esp; pop edi; ret", start_address);
	
	OPCODE_CASE(0x00c3585d, "%08x: pop ebp; pop eax; ret", start_address);
	OPCODE_CASE(0x00c3595d, "%08x: pop ebp; pop ecx; ret", start_address);
	OPCODE_CASE(0x00c35a5d, "%08x: pop ebp; pop edx; ret", start_address);
	OPCODE_CASE(0x00c35b5d, "%08x: pop ebp; pop ebx; ret", start_address);
//	OPCODE_CASE(0x00c35c5d, "%08x: pop ebp; pop esp; ret", start_address);
	OPCODE_CASE(0x00c35d5d, "%08x: pop ebp; pop ebp; ret", start_address);
	OPCODE_CASE(0x00c35e5d, "%08x: pop ebp; pop esi; ret", start_address);
	OPCODE_CASE(0x00c35f5d, "%08x: pop ebp; pop edi; ret", start_address);
	
	OPCODE_CASE(0x00c3585e, "%08x: pop esi; pop eax; ret", start_address);
	OPCODE_CASE(0x00c3595e, "%08x: pop esi; pop ecx; ret", start_address);
	OPCODE_CASE(0x00c35a5e, "%08x: pop esi; pop edx; ret", start_address);
	OPCODE_CASE(0x00c35b5e, "%08x: pop esi; pop ebx; ret", start_address);
//	OPCODE_CASE(0x00c35c5e, "%08x: pop esi; pop esp; ret", start_address);
	OPCODE_CASE(0x00c35d5e, "%08x: pop esi; pop ebp; ret", start_address);
	OPCODE_CASE(0x00c35e5e, "%08x: pop esi; pop esi; ret", start_address);
	OPCODE_CASE(0x00c35f5e, "%08x: pop esi; pop edi; ret", start_address);
	
	OPCODE_CASE(0x00c3585f, "%08x: pop edi; pop eax; ret", start_address);
	OPCODE_CASE(0x00c3595f, "%08x: pop edi; pop ecx; ret", start_address);
	OPCODE_CASE(0x00c35a5f, "%08x: pop edi; pop edx; ret", start_address);
	OPCODE_CASE(0x00c35b5f, "%08x: pop edi; pop ebx; ret", start_address);
//	OPCODE_CASE(0x00c35c5f, "%08x: pop edi; pop esp; ret", start_address);
	OPCODE_CASE(0x00c35d5f, "%08x: pop edi; pop ebp; ret", start_address);
	OPCODE_CASE(0x00c35e5f, "%08x: pop edi; pop esi; ret", start_address);
	OPCODE_CASE(0x00c35f5f, "%08x: pop edi; pop edi; ret", start_address);
	}
	
	switch(data.dword) {
	OPCODE_CASE(0x082464ff, "%08x: jmp [esp + 8]", start_address);
	OPCODE_CASE(0x082454ff, "%08x: call [esp + 8]", start_address);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::testESP_SUB_4(const OpcodeData &data, BaseTypes::address_t start_address) {
	switch(data.dword) {
	OPCODE_CASE(0xfc2464ff, "%08x: jmp [esp - 4]", start_address);
	OPCODE_CASE(0xfc2454ff, "%08x: call [esp - 4]", start_address);
	OPCODE_CASE(0xc304ec83, "%08x: sub esp, 4; ret", start_address);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DialogOpcodes::on_btnFind_clicked() {

	QList<QTableWidgetItem *> sel = ui.tableWidget->selectedItems();
	
	ByteShiftArray bsa(sizeof(OpcodeData));
	
	int classtype = ui.comboBox->currentIndex();

	ui.listWidget->clear();
	ui.progressBar->setValue(0);
	
	if(sel.size() == 0) {
		QMessageBox::information(this, "No Region Selected", "You must select a region which is to be scanned for the desired opcode.");
	} else {	
		
		BaseTypes::address_t start_address	= sel[0]->text().toUInt(0, 16);
		BaseTypes::address_t end_address	= sel[1]->text().toUInt(0, 16);

		const BaseTypes::address_t orig_start	= start_address;
		const BaseTypes::address_t howMany		= end_address - orig_start;
		bsa.clear();
		
		// create a reference to the bsa's data so we can pass it to the testXXXX functions
		const OpcodeData &opcode = *reinterpret_cast<const OpcodeData *>(bsa.data());
		
		// do the search for this region!
		while(start_address < end_address) {

			// read in the next byte
			bsa.shl();
			Debugger::debuggerBase->readBytes(start_address, &(bsa[bsa.size() - 1]), sizeof(uint8_t));

			switch(classtype) {
			case 0: 
				testEax(opcode, start_address - 3);					
				break;
			case 1:
				testEbx(opcode, start_address - 3);
				break;
			case 2:
				testEcx(opcode, start_address - 3);
				break;
			case 3:
				testEdx(opcode, start_address - 3);
				break;
			case 4:
				testEbp(opcode, start_address - 3);
				break;
			case 5:
				testEsp(opcode, start_address - 3);
				break;
			case 6:
				testEsi(opcode, start_address - 3);
				break;
			case 7:
				testEdi(opcode, start_address - 3);
				break;
			case 8:
				testEax(opcode, start_address - 3);					
				testEbx(opcode, start_address - 3);
				testEcx(opcode, start_address - 3);
				testEdx(opcode, start_address - 3);
				testEbp(opcode, start_address - 3);
				testEsp(opcode, start_address - 3);
				testEsi(opcode, start_address - 3);
				testEdi(opcode, start_address - 3);
				break;
			case 9:
				// [ESP] -> EIP
				testESP_ADD_0(opcode, start_address - 3);
				break;	
			case 10:
				// [ESP + 4] -> EIP
				testESP_ADD_4(opcode, start_address - 3);
				break;
			case 11:
				// [ESP + 8] -> EIP
				testESP_ADD_8(opcode, start_address - 3);
				break;
			case 12:
				// [ESP - 4] -> EIP
				testESP_SUB_4(opcode, start_address - 3);
				break;
			}

			const BaseTypes::address_t howManyDone	= start_address - orig_start;
			const int percent = static_cast<int>(static_cast<double>(howManyDone) / static_cast<double>(howMany) * 100);			

			ui.progressBar->setValue(percent);
			++start_address;
		}
		ui.progressBar->setValue(100);
	}
}
