FOR610 Work Notes
FOR610 Work Notes
Reverse-Engineering
Malware: Malware Analysis
Tools and Techniques
This document is FAR from being a replacement of the official
SANS materials but I made it to prepare myself for the GREM
Certification with the important topics I wanted to focus on.
The page number references are from the official SANS PDF
materials version FOR610_2_G01_05
Hope it helps
Investigation Tips
• Hide your origin (Page 19)
o TOR : but exit nodes are well known
o Commercial VPN : cheap and fast but exit nodes are well
known
o Custom VPN : Setup OpenVPN on a public cloud is the best
solution to keep investigations undetected
o Warning of DNS leakage : make sure DNS traffic goes through
VPN
• Using public services :
o Don't upload samples to 3rd party unless you're sure why
o Using public tools like urlQuery or vURL can reveal the
investigation
Tools
• pestr
• strings -a (once for ASCII, then --encoding=l for Unicode)
• peframe (REMnux) and PeStudio (Windows) both give a good overview
of the PE
• Detect It Easy and Exeinfo PE both give a good overview of the PE
Header (compiler, packer, entropy, hashes ...)
Tools
• Process Hacker
o Advanced Task Manager
o Network Tab is limited to active connections, no historical
view (check TcpLogView instead if needed)
• Process Monitor
o Records processes, registry, network, file activity
o Use filters to remove noise (unwanted / OS related events)
• RegShot : Take a shot before detonation, a shot after detonation,
and find modifications on files and registry keys
• ProcDot : Use Process Monitor output to create a graphical view
• Wireshark
o Sniff and analyze network packets (http, dns ...)
o Run it on REMnux with Windows Default GW defined with REMnux
IP
o Right Click -> "Follow" -> "TCP Stream" shows the complete
HTTP Request
• fakedns : On REMnux, will respond to DNS queries
• httpd : On REMnux, will respond to HTTP requests
• INetSim : On REMnux, will respond to DNS, HTTP, HTTPS queries,
will send fake files to malware ...
Ghidra
• Green Arrow -> Path if the condition is met
• Red Arrow -> Path if the condition is NOT met
• Blue Arrow -> Code Block ends with an unconditional JUMP
• To resize columns : Browser Field Formatter
• To see imported functions :
o Window -> Symbol References (+ filter by Imported)
o Focus on Access : "Call" in the right panel
• Rename variables and functions to make it more clear
Variables
• Local : Only accessible in the function that allocates it
• Global : Accessible from anywhere within the program and display
with an address (DAT_XXXXXXXX)
• Static : Only accessible in the function that allocates it but
not marked for reuse
short near
signed-
Instruction Description Flags jump jump
ness
opcodes opcodes
JO Jump if overflow OF = 1 70 0F 80
JNO Jump if not overflow OF = 0 71 0F 81
JS Jump if sign SF = 1 78 0F 88
JNS Jump if not sign SF = 0 79 0F 89
JE Jump if equal
ZF = 1 74 0F 84
JZ Jump if zero
JNE Jump if not equal
ZF = 0 75 0F 85
JNZ Jump if not zero
Jump if below
JB
Jump if not above or
JNAE unsigned CF = 1 72 0F 82
equal
JC
Jump if carry
JNB Jump if not below
JAE Jump if above or equal unsigned CF = 0 73 0F 83
JNC Jump if not carry
JBE Jump if below or equal CF = 1 or ZF =
unsigned 76 0F 86
JNA Jump if not above 1
Jump if above
JA CF = 0 and ZF =
Jump if not below or unsigned 77 0F 87
JNBE 0
equal
Jump if less
JL
Jump if not greater or signed SF <> OF 7C 0F 8C
JNGE
equal
JGE Jump if greater or equal
signed SF = OF 7D 0F 8D
JNL Jump if not less
JLE Jump if less or equal ZF = 1 or SF <>
signed 7E 0F 8E
JNG Jump if not greater OF
JG Jump if greater ZF = 0 and SF =
signed 7F 0F 8F
JNLE Jump if not less or equal OF
JP Jump if parity
PF = 1 7A 0F 8A
JPE Jump if parity even
JNP Jump if not parity
PF = 0 7B 0F 8B
JPO Jump if parity odd
Jump if %CX register is
JCXZ 0 %CX = 0
E3
JECXZ Jump if %ECX register is %ECX = 0
0
Unconditional Jumps
• JMP XXX
• CALL XXX : PUSH EIP, JUMP TO XXX
• RET : POP EIP
• OR (Page 125)
o Test each condition
o Jump to code block if a condition is met
o Negate logic of last condition and jump to end if true
Resources
• FindResourceW : Determine Location of a resource
• SizeofResource : Obtain the size of a resource
• LockResource : Obtain a pointer to the resource
• CreateFileA + WriteFile : Save the resource into a file
Mutex/Mutant
• CreateMutexA : creates or opens a mutex object
Stealers
• GetKeyState / GetAsyncKeyState : Determine is a specific key has
been pressed
• GetWindowText : Retrieves text from a windows's title bar
• OpenClipboard / GetClipboardData / CloseClipboard : Gather data
from user's clipboard
Internet
• InternetOpen / InternetConnect : Creates and HTTP Connection
• HttpOpenRequest / HttpAddRequestHeaders : Build the HTTP Request
• HttpSendRequest : Send the HTTP Request
• InternetReadFile : Read the response to the HTTP Request
Frequently used by downloaders
Risky Keywords
• Executing Embedded JavaScript : /JS, /JavaScript, /AcroForm, XFA
• Launching programs : /Launch, /EmbeddedFiles
• Action when file is opened : /OpenAction, /AA
• Interact with Websites : /URI, /SubmitForm
• Images : /XObject (to confirm with the /Subtype), /Xform
• Associate clickable link with an image : /Annots
• Object Stream (stream that contains other objects) : /ObjStm
Tools
• pdf-parser.py (Didier Stevens) (Page 9) :
o -a : show a summary of the file (pdf-parser.py doc.pdf -a)
o -s : select objects with a specific keyword (pdf-parser.py
doc.pdf -s /URI)
o -k : shows the values for the given key (pdf-parser.py
doc.pdf -k /URI)
o -o : examine a specific object (pdf-parser.py doc.pdf -o 6)
o -d : dump a specific object (pdf-parser.py doc.pdf -o 6 -d
object6.jpg)
o -f : apply filters
(FlateDecode,ASCIIHexDecode,ASCII85Decode,LZWDecode,RunLen
gthDecode)
o -w : raw
o -r : shows objects that reference the specified object number
(pdf-parser.py doc.pdf -r 6)
o -O : specifies pdf-parser to look into Object Streams if any
• pdf-parser.py doc.pdf -O -a
• pdf-parser.py doc.pdf -O -k /URI
• pdf-parser.py doc.pdf -O -r 39
o ++
• Fiddler (Page 20)
o Proxy tool to examine HTTP(S) requests, possibility to
extract file that was downloaded in the stream
Formats
• Before 2007 : Object Linking Embedding 2 (OLE2)
also called
o Structured Storage (SS)
o Compound File Binary Format (CFBF)
o Composite Document File V2 (CDFV2)
• After 2007 : Office Open XML (OOXML)
o ZIP and XML based
o Macros are saved in a OLE2 file inside the ZIP
Tools
• zipdump.py (Didier Stevens)
o Extract a specific file (zipdump.py doc.doc -s 5 -d >
image.jpg)
• Evilclippy :
o Remove the password on the macro (Macros are not crypted)
o evilclippy -uu doc.doc
VBA Purging
• P-code is removed from the file, source can still be extracted by
oledump.py
Tips
• If OLE2 file that contains VBA macros includes streams with "SRP"
-> cached compiled copy which can be examined with oledump.py and
strings for example (Page 53)
Tools
• rtfdump.py (Didier Stevens) (Page 102)
o Shows information (rtfdump.py doc.doc)
o Shows information about embedded objects (rtfdump.py doc.doc
-O)
o Extract embedded object (rtdump.py doc.doc -O -s 1 -d >
doc.object), the extracted object can be an OLE2 -> use
oledump.py to see inside.
o Look for unexpected/unknown characters in the "u=" tag (Page
108)
o Look for the group with a lot of hex characters at the
deepest nesting level in the "h=" tag
o Examine the content of a group (rtfdump.py doc.doc -s 5) and
look for NOP sleds (909090…)
o Extract the content of a group (rtfdump.py doc.doc -s 5 -H
-d > doc.bin) (Page 110)
• xorsearch
o Check if a file contains a shellcode by looking for well-
known operations (xorsearch -W -d 3 doc.bin) (Page 111)
Shellcode
• CALL / POP (can be some JMP around) to get EIP
• Access the PEB to locate kernel32.dll in the memory of the process.
A pointer to the PEB is at offset 0x30 of the TIB, which is at
address contained in the FS register
-> "MOV EAX, DWORD PTR FS:[30h]"
• Tools : scdbgc and runsc32
Keywords
• eval : Run the parameter as JavaScript
• document.write / document.body.appendChild /
document.parentNode.insertBefore (Page 6) -> Add other
JavaScript
• arguments.callee (ex var M1FDAB=arguments.callee.toString()) :
allow a function to examine his own body -> checks if code has
been modified! (Page 18)
REMnux
• js-beautify : can beautify JavaScript code
• extractscripts.py : extract JavaScript from a webpage
• SpiderMonkey
o JavaScript interpreter from Mozilla/FF (js -f script.js)
o JavaScript interpreter with other definitions and modified
eval function (js -f /usr/share/remnux/objects.js -f
script.js)
• box-js : For scripts designed to run outside browser
Windows
• Cscript (VBS and JS)
o AMSI Trace (Page 10)
• logman start AMSITrace -p Microsoft-Antimalware-Scan-
Interface Event1 -o AMSITrace.etl -ets
• cscript.exe script.js
• logman stop AMSITrace -ets
• AMSIScriptContentRetrieval > script-output.txt
o Redefine the "eval" function to add a "WScript.Echo(XXX)"
before eval(XXX) (Page 12)
Indicators
• Few readable strings
• Limited Imports
• High Entropy
• Section Names
• RawSize of section = 0 but VirtualSize > 0 (ie where the code
will be unpacked)
Tools
• PEStudio
• Detect It Easy
• Exeinfo PE
• Bytehist (for bytes distribution : not uniform -> not packed,
uniform -> packed)
Approach
• Disable ASLR (Modify the flag in the DllCharacteristics field of
PE Header or use "setdllcharacteristics -d file.exe")
• Run and let it unpack itself
• Search for strings with Process Hacker in the running process to
confirm it's unpacked
• Run Scylla.exe and attach to the process
o Dump
o IAT Autosearch
o Get Imports
o Fix Dump
While "dirty", this approach is suitable for static analysis and have
an overview of the malware. However, since we didn't fix the OEP, the
dumped executable won't run.
Approach
• Disable ASLR
• Load packed exe into x64dbg
• Find the end of the unpacker code
• Trace until OEP
o Confirm by "Search for" -> "Current Region" -> "String
References" to find some useful strings
o Confirm by "Search for" -> "Current Region" ->
"Intermodular calls" to find some useful API calls
• Dump process with "Plugins" -> "OllyDumpEx" -> "Dump Process"
o Get RIP as OEP (because we are on the first instruction of
the unpacked code)
o Set section UPX1 with MEM_WRITE attribute (otherwise
crash)
o Dump
• Rebuilt IAT with "Plugins" -> "Scylla"
o IAT Autosearch
o Get Imports
o Fix Dump (select the dump made with OllyDumpEx)
With this modified UPXed binary it's easy to find the end of the
unpacking code, but in real life, it may be more complicated.
Approach
• Disable ASLR
• Load packed exe into x64dbg and run it (F9)
• In Memory Map, look for memory segments with "E" (execute) flag
in the Protection column
• Right click and "Follow in Disassembler"
• Confirm by "Search for" -> "Current Region" -> "Intermodular
calls" to find some useful API calls
• Pick an interesting API and "Follow in Disassembler"
• Set a hardware breakpoint after the call to the API
• Restart the program (Ctrl-F2) and run it (F9)
• Continue debugging to get what you're looking for (decrypted
configuration files …)
Tools
• reg_export : Useful to export a registry key which contains
values that would have not have been exported properly by
Regedit
• PowerShell ISE :
o Set a breakpoint when the shellcode is ready but not yet
injected
o Save the shellcode into a file
([io.file]::WriteAllBytes('sc32.bin',$sc32))
• scdbg
o Emulates the shellcode
o Passing the shellcode address as parameter to the
shellcode implies modifying the stack -> complicated
• runsc32
o Runs the shellcode (runsc32 -f sc32.bin)
o Shellcode address is passed as parameter by runsc32 (-a to
disable this)
Approach
• Runs the shellcode
• Attach runsc32 process to x32dbg
• Set a breakpoint at address displayed by runsc32 (beginning of
shellcode)
• Continue the process in runsc32 window
• Come back to x32dbg and see in the stack that the address was
given as parameter
• Breakpoint on some interesting APIs
o Use the Call Stack to find the code that called the API
o Go to the code that called the API
o Next instruction -> "Run until selection"
• Scroll and find other interesting API (VirtualAlloc for example)
• Breakpoint on VirtualAlloc -> Ctrl-F9 -> F8
• Right-click on EAX -> "Follow in dump" -> Dump1
• Continue the process and repeat for each VirtualAlloc call,
check every Dump window for interesting stuff
• When a dump window contains a MZ header, it's interesting to
save it to disk
o Right-click -> "Follow in memory map"
o Dump memory to File
In this case the shellcode doesn't contain any GetEIP pattern but
expects his own address to be passed as argument by the PowerShell
script.
We could continue the analysis by analysing the MZ file we dumped from
the shellcode memory.
Code Injection
API Examples :
• CreateToolsHelp32Snapshot or EnumProcesses or CreateProcess
• OpenProcess
• VirtualAllocEx
• WriteProcessMemory
• CreateRemoteThread
Malware may use lower level calls to remain undetected (names start
with Nt/Zw/Rtl).
Methodology :
• Open the file in Ghidra
• Look for CreateRemoteThread in Windows -> Symbol References
• Look around for other API calls related to Code Injection :
o In Windows -> Function Call Trees
o In Windows -> Function Call Graph
• Note that VirtualAllocEx takes 0x40 as parameter
(PAGE_EXECUTE_READWRITE)
Methodology :
• ReadProcessMemory : Read the 1st few bytes of the targeted
function so they can be backed up for future use
• VirtualProtect : If needed to make the region writable
• WriteProcessMemory : Overwrite the start of the targeted
function with opcodes to jump into the rootkit
o JMP (0xE9)
o PUSH (0x68) / RET (0xC3) : less visible than a classic
jump
Debugger Detection
Well known detection techniques
• IsDebuggerPresent : 0 means no debugger
• CheckRemoteDebuggerPresent
• NtQueryInformationProcess (Example with Debugger port on Page
39) / ZwQueryInformationProcess
• OutputDebugString (valid value is being debugged)
• Check BeingDebugged field in PEB (Page 12)
o MOV EAX, FS:[30h] ; Get address of the PEB
o MOV EAX, [EAX+2] ; Get field value
o TEST EAX, EAX ; Test field value
• Check if NtGlobalFlag field in PEB is 0x70 (Page 13)
FLG_HEAP_ENABLE_TAIL_CHECK (0x10) + FLG_HEAP_ENABLE_FREE_CHECK
(0x20) + FLG_HEAP_VALIDATE_PARAMETERS (0X40)
o MOV EAX, DWORD PTR FS:[30]
o TEST BYTE PTR DS:[EAX+68], 70
• GetTickCount (Check if execution is too slow -> debugged)
• GetLocalTime, GetSystemTime
• RDTSC (Read Time-Stamp Counter) (Page 14)
Bypass
• Patch the return value manually
• Patch the program itself manually (Page 10)
• Easier way : Use ScyllaHide Plugin and check the 1st column
Data Protection
XOR
• Looking for a specific pattern like "http:" (Page 17) :
xorsearch -i -s getdown.exe http:
• Looking for English words (Page 18) : brxor.py hubert.dll
• Using multiple transformation types and get ranked results :
bbrack -l 1 hubert.dll
Stack Strings
• In Ghidra we can define a shortcut to convert to Char via the
Keybindings
• Extract Stack Strings (Page 24) : strdeobg.pl 9.exe
• Extract Stack Strings and other types (Page 25) :
o floss 9.exe
o floss 9.exe --no-static-strings
o -x to see address of the obfuscation method detected by
Floss
Discovered strings can indicate risky API calls that we can breakpoint
on (example RtlDecompressBuffer (Page 30))
Using Ghidra
• Confirm that dwCreationFlags=0x4 when CreateProcessA is called
(Page 49)
• Look around of other typical API calls related to process
hollowing
SEH record
• 32-bit programs : frame-based mechanisms
o The _EXCEPTION_REGISTRATION structure has 2 elements :
• Pointer to the next (previously defined) SEH record
• Pointer to the exception-handling mechanism
• 64-bit programs : table-based approach