0% found this document useful (0 votes)
24 views8 pages

CBJ 1 2 2006 Kruse Processless Threads

The document discusses a technique to create threads outside of the main application process to continue execution even if the main application terminates. It details how the technique works on Windows 2000/XP/2003 by resolving API function calls using the target process and creating memory areas for code and data injection.

Uploaded by

Basifo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
24 views8 pages

CBJ 1 2 2006 Kruse Processless Threads

The document discusses a technique to create threads outside of the main application process to continue execution even if the main application terminates. It details how the technique works on Windows 2000/XP/2003 by resolving API function calls using the target process and creating memory areas for code and data injection.

Uploaded by

Basifo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 8

CodeBreakers Journal – Vol. 1, No.

2, 2006

Processless Applications – Remote Threads on


Microsoft Windows 2000, XP, and 2003
by Thomas Kruse
Vol. 1, No. 2, 2006

Abstract:
The shown technique is able to run on all Windows operation systems. In order to avoid virus creation on it's best,
this technique is shown for W2K/XP/2K3 only. NT4 systems doesnt know several of the used API's, also it is possible
to rewrite them. Non NT­based systems need other techniques to detect the correct process to inject the code.

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com


CodeBreakers Journal – Vol. 1, No. 2, 2006

Processless Applications - • each created thread is allocating memory inside the


calling process memory area
Remotethreads on Microsoft
• the calling process memory area is available for
Windows 2000, XP, and 2003 reading
By Thomas Kruse New memory could be given by using VirtualAlloc API
from KERNEL32.DLL, which is available on all OS. If we
want to make ”crackers life” harder, we have to leave our
The shown technique is able to run on all Windows
calling process memory area and create threads outside
operation systems. In order to avoid virus creation on it's
our application. This could be realized by using
best, this technique is shown for W2K/XP/2K3 only. NT4 VirtualAllocEx.
systems doesnt know several of the used API's, also it is
possible to rewrite them. Non NT­based systems need
other techniques to detect the correct process to inject the 2 General Idea
code.
The idea to create a ”better protected” application is
This essay was created while searching for new software based on the given leaks shown before. We need some
protections to make "crackers life" even harder. Based on name conventions to understand the workflow:
"WatchDog theory" ­ another way to protect applications ­
• carrier application : application which will be
the idea is to create threads outside the main application
executed. It creates the remote thread, inject the code
which are able to continue workflow also if the main
and terminates itself.
application terminates. This essay will show up a way to
display a messagebox from process "Explorer.Exe", which • target application : application which recives the
is available on all OS. new thread. It’s avail if the carrier starts.

The created application is "processless" in that way that


the main application becomes terminated after creating When the thread becomes executed, it contains code, but
the external thread. The shown source code is in Microsoft didn’t know correct API function calls. There are two
Assembler style MASM. ways to avoid this leak:

• resolve the function calls via carrier application


1 Introduction • resolve the function calls via target application

Before we start to create our application, we need some


basic knowledge about processes and threads. And how ** The carrier application and it’s threads become
they interact. eliminated. But the created thread belongs to the target
application. In case of that, this thread isn’t affected
Whenever an application becomes executed, it creates a while terminating the carrier application!
process and it’s main thread. This application is able to
run until:
3 Resolving API Calls
• it’s process becomes terminated
• the number of threads of it’s process becomes NULL This topic sounds hard. But it isn’t. Again we need to
know a technique which NT­based operating systems use
to ”sort” dynamic link librarys in memory.
The last term will show up a new point: we are able to
When a remote thread becomes executed, it is able to
create more then one thread for an application. And this
access the Process Environment Block (PEB) of it’s main
technique is used for WatchDog protected applications.
process. In our case, it is able to access the PEB of our
System processes for example have many threads target application. And each PEB contains entry’s about:
running inside one process. Also if one thread
terminates, the process is still running. And in addition • InLoadOrderModuleList
to this, new threads could be created while the process is • InMemoryOrderModuleList
running. • InInitializationOrderModuleList
But there is another point which we need to inspect:
While debugging an application which creates more then The first two named contain the application itself as first
one thread (e.g. WatchDog protected app), it is possible to module, followed by needed DLL’s (and accessable ones).
trace the thread assembler code! The reason for this is The last is more interesting. NT­based operation systems
simple: need the NTDLL.DLL. This DLL is the first entry in

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com


