📄 shubloadercore.cpp
字号:
// ShubLoaderCore.cpp : Defines the entry point for the console application.
//
#include "NTInternals.h"
#include "ShubLoaderCore.h"
#include <Winbase.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// The one and only application object
using namespace std;
//////////////////////////////////////////////////////////////////////////
//This data member is static because it's easiest to handle it from derived classes.
HWND ShubLoaderCore::m_ghMainWnd;
BOOL ShubLoaderCore::m_SilentMode;
ShubLoaderCore::ShubLoaderCore()
{
m_ghMainWnd=NULL;
m_SilentMode=FALSE;
memset(&pi, 0, sizeof(PROCESS_INFORMATION));
m_bcheckCRC=FALSE;
m_dwVictimCRCValue=0;
m_dwCreationFlags=CREATE_SUSPENDED;
//default message if not specified otherwise using SetStartingMsg()
m_startingMsg="Shub-Nigurrath Loader is working...wait a little\n";
}
void ShubLoaderCore::SetMainWnd(HWND hWnd)
{
m_ghMainWnd = hWnd;
}
void ShubLoaderCore::SetSilentMode(BOOL bVal)
{
m_SilentMode= bVal;
}
int ShubLoaderCore::DoMyJob(int argc, TCHAR* argv[])
{
growing_arraystack<TextString> stkErrors;
growing_arraystack<Patch> stkPatches;
//Victim's parameters. Set by the function SetVictimPathandCRC().
TextString victimFileName;
//The whole patcher code is into a Try block because I will trap the errors
//into an unique place at the end of the function.
try {
//Must be properly defined in the LoaderActions.cpp file by the Loader creator
if(!InitializePatchStack(stkPatches))
throw BAD_INITIALIZEPATCHSTACK;
//Must be properly called by the developers into LoaderActions.cpp.
if(!SetVictimDetails(victimFileName))
throw BAD_SETVICTIMPATHANDCRC;
//////////////////////////////////////////////////////////////////////////
//This gate variable is set by the Gate Procedure when the condition required to patch
//the application is met.
//The condition that can be used to patch the application can be of different types:
//presence of the main window; presence of a specific value in memory;
//presence of a specific file on the disk and so on..
BOOL bCanPatchVictim=FALSE;
BOOL bProcessOpened=FALSE;
#ifdef _DEBUG
TextString strFinalReport; //string used to write the final MessageBox
#endif
//Number of patches pushed into the patches stack.
//It's used when the program is compiled in debugmode to report how bytes will be written
int NumberofPatches= stkPatches.size();
//Create the process
//PROCESS_INFORMATION have already been declared into the class's attributes.
STARTUPINFO si;
memset(&si, 0, sizeof(STARTUPINFO));
si.cb=sizeof(si);
if(!ActionsBeforeCreateProc())
throw BAD_ACTIONSBEFORECREATEPROC;
if(m_bcheckCRC)
if(!CRCFile(victimFileName.c_str(), m_dwVictimCRCValue))
throw BAD_CRCFILE;
//If a commmandline is provided to the process passes it to the victim as well
TextString commandline;
int idx=1;
if(argc!=NULL && argv!=NULL) {
while(idx<argc) {
if(argv[idx]!=NULL)
commandline << argv[idx] << " ";
idx++;
}
}
if(commandline.length()>0)
commandline[commandline.length()-1]='\0'; //eliminates the trailing space, to make it "perfect"
//As MSDN reports, if lpApplicationName is NULL, the first white-space delimited
//token of the command line specifies the module name.
TextString str;
str << victimFileName << " " << commandline;
if( !::CreateProcess( NULL, // No module name (use command line).
str.str(), // Command line.
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
NULL, // Set handle inheritance to FALSE.
m_dwCreationFlags, // suspended creation flags.
NULL, // Use parent's environment block.
NULL, // Use parent's starting directory.
&si, // Pointer to STARTUPINFO structure.
&pi ) // Pointer to PROCESS_INFORMATION structure.
)
{
throw BAD_CREATEPROCESS;
}
printf(m_startingMsg.c_str());
#ifdef _DEBUG
printf("ShubLoader is going to write %d bytes in the victim's process\n", stkPatches.size());
#endif //_DEBUG
if(!ActionsAfterCreateProc()) {
CloseHandle(pi.hProcess);
throw BAD_ACTIONSAFTERCREATEPROC;
}
//To resume the process is only OK for processes which are not debugged
if(m_dwCreationFlags == CREATE_SUSPENDED) {
if(ResumeThread(pi.hThread)==-1) {
CloseHandle(pi.hProcess);
throw BAD_RESUMEPROCESS;
}
}
//Main waiting loop, iterates till the gate condition has been found.
while(!bCanPatchVictim) {
//Eventually used to get the thread priority: if a thread has an high priority
//might not listen to our SuspendThread requests
//if !=0 then has been set
int thPriority=0;
if(!ActionsBeforeGateProcedure()) {
CloseHandle(pi.hProcess);
throw BAD_ACTIONSBEFOREGATEPROCEDURE;
}
//Check the GateCondition.
bCanPatchVictim=GateProcedure();
//Do the work only if the window we searched has been found
if(bCanPatchVictim) {
#ifdef _DEBUG
if(!m_SilentMode) {
//Message that the patching process started..
::MessageBox(NULL,"GateCondition reached",DEFAULT_MSG_CAPTION,
MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL);
}
#endif
//Before patching the victim application it's better to suspend it..
//If we cannot for some protection suspend the application then
//a little of tentatives are tried:
//1. repeat several time SuspendThread (see comment below to see why)
//2. try to lower the priority
//3. try using the kernel counterparts zwSuspendThread and zwSuspendProcess
//4. open the process to get another process handle.
// If all these things fails then closes the patcher with an error!
if(SuspendThread(pi.hThread)==-1) {
//If the thread is making a kernel call, SuspendThread fails.
//An application may need to repeat the SuspendThread several times for it
//to succeed.
int trials_count=0;
BOOL skiptherest=FALSE;
while(trials_count<=MAX_SUSPENDTHREAD_TRIALS) {
if(SuspendThread(pi.hThread)!=-1) {
skiptherest=TRUE;
break;
}
trials_count++;
}
//Try to lower the the thread's priority.
if(!skiptherest) {
thPriority=GetThreadPriority(pi.hThread);
if(thPriority!=THREAD_PRIORITY_NORMAL)
SetThreadPriority(pi.hThread,THREAD_PRIORITY_NORMAL);
if(SuspendThread(pi.hThread)!=-1)
skiptherest=TRUE;
}
//Try suspending the process using kernel equivalent functions
NTSTATUS ret=0;
if(!skiptherest) {
ret=ZwSuspendThread(pi.hThread, NULL);
if(ret>=0)
skiptherest=TRUE;
}
if(!skiptherest) {
ret=ZwSuspendProcess(pi.hProcess);
if(ret>=0)
skiptherest=TRUE;
}
if(!skiptherest) {
//All the other thing failed so much probably the handle you have it's still
//not valid then it's time to open the process, using the m_ghMainWnd, found
//using the GateProcedure(), which gives us access to the belonging ProcessId
//the through OpenProcess we have another time a valid hProcess.
//The only difference is that this time we must use zwSuspendProcess
//and zwResumeProcess undocumented ntdll exports, because we do not have
//a valid handle for the process's Treads.
CloseHandle(pi.hProcess);
memset(&pi, 0, sizeof(PROCESS_INFORMATION));
DWORD dwProcessId=0;
GetWindowThreadProcessId(m_ghMainWnd, &dwProcessId);
HANDLE hProc=OpenProcess(PROCESS_ALL_ACCESS,FALSE, dwProcessId);
#ifdef _DEBUG
if(!m_SilentMode) {
char str[25];
sprintf(str,"hProc=%x, dwProcessId=%d (%x)",hProc, dwProcessId,dwProcessId);
::MessageBox(NULL, str, DEFAULT_MSG_CAPTION, MB_OK);
}
#endif
if(hProc==NULL) {
CloseHandle(pi.hProcess);
throw BAD_SUSPENDPROCESS;
}
pi.hProcess=hProc;
bProcessOpened=TRUE;
NTSTATUS ret=ZwSuspendProcess(pi.hProcess);
if(ret>=0)
skiptherest=TRUE;
}
if(!skiptherest) {
CloseHandle(pi.hProcess);
throw BAD_SUSPENDPROCESS;
}
}
//////////////////////////////////////////////////////////////////////////
//Start the memory patching process. It uses the MBG_gsar class (see Oraculum's tutorial)
BMG_gsar gsar;
TextString strlastError;
//for each patch do the whole work
for (int idx=0; idx<NumberofPatches; idx++) {
//This boolean is used to write or not the patched bytes
//in case of mismatches between read and foreseen original bytes
//and if the patch byte is not set
BOOL bDonotWrite=FALSE;
//Used when, according to the constructor of Patch class, the bytes shouldn't be read
//for example when only a function must be called and no other actions.
BOOL bDonotRead=FALSE;
//takes the top (last in) patch from the patches stack
Patch ptc=stkPatches.top();
//Properly set the DonotRead and DonotWrite flags
if(ptc.OnlyDoCallback) {
bDonotRead=bDonotWrite=TRUE;
}
//Read the original bytes from the victim process
//The destination values are placed into the the Patch::orig
//and the bytes read are places into Patch::bytesread
//It is also performed a check of the just read memory byte, which should
//be equal to the original byte already specified in the Patch object, if any.
if(!bDonotRead) {
BYTE orig=0;
gsar.ReadProcessMemory(pi.hProcess,
(LPVOID)(ptc.address), (LPVOID)(&(orig)), 1,
&ptc.bytesread);
if(ptc.bytesread==0)
strlastError=GetLastErrorMsg();
else
strlastError=TextString("OK");
//If the Patch::checkorigByte flag is set and if there are mismatches between
//read and foreseen bytes, a proper message is formed and the flag which
//avoid writing in memory is set.
if(ptc.checkorigByte) {
if(ptc.orig!=orig) {
char msg[MAX_PATH];
sprintf(msg,
"Error at Address %X: expected original byte %X differs from read byte %X",
ptc.address, ptc.orig, orig);
ptc.msg=TextString(msg);
stkErrors.push(TextString(msg));
bDonotWrite=TRUE;
}
}
//the read value can be copied into the Patch data, because all the controls
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -