看了 Executing DLLs as a Windows Service
這篇文章後終於知道是怎麼一回事了,於是把相關的register value 砍掉,用sc delete "Server_name"砍掉Windows Service。
再找出對應的random.dll找出他修改日期。再搜出相關檔案一併移除。
https://code.google.com/p/malwarecookbook/source/browse/trunk/13/7/install_svc.py
You can find supporting material for this recipe on the companion DVD.
A service DLL has a special entry point that only executes properly if the DLL is running as a Windows service. This is similar to a host process restriction, except the primary factor is the context in which the DLL executes and other environmental factors, as opposed to the name of the host process. It is inevitable that you will need to perform behavioral analysis on service DLLs. Many trojans drop or download a DLL, load the DLL as a service, and then delete the dropper component. As a result, when you perform a forensic investigation, in most cases you will only find the DLL. This recipe shows how you can overcome the challenges of service DLLs.
Service DLL Entry Points
Most malware samples create a service of type SERVICE_WIN32_SHARE_PROCESS for their malicious service DLLs. This service type indicates that the DLL should run within a generic host process (svchost.exe) that can be shared with other DLLs also running services. When a particular service is activated by a call to the StartService API function, the svchost.exe process loads the service DLL and calls an exported function named ServiceMain. Now you know how to distinguish a service DLL from a normal DLL—just look for an export named ServiceMain.
Note |
Distinguishing service DLLs, based on the existence of an export named ServiceMain, works almost 100 percent of the time. However, the name of the service entry point can be configured per service by modifying the service's configuration in the registry such as:HKLM\System\CurrentControlSet\Service\<SERVICENAME>\Parameters\ServiceMain =“AlternateFunction”. In this case, you may find a service DLL that exports a function named AlternateFunction instead of ServiceMain. |
Service Initialization
The Service Control Manager (SCM), which is the services.exe process, requires that all newly started services must perform the following actions within the first few seconds of their execution:
-
Register its control handlers by calling RegisterServiceCtrlHandler
-
Report a status of SERVICE_RUNNING by calling SetServiceStatus
The initialization procedure is the crux of why you cannot execute service DLLs outside of a service context. For example, when you use StartService, the SCM becomes aware that a service should be starting. If you try to load a service DLL using a command such as
C:\> rundll32 malicious.dll,ServiceMain
the DLL's calls to RegisterServiceCtrlHandler will fail because the SCM is not expecting a service to start. In almost all cases, if the call toRegisterServiceCtrlHandler fails, the DLL will just exit, as shown in Figure 13-13.
Likewise, you also cannot run a normal DLL in a service context. In other words, if the DLL does not export a function named ServiceMain, or if theServiceMain function does not perform the required initialization tasks, then the SCM will assume the service has hung and forcefully terminate the host process.
Installing Service DLLs
At this point, you should understand how to distinguish service DLLs from normal DLLs and why you must run service DLLs in a proper service context. You can install the DLL as a service on your analysis machine by creating a simple batch script such as the following:
REM REM Usage: install_svc.bat <SERVICENAME> <DLLPATH> REM @echo off set SERVICENAME=%1 set BINPATH=%2 sc create "%SERVICENAME%" binPath= "%SystemRoot%\system32\svchost.exe \ -k %SERVICENAME%" type= share start= auto reg add "HKLM\System\CurrentControlSet\Services\%SERVICENAME%\Parameters" \ /v ServiceDll /t REG_EXPAND_SZ /d "%BINPATH%" /f reg add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\SvcHost" \ /v %SERVICENAME% /t REG_MULTI_SZ /d "%SERVICENAME%\0" /f sc start %SERVICENAME%
Of course, before running install_svc.bat, you can set up your dynamic analysis tools to capture the service's behavior.
Passing Arguments to Services
The only issue with the batch script is that you cannot pass custom arguments to the service. A ServiceMain function conforms to the following specification per Microsoft, which means it can accept a variable number of string-type arguments.
VOID WINAPI ServiceMain( __in DWORD dwArgc __in LPTSTR *lpszArgv ); dwArgc [in] The number of arguments in the lpszArgv array. lpszArgv [in] The null-terminated argument strings passed to the service by the call to the StartService function that started the service. If there are no arguments, this parameter can be NULL. Otherwise, the first argument (lpszArgv[0]) is the name of the service, followed by any additional arguments (lpszArgv[1] through lpszArgv[dwArgc-1]).
In many cases, the ServiceMain function will not accept arguments and you can start the service from a batch script, the services.msc snap-in, or Process Hacker. However, consider you find a DLL with the following code in its ServiceMain function:
VOID WINAPI ServiceMain( __in DWORD dwArgc __in LPSTR *lpszArgv) { // hard-coded password somewhere in the DLL binary LPSTR specialPass = "myPass"; // exit if no parameters were passed if (dwArgc < 2) return; // exit if the password does not match if (strcmp(lpszArgv[1], specialPass) != 0) return; //Perform malicious activity }
The previous code prevents a service from executing properly if the second argument is not equal to the hard-coded special password. This is a simplified version of what you might see in the wild, but that is the point—extremely simple things can prevent you from analyzing the service DLL's behavior. If you find a DLL with a ServiceMain export, examine the function in IDA to see if it accepts any arguments and if so, how it uses them. If you need to supply specific arguments to the DLL when starting the service, you can use the install_svc.py script, which is on the DVD that accompanies this book.
import win32service import win32con import win32api import sys if len(sys.argv) < 3: print 'Usage: %s <SVCNAME> <DLLPATH> [arg1 arg2 ...]' % sys.argv[0] sys.exit() ServiceName = sys.argv[1] ImagePath = sys.argv[2] ServiceArgs = sys.argv[3:] hscm = win32service.OpenSCManager( None, None, win32service.SC_MANAGER_ALL_ACCESS) try: hs = win32service.CreateService(hscm, ServiceName, "", win32service.SERVICE_ALL_ACCESS, win32service.SERVICE_WIN32_SHARE_PROCESS, win32service.SERVICE_DEMAND_START, win32service.SERVICE_ERROR_NORMAL, "C:\\WINDOWS\\System32\\svchost.exe -k " + ServiceName, None, 0, None, None, None) except: print "Cannot create service!" sys.exit() key = win32api.RegCreateKey(win32con.HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Services\\%s\\Parameters" % ServiceName) try: win32api.RegSetValueEx(key, "ServiceDll", 0, win32con.REG_EXPAND_SZ, ImagePath); finally: win32api.RegCloseKey(key) key = win32api.RegCreateKey(win32con.HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows NT\\CurrentVersion\\SvcHost") try: win32api.RegSetValueEx(key, ServiceName, 0, win32con.REG_MULTI_SZ, [ServiceName, '']); finally: win32api.RegCloseKey(key) win32service.StartService(hs, ServiceArgs) win32service.CloseServiceHandle(hs) win32service.CloseServiceHandle(hscm)
You can use the install_svc.py script to pass special arguments to a service DLL like this:
C:\> python install_svc.py testsvc C:\windows\system32\svc.dll myPass
Using the tricks described in this recipe, you can dynamically analyze DLLs that only run in a service context and that require specific arguments.
You can find can find supporting material for this recipe on the companion DVD.
There are many reasons why you may not want to execute a DLL exactly as the authors intended. For example, the DLL may contain anti-debugging tricks, noisy network communications, time-consuming sleep loops, or several functions that you need to bypass. Perhaps you only want to execute the function that extracts an embedded EXE to disk or that generates a random domain name to contact. This recipe describes how you can convert a DLL into an EXE and change its entry point to skip certain functions that you don't want to execute.
Consider the following example DLL:
BOOL Install(void) { if (DecodeEmbeddedEXE() && DropEmbeddedEXE()) return TRUE; return FALSE; } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: if (DebuggerActive() || !C2Active()) return FALSE; // Other insignificant code or anti-rce tricks // ... Install(); case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
In the DllMain routine, the DLL calls DebuggerActive (code not shown), which presumably returns TRUE if the malware detects the presence of a debugger. It also calls C2Active, which presumably returns TRUE if the malware can successfully contact its command and control server. If there are no debuggers attached to the DLL and the command and control server is active, the DLL calls the Install function to drop an executable. Otherwise, the DLL simply exits.
The purpose of this demonstration is to show how you can force execution of the Install function, without running the code in DllMain. Here are the steps you can follow:
-
Determine the relative virtual address (RVA) of the function you want to execute (see Recipe 13-1 for how to do this). Figure 13-14 shows that the RVA of the Install function is 0x10C0.
-
Use the dll2exe.py script, which you can find on the DVD that accompanies this book, to convert the DLL into an EXE and change theAddressOfEntryPoint value to the RVA of the Install function. To use the script, call it on the command line like this:
$ python dll2exe.py example.dll 0x10C0 Converting example.dll from DLL to EXE Characteristics 0x2102 => 0x102 Entry point RVA 0x1853 => 0x10C0 Saved new file as example.dll.exe
-
If you do not want to debug the function, you can execute example.dll.exe from cmd.exe. If you want to debug the function, open example.dll.exe in your debugger and it should automatically break at the new entry point. Figure 13-15 shows an example of what you'll see. The first instruction to be executed is at 0x100010C0, which is the beginning of the Install function. You bypassed all of the anti-debugging code in DllMain!
Figure 13-15: We bypassed DllMain and reached the Install functionHere is the code for dll2exe.py:
#!/usr/bin/python import pefile import sys, os IMAGE_FILE_DLL = 0x2000 if len(sys.argv) < 2 or not os.path.isfile(sys.argv[1]): print "\nUsage: dll2exe.py <filename> [EntryPoint RVA (hex)]\n" sys.exit() else: FileName = sys.argv[1] pe = pefile.PE(FileName) OldChars = pe.FILE_HEADER.Characteristics NewChars = OldChars - (OldChars & IMAGE_FILE_DLL) pe.FILE_HEADER.Characteristics = NewChars print "\nConverting %s from DLL to EXE" % FileName print "Characteristics 0x%x => 0x%x" % (OldChars, NewChars) if len(sys.argv) == 3: OldEP = pe.OPTIONAL_HEADER.AddressOfEntryPoint NewEP = int(sys.argv[2], 16) pe.OPTIONAL_HEADER.AddressOfEntryPoint = NewEP print "Entry point RVA 0x%x => 0x%x" % (OldEP, NewEP) ExeFileName = FileName + ".exe" pe.write(ExeFileName) print "Saved new file as %s\n" % ExeFileName
The method described in this recipe is not always as simple as it sounds. For example, if you want to force execution of a function that requires parameters, you will have to manually place those parameters on the stack before allowing the program to run. Additionally, if you redirect the entry point of a DLL or EXE that performs required startup routines or initializes global variables referenced by the function you want to execute, then you could run into serious issues. So, be aware of the caveats, but don't forget about the possibility of using this trick in the future.