kincses Posted September 7, 2016 Posted September 7, 2016 Hi. Making a new thread for this, but is actually a sub-problem coming from my other thread. I'm trying to make a loader (now in c++), which would use the ReadProcessMemory API. The process is created by the loader with CreateProcess(address,NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&startup_info,&process_info) I noticed, that while it reads the memory of an "Imag" memory block, it returns 0x00 (or 0xCC ?) when trying to read from a private memory area. How is this bypassable?
crystalboy Posted September 7, 2016 Posted September 7, 2016 As always MSDN is your friend. You can use VirtualProtect to change permission of page/sections.
kincses Posted September 7, 2016 Author Posted September 7, 2016 (edited) Silly me, I was using that (VirtualProtectEx to be exact but they seem to do the same essentially), just did not check for the error which it gave. Seemingly fixed those. Changed: Quote void* address = (void*)0x0019FF28; to Quote unsigned int address = 0x0019FF28; Still returns garbage. Next idea? I'm assuming a lack of permissions would return an error too, so that should not be it. Edited September 7, 2016 by kincses
crystalboy Posted September 7, 2016 Posted September 7, 2016 You are calling VirtualProtect using RVA, that's why your call fail. Before successfully call it you need to get the base address of loaded PE and sum RVA ( 0x0019FF28 ) 1
deepzero Posted September 7, 2016 Posted September 7, 2016 Prefer void* over uint for pointers. As for your problem as he said, msdn is your friend. Quote Changes the protection on a region of committed pages in the virtual address space of the calling process. That's your loader process. If you want to read the memory of another process (the one you just created) you need another API function. Which one might that be? Quote To change the access protection of any process, use the VirtualProtectEx function. VirtualProtectEx needs another paramter: a Handle to the process. Where can you get that? From the processinformation structure: process_info.hProcess. 1
deepzero Posted September 7, 2016 Posted September 7, 2016 (edited) --- Edited September 7, 2016 by deepzero
kao Posted September 7, 2016 Posted September 7, 2016 3 hours ago, kincses said: it returns 0x00 (or 0xCC ?) Sounds like uninitialized data. Are you checking the return value of ReadProcessMemory? 3 hours ago, kincses said: CREATE_SUSPENDED You do realize that suspended process doesn't run, the packer layer is not executed and therefore unpacked exe code is not present in memory, etc.? With the same success you could just use ReadFile and read from EXE file directly. Or, you're calling CreateThread/ResumeThread at some point and just didn't tell us? 1
kincses Posted September 7, 2016 Author Posted September 7, 2016 53 minutes ago, crystalboy said: You are calling VirtualProtect using RVA, that's why your call fail. Before successfully call it you need to get the base address of loaded PE and sum RVA ( 0x0019FF28 ) I'm actually using VirtualProtectEx(did I edit it too slow?), and even without it, ReadProcessMemory reads non-private memory correctly, so am I really not on the right track? Do private memory act different than non-private? 51 minutes ago, deepzero said: Prefer void* over uint for pointers. Was using that, did not work any way, it just generated an "Attempt to access invalid address" error (related to crystalboy's reply maybe?) Quote VirtualProtectEx needs another paramter: a Handle to the process. Where can you get that? From the processinformation structure: process_info.hProcess. Which I do pass to it, obtained from the CreateProcess, that should be OK, shouldn't it be?
kincses Posted September 7, 2016 Author Posted September 7, 2016 1 minute ago, kao said: Sounds like uninitialized data. Are you checking the return value of ReadProcessMemory? You do realize that suspended process doesn't run, the packer layer is not executed and therefore unpacked exe code is not present in memory, etc.? With the same success you could just use ReadFile and read from EXE file directly. Or, you're calling CreateThread/ResumeThread at some point and just didn't tell us? That's what I thought too, thing is, it even fails to read data that is present in ollydbg at load, (without executing the software), other than that, it would add up. Yes, I believe I am resuming it, might be wrong though because it does not seem to have the correct values at the planned breakpoint. (I am trying to read memory at a certain address execution). Good points, might be where I screwed it up. Have to look into it too...
evlncrn8 Posted September 7, 2016 Posted September 7, 2016 do a small test then, read the base address of the target process.. to see if the good old MZ is there, if that works, then expand on the code.. i dont think the api's are at fault here..
kincses Posted September 7, 2016 Author Posted September 7, 2016 (edited) 3 hours ago, kao said: You do realize that suspended process doesn't run, the packer layer is not executed and therefore unpacked exe code is not present in memory, etc.? Seems like you were right and it stayed in suspended mode, instead of stopping at the HWbreakpoint. And I have no further idea how to bypass this, sounds so simple and yet... I don't suppose anyone would be interested in making this for me in exchange of a small reward. Edited September 7, 2016 by kincses
kao Posted September 7, 2016 Posted September 7, 2016 14 minutes ago, kincses said: anyone would be interested in making this for me in exchange of a small reward Arghh.. And you started so well, trying to learn and asking smart questions. If you're going to ask someone else to do your job, why not ask for a properly unpacked file instead? 4
deepzero Posted September 7, 2016 Posted September 7, 2016 @kincses Come on mate. You can do it. Not that hard. To me more and more questions arise ... for example, where does that hwbp in your target process come from? 1. post your full code 2. describe what you expect to happen and what actually happens. 2
kincses Posted September 8, 2016 Author Posted September 8, 2016 14 hours ago, kao said: Arghh.. And you started so well, trying to learn and asking smart questions. If you're going to ask someone else to do your job, why not ask for a properly unpacked file instead? Because I want to see where I f*cked it up, can't do that from an unpacked file, right? 14 hours ago, deepzero said: Come on mate. You can do it. Not that hard. That's what I thought, but I'm on this for almost a week without any progress. I am finding threads about the same thing I wish to accomplish from 2003 and even those are without response or with dead links.
mrexodia Posted September 8, 2016 Posted September 8, 2016 We cannot magically guess what your code is doing from very vague descriptions, please post the full code. What are you trying to accomplish with the suspended process?
kincses Posted September 8, 2016 Author Posted September 8, 2016 22 minutes ago, mrexodia said: We cannot magically guess what your code is doing from very vague descriptions, please post the full code. What are you trying to accomplish with the suspended process? Its a work-in-progress pile of mess, that's why I did not intend on posting it, but alright... Quote #include "stdafx.h" #include <Windows.h> #include <stdio.h> #include "HwBrk.h" #include <iomanip> #include <thread> class HWBRK { public: void* a; HANDLE hT; HWBRK_TYPE Type; HWBRK_SIZE Size; HANDLE hEv; int iReg; int Opr; bool SUCC; HWBRK() { Opr = 0; a = 0; hT = 0; hEv = 0; iReg = 0; SUCC = false; } }; void InjectDLL(HANDLE Proc); void ModifyValues(HANDLE Proc); HANDLE hwbrkEvent, hwbrkEvent2; int FlagBit; bool Dr0Busy ; bool Dr1Busy ; bool Dr2Busy ; bool Dr3Busy ; void SetBits(DWORD_PTR &dw, size_t lowBit, size_t bits, size_t newValue) { DWORD_PTR mask = (1 << bits) - 1; dw = (dw & ~(mask << lowBit)) | (newValue << lowBit); } static DWORD WINAPI th ( LPVOID lpParameter ) { //lpParameter is passed via the CreateThread parameter so it has //to be typecasted back to a HWBRK type HWBRK* h=(HWBRK*)lpParameter; int j=0; int y=0; //Suspend the target thread so the breakpoint can be set. As a //reminder SuspendThread returns a threads SuspendCount j = SuspendThread (h->hT); y = GetLastError(); CONTEXT ct = {0}; //Get the context of the DRX register set. ct.ContextFlags = CONTEXT_DEBUG_REGISTERS ; //If GetThreadContext succeeeds it returns a non- zero number //otherwise it returns 0 j = GetThreadContext(h->hT,&ct); y = GetLastError(); int FlagBit=0; //Set all debug registers as unused bool Dr0Busy = false; bool Dr1Busy = false; bool Dr2Busy = false; bool Dr3Busy = false; //Each of these correspond to the Global //breakpoint enable registers. if ( ct.Dr7 & 1 ) {Dr0Busy = true;} if ( ct.Dr7 & 4 ) {Dr1Busy = true;} if ( ct.Dr7 & 16 ) {Dr2Busy = true ;} if( ct.Dr7 & 64 ) {Dr3Busy = true ;} //Opr set to 1 means remove breakpoints if ( h -> Opr == 1 ) { // Clear debug registers if ( h -> iReg == 0 ) { FlagBit = 0 ; ct.Dr0 = 0 ; Dr0Busy = false ; } if ( h -> iReg == 1 ) { FlagBit = 2 ; ct.Dr1 = 0 ; Dr1Busy = false ; } if ( h -> iReg == 2 ) { FlagBit = 4 ; ct.Dr2 = 0 ; Dr2Busy = false ; } if ( h -> iReg == 3 ) { FlagBit = 6 ; ct.Dr3 = 0 ; Dr3Busy = false ; } ct.Dr7 &= ~( 1 << FlagBit ); } //If opr was 0 then the below sets the first available //debug register and marks it as used else { if(!Dr0Busy ) { h -> iReg = 0 ; ct.Dr0 = (DWORD_PTR)h -> a ; Dr0Busy = true ; } else if(!Dr1Busy ) { h -> iReg = 1 ; ct.Dr1 = (DWORD_PTR)h -> a ; Dr1Busy = true ; } else if (!Dr2Busy) { h -> iReg = 2 ; ct.Dr2 = (DWORD_PTR) h -> a ; Dr2Busy = true ; } else if (! Dr3Busy ) { h -> iReg = 3 ; ct.Dr3 =( DWORD_PTR)h -> a ; Dr3Busy = true ; } else { h -> SUCC = false ; j = ResumeThread ( h -> hT ); y = GetLastError (); SetEvent ( h -> hEv ); return 0; } ct.Dr6 = 0 ; int st = 0 ; if ( h -> Type == HWBRK_TYPE_EXECUTE ) st = 0 ; if ( h -> Type == HWBRK_TYPE_READWRITE ) st = 3 ; if ( h -> Type == HWBRK_TYPE_WRITE) st = 1 ; int le = 0 ; if ( h -> Size == HWBRK_SIZE_1 ) le = 0 ; if ( h -> Size == HWBRK_SIZE_2 ) le = 1 ; if ( h -> Size == HWBRK_SIZE_4 ) le = 3 ; if ( h -> Size == HWBRK_SIZE_8 ) le = 2 ; SetBits ( ct.Dr7 , 16 + h -> iReg * 4 , 2 , st ); SetBits ( ct.Dr7 , 18 + h -> iReg * 4 , 2 , le ); SetBits ( ct.Dr7 , h -> iReg * 2 , 1 , 1 ); } ct.ContextFlags = CONTEXT_DEBUG_REGISTERS ; j = SetThreadContext ( h -> hT ,& ct ); y = GetLastError (); ct.ContextFlags = CONTEXT_DEBUG_REGISTERS ; j = GetThreadContext ( h -> hT ,& ct ); y = GetLastError (); j = ResumeThread ( h -> hT ); y = GetLastError (); h -> SUCC = true ; SetEvent ( h -> hEv ); return 0; } HANDLE SetHardwareBreakpoint(HANDLE hThread,HWBRK_TYPE Type,HWBRK_SIZE Size,void* s) { //Set constants HWBRK* h=new HWBRK; //Create a new hardware breakpoint h->a=s; //Address of the breakpoint h->Size=Size; //Size of the breakpoint to be set h->Type=Type; //Type of breakpoint to set (read/write/execute) h->hT=hThread; //The thread to set the breakpoint in h->hEv=CreateEvent(0,0,0,0); h->Opr=0; // 0 specifies set breakpoint 1 specifies remove breakpoint //Create a remote thread to set the hardware breakpoints. Hardware breakpoints //are not global in windows. They are specific to the context of one thread. HANDLE hY = CreateThread(0,0,th,(LPVOID)h,0,0); //Wait for the breakpoint instance's event object to be placed in a signaled state. WaitForSingleObject(h->hEv,INFINITE); CloseHandle(h->hEv); h->hEv=0; if(hThread==GetCurrentThread()) { CloseHandle(h->hT); } h->hT=hThread; if(!h->SUCC) { delete h; return 0; } return(HANDLE)h; } bool RemoveHardwareBreakpoint(HANDLE hBrk) { HWBRK* h = (HWBRK*)hBrk; if (!h) return false; bool C = false; if (h->hT == GetCurrentThread()) { DWORD pid = GetCurrentThreadId(); h->hT = OpenThread(THREAD_ALL_ACCESS,0,pid); C = true; } h->hEv = CreateEvent(0,0,0,0); h->Opr = 1; // Remove Break HANDLE hY = CreateThread(0,0,th,(LPVOID)h,0,0); WaitForSingleObject(h->hEv,INFINITE); CloseHandle(h->hEv); h->hEv = 0; if (C) { CloseHandle(h->hT); } delete h; return true; } bool RemoveHardwareBreakpoint_original(HANDLE hBrk) { HWBRK* h=(HWBRK*)hBrk; if(!h) return false; bool C=false; //Suspend the target thread so the breakpoint can be set. As a //reminder SuspendThread returns a threads SuspendCount SuspendThread(h->hT); CONTEXT ct={0}; //Get the context of the control register set. ct.ContextFlags=CONTEXT_CONTROL; //ct.ContextFlags = CONTEXT_DEBUG_REGISTERS; //Get the context of the thread. GetThreadContext(h->hT,&ct); //Set EIP to our target address //ct.Eip=0x004010D4; SetThreadContext(h->hT,&ct); ResumeThread(h->hT); delete h; return true; } BOOL main(int argc,char* argv[]) { PROCESS_INFORMATION process_info; STARTUPINFO startup_info; //0 out the area for our process' structures ZeroMemory(&startup_info,sizeof(startup_info)); startup_info.cb =sizeof(startup_info); ZeroMemory(&process_info,sizeof(process_info)); //Grabthe process to be loaded. (In this case that should be //the duelist 5 executable file due-cm5.exe if(!::CreateProcess("C:\\Program Files (x86)\\KRESZTESZT\\KRESZTESZT1501B.exe",//LPCTSTR lpApplicationName //Specify path/name on command line NULL, //LPTSTR lpCommandLine //Returns a pointer to the command line NULL, //LPSECURITY_ATTRIBUTES lpProcessAttributes //Process handle not inheritable NULL, //LPSECURITY_ATTRIBUTES lpThreadAttributes //Thread handle not inheritable FALSE, //BOOL bInheritHandles //Set handle inheritance to FALSE CREATE_SUSPENDED, //DWORD dwCreationFlags //Use suspended creation flags NULL, //LPVOID lpEnvironment //Use parent's environment block NULL, //LPCTSTR lpCurrentDirectory //Use parent's starting directory &startup_info, //LPSTARTUPINFO lpStartupInfo //Pointer to STARTUPINFO structure &process_info) //LPPROCESS_INFORMATION lpProcessInformation //Pointer to PROCESS_INFORMATION struture. ){ //If the process can't be created return an error message. printf("CreateProcess failed (%d).\n",GetLastError()); return FALSE; } //The address to place the breakpoint at. void* ptr =(void*)0x00DF5435; printf("Setting hardware breakpoint...\n"); //Set the hardware breakpoint. HANDLE hardwareBreakpoint =SetHardwareBreakpoint(process_info.hThread,HWBRK_TYPE_EXECUTE,HWBRK_SIZE_1,ptr); //a.join(); //Create an event that will be signaled when we are ready to remove the hardware //breakpoint. hwbrkEvent =CreateEvent(NULL,TRUE,FALSE,"hwbrkEvent"); hwbrkEvent2 =CreateEvent(NULL,TRUE,FALSE,"hwbrkEvent2"); //Make sure the event was created. if(hwbrkEvent ==NULL) { printf("CreateEvent error: %d\n",GetLastError()); return FALSE; } ResumeThread(hardwareBreakpoint); printf("Starting modification process...\n"); //ModifyValues(process_info.hProcess); printf("Resuming thread...\n"); //Resume the thread //ResumeThread(process_info.hThread); printf("Waiting to remove breakpoint...\n"); //Wait for the event object to be signaled. //std::thread first (ModifyValues,process_info.hProcess); //first.join(); //ResumeThread(process_info.hThread); //SetEvent(hwbrkEvent); //ResumeThread(process_info.hThread); //WaitForSingleObject(hwbrkEvent2,INFINITE); //ModifyValues(process_info.hThread); //Remove the hardware breakpoint printf("Waiting to remove breakpoint...2\n"); RemoveHardwareBreakpoint(hardwareBreakpoint); printf("Waiting to remove breakpoint...3\n"); //Tell the injected dll that the hardware breakpoint has been removed //hwbrkEvent2 =OpenEvent(EVENT_MODIFY_STATE,FALSE,"hwbrkEvent2"); //Make sure the event successfully opened /*if(hwbrkEvent2 ==NULL) { printf("Event failed to open."); return FALSE; } SetEvent(hwbrkEvent2);*/ //Set the event back to unsignaled //ResetEvent(hwbrkEvent); printf("Closing handle to process...\n"); //Close the handle and return. //ResumeThread(process_info.hThread); //WaitForSingleObject(process_info.hProcess, INFINITE); CloseHandle(process_info.hProcess); CloseHandle(process_info.hThread); //CloseHandle(process_info.hProcess); return TRUE; } /*void InjectDLL(HANDLE Proc) { //Declare constants. char buf[50]={0}; LPVOID RemoteString,LoadLibAddy,SetHandler; LPCSTR DLL_NAME ="HwBrkDLL.dll"; printf("Getting LoadLibrary handle...\n"); //Get the address of the LoadLibrary function in kernel32.dll LoadLibAddy =(LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"),"LoadLibraryA"); printf("Creating remote string with the name of the DLL to be injected...\n"); //Allocate space in the remote process to store the name of the library to be loaded. RemoteString =(LPVOID)VirtualAllocEx(Proc,NULL,strlen(DLL_NAME)+1,MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE); printf("Writing string to remote memory...\n"); //Write the name of the DLL into the allocated memory. WriteProcessMemory(Proc,(LPVOID)RemoteString,DLL_NAME,strlen(DLL_NAME)+1,NULL); printf("Creating remote thread to load DLL...\n"); //Create a remote thread in the victim process that loads the library. WaitForSingleObject(CreateRemoteThread(Proc,NULL,NULL,(LPTHREAD_START_ROUTINE)LoadLibAddy,(LPVOID)RemoteString,NULL,NULL),INFINITE); //Free the memory we used for the string. VirtualFreeEx(Proc,RemoteString,strlen(DLL_NAME)+1,MEM_RELEASE); }*/ std::string GetLastErrorAsString() { //Get the error message, if any. DWORD errorMessageID = ::GetLastError(); if(errorMessageID == 0) return std::string(); //No error message has been recorded LPSTR messageBuffer; size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); std::string message(messageBuffer, size); //Free the buffer. LocalFree(messageBuffer); return message; } void ReadProcessBYTES(HANDLE hProcess, DWORD lpAddress, void* buf, int len) { DWORD oldprot, dummy = 0; if(VirtualProtectEx(hProcess, (void*) lpAddress, len, PAGE_READWRITE, &oldprot)==0) { std::string tmp=GetLastErrorAsString(); printf("VirtualProtectEx error:",tmp.c_str(),"\n"); } if (!ReadProcessMemory(hProcess, (void*) lpAddress, buf, len, 0)) { printf("ReadProcessMemory error\n"); } if(VirtualProtectEx(hProcess, (void*) lpAddress, len, oldprot, &dummy)==0) { std::string tmp=GetLastErrorAsString(); printf("VirtualProtectEx error:",tmp.c_str(),"\n"); } } void ReadProcessBYTES2(HANDLE hProcess, DWORD lpAddress, void* buf, int len) { DWORD oldprot, dummy = 0; if(VirtualProtect((void*) lpAddress, len, PAGE_READWRITE, &oldprot)==0) { std::string tmp=GetLastErrorAsString(); printf("VirtualProtectEx error: %s \n",tmp.c_str()); } if (!ReadProcessMemory(hProcess, (void*) lpAddress, buf, len, 0)) { printf("ReadProcessMemory error\n"); } if(VirtualProtect((void*) lpAddress, len, oldprot, &dummy)==0) { std::string tmp=GetLastErrorAsString(); printf("VirtualProtectEx error: %s \n",tmp.c_str()); } } void ModifyValues(HANDLE Proc) { WaitForSingleObject(hwbrkEvent,INFINITE); void* address = (void*)0x00df5435; unsigned int address2 = 0x00df5435; const int nSize = 8; unsigned char num_char[nSize]; //char num_char2[1]; //unsigned long num; //unsigned long long num2; BYTE data[] = {0x2E, 0xF4, 0xA9, 0xA0}; DWORD dataSize = sizeof(data); ReadProcessMemory(Proc,(void*)address,&num_char,sizeof(num_char),0); //ReadProcessBYTES(Proc,(DWORD)address,&num_char,sizeof(num_char)); //ReadProcessMemory(Proc,(void*)address,num_char2,nSize,0); //ReadProcessMemory(Proc,(void*)address,&num,nSize,0); //char converted[nSize*2 + 1]; for (int i = 0; i < nSize; i++) { printf("%x", num_char[i]); //converted[i*2]<<std::hex<<num_char[i]; } printf("\n"); //printf("0x%llx\n", num2+ 2 ^ 32); //ReadProcessMemory(Proc,(void*)address,num2,nSize,0); //ReadProcessBYTES(Proc,address,num_char,nSize); //printf("before: %d \n",value); //WriteProcessMemory(Proc,(LPVOID)0x0019ff24,&data,dataSize,NULL); //ReadProcessMemory(Proc,(void*)address,&value,sizeof(value),0); //ReadProcessMemory(Proc,(void*)address,num_char,4,0); //printf("after: %d \n",value); //WaitForSingleObject(CreateRemoteThread(Proc,NULL,NULL,(LPTHREAD_START_ROUTINE)NULL,(LPVOID)"hwbrkEvent",NULL,NULL),INFINITE); SetEvent(hwbrkEvent2); printf("Creating remote thread to load DLL...\n"); } HwBrk.h: // #ifndef _HWBRK_H #define _HWBRK_H enum HWBRK_TYPE { HWBRK_TYPE_CODE, HWBRK_TYPE_READWRITE, HWBRK_TYPE_WRITE, HWBRK_TYPE_EXECUTE, }; enum HWBRK_SIZE { HWBRK_SIZE_1, HWBRK_SIZE_2, HWBRK_SIZE_4, HWBRK_SIZE_8, }; HANDLE SetHardwareBreakpoint(HANDLE hThread,HWBRK_TYPE Type,HWBRK_SIZE Size,void* s); bool RemoveHardwareBreakpoint(HANDLE hBrk); #endif I just noticed, that noone else uses HWBRK_TYPE_EXECUTE, is this even a thing or I made it up and should use TYPE_CODE instead? What I'm trying to do: -create a process without starting it -set a HW breakpoint at given address -start process -trigger the breakpoint and pause execution -modify it's memory values -resume process 1
mrexodia Posted September 8, 2016 Posted September 8, 2016 I see what you are trying to do, but how do you plan on receiving a notification for the hardware breakpoint without injecting a DLL with an exception handler in the process? You can find some source code I wrote for an Enigma loader here, but it requires you to inject this as a DLL in the process. What I advise is write EBFE to the entry point of the process, create a remote thread that injects this DLL and then make that DLL restore the original bytes (or signal your loader to restore the bytes). The problem with the suspended state of a process is that lots of things don't work yet. I think the entry point of the started process is somewhere in EDX or something when you start a process suspended.
kincses Posted September 8, 2016 Author Posted September 8, 2016 (edited) 2 hours ago, mrexodia said: I see what you are trying to do, but how do you plan on receiving a notification for the hardware breakpoint without injecting a DLL with an exception handler in the process? Thought the process would enter a special state that would be detectable for the parent process, or just wait it out I guess... didn't really want to mess with its memory or flow more than needed. Seems that was a mistake. Just to make sure I'm back on the right track: I have to make 2 projects: - 1 loader, which will launch the process, mess with the EP and do the injection - 1 exception handler that gets injected into the process as a DLL and actually does the patching Is that right? Edited September 8, 2016 by kincses
mrexodia Posted September 9, 2016 Posted September 9, 2016 Yes, or alternatively you could use CreateProcess with the DEBUG_ONLY_THIS_PROCESS flag to receive debug events. This might be problematic if your target has anti-debug detection though, I would recommend doing it the DLL injection route.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now