//---------------------------------------------------------------------------
#include <vcl.h>
#include <stdio.h>
#pragma hdrstop

#define MAINPROG
#include "disasm.h"

#include "N-Rec_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
// jmp  jnp  jno  jo  jecxz jc   jle  jnc  jp   js   jz   jg   jl   jns  ja   jbe  jge  jnz
// 2-bytes
byte short_opcodes[0x12]={0xEB, 0x7B, 0x71, 0x70, 0xE3, 0x72, 0x7E, 0x73, 0x7A, 0x78, 0x74, 0x7F, 0x7C, 0x79, 0x77, 0x76, 0x7D, 0x75 };
// 5-bytes
byte longjmp_opcodes[0x12]={0xE9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
// 6-bytes
word long_opcodes[0x12]={0x0000, 0x8B0F, 0x810F, 0x800F, 0x0000, 0x820F, 0x8E0F, 0x830F, 0x8A0F,
  0x880F, 0x840F, 0x8F0F, 0x8C0F, 0x890F, 0x870F, 0x860F, 0x8D0F, 0x850F };
byte nano_index[0x12];
//---------------------------------------------------------------------------
#pragma warn -csu
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner)
        : TForm(Owner)
{
  disasm=NULL;
}
//---------------------------------------------------------------------------
void __fastcall TMain::OpenSrcClick(TObject *Sender)
{
  OpenDialog->Filter="EXE files|*.exe|DLL files|*.dll|All files|*.*";
  OpenDialog->InitialDir=ExtractFilePath(ParamStr(0));
  if( OpenDialog->Execute() )
    SrcName->Text=OpenDialog->FileName;
  else
    SrcName->Text="";
}
//---------------------------------------------------------------------------
void __fastcall TMain::ProcessClick(TObject *Sender)
{
  int inFile;
  info->Clear();
  err->Clear();
  diffs->Clear();
  blocks.clear();
  disasm=NULL;
  cleanupSections();
  if( (inFile=FileOpen(SrcName->Text, fmOpenRead | fmShareDenyNone)) == -1 )
  {
    err->Items->Add("ERR: Can't open source file");
    return;
  }
  // read source file into memory
  int size=FileSeek(inFile, 0, 2);
  FileSeek(inFile, 0, 0);
  sourceFile=new unsigned char [size];
  if( !sourceFile )
  {
    FileClose(inFile);
    err->Items->Add("ERR: Can't allocate memory for source file mapping");
    return;
  }
  FileRead(inFile, sourceFile, size);
  FileClose(inFile);
  // prepare disasm tracing - split sections
  int EP_value=fillSections(sourceFile);
  delete sourceFile;
  if( !EP_value )
  {
    err->Items->Add("ERR: can't find entry point");
    return;
  }
  FILE *epf;
  char line[256], line2[256];
  if( (epf=fopen(EPName->Text.c_str(), "r")) == NULL )
  {
    err->Items->Add("ERR: Can't open ini file");
    return;
  }
  // process ini file
  int ln=0;
  bool isError=false;
  while( true )
  {
    fgets(line, 255, epf);
    ln++;
    if( feof(epf) )
      break;
    int len=strlen(line), ptr;
    for( ptr=0; ptr<len; ++ptr )
      if( !isspace(line[ptr]) )
        break;
    if( ptr == len || line[ptr] == '#' )
      continue;     // empty line or comment
    // line[ptr] - begin of string
    dword key=*(dword *)(line+ptr);
    strcpy(line2,line+ptr+4);
    len=strlen(line2);
    for( ptr=0; ptr<len; ++ptr )
      if( !isspace(line2[ptr]) )
        break;
    if( ptr == len )
    {
      err->Items->Add("ERR: incorrect syntax in ini file, line "+AnsiString(ln));
      isError=true;
      continue;
    }
    dword addr1, addr2, adr3, *keys;
    unsigned char *phys;
    codeBlock code;
    switch( key )
    {
      case 0x4c42544f:      // OTBL
        sscanf(line2+ptr, "%X %X", &addr1, &addr2);
        if( mapVirtualToPhys(addr1) == NULL || mapVirtualToPhys(addr2) == NULL )
          err->Items->Add("ERR: bad OTBL address in ini file, line "+AnsiString(ln));
        else
        {
          for( ptr=addr1; ptr<=addr2; ptr+=4 )
            if( (phys=mapVirtualToPhys(ptr)) != NULL )
              stack.push(*(dword *)phys);
          info->Items->Add("Execute addresses table from "+IntToHex((int)addr1,8)+" to "+IntToHex((int)addr2,8)+" added into analyses queue");
        }
        break;
      case 0x52444441:      // ADDR
        sscanf(line2+ptr, "%X", &addr2);
        if( mapVirtualToPhys(addr2) == NULL )
          err->Items->Add("ERR: bad ADDR address in ini file, line "+AnsiString(ln));
        else
        {
          stack.push(addr2);
          info->Items->Add("Execute address "+IntToHex((int)addr2,8)+" added into analyses queue");
        }
        break;
      case 0x544e434e:      // NCNT
        sscanf(line2+ptr, "%X", &nanoCount);
        info->Items->Add("Nanomites count "+IntToHex((int)nanoCount,8));
        break;
      case 0x44414343:      // CCAD
        sscanf(line2+ptr, "%s", line);
        if( (inFile=FileOpen(line, fmOpenRead)) == -1 )
        {
          err->Items->Add("ERR: Can't open CCAD file");
          isError=true;
        }
        else
        {
          nano_ccad=new dword[nanoCount];
          if( !nano_ccad )
          {
            err->Items->Add("ERR: Can't allocate memory for CCAD table");
            isError=true;
          }
          else
            FileRead(inFile, nano_ccad, nanoCount*4);
          FileClose(inFile);
          info->Items->Add("CCAD file loaded");
        }
        break;
      case 0x5059544a:      // JTYP
        sscanf(line2+ptr, "%s", line);
        if( (inFile=FileOpen(line, fmOpenRead)) == -1 )
        {
          err->Items->Add("ERR: Can't open JTYP file");
          isError=true;
        }
        else
        {
          nano_jtyp=new byte[nanoCount];
          if( !nano_jtyp )
          {
            err->Items->Add("ERR: Can't allocate memory for JTYP table");
            isError=true;
          }
          else
            FileRead(inFile, nano_jtyp, nanoCount);
          FileClose(inFile);
          info->Items->Add("JTYP file loaded");
        }
        break;
      case 0x5a49534a:      // JSIZ
        sscanf(line2+ptr, "%s", line);
        if( (inFile=FileOpen(line, fmOpenRead)) == -1 )
        {
          err->Items->Add("ERR: Can't open JSIZ file");
          isError=true;
        }
        else
        {
          nano_jsiz=new byte[nanoCount];
          if( !nano_jsiz )
          {
            err->Items->Add("ERR: Can't allocate memory for JSIZ table");
            isError=true;
          }
          else
            FileRead(inFile, nano_jsiz, nanoCount);
          FileClose(inFile);
          info->Items->Add("JSIZ file loaded");
        }
        break;
      case 0x5453444A:      // JDST
        sscanf(line2+ptr, "%s", line);
        if( (inFile=FileOpen(line, fmOpenRead)) == -1 )
        {
          err->Items->Add("ERR: Can't open JDST file");
          isError=true;
        }
        else
        {
          nano_jdst=new dword[nanoCount];
          if( !nano_jdst )
          {
            err->Items->Add("ERR: Can't allocate memory for CCAD table");
            isError=true;
          }
          else
            FileRead(inFile, nano_jdst, nanoCount*4);
          FileClose(inFile);
          info->Items->Add("JDST file loaded");
        }
        break;
      case 0x4b53444a:      // JDSK
        sscanf(line2+ptr, "%s", line);
        if( (inFile=FileOpen(line, fmOpenRead)) == -1 )
        {
          err->Items->Add("ERR: Can't open JDSK file");
          isError=true;
        }
        else
        {
          keys=new dword[0x10];
          if( !keys )
          {
            err->Items->Add("ERR: Can't allocate memory for JDSK table");
            isError=true;
          }
          else
          {
            FileRead(inFile, keys, 0x10*4);
            FileClose(inFile);
            if( !nano_jdst )
            {
              err->Items->Add("ERR: JDSK before JDST - check ini file");
              isError=true;
            }
            else
              for(ptr=0; ptr<nanoCount; ++ptr)
                nano_jdst[ptr]^=keys[ptr%0x10];
            delete keys;
            info->Items->Add("JDST array decrypted");
          }
        }
        break;
      case 0x3144434a:  // JCD1
        //                 0   1  2  3  4  5  6  7  8
        sscanf(line2+ptr, "%x %x %x %x %x %x %x %x %x",
          &nano_index[0x00],&nano_index[0x01],&nano_index[0x02],&nano_index[0x03],
          &nano_index[0x04],&nano_index[0x05],&nano_index[0x06],&nano_index[0x07],
          &nano_index[0x08]);
        info->Items->Add("JCD1 table loaded");
        break;
      case 0x3244434a:  // JCD2
        //                 9  a  b  c  d  e  f  10  11
        sscanf(line2+ptr, "%x %x %x %x %x %x %x %x %x",
          &nano_index[0x09],&nano_index[0x0a],&nano_index[0x0b],&nano_index[0x0c],
          &nano_index[0x0d],&nano_index[0x0e],&nano_index[0x0f],&nano_index[0x10],
          &nano_index[0x11]);
        info->Items->Add("JCD2 table loaded");
        break;
      case 0x47455343:   // CSEG
        sscanf(line2+ptr, "%X %X", &addr1, &addr2);
        if( mapVirtualToPhys(addr1) == NULL || mapVirtualToPhys(addr2) == NULL )
          err->Items->Add("ERR: bad CSEG address in ini file, line "+AnsiString(ln));
        else
        {
          code.begin=addr1;
          code.end=addr2;
          blocks.push_back(code);
          info->Items->Add("Code block from "+IntToHex((int)addr1,8)+" to "+IntToHex((int)addr2,8)+" added into analyses queue");
        }
        break;
      default:
        err->Items->Add("ERR: incorrect syntax in ini file, line "+AnsiString(ln));
        isError=true;
        break;
    }
  }
  fclose(epf);
  if( nano_ccad==NULL || nano_jdst==NULL || nano_jtyp==NULL || nano_jsiz==NULL )
  {
    err->Items->Add("ERR: requered tables not loaded - check ini file");
    return;
  }
  if( isWarn->Checked )
  {
    info->Items->Add("instr JMP has index "+IntToHex((int)nano_index[0x0],2));
    info->Items->Add("instr JNP has index "+IntToHex((int)nano_index[0x1],2));
    info->Items->Add("instr JNO has index "+IntToHex((int)nano_index[0x2],2));
    info->Items->Add("instr JO has index "+IntToHex((int)nano_index[0x3],2));
    info->Items->Add("instr JEXCZ has index "+IntToHex((int)nano_index[0x4],2));
    info->Items->Add("instr JC has index "+IntToHex((int)nano_index[0x5],2));
    info->Items->Add("instr JLE has index "+IntToHex((int)nano_index[0x6],2));
    info->Items->Add("instr JNC has index "+IntToHex((int)nano_index[0x7],2));
    info->Items->Add("instr JP has index "+IntToHex((int)nano_index[0x8],2));
    info->Items->Add("instr JS has index "+IntToHex((int)nano_index[0x9],2));
    info->Items->Add("instr JZ has index "+IntToHex((int)nano_index[0x0a],2));
    info->Items->Add("instr JG has index "+IntToHex((int)nano_index[0x0b],2));
    info->Items->Add("instr JL has index "+IntToHex((int)nano_index[0x0c],2));
    info->Items->Add("instr JNS has index "+IntToHex((int)nano_index[0x0d],2));
    info->Items->Add("instr JA has index "+IntToHex((int)nano_index[0x0e],2));
    info->Items->Add("instr JBE has index "+IntToHex((int)nano_index[0x0f],2));
    info->Items->Add("instr JGE has index "+IntToHex((int)nano_index[0x10],2));
    info->Items->Add("instr JNZ has index "+IntToHex((int)nano_index[0x11],2));
  }
  if( isError )
    return;
  bool isExec=true;
  HANDLE hSem, thr;
  dword res, dbgStatus, code, addr;
  STARTUPINFO si;
  PROCESS_INFORMATION pi;
  DEBUG_EVENT de;
  CONTEXT ctx;
  diffs->Add(ExtractFileName(SrcName->Text));
  switch( Mode->ItemIndex )
  {
    case 0:     // disasm mode
      info->Items->Add("Recover process started - disasm mode");
      info->Items->Add("File entry point at "+IntToHex(EP_value,8));
      stack.push( EP_value );
      if( (hSem=CreateEvent(NULL, 0, 1, "DisasmSemaphore")) == NULL )
      {
        err->Items->Add("ERR: Can't create disasm thread event");
        return;
      }
      disasm = new TDisasm( true );
      if( !disasm )
        err->Items->Add("ERR: Can't create disasm thread");
      else
      {
        SetButtons( false );
        ResetEvent( hSem );
        disasm->Resume();
        while( isExec )
        {
          Application->ProcessMessages();
          res=WaitForSingleObject(hSem, 100);
          if( res == WAIT_OBJECT_0 )
            isExec=false;
        }
        delete disasm;
        disasm=NULL;
        SetButtons( true );
      }
      CloseHandle( hSem );
      info->Items->Add("Recover process finished");
      break;
    case 1:     // debug mode
      info->Items->Add("Recover process started - debug mode");
      ZeroMemory( &si, sizeof(si) );
      si.cb=sizeof(si);
      dbgProcess=NULL;
      res=CreateProcess(SrcName->Text.c_str(), NULL, NULL, NULL, FALSE,
        CREATE_SUSPENDED | DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS | NORMAL_PRIORITY_CLASS,
        NULL, NULL, &si, &pi);
      if( !res )
      {
        err->Items->Add("ERR: Can't create process");
        return;
      }
      dbgProcess=pi.hProcess;
      SetButtons( false );
      info->Items->Add("Executing application...");
      isExec=true;
      threads.clear();
      AddThread(pi.dwThreadId, pi.hThread);
      ResumeThread(pi.hThread);
      while( isExec )
      {
        Application->ProcessMessages();
        if( WaitForDebugEvent(&de, 100) )
        {
          //dbgStatus=DBG_EXCEPTION_NOT_HANDLED;
          dbgStatus=DBG_CONTINUE;
          switch( de.dwDebugEventCode )
          {
            case EXCEPTION_DEBUG_EVENT:
              code=de.u.Exception.ExceptionRecord.ExceptionCode;
              addr=(dword)de.u.Exception.ExceptionRecord.ExceptionAddress;
              if( code == EXCEPTION_BREAKPOINT )
              {
                if( mapVirtualToPhys(addr) == NULL )
                  info->Items->Add("INT3 at "+IntToHex((int)addr,8)+" not handled");
                else
                  if( processNanomite(addr, 0, MODE_DEBUG) )
                  {
                    if( (thr=GetThread(de.dwThreadId)) == NULL )
                      err->Items->Add("ERR: Thread ID "+IntToHex((int)de.dwThreadId,8)+" not found");
                    else
                    {
                      ctx.ContextFlags = CONTEXT_INTEGER|CONTEXT_CONTROL;
                      GetThreadContext(thr, &ctx);
                      ctx.Eip--;
                      SetThreadContext(thr, &ctx);
                    }
                  }
                  else
                    dbgStatus=DBG_EXCEPTION_NOT_HANDLED;
              }
              else
              {
                err->Items->Add("ERR: Exception "+IntToHex((int)code,8)+" at "+IntToHex((int)addr,8)+" not handled");
                dbgStatus=DBG_EXCEPTION_NOT_HANDLED;
              }
              break;
            case EXIT_PROCESS_DEBUG_EVENT:
              if( isWarn->Checked )
                info->Items->Add("Debug event EXIT_PROCESS");
              isExec=false;
              break;
            case RIP_EVENT:
              info->Items->Add("Debug event RIP");
              isExec=false;
              break;
            case CREATE_THREAD_DEBUG_EVENT:
              if( isWarn->Checked )
               info->Items->Add("Debug event CREATE_THREAD");
              AddThread(de.dwThreadId, de.u.CreateThread.hThread);
              break;
            case EXIT_THREAD_DEBUG_EVENT:
              if( isWarn->Checked )
                info->Items->Add("Debug event EXIT_THREAD");
              DelThread(de.dwThreadId);
              break;
/*
            case CREATE_PROCESS_DEBUG_EVENT:
              if( isWarn->Checked )
                info->Items->Add("Debug event CREATE_PROCESS");
              break;
            case LOAD_DLL_DEBUG_EVENT:
              if( isWarn->Checked )
                info->Items->Add("Debug event LOAD_DLL");
              break;
            case UNLOAD_DLL_DEBUG_EVENT:
              if( isWarn->Checked )
                info->Items->Add("Debug event UNLOAD_DLL");
              break;
*/
          }
          res=ContinueDebugEvent(de.dwProcessId,de.dwThreadId,dbgStatus);
          if( !res )
            err->Items->Add("ERR: ContinueDebugEvent fail with err: "+GetLastError());
        }
      }
      TerminateProcess(pi.hProcess, 0);
      CloseHandle(pi.hThread);
      CloseHandle(pi.hProcess);
      dbgProcess=NULL;
      info->Items->Add("Recover process finished");
      SetButtons( true );
      break;
  }
  if( diffs->Count > 1 )
  {
    // write difference file
    AnsiString diffName=ChangeFileExt(ExtractFileName(SrcName->Text),".dif");
    diffs->SaveToFile(diffName);
    info->Items->Add("Difference file "+diffName+" created");
  }
  else
    info->Items->Add("No nanomites encountered - check ini file or change recover mode");
}
//---------------------------------------------------------------------------
void __fastcall TMain::SetButtons( bool state )
{
  OpenSrc->Enabled=state;
  OpenEP->Enabled=state;
  Process->Enabled=state;
  Mode->Enabled=state;
  isWarn->Enabled=state;
  Cancel->Enabled=!state;
}
//---------------------------------------------------------------------------
bool __fastcall TMain::processNanomite(dword addr, unsigned char *phys_addr, int mode)
{
  // try to find nano in address table
  int index;
  bool result=false;
  for( index=0; index<nanoCount; ++index )
    if( nano_ccad[index]-1 == addr )
      break;
  if( index == nanoCount )
  {
    info->Items->Add("INT3 found, but addr  "+IntToHex((int)addr,8)+" not in CCAD table");
    return result;
  }
  // nano found - need type, size and destination
  byte type=99;
  for( int i=0; i<0x12; ++i )
    if(nano_index[i] == nano_jtyp[index])
      type=i;
  if( type == 99 )
  {
    err->Items->Add("ERR: bad JCOD table, type "+IntToHex((int)nano_jtyp[index],2)+" not found");
    return result;
  }
  byte size=nano_jsiz[index], bt;
  dword dest=nano_jdst[index], offset=mapVirtualToFileOffset(addr);
  unsigned char processBytes[0x10];
  // set RW page access
  unsigned long oldProtect=0, bytes;
  if( mode == MODE_DEBUG )
  {
    VirtualProtectEx(dbgProcess, (void *)addr, size+1, PAGE_READWRITE, &oldProtect);
    // read bytes from process and set phys_addr -> bytes
    ReadProcessMemory(dbgProcess, (void *)addr, processBytes, size+1, &bytes);
    phys_addr=processBytes;
  }
  switch( size )
  {
    case 0x01:  // 2-bytes jmp/jcc
      if( short_opcodes[type] == 0 )
        err->Items->Add("ERR: opcode not found for 2-bytes jump/jcc type "+IntToHex((int)type,2)+" at "+IntToHex((int)addr,8));
      else
      {
        diffs->Add(IntToHex((int)offset+0,8)+": "+IntToHex((int)*(byte *)(phys_addr+0),2)+" "+IntToHex((int)short_opcodes[type],2));
        *(byte *)(phys_addr+0) = short_opcodes[type];
        diffs->Add(IntToHex((int)offset+1,8)+": "+IntToHex((int)*(byte *)(phys_addr+1),2)+" "+IntToHex((int)((dest-1)&0xFF),2));
        *(byte *)(phys_addr+1) = (byte)((dest-1)&0xFF);
        info->Items->Add("Recovered 2-bytes jmp/jcc at address "+IntToHex((int)addr,8));
        result=true;
      }
      break;
    case 0x04:  // 5-bytes jmp/jcc
      if( longjmp_opcodes[type] == 0 )
        err->Items->Add("ERR: opcode not found for 5-bytes jump type "+IntToHex((int)type,2)+" at "+IntToHex((int)addr,8));
      else
      {
        diffs->Add(IntToHex((int)offset+0,8)+": "+IntToHex((int)*(byte *)(phys_addr+0),2)+" "+IntToHex((int)longjmp_opcodes[type],2));
        *(byte *)(phys_addr+0) = longjmp_opcodes[type];
        bt=((dest-4)>>0)&0xFF;
        diffs->Add(IntToHex((int)offset+1,8)+": "+IntToHex((int)*(byte *)(phys_addr+1),2)+" "+IntToHex((int)bt,2));
        bt=((dest-4)>>8)&0xFF;
        diffs->Add(IntToHex((int)offset+2,8)+": "+IntToHex((int)*(byte *)(phys_addr+2),2)+" "+IntToHex((int)bt,2));
        bt=((dest-4)>>16)&0xFF;
        diffs->Add(IntToHex((int)offset+3,8)+": "+IntToHex((int)*(byte *)(phys_addr+3),2)+" "+IntToHex((int)bt,2));
        bt=((dest-4)>>24)&0xFF;
        diffs->Add(IntToHex((int)offset+4,8)+": "+IntToHex((int)*(byte *)(phys_addr+4),2)+" "+IntToHex((int)bt,2));
        *(dword *)(phys_addr+1) = dest-4;
        info->Items->Add("Recovered 5-bytes jmp at address "+IntToHex((int)addr,8));
        result=true;
      }
      break;
    case 0x05:  // 6-bytes jmp
      if( long_opcodes[type] == 0 )
        err->Items->Add("ERR: opcode not found for 6-bytes jump/jcc type "+IntToHex((int)type,2)+" at "+IntToHex((int)addr,8));
      else
      {
        bt=(long_opcodes[type]>>0)&0xFF;
        diffs->Add(IntToHex((int)offset+0,8)+": "+IntToHex((int)*(byte *)(phys_addr+0),2)+" "+IntToHex((int)bt,2));
        bt=(long_opcodes[type]>>8)&0xFF;
        diffs->Add(IntToHex((int)offset+1,8)+": "+IntToHex((int)*(byte *)(phys_addr+1),2)+" "+IntToHex((int)bt,2));
        *(word *)(phys_addr+0) = long_opcodes[type];
        bt=((dest-5)>>0)&0xFF;
        diffs->Add(IntToHex((int)offset+2,8)+": "+IntToHex((int)*(byte *)(phys_addr+2),2)+" "+IntToHex((int)bt,2));
        bt=((dest-5)>>8)&0xFF;
        diffs->Add(IntToHex((int)offset+3,8)+": "+IntToHex((int)*(byte *)(phys_addr+3),2)+" "+IntToHex((int)bt,2));
        bt=((dest-5)>>16)&0xFF;
        diffs->Add(IntToHex((int)offset+4,8)+": "+IntToHex((int)*(byte *)(phys_addr+4),2)+" "+IntToHex((int)bt,2));
        bt=((dest-5)>>24)&0xFF;
        diffs->Add(IntToHex((int)offset+5,8)+": "+IntToHex((int)*(byte *)(phys_addr+5),2)+" "+IntToHex((int)bt,2));
        *(dword *)(phys_addr+2) = dest-5;
        info->Items->Add("Recovered 6-bytes jmp/jcc at address "+IntToHex((int)addr,8));
        result=true;
      }
      break;
  }
  // restore page protection
  if( mode == MODE_DEBUG )
  {
    // write bytes to process
    WriteProcessMemory(dbgProcess, (void *)addr, processBytes, size+1, &bytes);
    VirtualProtectEx(dbgProcess, (void *)addr, size+1, oldProtect, &oldProtect);
  }
  return result;
}
//---------------------------------------------------------------------------
unsigned char * __fastcall TMain::mapVirtualToPhys(dword addr)
{
  unsigned char *phys=NULL;
  for( int i=0; i<sectionsCount; ++i )
    if( sections[i].base <= addr && sections[i].base+sections[i].size>addr )
    {
      phys=sections[i].data+(addr-sections[i].base);
      break;
    }
  return phys;
}
//---------------------------------------------------------------------------
dword __fastcall TMain::mapVirtualToFileOffset(dword addr)
{
  dword phys=0;
  for( int i=0; i<sectionsCount; ++i )
    if( sections[i].base <= addr && sections[i].base+sections[i].size>addr )
    {
      phys=sections[i].phys+(addr-sections[i].base);
      break;
    }
  return phys;
}
//---------------------------------------------------------------------------
dword __fastcall TMain::restSectionLen(dword addr)
{
  dword rest=0;
  for( int i=0; i<sectionsCount; ++i )
    if( sections[i].base <= addr && sections[i].base+sections[i].size>addr )
    {
      rest=sections[i].size-(addr-sections[i].base);
      break;
    }
  return rest;
}
//---------------------------------------------------------------------------
dword __fastcall TMain::mapPhysToVirtual(unsigned char *phys_addr)
{
  dword virt=0;
  for( int i=0; i<sectionsCount; ++i )
    if( sections[i].data <= phys_addr && sections[i].data+sections[i].size>phys_addr )
    {
      virt=sections[i].base+(phys_addr-sections[i].data);
      break;
    }
  return virt;
}
//---------------------------------------------------------------------------
dword __fastcall TMain::fillSections(unsigned char *sourceFile)
{
  // check format
  unsigned char *PEHdrOffset=NULL;
  if( *(word *)(sourceFile+0x00) == 0x5a4d && *(word *)(sourceFile+0x18) >= 0x40 )
    PEHdrOffset=(*(dword *)(sourceFile+0x3c)+sourceFile);
  if( PEHdrOffset==NULL || *(word *)(PEHdrOffset+0x00) != 0x4550 )
  {
    err->Items->Add("ERR: invalid PE file");
    return 0;
  }
  imageBase=*(dword *)(PEHdrOffset+0x34);
  int entry_point=*(dword *)(PEHdrOffset+0x28)+imageBase;
  sectionsCount=*(word *)(PEHdrOffset+0x06);
  unsigned char *secDesc=PEHdrOffset+0xf8;
  // create sections
  sections = new section[sectionsCount];
  for( int i=0; i<sectionsCount; ++i, secDesc+=0x28 )
  {
    sections[i].size=*(dword *)(secDesc+0x08);
    sections[i].base=*(dword *)(secDesc+0x0c)+imageBase;
    sections[i].phys=*(dword *)(secDesc+0x14);
    sections[i].data = new unsigned char[sections[i].size];
    if( !sections[i].data )
    {
      err->Items->Add("ERR: Can't allocate memory for section mapping");
      return 0;
    }
    // copy section data
    unsigned char *from = (sections[i].phys+sourceFile);
    int size=*(dword *)(secDesc+0x10);
    memcpy(sections[i].data, from, size);
  }
  info->Items->Add("Sections count: "+AnsiString(sectionsCount));
  return entry_point;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
{
  cleanupSections();
  delete diffs;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormCreate(TObject *Sender)
{
  diffs=new TStringList();
  sections=NULL;
  nano_ccad=nano_jdst=NULL;
  nano_jtyp=nano_jsiz=NULL;
  nanoCount=0;
}
//---------------------------------------------------------------------------
void __fastcall TMain::OpenEPClick(TObject *Sender)
{
  OpenDialog->Filter="EP list files|*.txt|All files|*.*";
  OpenDialog->InitialDir=ExtractFilePath(ParamStr(0));
  if( OpenDialog->Execute() )
    EPName->Text=OpenDialog->FileName;
  else
    EPName->Text="";
}
//---------------------------------------------------------------------------
void __fastcall TMain::cleanupSections( void )
{
  // erase sections
  if( sections )
  {
    for( int i=0; i<sectionsCount; ++i )
      delete sections[i].data;
    delete sections;
  }
  sections=NULL;
  // clear stack
  while( !stack.empty() )
    stack.pop();
  // clear nano arrays
  if( nano_ccad )
    delete nano_ccad;
  if( nano_jtyp )
    delete nano_jtyp;
  if( nano_jsiz )
    delete nano_jsiz;
  if( nano_jdst )
    delete nano_jdst;
  nano_ccad=nano_jdst=NULL;
  nano_jtyp=nano_jsiz=NULL;
  nanoCount=0;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void __fastcall TDisasm::Execute(void)
{
  // дизасмим CSEG блоки, дополняя stack
  for(int i=0; i<Main->blocks.size(); ++i)
    disasmThread(Main->blocks[i].begin,true,Main->blocks[i].end);
  // основной цикл дизассемблера
  while( !Main->stack.empty() && !Terminated )
  {
    dword addr=Main->stack.top();
    Main->stack.pop();
    if( !searchMarked(addr) )
      disasmThread(addr);
  }
  SetEvent( hSem );
}
//---------------------------------------------------------------------------
__fastcall TDisasm::TDisasm(bool CreateSuspended) : TThread(CreateSuspended)
{
  hSem = OpenEvent(EVENT_MODIFY_STATE, FALSE, "DisasmSemaphore");
}
//---------------------------------------------------------------------------
__fastcall TDisasm::~TDisasm(void )
{
  // clear marks
  marks.clear();
  CloseHandle( hSem );
}
//---------------------------------------------------------------------------
bool __fastcall TDisasm::searchMarked(dword addr)
{
  for( int i=0; i<marks.size(); ++i )
    if( marks[i] == addr )
      return true;
  return false;
}
//---------------------------------------------------------------------------
void __fastcall TDisasm::disasmThread(dword addr, bool isBlock, dword endAddr)
{
  int max_size, secadr;
  unsigned char *phys_addr, *phaddr;
  // process disasm from addr till jmp/jcc/call/ret
  t_disasm da;
  t_asmmodel am;
  if( !isBlock )
    marks.push_back(addr);
  // calc addr -> phys_addr
  if( (phys_addr=Main->mapVirtualToPhys(addr)) == NULL )
  {
    if( Main->isWarn->State == cbChecked )
      AddErr("ERR: Can't map virtual address "+IntToHex((int)addr,8));
    return;
  }
  // calc max_size from phys_addr
  max_size=Main->restSectionLen(addr);
  bool process=true;
  ideal=1; lowercase=1; putdefseg=0;
  while( process )
  {
    int len=Disasm(phys_addr,max_size,addr,&da,DISASM_CODE);
    if( da.error == DAE_NOERR )
    {
      // check for INT3 instruction
      if( len == 1 && *phys_addr == 0xCC )
        if( Main->processNanomite(addr, phys_addr, MODE_DISASM) )
          continue;
      switch( da.cmdtype&C_TYPEMASK )
      {
        case C_JMP:
          process=false;
        case C_JMC:
        case C_CAL:
          if( da.jmpconst && !isBlock )
            Main->stack.push(da.jmpconst);
          else
            switch( da.memtype )
            {
              case DEC_DWORD:
                // try to get destination address
                if( (phaddr=Main->mapVirtualToPhys(da.adrconst)) == NULL )
                {
                  if( Main->isWarn->State == cbChecked )
                    AddInfo("WARN: indirect jump/jcc/call at "+IntToHex((int)addr,8)+": "+da.result+" -> can't map virtual address "+IntToHex((int)da.adrconst,8));
                }
                else
                {
                  secadr=*(dword *)phaddr;
                  if( Main->mapVirtualToPhys(secadr) == NULL )
                    if( Main->isWarn->State == cbChecked )
                      AddInfo("WARN: indirect jump/jcc/call at "+IntToHex((int)addr,8)+": "+da.result+" -> can't map virtual address "+IntToHex((int)secadr,8));
                  else
                  {
                    if( Main->isWarn->State == cbChecked )
                      AddInfo("WARN: indirect jump/jcc/call at "+IntToHex((int)addr,8)+": "+da.result+" -> trying at "+IntToHex((int)secadr,8));
                    if( !isBlock )
                      Main->stack.push(secadr);
                  }
                }
                break;
              default:
                if( Main->isWarn->State == cbChecked )
                  AddInfo("WARN: indirect jump/jcc/call at "+IntToHex((int)addr,8)+": "+da.result);
                break;
            }
          break;
        case C_RET:
          process=false;
          break;
      }
      // go to next instruction
      phys_addr+=len;
      addr+=len;
      // check for block disasm and code filling
      if( isBlock )
      {
        process=true;
        if( (da.cmdtype&C_TYPEMASK) == C_RET )
        {
          dword wasAddr=addr;
          // need to check code filling
          switch( *phys_addr )
          {
            case 0xcc:
              while( addr<endAddr && *phys_addr == 0xcc )
              { addr++; phys_addr++; }
              break;
            case 0x90:
              while( addr<endAddr && *phys_addr == 0x90 )
              { addr++; phys_addr++; }
              break;
          }
          if( addr>wasAddr && Main->isWarn->State == cbChecked )
            AddInfo("WARN: code filling from "+IntToHex((int)wasAddr,8)+" to "+IntToHex((int)addr,8)+" skipped");
        }
        if( addr > endAddr )
          process=false;
      }
    }
    else
    {
      AddErr("ERR: Can't disassembly at "+IntToHex((int)addr,8)+", error code "+IntToHex(da.error,2));
      process=false;
    }
  }
}
//---------------------------------------------------------------------------
void __fastcall TDisasm::AddInfo( AnsiString string )
{
  Main->info->Items->Add(string);
}
//---------------------------------------------------------------------------
void __fastcall TDisasm::AddErr( AnsiString string )
{
  Main->err->Items->Add(string);
}
//---------------------------------------------------------------------------
void __fastcall TMain::CancelClick(TObject *Sender)
{
  switch( Mode->ItemIndex )
  {
    case 0:
      if( disasm )
      {
        info->Items->Add("Terminating recover process...");
        disasm->Terminate();
      }
      else
        err->Items->Add("ERR: No recover process found");
      break;
    case 1:
      if( dbgProcess )
      {
        info->Items->Add("Terminating recover process...");
        TerminateProcess( dbgProcess, 0 );
      }
      else
        err->Items->Add("ERR: No recover process found");
      break;
  }
}
//---------------------------------------------------------------------------
void __fastcall TMain::AddThread(dword id, HANDLE hnd)
{
  thread th;
  th.id=id;
  th.hnd=hnd;
  threads.push_back(th);
}
//---------------------------------------------------------------------------
void __fastcall TMain::DelThread(dword id)
{
  for(int i=0; i<threads.size(); ++i)
    if( threads[i].id == id )
    {
      threads[i].id=0;
      threads[i].hnd=NULL;
      break;
    }
}
//---------------------------------------------------------------------------
HANDLE __fastcall TMain::GetThread(dword id)
{
  HANDLE hnd=NULL;
  for(int i=0; i<threads.size(); ++i)
    if( threads[i].id == id )
    {
      hnd=threads[i].hnd;
      break;
    }
  return hnd;
}
//---------------------------------------------------------------------------
#pragma warn +csu
//---------------------------------------------------------------------------

