Document title: complete process hiding in winnt & Win2k
In this paper, the conversion from C ++ builder research-http://www.ccrun.com/article/go.asp? I = 360 & D = 8kjl0n
Faced with a large number of computer experts, after a long time of consideration, I finally decided to look ugly, Article Try to use the most concise and easy-to-understand vocabulary and examples to introduce, and hope to be helpful to some beginners and advanced students.
There are countless examples of process hiding in 98. The hidden method in winnt/Win2k, shotgun, a master of the Temple of the West, launched an example on the Internet in June last year: unveiling the secrets of Trojans <4>. I read his articles many times and admire his computer skills and enthusiasm for helping his friends. Here is a supplement and in-depth introduction to shotgun's article.
In WINNT, "Truly hiding processes" is simply impossible, as long as our Program Running in the form of a process kernel is impossible to escape from the eyes of CTRL + ALT + DEL. So strange, isn't this in conflict with our title "completely hiding processes under winnt & Win2k? Yes, it should actually be: Execute the target in non-process mode Code To "process hiding.
What we use here is to execute our code in the thread mode in the host process. Implementation is very simple. First, create a thread that does not execute any statements.
DWORD stdcall threadproc (lpvoid * lpvoid ){
Return 0;
}
Then, copy the thread code to any place that the host process can execute (that is, the page attribute is pagge_execute_readwrite), such as the shared memory injection area and the host process. Here we select the host process. When copying a backup, we need to first use the virtualallocex function in the host process to apply for a piece of memory, and then use writeprocessmemory to write the thread body to the host process.
After the preceding steps are completed, You can activate the createremotethread function. The following is a complete example.
// Remote thread execution body
DWORD _ stdcall threadproc (void * lppara ){
Return 0;
}
Int main (INT argc, char * argv []) {
Const DWORD threadsize = 1024*4; // The tentative thread size is 4 K, which is not so large. I will introduce it later
DWORD byte_write;
// Obtain the ID handle of the specified process and set its permission to process_all_access. 992 indicates the ID of the local process. I will not talk about how to obtain the ID.
Handle hwnd =: OpenProcess (process_all_access, false, 992 );
If (! Hwnd) return 0;
Void * premotethread =: virtualallocex (hwnd, 0, threadsize, mem_commit | mem_reserve, page_execute_readwrite); // apply
If (! Premotethread) return 0;
If (! : Writeprocessmemory (hwnd, premotethread, & threadproc, threadsize, 0) // write process
Return 0;
// Start the thread
Handle hthread =: createremotethread (hwnd, 0,0, (DWORD (_ stdcall *) (void *) premotethread, null, 0, & byte_write );
If (! Hthread) {// memory allocation not released
Return 0;
}
Return 0;
}
By now, the hidden methods have come to an end. I believe my friends have a very clear idea of this idea.
After understanding the hidden methods, we began to write the execution part of the thread. As follows:
DWORD _ stdcall threadproc (void * lppara ){
MessageBox (null, "hello", "hello", 0 );
Return 0;
}
After compilation and execution, you will find an invalid operation error. Why? In the Win2k operating system managed by segment-page memory, all constants are compiled in the PE file during compilation. data section, while the code segment is in. text, so the code copied to the host process is in. the code in text, MessageBox (null, (char *) pointer, P, 0); the address pointed to is the memory virtual address of the process. But cannot be accessed in the host process. The solution is simple. Copy "hello" to the target process and then reference it. Similarly, when the MessageBox function address is compiled, it is stored in. in the import, anyone who has written the Win2k virus knows that all constants and function entry addresses must be defined and obtained in the code segment. Here we are a little similar to this. To put it bluntly, we also write the function entry address together into the target process. // First define the parameter structure
Typedef struct _ remotepara {// parameter structure
Char pmessagebox [12];
DWORD dwmessagebox;
} Remotepara;
// Pay the value
Remotepara myremotepara;
: Zeromemory (& myremotepara, sizeof (remotepara ));
Hinstance huser32 =: loadlibrary ("user32.dll ");
Myremotepara. dwmessagebox = (DWORD): getprocaddress (huser32, "messageboxa ");
Strcat (myremotepara. pmessagebox, "Hello \ 0 ");
// Write it into the target process
Remotepara * premotepara = (remotepara *): virtualallocex (hwnd, 0, sizeof (remotepara), mem_commit, page_readwrite); // pay attention to the page protection attribute when applying for a space.
If (! Premotepara) return 0;
If (! : Writeprocessmemory (hwnd, premotepara, & myremotepara, sizeof myremotepara, 0) return 0;
// Enable and PASS Parameters
Handle hthread =: createremotethread (hwnd, 0,0, (DWORD (_ stdcall *) (void *) premotethread, premotepara, 0, & byte_write );
If (! Hthread ){
Return 0;
} Okay, that's easy. Next we will provide a MessageBox instance: // remotethread. cpp: defines the entry point for the console application.
//
# Include "stdafx. H"
Typedef struct _ remotepara {// parameter structure
Char pmessagebox [12];
DWORD dwmessagebox;
} Remotepara;
// Remote thread
DWORD _ stdcall threadproc (remotepara * lppara ){
Typedef int (_ stdcall * mmessageboxa) (hwnd, lpctstr, lpctstr, DWORD); // defines the MessageBox Function
Mmessageboxa mymessageboxa;
Mymessageboxa = (mmessageboxa) lppara-> dwmessagebox; // obtain the function entry address.
Mymessageboxa (null, lppara-> pmessagebox, lppara-> pmessagebox, 0); // call
Return 0;
}
Void enabledebugpriv (); // raise the application-level debugging permission
Int main (INT argc, char * argv []) {
Const DWORD threadsize = 1024*4;
DWORD byte_write;
Enabledebugpriv (); // raise the permission
Handle hwnd =: OpenProcess (process_all_access, false, 992 );
If (! Hwnd) return 0;
Void * premotethread =: virtualallocex (hwnd, 0, threadsize, mem_commit | mem_reserve, page_execute_readwrite );
If (! Premotethread) return 0;
If (! : Writeprocessmemory (hwnd, premotethread, & threadproc, threadsize, 0 ))
Return 0;
// Pay the value again
Remotepara myremotepara;
: Zeromemory (& myremotepara, sizeof (remotepara ));
Hinstance huser32 =: loadlibrary ("user32.dll ");
Myremotepara. dwmessagebox = (DWORD): getprocaddress (huser32, "messageboxa ");
Strcat (myremotepara. pmessagebox, "Hello \ 0 ");
// Write it into the target process
Remotepara * premotepara = (remotepara *): virtualallocex (hwnd, 0, sizeof (remotepara), mem_commit, page_readwrite); // pay attention to the page attributes when applying for a space.
If (! Premotepara) return 0;
If (! : Writeprocessmemory (hwnd, premotepara, & myremotepara, sizeof myremotepara, 0) return 0;
// Start the thread
Handle hthread =: createremotethread (hwnd, 0,0, (DWORD (_ stdcall *) (void *) premotethread, premotepara, 0, & byte_write );
If (! Hthread ){
Return 0;
}
Return 0;
}
// Escalate Permissions
Void enabledebugpriv (void)
{
Handle htoken;
Luid sedebugnamevalue;
Token_privileges tkp;
If (! Openprocesstoken (getcurrentprocess (),
Token_adjust_privileges | token_query, & htoken ))
Return;
If (! Lookupprivilegevalue (null, se_debug_name, & sedebugnamevalue )){
Closehandle (htoken );
Return;
}
Tkp. privilegecount = 1;
Tkp. Privileges [0]. luid = sedebugnamevalue;
Tkp. Privileges [0]. Attributes = se_privilege_enabled;
If (! Adjusttokenprivileges (htoken, false, & tkp, sizeof tkp, null, null ))
Closehandle (htoken );
}
After the program is compiled and executed, a thread is created in the process with process number 992. The Hello dialog box is displayed. Is it very simple!
Note the following points:
1. When the remote thread applies for space in the host process, the space size is determined as a problem that I have been unable to solve. I used two threads that are close to each other, taking the distance between threads and adding the parameter size as the applied space, the following operations will still occur:
Static void startthread (lpvoid * lppara ){
Return;
}
Static void endthread (lpvoid * lppara ){
Return;
}
Then use DWORD dwlenght = (DWORD) (char *) & startthread-(char *) & endthread); // obtain the length of the startthread thread code,
Dwlenght + = sizeof (threadpara );
I am confused that illegal operations will still occur. In Win2k, the page size of the thread's default stack is 4 kb. Here, when I apply for memory for the thread, the applied size uses a constant, always a multiple of 4 kb, make sure that the value is as big as possible during selection. After the thread can run successfully, change the value slightly. The solution is a bit dumb. For example, if your friends here have a better method, please kindly advise.
2. When and what parameters need to be passed in from the outside? I don't have a very strong answer here. My understanding is: apart from. all sections except the text section must be passed to the thread using external parameters, such :. rsrc ,. data, RDATA, and other 15 sections. In our actual compiling process, beginners do not know where our code will be compiled. At this time, we can decompile Alt + 8 (shortcut key in VC) during running, generally, there are address fetch statements such as Lea eax P and push offset P. At this time, most of us need to pass in with parameters. Therefore, when writing, you must pay attention to parameters, because thread execution is in other processes, and an application with common permissions cannot debug other processes across processes. Including VC, and we cannot debug our remote threads. We are familiar with compilation and can use SoftICE for debugging. This requires a certain degree of knowledge.
3. Permission. This is very important. Shotgun has made it clear in this regard, and there are many related articles on the Internet, so I will not talk about it much. The enabledebugpriv function in this article allows this process to create threads in processes such as Internet, winlogin, and LSASS. The Win2k process viewer cannot remove it.
4. There are many methods to obtain process IDs, such as enumprocesses, createconlhelp32snapshot, process32first/process32next, and ntquerysysteminformation. To reduce code, in this example, the process ID is obtained directly in the process viewer. Finally, let's go back to shotgun's article. At this time, we have been very clear about why a DLL file exists in his method. The thread body of the remote thread itself is the loadlibrary function, that is, the thread entry address is the loadlibrary entry address, which is a function in the system kernel32.dll and can be called by any process. The loadlibrary function is used in the thread to load our DLL into the system space. When the thread is executed, our DLL starts to work. After the thread is executed, do not forget to use virtualfreeex to release the applied memory zone.
The two methods are compared, obviously:
1. When using DLL, it is very simple to create, and does not require too much operating system and memory operation knowledge, and you can directly debug the DLL file. It is easy to implement.
2. The method of copying data directly to the process is a little more complicated. If you are not careful, it is easy to perform illegal operations. Of course, the annoying DLL file is also removed. After the program is executed, it is difficult to find its source. It is the preferred method for hiding Trojans other than viruses. Here I have made a lot of reference to the source code of the nongmin.cn (farmer) program. His program is very helpful to me. Although I have never met anyone, I admire him for his computer skills and achievements, and respect his style of work. In the future, all the non-commercial software or small code I wrote will appear in the form of source code. This is a bit messy. I hope it will be helpful to everyone. I would like to share with all my friends who love computers and work on computers.