Ease code injection

[Update: format the post and remove some nonsense]
It is always inconvenient to debug .NET Profiler within Visual Studio IDE. Why? Because Visual Studio is acting as a CLR host, which means if we setup environment variables, CLR hosted by Visual Studio will be profiled, instead of the application/debugee. As we all know, environment variables can be modified before the child procss is launched, otherwise, there’s no direct way to do it. In other words, we have to look for indirect way to achieve the task. So, here comes the code injection.
 
Usually, there are three ways to inject code into another process (search the key words "Three ways" with google, you will find it). I prefer the simplest one (combination of CreateRemoteThread and LoadLibrary)^^, actually it is one of the two ways that we can use to inject a console application. Another is using WriteProcessMemory to copy binary/machine code into target process. Since I am not so sophiscated with ASM, I gave it up^^ Here is the source code and usage, really easy?? hehe~~~
 
For Agent.dll, I will not attach source code here, because it is really easy, you should get it done in less than 5 minutes after looking up it in MSDN. The core work of it is just to modify the environment variable with SetEnvironmentVariables API.
 
  • Command Line: inject.exe <Target Process> <DLL to be injected>
  • For example: inject.exe notepad.exe Agent.dll 
  • Source Code (inject.exe):

#include "windows.h"

#include "tchar.h"

#include "TLHELP32.H"

#include "stddef.h"

#include "stdio.h"

#include "Shlwapi.h"

 

#pragma comment(lib, "shlwapi.lib")

 

DWORD FindTarget(LPCTSTR lpszProcess);

void Inject(DWORD dProcessId, LPCTSTR lpszDllName);

 

DWORD hLibModule = 0;

 

int _tmain(int argc, _TCHAR* argv[])

{

      if (argc != 3)

      {

           printf("Usage: Envedit.exe <process id> <dll to be injected>\n");

           return -1;

      }

 

      DWORD dProcessId = 0;

 

      dProcessId = FindTarget((LPCTSTR)argv[1]);

      if (dProcessId == 0)

      {

           printf("Cannot find process specified as the command line option.\n");

           return -1;

      }

 

      TCHAR szLibPath[MAX_PATH];

      GetCurrentDirectory(MAX_PATH, szLibPath);

      _tcscat(szLibPath, _T("\\"));

      _tcscat(szLibPath, (LPCTSTR)argv[2]);

 

      if (!PathFileExists(szLibPath))

      {

           printf("Cannot find .dll file specified as the command line option.\n");

           return -1;

      }

 

      Inject(dProcessId, szLibPath);

 

      return 0;

}

 

DWORD FindTarget(LPCTSTR lpszProcess)

{

      DWORD  dwRet     = 0;

      PROCESSENTRY32 pe32 = { sizeof( PROCESSENTRY32 ) };

      HANDLE hSnapshot = NULL;

 

      hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

 

      Process32First(hSnapshot, &pe32);

      do

      {

           if (0 == lstrcmpi(pe32.szExeFile, lpszProcess))

           {

                 dwRet = pe32.th32ProcessID;

                 break;

           }

      } while (Process32Next(hSnapshot, &pe32));

 

      CloseHandle(hSnapshot);

      return dwRet;

}

 

void Inject(DWORD dProcessId, LPCTSTR lpszDllName)

