/*

  Debugguer by JoE xD
  http://joe-is-a-rocknroll-star.blogspot.com/

 */

#define _WIN32_WINNT 0x0502
#define BUFSIZE 512

#include <windows.h>
#include <winbase.h>
#include <stdio.h>
#include <winnt.h>
#include <Psapi.h>
#include <string.h>

// manage errors
#define CHECK_ERROR_ZERO(a,s) if(!a){printf("> Error with code 0x%08x : %s",GetLastError(),s);return 0;}

// functions
int LaunchDebugLoop(void);
int CreateTheFuckingProcess(char * execToDebug,int typeOfDebug);
DWORD getTheAddressOfEP(char * nameOfPEFile);
int getTheNameOfTheFile(HANDLE * file);
int SetDebugPrivilege(void);
int hookNtQueryProcessInformation(void);

// beurk
HANDLE debuggedProcessHandle;
HANDLE debuggedProcessHandleMainThread;

int debuggedProcessID;
int debuggedMode;

// hook of NtQuerySystemInformation
char jmpOnTheHook[]={0xB8,0xFE,0xFE,0xFA,0xFA, // mov eax,0xFAFAFEFE
		     0xFF,0xE0,0x90,0x90,0x5B};// jmp eax + pop ebx

char hook[]={0x83,0x7C,0x24,0x08,0x07, // cmp dword ptr ss:[esp+8],7
	     0x75,0x26,// jnz short +e
	     0xB8,0x00,0x00,0x00,0x00, // mov eax, 0
	     0x8B,0x54,0x24,0x0C, // mov edx,dword ptr ss:[esp+c]
	     0x36,0xC7,0x02,0x00,0x00,0x00,0x00,// mov dword ptr ss:[edx],0
	     0x3E,0x83,0x7C,0x24,0x14,0x00, // cmp dword ptr ds:[esp+14],0
	     0x74,0x0B,// je short +b
	     0x8B,0x54,0x24,0x14,// mov edx,dword ptr ss:[esp+14]
	     0x36,0xC7,0x02,0x04,0x00,0x00,0x00,// mov dword ptr ss:[edx],4
	     0xC2,0x14,0x00,// retn 14
	     0xB8,0x9A,0x00,0x00,0x00, // mov eax,9A
	     0xBA,0x00,0x03,0xFE,0x7F, // mov edx,7FFE0300
	     0x53, // push ebx
	     0xBB,0xE9,0xD7,0x91,0x7C, // mov ebx,ntdll.7C91D7E9
	     0xFF,0xE3}; //jmp ebx


