NotepadExec – Using notepad.exe to launch an EXE without code injection

渗透技巧 2年前 (2022) admin
910 0 0

This code demonstrates how to programmatically launch another executable file from within notepad.exe using window messages (SendMessage / PostMessage). I can’t think of any real use for this tool – this is just a quick proof-of-concept for fun.

NotepadExec - Using notepad.exe to launch an EXE without code injection

This works as follows:

1. Use CreateProcess to launch a hidden notepad.exe process.
2. Find the main window of the new notepad.exe process using EnumWindows. Use GetWindowThreadProcessId to check if the window is owned by the new notepad.exe process.
3. Launch the “Open File” dialog programmatically using SendMessage.
4. Send various messages to the “Open File” dialog to navigate to the target directory and select the EXE file. There will be cleaner ways to access the undocumented DirectUIHWND window class, but I didn’t spend any time looking into this.
5. Send the WM_CONTEXTMENU message to the “Open File” dialog to simulate right-clicking on the EXE file.
6. Select the “Open” button on the right-click menu to execute the program.

The result of the steps above is a child process created from within the notepad.exe process. This works because the “Open File” dialog in Windows is implemented within each process using shell API DLLs – this means that it can be used as a “cut-down” version of explorer.exe.

I should also add that this method isn’t particularly reliable. It is dependant on some hard-coded Sleep calls, and the whole thing is likely to break on future Windows versions. It also expects English language labels, although these can easily be changed. This has only been tested on Windows 10.

Full code below:

#include <stdio.h>
#include <windows.h>

DWORD dwGlobal_HideWindows = 0;
DWORD dwGlobal_NotepadPID = 0;
HWND hGlobal_NotepadWindow = NULL;
HWND hGlobal_OpenFileWindow = NULL;
HWND hGlobal_OpenFileEditControl = NULL;
HWND hGlobal_OpenFileOpenButton = NULL;
HWND hGlobal_OpenFileListControl = NULL;
HWND hGlobal_PopupWindow = NULL;
HANDLE hGlobal_NotepadProcess = NULL;

BOOL CALLBACK FindNotepadWindow(HWND hWnd, LPARAM lParam)
{
 DWORD dwPID = 0;

 // check if this window is within the new notepad process
 GetWindowThreadProcessId(hWnd, &dwPID);
 if(dwPID == dwGlobal_NotepadPID)
 {
  if(GetWindow(hWnd, GW_OWNER) == 0)
  {
   // found main window
   hGlobal_NotepadWindow = hWnd;
   return 0;
  }
 }

 return 1;
}

BOOL CALLBACK FindOpenFileWindow(HWND hWnd, LPARAM lParam)
{
 DWORD dwPID = 0;
 char szClassName[512];

 // check if this window is within the new notepad process
 GetWindowThreadProcessId(hWnd, &dwPID);
 if(dwPID == dwGlobal_NotepadPID)
 {
  memset(szClassName, 0, sizeof(szClassName));
  GetClassName(hWnd, szClassName, sizeof(szClassName) - 1);
  if(strcmp(szClassName, "#32770") == 0)
  {
   // found "open file" window
   hGlobal_OpenFileWindow = hWnd;
   return 0;
  }
 }

 return 1;
}

BOOL CALLBACK FindPopupWindow(HWND hWnd, LPARAM lParam)
{
 DWORD dwPID = 0;
 char szClassName[512];

 // check if this window is within the new notepad process
 GetWindowThreadProcessId(hWnd, &dwPID);
 if(dwPID == dwGlobal_NotepadPID)
 {
  memset(szClassName, 0, sizeof(szClassName));
  GetClassName(hWnd, szClassName, sizeof(szClassName) - 1);
  if(strcmp(szClassName, "#32768") == 0)
  {
   // found context menu
   hGlobal_PopupWindow = hWnd;
   return 0;
  }
 }

 return 1;
}

BOOL CALLBACK FindOpenFileBaseControls(HWND hWnd, LPARAM lParam)
{
 DWORD dwPID = 0;
 char szClassName[512];
 char szButtonText[512];

 // check class name
 memset(szClassName, 0, sizeof(szClassName));
 GetClassName(hWnd, szClassName, sizeof(szClassName) - 1);
 if(strcmp(szClassName, "Edit") == 0)
 {
  if(GetParent(GetParent(GetParent(hWnd))) == hGlobal_OpenFileWindow)
  {
   // found file name edit control
   hGlobal_OpenFileEditControl = hWnd;
  }
 }
 else if(strcmp(szClassName, "Button") == 0)
 {
  memset(szButtonText, 0, sizeof(szButtonText));
  GetWindowText(hWnd, szButtonText, sizeof(szButtonText) - 1);
  if(strcmp(szButtonText, "&Open") == 0)
  {
   // found open button
   hGlobal_OpenFileOpenButton = hWnd;
  }
 }

 return 1;
}