{

      HANDLE hProcess = NULL;

      HANDLE hThread = NULL;

      LPVOID lpRemoteAddress = NULL;

      BOOL bResult = FALSE;

 

      do

      {

           hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE,

                 FALSE, dProcessId);

           if (hProcess == NULL)

           {

                 break;

           }

 

           lpRemoteAddress = VirtualAllocEx(hProcess, NULL, _tcslen(lpszDllName) * sizeof(TCHAR),

                 MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);

           if (lpRemoteAddress == NULL)

           {

                 break;

           }

 

           bResult = WriteProcessMemory(hProcess, lpRemoteAddress, (LPVOID)lpszDllName, _tcslen(lpszDllName) * sizeof(TCHAR), NULL);

           if (bResult == FALSE)

           {

                 break;

           }

 

           LPTHREAD_START_ROUTINE pFun =

                 (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(_T("Kernel32.dll")), "LoadLibraryW");

 

           hThread = CreateRemoteThread(hProcess, NULL, 0, pFun, lpRemoteAddress, 0, NULL);

           if (hThread == NULL)

           {

                 break;

           }

 

           if (WAIT_FAILED == WaitForSingleObject(hThread, INFINITE))

           { 

                 break;

           }

 

           if (0 == GetExitCodeThread(hThread, &hLibModule))

           {

                 break;

           }

 

           CloseHandle(hThread);

 

           pFun = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(_T("Kernel32.dll")), "FreeLibrary" );

 

           hThread = CreateRemoteThread(hProcess, NULL, 0, pFun, lpRemoteAddress, 0, NULL);

           if (hThread == FALSE)

           {

                 break;

           }

 

           if (WAIT_FAILED == WaitForSingleObject(hThread, INFINITE))

           {

                 break;

           }

 

      } while (0);

 

      if (hThread != NULL)

      {

           CloseHandle(hThread);

      }

      if (hProcess != NULL)

      {

           CloseHandle(hProcess);

      }

      if (lpRemoteAddress != NULL)

      {

           VirtualFreeEx( hProcess, lpRemoteAddress, _tcslen(lpszDllName) * sizeof(TCHAR), MEM_RELEASE);

      }

}

Hope this can help…

Walk Native Callstack without Symbolic Info

As being taking charge of CER project, reviewing some basic knowledge of callstack should be useful. Here is a demo code used to walk callstack when an unhandled exception is raised. In fact, the demo code of StackWalk1 should be able to walk in a common case. It’s just difficult to obtain a thread-related CONTEXT. Win32 API GetThreadContext will only work as expected when the thread being inspected is suppended.
[principle]
I think the principle is supposed to be easy enough, just using ebp register, and traverse the list that ebp represents.
[Note]
The demo code is CPU specific. I am working on an X86 duel core CPU.
The demo code should be running with out FPO (Frame Pointer Omit) optimization.
[Shortcoming]
The callstack dumped out is shown as raw linear address.
 
#include "stdafx.h"
#include "windows.h"
LPTOP_LEVEL_EXCEPTION_FILTER previousFilter;
void StackWalk1(PCONTEXT pContext);
LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo);
int _tmain(int argc, _TCHAR* argv[])
{
 previousFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
 // cause a EXCEPTION_FLT_DIVIDE_BY_ZERO
 int a = 0;
 int b = 1 / a;
 return 0;
}
LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo)
{
 PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
 _tprintf(_T("Exception code: [%08x]\n"),
  pExceptionRecord->ExceptionCode);
 StackWalk1(pExceptionInfo->ContextRecord);
 if (previousFilter)
  return previousFilter(pExceptionInfo); // bypass to the user-installed handler
 else
  return EXCEPTION_CONTINUE_SEARCH; // transfer the control to operating system
}
//
// stack walker version 1, without symbol information
// should not be FPO optimization
//
void StackWalk1(PCONTEXT pContext)
{
 _tprintf(_T("Callstack:\n"));
 _tprintf(_T("Address     Frame     Module\n"));
 // instruction pointer (EIP) at the time of the exception
 DWORD pc = pContext->Eip;
 PDWORD pFrame, pPrevFrame;
 pFrame = (PDWORD)pContext->Ebp;
 do
 {
  TCHAR szModule[MAX_PATH] = { 0 };
  MEMORY_BASIC_INFORMATION mbi;
  VirtualQuery((PVOID)pc, &mbi, sizeof(mbi));
  GetModuleFileName((HMODULE)mbi.AllocationBase, szModule, sizeof(szModule));
  _tprintf(_T("%08x    %08x    %s\n"), pc, pFrame, szModule);
  // push ebp
  // mov ebp, esp, looking at the following snapshot of a stack
  // [0x…]return address   pContext->Ebp[1]
  // [0x…]ebp                 pContext->Ebp[0]

  pc = pFrame[1];
  pPrevFrame = pFrame;
  pFrame = (PDWORD)pFrame[0];
  // check if the ebp is still valid, otherwise stop walking
  if ((DWORD)pFrame & 3)
   break;
  if (pFrame <= pPrevFrame)
   break;
  // check if the address pointed to by ebp is committed
  if (IsBadWritePtr(pFrame, sizeof(PVOID) * 2))
   break;
 } while (1);
 // when to stop the walking?
 // when it encounters a frame address that doesn’t look valid
}
[Result/Output]
Exception code: [c0000094]
Callstack:
Address     Frame     Module
00bb1414    0016f8a0    d:\Visual Studio 2008\VectoredExceptDemo\Debug\MyStackwalker.exe
00bb25b8    0016f8f0    d:\Visual Studio 2008\VectoredExceptDemo\Debug\MyStackwalker.exe
00bb23ff    0016f8f8    d:\Visual Studio 2008\VectoredExceptDemo\Debug\MyStackwalker.exe
76d84911    0016f904    C:\Windows\system32\kernel32.dll
76e5e4b6    0016f944    C:\Windows\system32\ntdll.dll
76e5e489    0016f95c    C:\Windows\system32\ntdll.dll