int main(int argc, char * argv[])
{

  DWORD addressOfEP;
  DWORD oldProtection;
  BYTE infiniteLoop[] = {0xEB, 0xFE};
  BYTE originalOpcodesEP[2];
  CONTEXT context;
  DWORD myRights;

  // Usefull stuff to own anti-debug functions
  HANDLE remoteThread;
  BYTE * remoteAddressOfPEB;
  PVOID (*addressOfRtlGetCurrentPeb)();
  DWORD targetTID;
  BYTE isDebugged[1];
 
  ZeroMemory(isDebugged,1);

  if(argc!=3)
    {
      printf("Use of the program : %s typeOfDebug executableToDebug\n",argv[0]);
      printf("Where the typeOfDebug is 1 for intrusiv debug and 0 for non intrusiv debug\n");
      return 1;
    }
  
  debuggedMode=atoi(argv[1]);
  
  printf("\n------------------------------\n");
  printf("----|  Debugg3r by JoE   |----\n");
  ((debuggedMode)?printf("----|  (intrusiv mode)   |----\n"):printf("----|(non-intrusiv mode) |----\n"));
  printf("------------------------------\n\n");
  
  // Give me power to open handles on all process :-)  
  if(SetDebugPrivilege())
    {
      printf("Error with SetDebugPrivilege()\n");
      return 1;
    }
  
  if(debuggedMode)
    {
      // Intrusive mode : just create the process with the good argument...
      CHECK_ERROR_ZERO(CreateTheFuckingProcess(argv[2],debuggedMode),"CreateTheFuckingProcess()");
    }
  else
    { // Non-intrusiv mode
      
      // 1- First step : creation of the process in SUSPENDED mode
      
      CHECK_ERROR_ZERO(CreateTheFuckingProcess(argv[2],debuggedMode),"CreateTheFuckingProcess()");
      
      // 2- Second step : change the opcodes at the entry point (EP)
      
      addressOfEP=getTheAddressOfEP(argv[2]);
      
      // 2-1 Desactivate the memory protection at the EP
      
      CHECK_ERROR_ZERO(VirtualProtectEx(debuggedProcessHandle,(LPVOID)addressOfEP,(DWORD)2,PAGE_EXECUTE_READWRITE,&oldProtection),"VirtualProtectEx");
      
      // 2-2 Save the original opcodes at EP
      
      CHECK_ERROR_ZERO(ReadProcessMemory(debuggedProcessHandle,(LPCVOID)addressOfEP,originalOpcodesEP,2,NULL),"ReadProcessMemory");
      
      // 2-3 Write the infinite loop
      
      CHECK_ERROR_ZERO(WriteProcessMemory(debuggedProcessHandle,(LPVOID)addressOfEP,(LPVOID)infiniteLoop,2,NULL),"WriteProcessMemory");
      FlushInstructionCache(debuggedProcessHandle,(LPVOID)addressOfEP,2);
      
      // 3- Wait until the thread reaches the EP
      if(ResumeThread(debuggedProcessHandleMainThread)==0xFFFFFFFF)
	{
	  printf("Error %d when resuming the thread\n",GetLastError());
	  return 1;
	}
      
      
      ZeroMemory(&context,sizeof(CONTEXT));
      context.ContextFlags=CONTEXT_FULL;
      
      do{
	CHECK_ERROR_ZERO(GetThreadContext(debuggedProcessHandleMainThread,&context),"GetThreadContext");
      }while(context.Eip!=addressOfEP);
      
      //
      // 4-2 Time to own anti-debug functions :-)
      //

      // 4-2-1 Get the @ of PEB
      if((addressOfRtlGetCurrentPeb=(PVOID)GetProcAddress(GetModuleHandle("ntdll"),"RtlGetCurrentPeb"))==NULL)
	{
	  printf("Error %d with GetProcAddress\n",GetLastError());
	  return 1;
	}
      
      
      if((remoteThread=CreateRemoteThread(debuggedProcessHandle,NULL,0,(PVOID)addressOfRtlGetCurrentPeb,NULL,0,&targetTID))==NULL)
	{
	  printf("Error %d with CreateRemoteThread\n",GetLastError());
	  return 1;
	}
      
      // wait the terminaison of the thread and get the remoteAddressOfPEB
      WaitForSingleObject(remoteThread,INFINITE);
      GetExitCodeThread(remoteThread,(LPDWORD)&remoteAddressOfPEB);

      // rewrite the code of DbgUiRemoteBreakIn()
      
      // hook xD
      if(hookNtQueryProcessInformation())
	{
	  printf("Error %d during hookNtQueryProcessInformation\n",GetLastError());
	  return 1;
	}

      // attach to the process (set flags, create thread on DbgUiRemoteBreakIn()...)
      CHECK_ERROR_ZERO(DebugActiveProcess(debuggedProcessID),"DebugActiveProcess");

      // change the peb :)
      CHECK_ERROR_ZERO(WriteProcessMemory(debuggedProcessHandle,remoteAddressOfPEB+0x2,isDebugged,1,NULL),"WriteProcessMemory");

    
      // 5-1 Reload the initial opcodes at the EP (launch the target process)
      CHECK_ERROR_ZERO(WriteProcessMemory(debuggedProcessHandle,(LPVOID)addressOfEP,(LPVOID)originalOpcodesEP,2,NULL),"WriteProcessMemory");
      FlushInstructionCache(debuggedProcessHandle,(LPVOID)addressOfEP,2);
      
      // 5-2 Restore the old rights
      CHECK_ERROR_ZERO(VirtualProtectEx(debuggedProcessHandle,(LPVOID)addressOfEP,(DWORD)2, oldProtection,&myRights),"VirtualProtectEx");
      
    }
  
  // the debug loop
  CHECK_ERROR_ZERO(LaunchDebugLoop(),"LaunchDebugLoop()");
  
  return 0;
}

