//					ANALYSE.CPP

#include <windows.h>
#include <commctrl.h>

#include "opcodes.h"
#include "imports.h"
#include "exports.h"
#include "disasm.h"
#include "xref.h"
#include "textout.h"
#include "dasm.h"
#include "comments.h"
#include "analyse.h"

analysis::analysis(void)
{ flist=NULL;
}

analysis::~analysis(void)
{ struct flowlist *nxt;
  while(flist!=NULL)
  { nxt=flist->next;
	 delete flist;
	 flist=nxt;
  }
}

void analysis::imported(void)
{ struct asmlist *dis,*tracer;
  unsigned long importind;
  dis=disassemble.getdisassembly();
  PrintBuff("Analysing imports...");
  PrintBuff("");
  while(dis!=NULL)
  { if(dis->arg1type==MEMLOCD)
	 { if(imports.isimport(dis->arg1))
		{ dis->arg1type=IMPORT;
		  if(dis->opcode==OP_JMP)
		  { xr.reset();
			 tracer=disassemble.getdisassembly();
			 importind=xr.getref(dis->addr);
			 while(importind)
			 { if(tracer->addr>importind)tracer=disassemble.getdisassembly();
				while(tracer->addr<importind)tracer=tracer->next;
				if(tracer->addr==importind)
				{ tracer->arg1type=IMPORT;
				  tracer->arg1=dis->arg1;
				}
				importind=xr.getref(dis->addr);
			 }
		  }
		}
	 }
	 if(exports.isexport(dis->addr))
	 { xr.reset();
		tracer=disassemble.getdisassembly();
		importind=xr.getref(dis->addr);
		while(importind)
		{ if(tracer->addr>importind)tracer=disassemble.getdisassembly();
		  while(tracer->addr<importind)tracer=tracer->next;
		  if(tracer->addr==importind)
		  { tracer->arg1type=EXPORT;
		  }
		  importind=xr.getref(dis->addr);
		}
	 }
	 dis=dis->next;
  }
}