ReadDirectoryChangesW

Just find a attractive API unexpectedly, ReadDirectoryChangesW can be used to watch a directory. Whenever there occurs an operation on/inside it, we can get notified with information of the operation which file/subdirectory is on, and even their names. Someone on the CodeProject has already attached a wrapped library to do the same thing, with calling ReadDirectoryChangesW under the hood. 🙂

After survey the MSDN, I also found another 3 API group used to watch a directory: FindFirstChangeNotification, FindNextChangeNotification, and FindCloseChangeNotification. The shorti coming of these 3 APIs is that they seem not be able to get the enough information on what happened there.

Below is the simplest demo code used to watch a directory, I use it in a synchronous way. (Some advanced usage of it will involve IO Completion Routine, but since it is too small here, I don’t think IO Completion is suitable for my demo code here).

#include "stdafx.h"
#include "windows.h"

void DumpChangesInfo(const PFILE_NOTIFY_INFORMATION fni);
void StartWatchDirectory(const TCHAR * strDirectoryName);

int _tmain(int argc, _TCHAR* argv[])
{
 if (argc != 2)
  return 0;
 StartWatchDirectory(argv[1]);
 return 0;
}

void StartWatchDirectory(const TCHAR * strDirectoryName)
{
 HANDLE hDirectory;

 hDirectory = CreateFile(
  strDirectoryName,               // Directory name/path
  FILE_LIST_DIRECTORY | FILE_TRAVERSE | GENERIC_READ, // Desired Access
  FILE_SHARE_WRITE | FILE_SHARE_READ,       // File shared mode
  NULL,
  OPEN_EXISTING,
  FILE_FLAG_BACKUP_SEMANTICS,          // Flags and Attributes
  NULL
  );

 if (hDirectory == INVALID_HANDLE_VALUE)
 {
  printf("Error Code[%d]: Open Directory Failed…\n", GetLastError());
  return;
 }

 BYTE buffer[1024] = { 0 }; // hardcode the length of the buffer, ReadDirectoryChangesW may return 0 in dwBytesRet.

 BOOL bResult = FALSE;
 FILE_NOTIFY_INFORMATION * fni = NULL;
 DWORD dwBufferLen = 0;
 DWORD dwBytesRet = 0;
 DWORD dwNotifyFilter =
  FILE_NOTIFY_CHANGE_FILE_NAME |
  FILE_NOTIFY_CHANGE_DIR_NAME |
  FILE_NOTIFY_CHANGE_ATTRIBUTES |
  FILE_NOTIFY_CHANGE_SIZE  |
  FILE_NOTIFY_CHANGE_LAST_WRITE |
  FILE_NOTIFY_CHANGE_LAST_ACCESS |
  FILE_NOTIFY_CHANGE_CREATION |
  FILE_NOTIFY_CHANGE_SECURITY; // Notify for every operations

 printf("Directory Changes is starting…\n");

 do
 {
  ZeroMemory(&buffer, 1024);
  bResult = ReadDirectoryChangesW(
   hDirectory,     // Directory handle
   (LPVOID)&buffer,   // Buffer
   1024,       // Length of the buffer
   TRUE,       // If watch sub directory
   dwNotifyFilter,    // NotifyFilter
   &dwBytesRet,    // Number of Byte returned
   NULL,       // OVERLAPPED structure
   NULL       // IO Completion Routine
   );

  if (bResult == 0)
  {
   printf("Error Code[%d]: Read Directory Changes Failed…\n", GetLastError());
   break;
  }
  fni = (PFILE_NOTIFY_INFORMATION)&buffer;
  DumpChangesInfo(fni);
 } while(bResult);

 printf("Directory Watcher is shutting down…\n");
 CloseHandle(hDirectory);
}