CodeBreakers Journal – Vol. 1, No. 2, 2006

InInitializationOrderModuleList. And the next module • inject code in code area


is; KERNEL32.DLL! Exactly what we need, because this
• create the remote thread in suspended mode
library contains the API functions for two important
calls: GetProcAddress[1] and LoadLibraryA. By resolving By using two memory areas ­ for code and data ­ we
these two calls we are able to import what ever we want where able to create address independent code during
to. But where to store the resolved addresses? development!
We are leaving the carrier application process memory
4 Preparing memory areas range and have ­ during startup of our thread in target ­
no idea about address ranges in our target application
We where able to resolve needed API calls, but we need
process. And by using this technique, the code is target
to find a way to store them. One way is to store them on
independent, regardless which application is affected.
stack, another to create a seperate memory area for the
data of our thread. But by using the second technique, we
need to tell our remote thread where to find the data. 5 Thread Data Part
Lets have a closer look to API function
CreateRemoteThread: The data part is created via VirtualAllocEx and
WriteProcessMemory[1] inside the process memory
HANDLE CreateRemoteThread( range of our target application. By using VirtualAllocEx,
HANDLE hProcess, we where able to name the target process within the first
parameter of this API call. While debugging the carrier
LPSECURITY_ATTRIBUTES lpThreadAttributes,
application, this memory area isn’t easy to trace, because
DWORD dwStackSize, it belongs to another process and the debugger won’t
LPTHREAD_START_ROUTINE lpStartAddress, know this memory areas! For our example, we use the
following data part:
LPVOID lpParameter,
DWORD dwCreationFlags, .data
LPDWORD lpThreadId _sizeofInject dd offset _inject_end ­ offset _inject
); _sizeofData dd offset _ENDOFDATA ­ offset _dwThreadID

• lpStartAddress: Pointer to the application­defined _dwThreadID dd 0


function of type LPTHREAD START ROUTINE to be _dwThreadHandle dd 0
executed by the thread and represents the starting _lpCode dd 0
address of the thread in the remote process. The
_lpData dd 0
function must exist in the remote process.
_lpRsrc dd 0
• lpParameter: Specifies a single 32­bit value passed to
the thread function. _dwExitCode dd 0
_Kernel32 dd 0
If our thread becomes executed, lpParameter is still on _User32 dd 0
stack. It is available via following codepart of our injected _GetProcAddress dd 0
threadcode:
_GetExitCodeThread dd 0
; ­­ access the given parameter from Create(Remote)Thread _ExitThread dd 0
mov eax,dword ptr [ebp+0Ch] ; get parameter _szMessageBoxA db "MessageBoxA",0
push eax ; store this value on stack _szExitThread db "ExitThread",0
We are able to split data and code in two memory areas. _szExitCodeThread db "GetExitCodeThread",0
We simply use the API VirtualAllocEx twice: _szText db "Hello from Thread!",0

• create data area in target application _szCaption db "Thread:",0


• store the memory address, we need it as lpParameter _ENDOFDATA dd 0
or CreateRemoteThread
• inject data in data area
The first two and the last shown entry where NOT
• create code area in target application implemented! They only describe the size to inject. The
• store the memory address, we need it as data part to inject starts with dwThreadID and end up
lpStartAddress with szCaption as last entry. The data part contains

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com


CodeBreakers Journal – Vol. 1, No. 2, 2006

several placeholders for resolved API function calls and * The reader who understand the shown thread code in
strings (for MessageBox call and additional API Section IX might have seen, that the base address of
functions to resolve). There are several placeholders USER32.DLL is not resolved by our thread code! It’s up
which are not used in our example, but this data part is to you to change it.
prepared for further projects, too.
Knowing this workflow we are able to create the carrier
application general part. It contains needed data and
6 Thread Code Part included functions and is shown in Section X.