void analysis::codeflow(void)
{ struct asmlist *tracer;
  struct flowlist *flist_trace,*prev,*next_flist,*prev_flist,*next2_flist;
  unsigned long nxtaddr,maxcall,procaddr,prevaddr,nxtref,nxt2addr,nxt3addr;
  unsigned char next_type;
  unsigned long stepsize,barct,speed,analct;
  BOOL updated;
  tracer=disassemble.getdisassembly();
  stepsize=disassemble.getinstct()/1000;
  barct=0;
  speed=0;
  analct=0;
  PrintBuff("Compiling codeflow...");
  PrintBuff("");
  while(tracer!=NULL)
  { if(KillThread)return;
	 speed++;
	 if(speed>stepsize)
	 { barct++;
		Progress_SetPos(ProgressBar,barct);
		speed-=stepsize;
	 }
	 switch(tracer->opcode)
	 { case OP_RETF:
		case OP_RETN:
		case OP_IRET:
		  addtoflowlist(tracer->addr,FLOW_RET,0);
		  analct++;
		  break;
		case OP_JMP:
		  if(tracer->arg1type==MEM)
		  { addtoflowlist(tracer->addr,FLOW_JMP,tracer->arg1);
			 addtoflowlist(tracer->arg1,FLOW_JMPTARGET,0);
			 analct+=2;
		  }
		  break;
		case OP_CALL:
		  if(tracer->arg1type==MEM)
		  { addtoflowlist(tracer->arg1,FLOW_CALLTARGET,0);
			 analct++;
		  }
		  break;
		case OP_JO:
		case OP_JNO:
		case OP_JC:
		case OP_JNC:
		case OP_JZ:
		case OP_JNZ:
		case OP_JBE:
		case OP_JA:
		case OP_JS:
		case OP_JNS:
		case OP_JPE:
		case OP_JPO:
		case OP_JL:
		case OP_JGE:
		case OP_JLE:
		case OP_JG:
		case OP_LOOPNZ:
		case OP_LOOPZ:
		case OP_LOOP:
		case OP_JECXZ:
		  if(tracer->arg1type==MEM)
		  { addtoflowlist(tracer->addr,FLOW_CONDJMP,tracer->arg1);
			 addtoflowlist(tracer->arg1,FLOW_CONDJMPTARGET,0);
			 analct+=2;
		  }
		  break;
		default:
		  break;
	 }
	 tracer=tracer->next;
  }
  flist_trace=flist;
  procaddr=0;
  maxcall=0;
  PrintBuff("Analysing codeflow...");
  PrintBuff("");
  stepsize=analct/1000;
  barct=0;
  speed=0;
  while(flist_trace!=NULL)
  { if(flist_trace->type==FLOW_CALLTARGET)
	 { procaddr=flist_trace->addr;
		maxcall=flist_trace->addr;
	 }
	 if((flist_trace->type==FLOW_JMP)||(flist_trace->type==FLOW_CONDJMP))
	 { if(flist_trace->refto>maxcall)maxcall=flist_trace->refto;
	 }
	 if(flist_trace->type==FLOW_RET)
	 { if((maxcall<=flist_trace->addr)&&(procaddr))
		{ dis_comments.addcomment(flist_trace->addr,COMMENT_ENDPROC,TRUE);
		  dis_comments.addcomment(procaddr,COMMENT_PROC,FALSE);
		  procaddr=0;
		  maxcall=0;
		}
		else dis_comments.addcomment(flist_trace->addr,COMMENT_RET,FALSE);
	 }
	 flist_trace=flist_trace->next;
  }
  do
  { if(KillThread)return;
	 updated=FALSE;
	 flist_trace=flist;
	 while(flist_trace!=NULL)
	 { next_flist=flist_trace->next;
		prev_flist=flist_trace->last;
		next_type=0;
		if(next_flist!=NULL)
		{ nxtaddr=next_flist->addr;
		  nxtref=next_flist->refto;
		  next_type=next_flist->type;
		  next2_flist=next_flist->next;
		  if(next2_flist!=NULL)
		  { nxt2addr=next2_flist->addr;
			 if(next2_flist->next!=NULL)
				nxt3addr=next2_flist->next->addr;
			 else
				nxt3addr=0;
		  }
		  else
		  { nxt2addr=0;
			 nxt3addr=0;
		  }
		}
		else if(flist_trace->addr<flist_trace->refto)
		  nxtaddr=flist_trace->refto;
		else
		  nxtaddr=flist_trace->addr;
		if(prev_flist!=NULL)
		  prevaddr=prev_flist->addr;
		else if(flist_trace->addr<flist_trace->refto)
		  prevaddr=flist_trace->addr;
		else prevaddr=flist_trace->refto;
		if((flist_trace->type==FLOW_CONDJMP)&&(flist_trace->addr<flist_trace->refto)&&
		  (flist_trace->refto<=nxtaddr))
		{ tracer=disassemble.getdisassembly();
		  while((tracer->next!=NULL)&&(tracer->addr<flist_trace->refto))
			 tracer=tracer->next;
		  if(tracer!=NULL)
			 if(tracer->last!=NULL)
			 {	dis_comments.addcomment(flist_trace->addr,COMMENT_IF,TRUE);
				dis_comments.addcomment(tracer->last->addr,COMMENT_ENDIF,FALSE);
			 }
		  updated=TRUE;
		  speed+=2;
		  prev=flist_trace;
		  flist_trace=flist_trace->next;
		  delfromflowlist(prev);
		  while(flist_trace->type!=FLOW_CONDJMPTARGET)flist_trace=flist_trace->next;
		  prev=flist_trace;
		  flist_trace=flist_trace->next;
		  delfromflowlist(prev);
		}
		else if((flist_trace->type==FLOW_CALLTARGET)||(flist_trace->type==FLOW_RET))
		{ updated=TRUE;
		  speed++;
		  prev=flist_trace;
		  flist_trace=flist_trace->next;
		  delfromflowlist(prev);
		}
		else if((flist_trace->type==FLOW_CONDJMP)&&(flist_trace->addr>flist_trace->refto)&&
		  (flist_trace->refto>=prevaddr))
		{ tracer=disassemble.getdisassembly();
		  while((tracer->next!=NULL)&&(tracer->addr<flist_trace->refto))
			 tracer=tracer->next;
		  if(tracer!=NULL)
			 {	dis_comments.addcomment(flist_trace->addr,COMMENT_DOWHILE,TRUE);
				dis_comments.addcomment(tracer->addr,COMMENT_DO,FALSE);
			 }
		  updated=TRUE;
		  speed+=2;
		  prev=flist_trace;
		  flist_trace=flist_trace->last;
		  delfromflowlist(prev);
		  while(flist_trace->type!=FLOW_CONDJMPTARGET)flist_trace=flist_trace->last;
		  prev=flist_trace;
		  flist_trace=flist_trace->next;
		  delfromflowlist(prev);
		}
		else if((flist_trace->type==FLOW_CONDJMP)&&(flist_trace->addr<flist_trace->refto)&&
		  (flist_trace->refto>nxtaddr)&&(next_type==FLOW_JMP)&&(flist_trace->refto<=nxt2addr)&&
		  (nxtref<=nxt3addr)&&(nxtref>nxt2addr))
		{ tracer=disassemble.getdisassembly();
		  while((tracer->next!=NULL)&&(tracer->addr<nxtref))
			 tracer=tracer->next;
		  if(tracer!=NULL)
			 if(tracer->last!=NULL)
			 {	dis_comments.addcomment(flist_trace->addr,COMMENT_IF,TRUE);
				dis_comments.addcomment(nxtaddr,COMMENT_ELSE,FALSE);
				dis_comments.addcomment(tracer->last->addr,COMMENT_ENDIF,FALSE);
			 }
		  updated=TRUE;
		  speed+=4;
		  prev=flist_trace;
		  flist_trace=flist_trace->next;
		  delfromflowlist(prev);
		  prev=flist_trace;
		  flist_trace=flist_trace->next;
		  delfromflowlist(prev);
		  prev=flist_trace;
		  flist_trace=flist_trace->next;
		  delfromflowlist(prev);
		  prev=flist_trace;
		  flist_trace=flist_trace->next;
		  delfromflowlist(prev);
		}
		else
		  flist_trace=flist_trace->next;
		if(speed>stepsize)
		{ barct++;
		  Progress_SetPos(ProgressBar,barct);
		  speed-=stepsize;
	   }
	 }
  } while(updated);
}