void DumpChangesInfo(const PFILE_NOTIFY_INFORMATION fni)
{
 // FileNameLength returned in PFILE_NOTIFY_INFORMATION is a WCHAR * without ‘0\’ as its termination.
 WCHAR * strFileName = (WCHAR * )malloc(fni->FileNameLength + sizeof(WCHAR));
 wcscpy(strFileName, fni->FileName);
 strFileName[fni->FileNameLength/sizeof(WCHAR)] = _T(”);

 TCHAR strAction[20];
 memset(&strAction, ”, 20 * sizeof(TCHAR));
 switch (fni->Action)
 {
 case FILE_ACTION_ADDED:
  _tcscpy(strAction, _T("ADD"));
  break;
 case FILE_ACTION_REMOVED:
  _tcscpy(strAction, _T("REMOVED"));
  break;
 case FILE_ACTION_MODIFIED:
  _tcscpy(strAction, _T("MODIFIED"));
  break;
 case FILE_ACTION_RENAMED_OLD_NAME:
  _tcscpy(strAction, _T("RENAMED"));
  break;
 case FILE_ACTION_RENAMED_NEW_NAME:
  _tcscpy(strAction, _T("RENAMED"));
  break;
 }

 _tprintf(_T("\tFile/Directory:\t%s -> %s\n"), strFileName, strAction);
 free(strFileName);
}

[Update] Just a minute ago,  I found another guy posted a more object-oriented sample code >”<
http://blog.vckbase.com/bruceteen/articles/229.html

Dump Imported Table of an Image

// PEDemo.cpp : Defines the entry point for the console application.
// I think this is a good starting to learn PE. Src attached here is with security check code stripped out.
// It is the simpliest way to dump out an imported table of a .Dll or .EXE.
#include "stdafx.h"
#include "windows.h"
#include "Dbghelp.h"
 
#pragma comment(lib, "Dbghelp.lib")
 