int hookNtQueryProcessInformation(void)
{

  HANDLE targetHandle;
  void * addressToWriteTheJumpOnTheHook;
  void * addressToWriteTheHook;

  // to avoid right problems :-)
  if((targetHandle=OpenProcess(PROCESS_ALL_ACCESS,TRUE,debuggedProcessID))==NULL)
    {
      printf("Error %d with OpenProcess()\n",GetLastError());
      return 1;
    }
  
 
  
  // reserve the memory
  if((addressToWriteTheHook=VirtualAllocEx(targetHandle,NULL,sizeof(hook),MEM_RESERVE|MEM_COMMIT,PAGE_EXECUTE_READWRITE))==NULL)
    {
      printf("Error %d with VirtualAllocEx()\n",GetLastError());
      return 1;
    }
  
  // write the hook
  if(!WriteProcessMemory(targetHandle,(void *)addressToWriteTheHook,(void *)hook,sizeof(hook),NULL))
    {
      printf("Error %d with WriteProcessMemory()\n",GetLastError());
      return 1;
    }
  FlushInstructionCache(targetHandle,(LPVOID)addressToWriteTheHook,sizeof(hook));

  
  // patch the loader
  if(!WriteProcessMemory(GetCurrentProcess(),(void *)(jmpOnTheHook+0x1),&addressToWriteTheHook,4,NULL))
    {
      printf("Error %d with WriteProcessMemory()\n",GetLastError());
      return 1;
    }

 

  
  if((addressToWriteTheJumpOnTheHook=(PVOID)GetProcAddress(GetModuleHandle("ntdll"),"ZwQueryInformationProcess"))==NULL)
    {
      printf("Error %d with GetProcAddress\n",GetLastError());
      return 1;
    }
  
  // write the jmp
  if(!WriteProcessMemory(targetHandle,addressToWriteTheJumpOnTheHook,(void *)jmpOnTheHook,sizeof(jmpOnTheHook),NULL))
    {
      printf("Error %d with WriteProcessMemory()\n",GetLastError());
      return 1;
    }
  FlushInstructionCache(targetHandle,(LPVOID)addressToWriteTheJumpOnTheHook,sizeof(jmpOnTheHook));
  
  return 0;

}



int SetDebugPrivilege(void)
{

  // Usefull structs :

  /*typedef struct _TOKEN_PRIVILEGES {
    DWORD PrivilegeCount;
    LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
    } TOKEN_PRIVILEGES; 
    
    typedef struct _LUID_AND_ATTRIBUTES {
    LUID Luid;
    DWORD Attributes;
    } LUID_AND_ATTRIBUTES;*/

    TOKEN_PRIVILEGES privilege;
    HANDLE currentProcess;
    HANDLE tokenProc;

    if((currentProcess=OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId()))==NULL)
      {
	printf("Error %d with OpenProcess\n",GetLastError());
	return 1;
      }

    // 1- open the access token associated with the process
    CHECK_ERROR_ZERO(OpenProcessToken(currentProcess, TOKEN_ALL_ACCESS, &tokenProc),"OpenProcessToken");

    // 2- fill the TOKEN_PRIVILEGES struct

    // 2-1 retrieve the locally unique identifier (LUID) used on a specified system to locally represent the specified privilege name
    CHECK_ERROR_ZERO(LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &privilege.Privileges[0].Luid),"LookupPrivilegeValue");

    // 2-2 other fields
    privilege.PrivilegeCount = 1;
    privilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    // 3- give me power :p
    CHECK_ERROR_ZERO(AdjustTokenPrivileges(tokenProc, FALSE, &privilege, 0, NULL, NULL),"AdjustTokenPrivileges");

    CloseHandle(tokenProc);
    CloseHandle(currentProcess);
    
    return 0;
}