void analysis::addtoflowlist(unsigned long addr,unsigned char type,unsigned long xref)
{ struct flowlist *addit,*newone,*prev;
  if(flist==NULL)
  { flist=new struct flowlist;
	 addit=flist;
	 addit->next=NULL;
	 addit->last=NULL;
  }
  else
  { addit=flist;
	 prev=NULL;
	 while((addr>addit->addr)&&(addit->next!=NULL))
	 { prev=addit;
		addit=addit->next;
	 }
	 if(addr>addit->addr)
	 { addit->next=new struct flowlist;
		prev=addit;
		addit=addit->next;
		addit->next=NULL;
		addit->last=prev;
	 }
	 else
	 { newone=new struct flowlist;
		newone->next=addit;
		addit->last=newone;
		addit=newone;
		if(prev!=NULL)
		{ prev->next=newone;
		  addit->last=prev;
		}
		else
		{ flist=newone;
		  addit->last=NULL;
		}
	 }
  }
  addit->type=type;
  addit->addr=addr;
  addit->refto=xref;
}

void analysis::delfromflowlist(struct flowlist *del_flist)
{ if(del_flist->last==NULL)
  { flist=flist->next;
	 flist->last=NULL;
  }
  else if(del_flist->next==NULL)
  { del_flist->last->next=NULL;
  }
  else
  { del_flist->last->next=del_flist->next;
	 del_flist->next->last=del_flist->last;
  }
  delete del_flist;
}

void analysis::offsets(void)
{ struct asmlist *tracer,*hunter;
  unsigned long stepsize,barct,speed,ofadr;
  tracer=disassemble.getdisassembly();
  stepsize=disassemble.getinstct()/1000;
  barct=0;
  speed=0;
  PrintBuff("Analysing offsets...");
  PrintBuff("");
  while(tracer!=NULL)
  { if(KillThread)return;
	 speed++;
	 if(speed>stepsize)
	 { barct++;
		Progress_SetPos(ProgressBar,barct);
		speed-=stepsize;
	 }
	 ofadr=0;
	 if(tracer->arg1type==OFFSET)
	 { ofadr=tracer->arg1;
	 }
	 if(tracer->arg2type==OFFSET)
	 { ofadr=tracer->arg2;
	 }
	 if(ofadr)
	 { hunter=tracer;
		if(hunter->addr>ofadr)hunter=disassemble.getdisassembly();
		while(hunter!=NULL)
		{ if(hunter->addr<ofadr)hunter=hunter->next;
		  else break;
		}
		if(hunter!=NULL)
		{ if(hunter->addr==ofadr)
		  { if(hunter->opcode==OP_DS)
			 { dis_comments.addoffscomment(tracer->addr,COMMENT_DS,hunter->data);
			 }
		  }
		}
	 }
	 tracer=tracer->next;
  }
}