BOOL CALLBACK FindOpenFileListControl(HWND hWnd, LPARAM lParam)
{
 char szClassName[512];

 // check class name
 memset(szClassName, 0, sizeof(szClassName));
 GetClassName(hWnd, szClassName, sizeof(szClassName) - 1);
 if(strcmp(szClassName, "DirectUIHWND") == 0)
 {
  // get parent class name
  memset(szClassName, 0, sizeof(szClassName));
  GetClassName(GetParent(hWnd), szClassName, sizeof(szClassName) - 1);
  if(strcmp(szClassName, "SHELLDLL_DefView") == 0)
  {
   // found file list control
   hGlobal_OpenFileListControl = hWnd;
  }
 }

 return 1;
}

DWORD WINAPI HideOpenFileWindowThread(LPVOID lpArg)
{
 for(;;)
 {
  Sleep(10);

  // check if we have a valid window handle
  if(hGlobal_OpenFileWindow != NULL)
  {
   // hide window
   ShowWindow(hGlobal_OpenFileWindow, SW_HIDE);
  }
 }
}

DWORD CreateNotepadProcess()
{
 PROCESS_INFORMATION ProcessInfo;
 STARTUPINFO StartupInfo;

 // initialise startup data
 memset((void*)&StartupInfo, 0, sizeof(StartupInfo));
 StartupInfo.cb = sizeof(StartupInfo);

 // check if the window should be hidden
 if(dwGlobal_HideWindows != 0)
 {
  StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
  StartupInfo.wShowWindow = SW_HIDE;
 }

 // create notepad process
 if(CreateProcess(NULL, "notepad.exe", NULL, NULL, 0, 0, NULL, NULL, &StartupInfo, &ProcessInfo) == 0)
 {
  return 1;
 }

 // store process ID and handle
 dwGlobal_NotepadPID = ProcessInfo.dwProcessId;
 hGlobal_NotepadProcess = ProcessInfo.hProcess;

 // close thread handle
 CloseHandle(ProcessInfo.hThread);

 // get main notepad window
 for(;;)
 {
  Sleep(10);

  // search for notepad window
  EnumWindows(FindNotepadWindow, NULL);
  if(hGlobal_NotepadWindow != NULL)
  {
   break;
  }
 }

 return 0;
}

DWORD LaunchTargetProcess(char *pDirectoryPath, char *pTargetExe)
{
 HMENU hMenu = NULL;
 MENUITEMINFO MenuItemInfo;
 char szMenuItemString[512];
 DWORD dwItemCount = 0;
 DWORD dwFoundOpenButton = 0;
 DWORD dwMenuKeyDownCount = 0;
 char szSendString[512];

 // open "open file" dialog
 PostMessage(hGlobal_NotepadWindow, WM_COMMAND, 0x10002, 0);

 // find "open file" window
 for(;;)
 {
  Sleep(10);

  // search for window
  EnumWindows(FindOpenFileWindow, NULL);
  if(hGlobal_OpenFileWindow != NULL)
  {
   break;
  }
 }

 // find controls within "open file" window
 for(;;)
 {
  Sleep(10);

  // search for controls
  EnumChildWindows(hGlobal_OpenFileWindow, FindOpenFileBaseControls, NULL);
  if(hGlobal_OpenFileEditControl != NULL && hGlobal_OpenFileOpenButton != NULL)
  {
   break;
  }
 }

 // set directory path
 memset(szSendString, 0, sizeof(szSendString));
 _snprintf(szSendString, sizeof(szSendString) - 1, "%s\\", pDirectoryPath);
 SendMessage(hGlobal_OpenFileEditControl, WM_SETTEXT, strlen(szSendString), (long)szSendString);
 SendMessage(hGlobal_OpenFileOpenButton, BM_CLICK, 0, 0);

 // wait for 1 second
 Sleep(1000);

 // set '*.exe' filter
 memset(szSendString, 0, sizeof(szSendString));
 _snprintf(szSendString, sizeof(szSendString) - 1, "*.exe");
 SendMessage(hGlobal_OpenFileEditControl, WM_SETTEXT, strlen(szSendString), (long)szSendString);
 SendMessage(hGlobal_OpenFileOpenButton, BM_CLICK, 0, 0);

 // wait for 1 second
 Sleep(1000);

 // find directory listing control within "open file" window
 for(;;)
 {
  Sleep(10);

  // find list control
  EnumChildWindows(hGlobal_OpenFileWindow, FindOpenFileListControl, NULL);
  if(hGlobal_OpenFileListControl != NULL)
  {
   break;
  }
 }

 // send tab character to move focus away from the edit control
 PostMessage(hGlobal_OpenFileWindow, WM_KEYDOWN, VK_TAB, 1);
 PostMessage(hGlobal_OpenFileWindow, WM_KEYUP, VK_TAB, 0xc0000001);

 // wait for 1 second
 Sleep(1000);

 // send target file name characters to the directory listing control to select the target file
 for(DWORD i = 0; i < strlen(pTargetExe); i++)
 {
  // send current character
  SendMessage(hGlobal_OpenFileListControl, WM_CHAR, *(char*)(pTargetExe + i), 1);
 }

 // open the context menu for this file
 PostMessage(hGlobal_OpenFileListControl, WM_CONTEXTMENU, (WPARAM)hGlobal_OpenFileListControl, 0xFFFFFFFF);

 // find context menu
 for(;;)
 {
  Sleep(10);

  // search for context menu
  EnumWindows(FindPopupWindow, NULL);
  if(hGlobal_PopupWindow != NULL)
  {
   break;
  }
 }

 // send MN_GETHMENU message
 hMenu = (HMENU)SendMessage(hGlobal_PopupWindow, 0x01E1, 0, 0);
 if(hMenu == NULL)
 {
  return 1;
 }

 // find "Open" entry within the menu
 dwItemCount = GetMenuItemCount(hMenu);
 for(i = 0; i < dwItemCount; i++)
 {
  // get current menu item info
  memset((void*)&MenuItemInfo, 0, sizeof(MenuItemInfo));
  MenuItemInfo.cbSize = sizeof(MenuItemInfo);
  MenuItemInfo.fMask = 0x100;
  GetMenuItemInfo(hMenu, i, 1, &MenuItemInfo);

  // check if this is a separator item
  if(MenuItemInfo.fType & MFT_SEPARATOR)
  {
   // ignore
   continue;
  }

  // increase the number of "key down" presses
  dwMenuKeyDownCount++;

  // check if this is the "Open" button
  memset(szMenuItemString, 0, sizeof(szMenuItemString));
  GetMenuString(hMenu, i, szMenuItemString, sizeof(szMenuItemString) - 1, MF_BYPOSITION);
  if(strcmp(szMenuItemString, "&Open") == 0)
  {
   // found "Open" button
   dwFoundOpenButton = 1;
   break;
  }
 }

 // ensure the "Open" button was found in the context menu
 if(dwFoundOpenButton == 0)
 {
  return 1;
 }

 // send "down" key presses
 for(i = 0; i < dwMenuKeyDownCount; i++)
 {
  PostMessage(hGlobal_PopupWindow, WM_KEYDOWN, VK_DOWN, 0x2A0001);
  PostMessage(hGlobal_PopupWindow, WM_KEYUP, VK_DOWN, 0xD02A0001);
 }

 // send "enter" key to execute the target exe
 PostMessage(hGlobal_PopupWindow, WM_KEYDOWN, VK_RETURN, 0x2A0001);
 PostMessage(hGlobal_PopupWindow, WM_KEYUP, VK_RETURN, 0xD02A0001);

 return 0;
}