We are able to imagine the thread code workflow by The code part shown in Section XI realises exactly what
analyzing this given data part: we want to do. And this code could be used as general
template to create such applications! If you want to use
• resolve GetProcAddress API from KERNEL32.DLL resources later on, simply create another memory region
• resolve GetExitCodeThread API from by using VirtualAllocEx and store the memory address
KERNEL32.DLL inside the thread data ( lpRsrc).
• resolve ExitThread API from KERNEL32.DLL
• resolve MessageBoxA API from User32.DLL 8 Conclusions
• prepare MessageBoxA parameters on stack The shown technique uses one target which executes the
• show the messagebox injected code. But it is also possible to:
• prepare GetExitCodeThread parameter on stack
• inject code in DIFFERENT target applications and
( dwThreadHandle)
communicate between these targets using the
• get the exitcode technique from carrier application to access a process
• exit the tread (thread terminates, process continue!) • WALK from one target application to another,
destroying itself after inject needed parts to new
And again: In order to make ”crackers life” even harder, target by using polymorphic code
we use several tricks to realize this code! The code sown • WALK from one target application to another, leaving
in Section IX needs some ”brain gymnastics”, but it is itself intact and create ”splitted” applications which
documented and ­ as time goes by ­ easy to understand. communicate together
• INTERACT with still running carrier application as
7 Carrier Application kind of ”external” WatchDog from target
application(s)
The prepared thread code is useless without the carrier • all COMBINATIONS of shown examples above
application. Lets have a closer look on what to do:

• check the operating system (do not execute on 9X) In combination with already existing software
• resolve USER32.DLL base address for our thread (*) protections it is possible to avoid ”readable” thread data.
• create a snapshot of running processes By using polymorphic code and crypto algorythms the
reversing approach becomes nearly impossible.
• find the target application (explorer.exe)
• if not found, terminate with error message
• store target processID 9 SourceCode Thread.inc
• free snapshot We use several structures for resolving the export
• grant access to target application via OpenProcess[1] address table from KERNEL32.DLL. These structures
are changed into correct code during compilation and in
• store the target process handle
case of that, we didn’t need to make life harder than it is.
• create two memory areas via VirtualAllocEx
• create a remote thread in suspended mode ;­­ thread.inc (data and code)

• inject the parts via WriteProcessMemory .data

• resume the remote thread to become executed via _sizeofInject dd offset _inject_end ­ offset _inject
ResumeThread[1] _sizeofData dd offset _ENDOFDATA ­ offset _dwThreadID
• close the target process handle _dwThreadID dd 0
• terminate the carrier via ExitProcess _dwThreadHandle dd 0
_lpCode dd 0

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com


CodeBreakers Journal – Vol. 1, No. 2, 2006

_lpData dd 0 add eax,[eax].e_lfanew


_lpRsrc dd 0 assume eax:ptr IMAGE_NT_HEADERS
_dwExitCode dd 0 mov
edi,[eax].OptionalHeader.DataDirectory[0].VirtualAddress
_Kernel32 dd 0
add edi,ebp ; add the base value
_User32 dd 0
assume edi:ptr IMAGE_EXPORT_DIRECTORY
_GetProcAddress dd 0
mov esi,[edi].AddressOfNames
_GetExitCodeThread dd 0
add esi,ebp ; add the base value
_ExitThread dd 0
xor edx,edx ; clear edx
_szMessageBoxA db "MessageBoxA",0
assume eax:nothing ; avoid errors
_szExitThread db "ExitThread",0
_szExitCodeThread db "GetExitCodeThread",0
_loopstart:
_szText db "Hello from Thread!",0
mov eax,[esi]
_szCaption db "Thread:",0
add eax,ebp ; add the base value
_ENDOFDATA dd 0

cmp word ptr [eax+0Ch],"ss"