DWORD getTheAddressOfEP(char * nameOfPEFile)
{
  //i need to get the RVA of EP and the base address in the PE header to get the VA of EP (=RVA+base)
  
  DWORD rvaOfEP;
  DWORD baseAddress;
  DWORD offsetToPEHeader;
  LPVOID addressOfMappedFile;
  
  HANDLE peFile;
  HANDLE peFileMapped;
  
  if((peFile=CreateFile((LPCTSTR)nameOfPEFile,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL))==INVALID_HANDLE_VALUE)
    {
      printf("Error %d when opening the file...\n", GetLastError());
      return 1;
    }
  
  if((peFileMapped=CreateFileMapping(peFile,NULL,PAGE_READONLY,0,0,NULL))==NULL)
    {
      printf("Error %d when creating the file mapping...\n", GetLastError());
      CloseHandle(peFile);
      return 1;
    }
  
  if((addressOfMappedFile=MapViewOfFile(peFileMapped,FILE_MAP_READ,0,0,0))==NULL)
    {
      printf("Error %d when mapping the file...\n", GetLastError());
      CloseHandle(peFile);
      CloseHandle(peFileMapped);
      return 1;
    }
  
  // Use the structure of the PE file to get what you want :-)
  offsetToPEHeader=*((LPDWORD)((LPSTR)addressOfMappedFile + 0x0000003C));
  
  rvaOfEP=*((LPDWORD)((LPSTR)addressOfMappedFile+offsetToPEHeader+0x00000028));
  
  baseAddress=*((LPDWORD)((LPSTR)addressOfMappedFile+offsetToPEHeader+0x00000034));
  
  CHECK_ERROR_ZERO(UnmapViewOfFile(addressOfMappedFile),"UnmapViewOfFile");
  
  CloseHandle(peFileMapped);
  CloseHandle(peFile);
  
  return (baseAddress+rvaOfEP);
  
}





int CreateTheFuckingProcess(char * execToDebug, int typeOfDebug)
{

  STARTUPINFO si;
  PROCESS_INFORMATION pi;
  ZeroMemory(&si, sizeof(si));
  si.cb = sizeof(si);
  ZeroMemory(&pi, sizeof(pi));

  if(typeOfDebug)
    {
      CHECK_ERROR_ZERO(CreateProcess((LPCTSTR)execToDebug,NULL,NULL,NULL,FALSE,DEBUG_PROCESS,NULL,NULL,&si,&pi),"CreateProcess() debug mode");
    }
  else {
    CHECK_ERROR_ZERO(CreateProcess((LPCTSTR)execToDebug,NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi),"CreateProcess() normal mode");
  }
  
  debuggedProcessHandle=pi.hProcess;
  debuggedProcessHandleMainThread=pi.hThread;
  debuggedProcessID=pi.dwProcessId;

  return 1;
  
}