int main(int argc, char *argv[])
{
 DWORD dwThreadID = 0;
 HANDLE hHideOpenFileWindowThread = NULL;
 char szDirectoryPath[512];
 char szTargetExe[512];
 char *pLastSlash = NULL;

 printf("NotepadExec - www.x86matthew.com\n\n");

 if(argc != 2)
 {
  printf("%s <full_exe_path>\n\n", argv[0]);
  return 1;
 }

 // split target exe path
 memset(szDirectoryPath, 0, sizeof(szDirectoryPath));
 strncpy(szDirectoryPath, argv[1], sizeof(szDirectoryPath) - 1);

 // find last slash
 pLastSlash = strrchr(szDirectoryPath, '\\');
 if(pLastSlash == NULL)
 {
  printf("Invalid exe path\n");
  return 1;
 }

 // remove exe name from directory path
 *pLastSlash = '\0';

 // store exe name
 memset(szTargetExe, 0, sizeof(szTargetExe));
 pLastSlash++;
 strncpy(szTargetExe, pLastSlash, sizeof(szTargetExe) - 1);

 // hide windows
 dwGlobal_HideWindows = 1;

 printf("Creating notepad.exe process...\n");

 // create notepad process
 if(CreateNotepadProcess() != 0)
 {
  // error
  printf("Failed to create notepad process\n");
  return 1;
 }

 if(dwGlobal_HideWindows != 0)
 {
  // create background thread to ensure the "open file" window remains hidden.
  // this is because the "open file" window makes itself visible again when performing certain actions.
  hHideOpenFileWindowThread = CreateThread(NULL, 0, HideOpenFileWindowThread, NULL, 0, &dwThreadID);
  if(hHideOpenFileWindowThread == NULL)
  {
   // error
   printf("Failed to create thread\n");

   TerminateProcess(hGlobal_NotepadProcess, 0);
   return 1;
  }
 }

 printf("Sending window messages to notepad...\n");

 // launch target process
 if(LaunchTargetProcess(szDirectoryPath, szTargetExe) != 0)
 {
  // error
  printf("Failed to launch target process\n");

  if(dwGlobal_HideWindows != 0)
  {
   TerminateThread(hHideOpenFileWindowThread, 0);
  }

  TerminateProcess(hGlobal_NotepadProcess, 0);
  return 1;
 }

 printf("Notepad launched %s successfully\n", szTargetExe);

 // wait for 2 seconds
 Sleep(2000);

 // terminate background thread
 if(dwGlobal_HideWindows != 0)
 {
  TerminateThread(hHideOpenFileWindowThread, 0);
 }

 // terminate notepad process
 TerminateProcess(hGlobal_NotepadProcess, 0);

 return 0;
}

 

版权声明:admin 发表于 2022年3月26日 上午9:42。
转载请注明:NotepadExec – Using notepad.exe to launch an EXE without code injection | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...