.code
jne _nextloop
_inject: ; start offet of code to inject
cmp dword ptr [eax+04h], "Acor"
jne _nextloop
; ­­ access the given parameter from Create(Remote)Thread
cmp dword ptr [eax+00h], "PteG"
mov eax,dword ptr [ebp+0Ch] ; get parameter
jne _nextloop
push eax ; store this value on stack
cmp dword ptr [eax+08h], "erdd"
jne _nextloop
; resolve the kernel32 base via PEB (NT­based!)
mov eax,7FFDF00Ch ; pointer to PEB_LDR
mov eax,[edi].AddressOfNameOrdinals
mov eax,[eax]
add eax,ebp ; add the base value
add eax,1Ch ; pointer to InInitOrder_First
movzx ebx,word ptr [edx*2+eax]; get ordinal
mov eax,[eax] ; NTDLL.DLL InitOrder_Next
mov eax,[eax] ; K32.dll InitOrder_Next
mov eax,[edi].AddressOfFunctions
add eax,8h ; pointer to base address
add eax,ebp ; add the base value
mov eax,[eax] ; get the base addess
mov ebx,[ebx*4+eax]
mov ebx,[esp] ; get base data
add ebx,ebp ; ebx holds value of function
add ebx,18h ; kernel32 address place
jmp _found ; quit searching
mov [ebx],eax ; store kernel32 base
mov ebx,eax ; to ebx
_nextloop:
add esi,4
; ­­ GetProcAddress in another style
inc edx
push edi ; store edi
cmp edx,[edi].NumberOfNames
push esi ; store esi
jne _loopstart
push ebp ; store ebp
push edx ; store edx
_found:
mov eax,ebx ; kernel32
assume edi:nothing ; avoiding errors
mov ebp,eax ; ebp becomes base address
pop edx ; restore edx
assume eax: ptr IMAGE_DOS_HEADER
pop ebp ; restore ebp

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com


pop esi ; restore esi sub eax,1ch ; restore the original data base
pop edi ; restore edi
; ­­ start visible "action" inside the created thread
mov eax,[esp] ; get base data push eax ; store data base
add eax,20h ; GetProcAddress placeholder mov ebx,eax ; ebx becomes data base
mov [eax],ebx ; store API address mov edx,[eax+1Ch] ; function MessageBoxA
add ebx,68h ; pointer to caption
mov eax,[esp] ; init eax with base data push 0 ; MB_OK
push ebx ; caption
; ­­ resolve all needed API's for this thread sub ebx,13h ; pointer to text
mov ebx,[eax+18h] ; base address kernel32 push ebx ; text
mov edx,[eax+20h] ; function GetProcAddess push 0 ; handle
push eax ; store base data on stack call edx ; MessageBoxA
add dword ptr [esp],38h ; pointer to ExitThread String pop eax ; restore data base/correct stack!
push ebx ; the kernel32 base
call edx ; call GetProcAddress ; ­­ final cleanup and destroying the created thread
xchg eax,ebx push eax ; store data base
pop eax ; restore data base mov ebx,eax
add eax,28h ; placeholder of ExitThread add ebx,14h ; pointer to ExitCode
mov dword ptr [eax],ebx ; store the resolved address push ebx ; address to recieve the ExitCode
sub eax,28h ; restore the original data base sub ebx,10h ; pointer to ThreadHandle
push eax ; store it on stack push dword ptr[ebx]
mov ebx,[eax+18h] ; base address kernel32 add ebx,20h ; pointer to GetExitCodeThread
mov edx,[eax+20h] ; function GetProcAddess call dword ptr[ebx] ; GetExitCodeThread
push eax ; store base data on stack xchg eax,ebx
add dword ptr [esp],43h ; GetExitCodeThread String pop eax ; restore data base
push ebx ; the kernel32 base mov ebx,eax
call edx ; call GetProcAddress add ebx,14h ; pointer to ExitCode
xchg eax,ebx push dword ptr[ebx] ; ExitCode for this thread
pop eax ; restore data base add ebx,14h ; pointer to ExitThread
add eax,24h ; placeholder of ExitThread call dword ptr[ebx] ; ExitThread
mov dword ptr [eax],ebx ; store the resolved address _inject_end: ; end offset of code to inject
sub eax,24h ; restore the original data base
push eax ; store it on stack
mov ebx,[eax+1ch] ; base address user32 10 SourceCode inject.inc
mov edx,[eax+20h] ; function GetProcAddess
; ­­ inject.inc
push eax ; store base data on stack
.386 ; minimum required CPU
add dword ptr [esp],2ch ; pointer to MessageBoxA String
.model flat,stdcall ; win 32 app
push ebx ; the user32 base
option casemap:none ; case sensitive
call edx ; call GetProcAddress
xchg eax,ebx
include windows.inc ; stuff we need
pop eax ; restore data base
add eax,1ch ; placeholder MessageBoxA (User32)
incboth macro incl ; macro for less typing
mov dword ptr [eax],ebx ; store the resolved address

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com