int LaunchDebugLoop(void)
{
  DEBUG_EVENT dbgEvent;
  char unicodeBuffer[512];
  char ansiBuffer[256];
  int address;
  int firstException=0;
  
  char fileName[513];
  void * addressDll;
  HANDLE mappedDll;
  
  while(1)
    {
      
      address=0;
      ZeroMemory(unicodeBuffer,100);
      ZeroMemory(ansiBuffer,100);
      
      CHECK_ERROR_ZERO(WaitForDebugEvent(&dbgEvent, INFINITE),"WaitForDebugEvent");
      
      switch (dbgEvent.dwDebugEventCode)
	{
	case EXCEPTION_DEBUG_EVENT:
	  
	  printf("> EXCEPTION :");
	  
	  switch(dbgEvent.u.Exception.ExceptionRecord.ExceptionCode)
	    {
	    case EXCEPTION_ACCESS_VIOLATION:
	      printf(" ACCESS VIOLATION\n");
	      break;
	      
	    case EXCEPTION_BREAKPOINT:
	      printf(" BREAKPOINT");
	      // commented because when you modify the PEB!IsDebugged, there is no more first breakpoint exception
	      /*if(!firstException)
		{
		  printf(" (normal in debug mode)");
		  firstException=1;
		  }*/
	      printf("\n\n");
	      break;
	      
	    case EXCEPTION_SINGLE_STEP:
	      printf(" SINGLE STEP\n");
	      break;
	      
	    default:
	      printf(" UNKNOWED\n");
	      break;
            }
	  break;
	  
	case CREATE_THREAD_DEBUG_EVENT:
	  printf("> CREATE THREAD EVENT\n");
	  
	  printf("\n");
	  break;
	  
	case CREATE_PROCESS_DEBUG_EVENT:
	  printf("> CREATE PROCESS EVENT\n");
	  printf("  Base address : 0x%08x\n",dbgEvent.u.CreateProcessInfo.lpBaseOfImage);
	  printf("  TEB : 0x%08x\n",dbgEvent.u.CreateProcessInfo.lpThreadLocalBase);
	  //lpImageName can be null
	  if(dbgEvent.u.CreateProcessInfo.lpImageName) //USELESS -> "Windows does not provide an image name for a create process event"
	    {
	      printf("  ppImageName  : 0x%08x\n",dbgEvent.u.CreateProcessInfo.lpImageName);
	      CHECK_ERROR_ZERO(ReadProcessMemory(debuggedProcessHandle,dbgEvent.u.CreateProcessInfo.lpImageName,&address,4,NULL),"ReadProcessMemory");
	      //address can be null
	      if(address)
		{
		  printf("  pImageName   : 0x%08x\n",address);
		  CHECK_ERROR_ZERO(ReadProcessMemory(debuggedProcessHandle,(void*)address,unicodeBuffer,512,NULL),"ReadProcessMemory");
		  //convert the unicode buffer if needed
		  if(dbgEvent.u.CreateProcessInfo.fUnicode)
		    {
		      WideCharToMultiByte(CP_ACP,0,(LPCWSTR)unicodeBuffer,-1,ansiBuffer,512,NULL,NULL);
		      printf("  Module name  : %s\n",ansiBuffer);
		    }
		  else printf("  Module name  : %s\n",unicodeBuffer);
		}
	      else printf("  pImageName   : unknowed\n  Module name  : unknowed\n");
	    }
	  else printf("  ppImageName  : unknowed\n  pImageName   : unknowed\n  Module name  : unknowed\n");
	  printf("\n");

	  CloseHandle(dbgEvent.u.CreateProcessInfo.hFile);
	  CloseHandle(dbgEvent.u.CreateProcessInfo.hProcess);
	  CloseHandle(dbgEvent.u.CreateProcessInfo.hThread);

	  break;

	case EXIT_THREAD_DEBUG_EVENT:
	  printf("> EXIT THREAD EVENT\n");
	  printf("\n");
	  break;

	case EXIT_PROCESS_DEBUG_EVENT:
	  printf("> EXIT PROCESS EVENT\n");
	  CloseHandle(debuggedProcessHandle);
	  CloseHandle(debuggedProcessHandleMainThread);
	  return 1;
	  break;

	case LOAD_DLL_DEBUG_EVENT:
	  printf("> LOAD DLL DEBUG EVENT\n");
	  printf("  Base address : 0x%08x\n",dbgEvent.u.LoadDll.lpBaseOfDll);

	  //lpImageName can be null
	  if(dbgEvent.u.LoadDll.lpImageName)
	    {
	      printf("  ppImageName  : 0x%08x\n",dbgEvent.u.LoadDll.lpImageName);
	      CHECK_ERROR_ZERO(ReadProcessMemory(debuggedProcessHandle,dbgEvent.u.LoadDll.lpImageName,&address,4,NULL),"ReadProcessMemory");
	      //address can be null
	      if(address)
		{
		  printf("  pImageName   : 0x%08x\n",address);
		  CHECK_ERROR_ZERO(ReadProcessMemory(debuggedProcessHandle,(LPCVOID)address,unicodeBuffer,512,NULL),"ReadProcessMemory");
		  //convert the unicode buffer if needed
		  if(dbgEvent.u.LoadDll.fUnicode)
		    {
		      WideCharToMultiByte(CP_ACP,0,(LPCWSTR)unicodeBuffer,-1,ansiBuffer,512,NULL,NULL);
		      printf("  Module name  : %s\n",ansiBuffer);
		    }
		  else
		    {
		      printf("  Module name  : %s\n",unicodeBuffer);

		    }
		}
	      else
		{
		  printf("  pImageName   : unknowed\n");
		  if(getTheNameOfTheFile(dbgEvent.u.LoadDll.hFile))
		    {
		      printf("Error during getTheNameOfTheFile()\n");
		      return 0;
		    }
		      
		}
	    }
	  else
	    {
	      printf("  ppImageName  : unknowed\n  pImageName   : unknowed\n  Module name  : unknowed\n");
	    }
	  printf("\n");
	  CloseHandle(dbgEvent.u.LoadDll.hFile);
	  break;
	  
	case UNLOAD_DLL_DEBUG_EVENT:
	  printf("> UNLOAD DLL EVENT\n");
	  break;
	  
	case OUTPUT_DEBUG_STRING_EVENT:
	  //printf("> OUTPUT DEBUG STRING EVENT\n");
	  break;

	case RIP_EVENT:
	  printf("> RIP EVENT\n");
	  break;

	}

      if(debuggedMode)
	ContinueDebugEvent(dbgEvent.dwProcessId,
			   dbgEvent.dwThreadId,
			   DBG_CONTINUE);
      else
	ContinueDebugEvent(dbgEvent.dwProcessId,
			   dbgEvent.dwThreadId,
			   DBG_EXCEPTION_NOT_HANDLED); // we let the target manage her exception
     
    }


  return 1;
}