DWORD RVA2Offset(PIMAGE_NT_HEADERS pNTHeader, LPVOID pFileBase, DWORD dwRVA);
int _tmain(int argc, _TCHAR* argv[])
{
 PIMAGE_DOS_HEADER pDosHeader;
 PIMAGE_NT_HEADERS pNTHeader;
 PIMAGE_FILE_HEADER pFileHeader;
 PIMAGE_OPTIONAL_HEADER pOptionalHeader;
 
 TCHAR * filename = _T("C:\\WINDOWS\\notepad.exe");
 
 HANDLE hFile = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
 HANDLE hMappingFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
 LPVOID pFileBase = MapViewOfFile(hMappingFile, FILE_MAP_READ, 0, 0, 0);
 
 pDosHeader = (PIMAGE_DOS_HEADER)pFileBase;
 pNTHeader = (PIMAGE_NT_HEADERS)((BYTE*)pFileBase + pDosHeader->e_lfanew); // e_lfanew is a File Address.
 pFileHeader = &pNTHeader->FileHeader;
 pOptionalHeader = &pNTHeader->OptionalHeader;
 
 // Need to convert RVA to File Offset first
 DWORD dwRVA = pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
 DWORD dwOffset = RVA2Offset(pNTHeader, pFileBase, dwRVA);
 PIMAGE_IMPORT_DESCRIPTOR pDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE*) pFileBase + dwOffset);
 
 while (pDescriptor->OriginalFirstThunk != 0)
 {
  char * dllname = (char*)((BYTE*)pFileBase + RVA2Offset(pNTHeader, pFileBase, pDescriptor->Name));
  printf("Dll Name: %s\n", dllname);
  PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((BYTE*)pFileBase + RVA2Offset(pNTHeader, pFileBase, pDescriptor->OriginalFirstThunk));
  while (pThunk->u1.Function)
  {
   // u1.AddressOfData is the RVA of IMAGE_IMPORT_BY_NAME
   char * functionname = (char*)((BYTE*)pFileBase + RVA2Offset(pNTHeader, pFileBase, (DWORD)pThunk->u1.AddressOfData + 2));
   BYTE * functionordinal = (BYTE*)((BYTE*)pFileBase + RVA2Offset(pNTHeader, pFileBase, (DWORD)pThunk->u1.AddressOfData));
   printf("\t%X\t%s\n", *functionordinal, functionname);
   pThunk++;
  }
  pDescriptor++;
 }
 UnmapViewOfFile(pFileBase);
 CloseHandle(hMappingFile);
 CloseHandle(hFile);
 return 0;
}
DWORD RVA2Offset(PIMAGE_NT_HEADERS pNTHeader, LPVOID pFileBase, DWORD dwRVA)
{
 DWORD dwOffset;
 // Need to convert RVA to File Offset first
 PIMAGE_SECTION_HEADER pSectionHeader = ImageRvaToSection(pNTHeader, pFileBase, dwRVA);
 dwOffset = dwRVA + pSectionHeader->PointerToRawData – pSectionHeader->VirtualAddress;
 return dwOffset;
}

Dos Stub in PE

0x003D0000  4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00  MZ…………. // MZ, Mark Zbikowski
0x003D000F  00 b8 00 00 00 00 00 00 00 40 00 00 00 00 00  ………@…..
0x003D001E  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ……………
0x003D002D  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ……………
0x003D003C  e0 00 00 00 0e 1f ba 0e 00 b4 09 cd 21 b8 01  …………!..
0x003D004B  4c cd 21 54 68 69 73 20 70 72 6f 67 72 61 6d  L.!This program
0x003D005A  20 63 61 6e 6e 6f 74 20 62 65 20 72 75 6e 20   cannot be run
0x003D0069  69 6e 20 44 4f 53 20 6d 6f 64 65 2e 0d 0d 0a  in DOS mode….
0x003D0078  24 00 00 00 00 00 00 00 ec 85 5b a1 a8 e4 35  $

The first 40h bytes of the PE Image contains IMAGE_DOS_HEADER, and then Dos Stub Program follows immediately. Try to launch whatever program with DEBUG in cmd. Step through with P command, "This program cannot be run in DOS mode" will be shown.

In Assembly, the accordant instrument is as below:

PUSH    CS              // Push Code Segment onto the stack
POP      DS              // Pop off the Code Segment and save it in DS
MOV     DX,000E       // DX, set the string that will be displayed with INT 21, with function 09
MOV     AH,09
INT      21
MOV    AX,4C01        // Exit the program
INT      21