CodeBreakers Journal – Vol. 1, No. 2, 2006

include incl.inc invoke MessageBox,NULL,addr szErrorOS,addr


szErrorCaption,MB_OK
includelib incl.lib
jmp _exitApp
endm
.endif
invoke LoadLibrary,addr szUser32
incboth kernel32 ; for kernel32 API's
mov _User32,eax
incboth user32 ; for user32 API's
invoke FreeLibrary,_User32
invoke CreateToolhelp32Snapshot, TH32CS_SNAPALL, 0
include thread.inc ; thread related data
mov hSnapShot, eax ; store the snapshot handle
mov myProcess.dwSize, sizeof myProcess
.data
invoke Process32First,hSnapShot, ADDR myProcess
szErrorText db "Process EXPLORER.EXE not found",0
.while eax
szErrorOS db "You need an NT­based OS!",0
lea eax,myProcess.szExeFile
szErrorCaption db "Error occured:",0
lea esi,szExplorer ; the name to search
szExplorer db "explorer.EXE",0
invoke lstrcmpi,eax,esi ; compare both strings
szUser32 db "user32.dll",0
.if eax==0 ; if the same...
push myProcess.th32ProcessID
.data?
pop _myProcessID ; get the process ID and
hInstance dd ? ; application instance handle
jmp @F ; leave search routine
_myProcessID dd ? ; resolved process ID
.else
_myProcessHandle dd ? ; resolved process handle
invoke Process32Next,hSnapShot, ADDR myProcess
_BytesWritten dd ? ; for WriteProcessMemory
.endif
hSnapShot dd ? ; snapshot handle
@@:
myProcess PROCESSENTRY32 <>
.endw
invoke CloseHandle,hSnapShot ; close snapshot handle

11 SourceCode inject.asm
mov eax,_myProcessID ; get the process ID
test eax,eax ; is there an ID
; ­­ inject.asm jne _goOn ; if so, continue
include inject.inc ; additional data and includes invoke MessageBox,NULL,addr szErrorText,\
addr szErrorCaption, MB_OK
.code jmp _exitApp ; leave application
start: ; application entry point
; ­­ carrier application _goOn:
invoke GetModuleHandle,NULL ; get application module invoke
handle OpenProcess,PROCESS_ALL_ACCESS,NULL,_myProcessID
mov hInstance,eax ; and store it mov _myProcessHandle,eax ; store the process handle

assume fs:nothing ; needed for MASM ; ­­ create two mem parts: one for code, one for data
mov eax,fs:[18h] ; get TEB self pointer invoke
mov ebx,fs:[30h] ; get PEB pointer VirtualAllocEx,_myProcessHandle,NULL,1000h,MEM_COMM
IT,\
PAGE_EXECUTE_READWRITE
.if eax!=7FFDE000h || ebx!=7FFDF000h ; NT check
mov _lpCode,eax ; store code base in thread data

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com


CodeBreakers Journal – Vol. 1, No. 2, 2006

invoke
VirtualAllocEx,_myProcessHandle,NULL,100h,MEM_COMMI
T,\
PAGE_EXECUTE_READWRITE
mov _lpData,eax ; store data base in thread data

; ­­ create the thread in suspended mode!


; ­> the 4th (here: _lpData) parameter is important; it
; guarantees an memory independent inject code...
invoke
CreateRemoteThread,_myProcessHandle,NULL,NULL,_lpCod
e,\
_lpData, CREATE_SUSPENDED,\
addr _dwThreadID
mov _dwThreadHandle,eax ; store handle in thread data

; ­­ write code section


invoke WriteProcessMemory,_myProcessHandle,_lpCode,\
addr _inject, _sizeofInject,\
addr _BytesWritten

; ­­ write data section


invoke WriteProcessMemory,_myProcessHandle,_lpData,\
addr _dwThreadID, _sizeofData,\
addr _BytesWritten
invoke ResumeThread,_dwThreadHandle ; start the thread
invoke CloseHandle,_myProcessHandle ; close process
handle
_exitApp:
invoke ExitProcess,NULL ; exit application
end start ; end of application code section

© CodeBreakers Journal, http://www.CodeBreakers­Journal.com

You might also like