int getTheNameOfTheFile(HANDLE * file)
{
  // with the handle of the file, get the name of the file using the technique describes here : 
  // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx

  char fileName[512];
  void * addressDll;
  HANDLE mappedDll;
  HANDLE testFile;
  char driveStrings[512];
  char queryBuffer[512];
  char * p=NULL;
  char driveLetter[3];
  int countInDriveStrings=0;
  int countSlashes=0;
  char hardFileName[512];
  int lengthHardName=0;
  char finalHardPath[512];
  char finalBuffer[512];

  ZeroMemory(fileName,512);
  ZeroMemory(driveStrings,512);
  ZeroMemory(queryBuffer,512);
  ZeroMemory(hardFileName,512);
  ZeroMemory(finalBuffer,512);
  ZeroMemory(finalHardPath,512);
  
  if((mappedDll=CreateFileMapping(file,NULL,PAGE_READONLY,0,1,NULL))==NULL)
    {
      printf("Error %d in CreateFileMapping\n",(int)GetLastError());
      return 1;
    }
  
  
  if((addressDll=MapViewOfFile(mappedDll,FILE_MAP_READ,0,0,1))==NULL)
    {
      printf("Error %d in MapViewOfFile\n",(int)GetLastError());
      return 1;
    }
  
   CHECK_ERROR_ZERO(GetMappedFileName(GetCurrentProcess(),addressDll,fileName,512),"GetMappedFileName");

   // we have a hardware path (\Device\HarddiskVolume1\...), and now we want to replace him by the logical device (C:\...)
   // by using the functions GetLogicalDriveStrings and QueryDosDevice

   p=fileName;
   while(countSlashes!=3)
     {
       if(*p=='\\')
	 countSlashes++;
       lengthHardName++;
       p++;
     }
   memcpy(hardFileName,fileName,lengthHardName-1);
   strcpy(finalHardPath,fileName+lengthHardName-1);
   
   CHECK_ERROR_ZERO(GetLogicalDriveStrings(511,driveStrings),"GetLogicalDriveStrings");

   p=driveStrings;
   
   while(*p!='\0')
     {
       ZeroMemory(driveLetter,3);
       memcpy(driveLetter,p,2);
       CHECK_ERROR_ZERO(QueryDosDevice(driveLetter,queryBuffer,512),"QueryDosDevice");
       
       if(strcmp(hardFileName,queryBuffer)==0)
	 {
	   memcpy(finalBuffer,driveLetter,2);
	   strncat(finalBuffer,finalHardPath,strlen(finalHardPath));
	   printf("  Module name  : %s\n",finalBuffer);
	   return 0;
	 }
       p+=4;
     }
   return 1;
}

