Repository: vallejocc/Reverse-Engineering-Arsenal
Branch: master
Commit: e029ad79abef
Files: 40
Total size: 144.9 KB
Directory structure:
gitextract_j6atbifn/
├── .gitmodules
├── IDA/
│ ├── readme.md
│ ├── set_symbols_for_addresses.py
│ └── stack_strings_deobfuscator_1.py
├── README.md
└── WinDbg/
├── Readme.md
├── anti_antidebug_rdtsc.wdbg
├── break_each_new_process.wdbg
├── change_object_name.wdbg
├── change_process_name.wdbg
├── dump_injected_pe_rwemem.wdbg
├── dump_injected_pe_rwemem_fast.wdbg
├── dump_main_module.wdbg
├── dump_pe.wdbg
├── dump_process_symbols_to_file.wdbg
├── dump_process_symbols_to_file_preselect.wdbg
├── dump_process_symbols_to_file_preselect_curproc.wdbg
├── find_injected_pe_rmem.wdbg
├── find_injected_pe_rwemem.wdbg
├── find_injected_pe_search.wdbg
├── find_objects_by_name.wdbg
├── hang_exited_processes.wdbg
├── isx64.wdbg
├── lengthDisassembler.wdbg
├── load_code_to_kernel_memory.wdbg
├── load_swish.wdbg
├── log_processes.wdbg
├── monitoring_breakpoints.wdbg
├── pagein_range.wdbg
├── remove_device_acls.wdbg
├── search_bytes_all_processes.wdbg
├── search_bytes_target_process.wdbg
├── search_string_all_processes.wdbg
├── search_string_target_process.wdbg
├── secure_writemem.wdbg
├── show_address_info.wdbg
├── show_pe_headers.wdbg
├── show_proc_from_handle.wdbg
├── symbols.wdbg
└── write_mem_dump.wdbg
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitmodules
================================================
[submodule "standalone/RevealPE"]
path = standalone/RevealPE
url = https://github.com/vallejocc/RevealPE.git
[submodule "IDA/IdaDiscover"]
path = IDA/IdaDiscover
url = https://github.com/vallejocc/idaDiscover.git
================================================
FILE: IDA/readme.md
================================================
# set_symbols_for_addresses
This scripts asks you for a file containing pairs address - symbol. It walks all segments searching for DWORDs matching the addresses of the given file of pairs address - symbols, and it will name the variable containing the address with the symbol name. This script is thought to be used together with the windbg script dump_process_symbols_to_file.wdbg.
# stack_strings_deobfuscator_1
Some malware families construct strings into the stack, like this:
mov dword ptr [ebp-18h], 61737376h ; vssa
mov dword ptr [ebp-14h], 642E6970h ; pi.d
mov word ptr [ebp-10h], 6C6Ch; ll
In addition, i have found malware families permutating code (they split the code in portions and the mix these portions,
adding jumps from a portion to the next one for getting the code being executed in the correct order), and constructing
strings in stack, for example:
loc_4751A4:
nop
mov dword ptr [ebp-18h], 61737376h ; vssa
nop
jmp loc_474E10
|
v
loc_474E10:
nop
mov dword ptr [ebp-14h], 642E6970h ; pi.d
nop
jmp loc_475532
|
v
loc_475532:
mov word ptr [ebp-10h], 6C6Ch; ll
jmp loc_4750C3
This script add coments at points of code where each part of the string is being reconstructed. In addition it tries to
construct for each function the string being constructed into the function. For this purpose, it needs to follow basic
blocks of each funcion in the same order that they are going to be executed (in this way the strings will be reconstructed
in the same order in spite of the fact the code is permutated). When it constructs an string, the output is like this:
"Function text constructed in stack: sub_474CA0 | vssapi.dllCreateVssBackupComponentsInternaVssFreeSnapshotPropertiesInternaupComponents@@@Zonents@@YGJPAPAVIVssBackreeSnapshotPropertiessBackupCompateVVssF?Cre"
================================================
FILE: IDA/set_symbols_for_addresses.py
================================================
################################################
##
## Author: Javier Vicente Vallejo
## Twitter: @vallejocc
## Web: http://www.vallejo.cc
##
################################################
#
# This scripts asks you for a file containing pairs address - symbol.
#
# This script walks all segments searching for DWORDs matching the addresses of the given file of pairs address - symbols,
# and it will name the variable containing the address with the symbol name.
#
# This script is thought to be used together with the windbg script dump_process_symbols_to_file.wdbg.
#
################################################
import idaapi
import idc
import idautils
import tkFileDialog
################################################
def binarySearch(alist, item):
first = 0
last = len(alist)-1
found = False
retval = 0
while first<=last and not found:
midpoint = (first + last)//2
#algunos malware saltan al comienzo de la api mas algunas instrucciones, por ejemplo:
#ADVAPI32!RegDeleteValueW:
#77daedf1 8bff mov edi,edi
#77daedf3 55 push ebp
#77daedf4 8bec mov ebp,esp
#77daedf6 83ec0c sub esp,0Ch <- malware salta aqui y ejecuta el push ebp, mov ebp, esp en su codigo
#Por eso no comparamos la direccion dada con la de la lista, sino que aceptamos q sea la de la lista o hasta 10 posiciones mas alante
if alist[midpoint][0] <= item and item < alist[midpoint][0]+10:
found = True
retval = midpoint
else:
if item < alist[midpoint][0]:
last = midpoint-1
else:
first = midpoint+1
return found, retval
################################################
symbols = []
imagebase = idaapi.get_imagebase()
ea = here()
symbols_file_path = tkFileDialog.askopenfilename()
f = open(symbols_file_path, "r+b")
lines = f.readlines()
f.close()
##### Collect symbols by content and set symbols by rva
for line in lines:
print line
linesplit = line.split(" ")
if len(linesplit)>0:
symbolstr = linesplit[1].strip()
symbolstr = symbolstr.replace(" = <no type information>", "").replace("(<no parameter info>)", "").replace("__CARRIAGE_RETURN__", "\r").replace("__NEWLINE__", "\n")
if " byrva" in symbolstr:
symbolstr = symbolstr.replace(" byrva", "")
if " comment" in symbolstr:
symbolstr = symbolstr.replace(" comment", "")
MakeComm(imagebase+int(linesplit[0],16), symbolstr)
elif " rptcomment" in symbolstr:
symbolstr = symbolstr.replace(" rptcomment", "")
MakeRptCmt(imagebase+int(linesplit[0],16), symbolstr)
else:
MakeNameEx(imagebase+int(linesplit[0],16), symbolstr.replace("!", "_").replace(" ", ""), 0)
else:
symbol = (int(linesplit[0],16), symbolstr)
symbols.append(symbol)
##### Set symbols by content
if len(symbols):
symbols = sorted(symbols, key=lambda symbols: symbols[0])
for seg_ea in Segments():
for ea in range(seg_ea, SegEnd(seg_ea)):
vop1 = None
vop2 = None
bIsCode = isCode(GetFlags(ea))
if bIsCode:
op1type = idc.GetOpType(ea, 0)
op2type = idc.GetOpType(ea, 1)
if op1type == 5 or op1type == 6 or op1type == 7:
vop1 = GetOperandValue(ea,0)
if op2type == 5 or op2type == 6 or op2type == 7:
vop2 = GetOperandValue(ea,1)
v = Dword(ea)
isymbol = binarySearch(symbols, v)
if vop1 and not isymbol[0]: isymbol = binarySearch(symbols, vop1)
if vop2 and not isymbol[0]: isymbol = binarySearch(symbols, vop2)
if isymbol[0]:
i = isymbol[1]
if bIsCode:
print "Is code!! %x %s\n" % (ea, symbols[i][1])
MakeComm(ItemHead(ea),symbols[i][1])
else:
print "%x %s\n" % (ea, symbols[i][1])
MakeUnkn(ea,4)
MakeDword(ea)
MakeNameEx(ea,symbols[i][1].replace("!", "_").replace(" ", ""),0)
MakeComm(ea,symbols[i][1])
symbols[i] = (symbols[i][0], "_"+symbols[i][1])
################################################
================================================
FILE: IDA/stack_strings_deobfuscator_1.py
================================================
################################################
##
## Author: Javier Vicente Vallejo
## Twitter: @vallejocc
## Web: http://www.vallejo.cc
##
################################################
##
## Some malware families construct strings into the stack, like this:
##
## mov dword ptr [ebp-18h], 61737376h ; vssa
## mov dword ptr [ebp-14h], 642E6970h ; pi.d
## mov word ptr [ebp-10h], 6C6Ch; ll
##
## In addition, i have found malware families permutating code (they split the code in portions and the mix these portions,
## adding jumps from a portion to the next one for getting the code being executed in the correct order), and constructing
## strings in stack, for example:
##
## loc_4751A4:
## nop
## mov dword ptr [ebp-18h], 61737376h ; vssa
## nop
## jmp loc_474E10
## |
## v
## loc_474E10:
## nop
## mov dword ptr [ebp-14h], 642E6970h ; pi.d
## nop
## jmp loc_475532
## |
## v
## loc_475532:
## mov word ptr [ebp-10h], 6C6Ch; ll
## jmp loc_4750C3
##
## This script add coments at points of code where each part of the string is being reconstructed. In addition it tries to
## construct for each function the string being constructed into the function. For this purpose, it needs to follow basic
## blocks of each funcion in the same order that they are going to be executed (in this way the strings will be reconstructed
## in the same order in spite of the fact the code is permutated). When it constructs an string, the output is like this:
##
## "Function text constructed in stack: sub_474CA0 | vssapi.dllCreateVssBackupComponentsInternaVssFreeSnapshotPropertiesInternaupComponents@@@Zonents@@YGJPAPAVIVssBackreeSnapshotPropertiessBackupCompateVVssF?Cre"
##
## Other malware constructs strings on the stack by moving each byte to the stack, like this:
##
## mov [ebp+var_30], 48h
## mov [ebp+var_2F], 65h
## mov [ebp+var_2E], 6Ch
## mov [ebp+var_2D], 6Ch
## mov [ebp+var_2C], 6Fh
##
## For this cases i recommend to you to read this article (with ida script included):
##
## https://www.fireeye.com/blog/threat-research/2014/08/flare-ida-pro-script-series-automatic-recovery-of-constructed-strings-in-malware.html
##
################################################
import idaapi
import idc
import idautils
loutput = []
for segea in Segments():
for funcea in Functions(SegStart(segea), SegEnd(segea)):
functxt = ""
functionName = GetFunctionName(funcea)
#print "Current function: %s" % functionName
f = idaapi.get_func(funcea)
fc = idaapi.FlowChart(f)
lblocks = []
for block in fc:
lblocks.append(block)
lorderedblocks = []
while len(lblocks):
first = lblocks.pop(0)
lorderedblocks.append(first)
for head in Heads(first.startEA, first.endEA):
ins = GetMnem(head)
if len(ins) and ins[0]=='j':
op0 = GetOpType(head, 0)
if op0==5 or op0==6 or op0==7:
v = GetOperandValue(head, 0)
for i in range(0, len(lblocks)):
if v == lblocks[i].startEA:
#print "Moving block %x:%x" % (head, v)
lblocks.insert(0, lblocks.pop(i))
break
for block in lorderedblocks:
for head in Heads(block.startEA, block.endEA):
dism = GetDisasm(head)
if "mov dword ptr [ebp" in dism or "mov word ptr [ebp" in dism:
op1 = GetOpType(head, 1)
if op1==5 or op1==6 or op1==7:
v = GetOperandValue(head, 1)
curtxt = None
if ("mov dword ptr [ebp" in dism) and v>0xffffff:
curtxt = chr(v&0xff) + chr((v&0xff00)>>8) + chr((v&0xff0000)>>16) + chr((v&0xff000000)>>24)
if ("mov word ptr [ebp" in dism) and v>0xff:
curtxt = chr(v&0xff) + chr((v&0xff00)>>8)
if curtxt:
print hex(head), ":", GetDisasm(head), "--->", curtxt
MakeRptCmt(head, curtxt)
functxt += curtxt
if len(functxt):
loutput.append("Function text constructed in stack: " + functionName + " | " + functxt)
for e in loutput:
print e
================================================
FILE: README.md
================================================
Useful Scripts for helping in reverse engeenering.
================================================
FILE: WinDbg/Readme.md
================================================
$$>a<dump_injected_pe_rwemem.wdbg <destination directory>
--------------------------------------------------------
This windbg script will walk the results of !address command for each process in the debuggee machine,
searching for RWE memory containing PE files (based on the analysis of PE header).
When a PE file in RWE memory is found, the script will dump it. In addition to dump it, it will fix
some fields of PE header: imagebase will be set to the address where the PE is loaded, and
section[i].PointerToRawData = section[i].VirtualAddress (because we are dumping a mapped PE to disk and,
if we want to analyze the dumped PE with a disassembler for example, we need to fix the sections).
Article talking about this script: https://vallejo.cc/2017/08/13/tools-for-unpacking-malware-part-1-dumping-executables-from-rwe-memory/
$$>a<dump_injected_pe_rwemem_fast.wdbg <destination directory>
---------------------------------------------------------------
This windbg script will walk the results of !address command for each process in the debuggee machine,
searching for RWE memory containing PE files (based on the analysis of PE header).
When a PE file in RWE memory is found, the script will dump it. In addition to dump it, it will fix
some fields of PE header: imagebase will be set to the address where the PE is loaded, and
section[i].PointerToRawData = section[i].VirtualAddress (because we are dumping a mapped PE to disk and,
if we want to analyze the dumped PE with a disassembler for example, we need to fix the sections).
The difference with the script dump_injected_pe_rwemem.wdbg (the non fast version) it is this script is
not paging-in each page before trying to dump a PE file. Usually pages will be there, but it could fail
to dump the entire file if a page is not mapped.
Anyway, for debugging, i recommend to disable swapping, for having pages always in memory.
Article talking about this script: https://vallejo.cc/2017/08/13/tools-for-unpacking-malware-part-1-dumping-executables-from-rwe-memory/
$$>a<find_injected_pe_rwemem.wdbg
---------------------------------
This windbg script will walk the results of !address command for each process in the debuggee machine,
searching for RWE memory containing PE files (based on the analysis of PE header).
Article talking about this script: https://vallejo.cc/2017/08/13/tools-for-unpacking-malware-part-1-dumping-executables-from-rwe-memory/
$$>a<anti_antidebug_rdtsc.wdbg
------------------------------
This script works in similar way than anti-rdtsc tools that install a driver.
The script enables flag 2 of cr4: TSD Time Stamp Disable. In this way rdtsc is a privileged instruction.
After that, it enables the option for stopping when user mode exception (gflag +sue +soe, gflags 0x20000001).
Then we enable 0xc0000096 -> privileged instruction.
In this way, when rdtsc is executed by an application, an exception will occur and windbg will catch the exception.
In that moment, the script checks the ins code of rdtsc, 0x310f. If it is a rdtsc instruction, it skips
the instruction ip = ip+2.
Finally it sets edx = 0, and eax = last_counter+1.
Applications execution rdtsc will see an increment of 1 each rdtsc execution.
$$>a<change_object_name.wdbg <full object path + name>
------------------------------------------------------
i.e. pafish tries to open vmware devices "\\\\.\\HGFS" and "\\\\.\\vmci",
if can use this script to rename these devices in this way:
change_object_name.wdbg \\global??\\hgfs (in this case we rename the symboliclink) \\global??\\hgfs -> \\global??\\agfs
change_object_name.wdbg \\devices\\vmci (in this case we rename the deviceobject) \\devices\\vmci -> \\devices\\amci
The script changes the first letter of the name (setting 'a').
If you need other letter or additional modifications, it is easy to modify the script.
$$>a<change_process_name.wdbg <main module of the process to be renamed>
------------------------------------------------------------------------
i.e. if we want to rename vmtoolsd.exe:
$$>a<change_process_name.wdbg vmtoolsd.exe -> it will rename the process to vmtoolse
The script increase +1 the last letter of the name. If you need other or additional modifications,
it is easy to modify the script.
$$>a<dump_process_symbols_to_file.wdbg <path> <proc>
----------------------------------------------------
This simple script will dump to a file all the symbols of the given process.
If you dump a PE from memory, it could have variables pointing to symbols (for example, api
addresses that it got with GetProcAddress, etc...).
It is useful to have a list of pairs (symbol, address) because in this way if we open the
dumped PE with IDA we can search for that addresses and set a name for the variable containing them.
Article talking about this script: https://vallejo.cc/2017/08/13/tools-for-unpacking-malware-part-1-dumping-executables-from-rwe-memory/
$$>a<load_code_to_kernel_memory.wdbg <src code> <mem size> <offset start routine>
---------------------------------------------------------------------------------
Allocates kernel memory and load a block of data to that kernel memory. Later it creates a kernel thread
starting to run on the given offset.
$$>a<log_processes.wdbg <destination directory>
-----------------------------------------------
Log running processes to a given file.
$$>a<pagein_range.wdbg <start_address> <end_address> <process>
--------------------------------------------------------------
Page into memory a range of memory of the given process.
$$>a<search_bytes_all_processes.wdbg <byte1> <byte2> ... <byteN> (max 16 bytes)
-------------------------------------------------------------------------------------
This script is useful for search a max of 16 given bytes through all the running processes.
$$>a<search_string_target_process.wdbg <proc> <byte1> <byte2> .. <byteN>
------------------------------------------------------------------------
This script is useful for search a max of 16 given bytes in the given process.
$$>a<search_string_all_processes.wdbg <string>
----------------------------------------------
This script is useful for search a given string through all the running processes.
$$>a<search_string_target_process.wdbg <proc> <string>
-----------------------------------------------------
This script is useful for search a given string in a given process.
$$>a<secure_writemem.wdbg <start> <end> <process> <targetdir> <ext>
-------------------------------------------------------------------
this script tries to dump a range of memory.
If its not possible to dump a part of the range, that part if filled with random data
(really its filled with "\x11\x11\x11......\x11\x20\x0d\x0a" (total length 0x1000 for each page filled),
but we must not assume it will always contain this value.
$$>a<show_address_info.wdbg <address> <process>
-----------------------------------------------
Show info about a given address of a given process.
$$>a<show_proc_from_handle.wdbg <handle>
----------------------------------------
Show a process info from a given handle.
$$>a<symbols.wdbg
-----------------
Load symbols.
================================================
FILE: WinDbg/anti_antidebug_rdtsc.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<anti_antidebug_rdtsc.wdbg
$$
$$ This script works in similar way than anti-rdtsc tools that install a driver.
$$
$$ The script enables flag 2 of cr4: TSD Time Stamp Disable. In this way rdtsc is a privileged instruction.
$$ After that, it enables the option for stopping when user mode exception (gflag +sue +soe, gflags 0x20000001).
$$ Then we enable 0xc0000096 -> privileged instruction.
$$ In this way, when rdtsc is executed by an application, an exception will occur and windbg will catch the exception.
$$ In that moment, the script checks the ins code of rdtsc, 0x310f. If it is a rdtsc instruction, it skips the instruction ip = ip+2.
$$ Finally it sets edx = 0, and eax = last_counter+1.
$$ Applications execution rdtsc will see an increment of 1 each rdtsc execution.
$$
$$set rdtsc as priv instruction, then catch exceptions for priv instructions and skip rdtsc(eip=eip+2) and set edx:eax = last rdtsc returned value +1
$$use $t9 for counter
r $t9 = 0
$$rdtsc = privileged instruction
r cr4 = cr4 | 4
$$Stop on exception
!gflag +soe
$$Stop on unhandled user-mode exception
!gflag +sue
$$disable access violation (we have enabled exception in user mode, and access violation will cause lot of exceptions)
sxd av
$$we enable to catch privileged instructions execution (we have converted rdtsc in priv ins with cr4)
$$in this moment we check if it is rdtsc, and in this case, we jump over the instruction and we set eax=0 edx=0
sxe -c ".if((poi(eip)&0x0000ffff)==0x310f){.printf \"rdtsc\r\n\";r eip = eip+2;r eax=@$t9;r edx=0;r $t9=@$t9+1; gh;}" c0000096
================================================
FILE: WinDbg/break_each_new_process.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<break_each_new_process.wdbg
$$
.block
{
.sympath "SRV*c:\symcache*https://msdl.microsoft.com/download/symbols;SRV*c:\symcache*https://chromium-browser-symsrv.commondatastorage.googleapis.com;SRV*c:\symcache\*https://symbols.mozilla.org/";
.reload
}
ba e1 MmCreateProcessAddressSpace
================================================
FILE: WinDbg/change_object_name.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<change_object_name.wdbg <full object path + name>
$$
$$ i.e. pafish tries to open vmware devices "\\\\.\\HGFS" and "\\\\.\\vmci", if can use this script to rename these devices in this way:
$$
$$ change_object_name.wdbg \\global??\\hgfs (in this case we rename the symboliclink) \\global??\\hgfs -> \\global??\\agfs
$$ change_object_name.wdbg \\devices\\vmci (in this case we rename the deviceobject) \\devices\\vmci -> \\devices\\amci
$$
$$ The script changes the first letter of the name (setting 'a'). If you need other letter or additional modifications, it is easy to modify the script
$$
aS stage @$t19
aS x64arch $t18
aS objhnameinfodisp $t17
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
$$is x64?
r x64arch = 0;
r objhnameinfodisp = 0x10;
.foreach( tok { .effmach } )
{
.if($scmp("${tok}","x64")==0)
{
r x64arch = 1;
r objhnameinfodisp = 0x20;
.break;
};
};
}
r stage = 0
.foreach( tok { !object "${$arg1}" } )
{
.printf "${tok}\r\n"
.if(${stage}==1)
{
.echo ${tok}
dt _OBJECT_HEADER ${tok}
r $t0 = ${tok}
dt _OBJECT_HEADER_NAME_INFO (@$t0-${objhnameinfodisp})
$$ $t0 -> OBJECT_HEADER_NAME_INFO
r $t0 = @$t0 - ${objhnameinfodisp}
$$ $t0 -> OBJECT_HEADER_NAME_INFO.UNICODE_STRING
r $t0 = @$t0 + @@c++(#FIELD_OFFSET(_OBJECT_HEADER_NAME_INFO, Name))
$$ $t0 -> OBJECT_HEADER_NAME_INFO.UNICODE_STRING.Buffer
r $t0 = @$t0 + @@c++(#FIELD_OFFSET(_UNICODE_STRING, Buffer))
db poi $t0
$$change the first letter for 'a'
eb (poi $t0) 'a'
.printf "--------------------\r\n"
db poi $t0
.break
}
.if(${stage}==0)
{
.if($scmp("${tok}","ObjectHeader:")==0)
{
r stage = 1
}
}
}
================================================
FILE: WinDbg/change_process_name.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<change_process_name.wdbg <main module of the process to be renamed>
$$
$$ i.e. if we want to rename vmtoolsd.exe:
$$
$$ $$>a<change_process_name.wdbg vmtoolsd.exe -> it will rename the process to vmtoolse
$$
$$ The script increase +1 the last letter of the name. If you need other or additional modifications, it is easy to modify the script
.logopen ${$arg2}\change_process_name.start
.printf "start"
.logclose
aS stage @$t19
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
r stage = 2
.printf "xxxx"
.foreach (processes_tok { !process /m ${$arg1} 0 0 })
{
.if($scmp("${processes_tok}","PROCESS")==0)
{
.if(${stage}==2)
{
$$stage==2 is used to skip the first apparition of PROCESS string in the results of !process 0 0
r stage = 0
}
.else
{
r stage = 1
}
}
.elsif(${stage}==1)
{
.printf /D "<b>Renaming process ${processes_tok}</b>\n"
r stage = 0
r $t4 = ${processes_tok}
r $t0 = @@c++( ( ( nt!_EPROCESS * ) @$t4 )->SeAuditProcessCreationInfo.ImageFileName )
r $t1 = (poi @$t0)&0xffff
r $t2 = (poi (@$t0+2))&0xffff
r $t3 = (poi (@$t0+@@c++(#FIELD_OFFSET(nt!_UNICODE_STRING, Buffer))))
db ($t3 + $t1 - a)
$$go to end of buffer of _UNICODE_STRING, and go back 0xa bytes. For example <filepath....><lastbyte>.exe. We locate on lastbyte, and we increase 1 the value of last byte
$$For example <fullpath>\vmtoolsd.exe, will be modified to <fullpath>\vmtoolse.exe
eb ($t3 + $t1 - a) ((poi($t3 + $t1 - a)&0xff)+1)
!process @$t4 0
}
}
}
.logopen ${$arg2}\change_process_name.end
.printf "end"
.logclose
================================================
FILE: WinDbg/dump_injected_pe_rwemem.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<dump_injected_pe_rwemem.wdbg <destination directory>
$$
$$ This windbg script will walk the results of !address command for each process in the debuggee machine,
$$ searching for RWE memory containing PE files (based on the analysis of PE header).
$$
$$ When a PE file in RWE memory is found, the script will dump it. In addition to dump it, it will fix
$$ some fields of PE header: imagebase will be set to the address where the PE is loaded, and
$$ section[i].PointerToRawData = section[i].VirtualAddress (because we are dumping a mapped PE to disk and,
$$ if we want to analyze the dumped PE with a disassembler for example, we need to fix the sections).
$$
$$.sympath SRV*c:\symcache*http://msdl.microsoft.com/download/symbols
$$.reload
.logopen ${$arg1}\dump_injected_pe_rwemem.start
.printf "start"
.logclose
aS stage @$t19
aS temp @$t18
aS temp2 @$t17
aS temp3 @$t16
aS isPossiblePE @$t15
aS isDosMessageBased @$t14
aS pe @$t13
aS fileheader @$t12
aS optionalheader @$t11
aS sections @$t10
aS nsections @$t9
aS lastsect @$t8
aS prev1 @$t7
aS prev2 @$t6
aS baseaddr @$t5
aS baseaddrlen @$t4
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
r stage = 2
.printf "xxxx"
.foreach (processes_tok { !process 0 0 })
{
.if($scmp("${processes_tok}","PROCESS")==0)
{
.if(${stage}==2)
{
$$stage==2 is used to skip the first apparition of PROCESS string in the results of !process 0 0
r stage = 0
}
.else
{
r stage = 1
}
}
.elsif(${stage}==1)
{
.printf /D "<b>Analyzing process ${processes_tok}</b>\n"
r stage = 0
.process /i ${processes_tok}
g
.block
{
.reload /user
}
$$search for memory blocks with ReadWriteExecute protection
$$careful:
$$ when the baseaddress is over 0x10000000 findstr tokens will be:
$$ 93:7640:20010000
$$ 2002c000
$$ 1c000
$$ UserRange
$$ ...
$$ however if the addess is under 0x10000000:
$$ 25:1364:
$$ 60000
$$ 61000
$$ 1000
$$ UserRange
$$ The reason for this its windbg puts spaces before the base address when it hasnt 8 characters to complete 8 characters, but if the address is
$$ ???????? then it doesnt put spaces. We need to have in mind both case, and this is the reason of the stages of the next code
$$
.foreach (tok { .shell -ci "!address" findstr /N /O /R /C:"UserRange.*ExecuteReadWrite" /C:"UserRange.*ReadWriteExecute" })
{
r isPossiblePE = 0
r isDosMessageBased = 0
.printf "${tok}\n"
.if($spat("${tok}","*:*:*")!=0)
{
r stage = 1
}
.elsif(${stage}==1)
{
r prev1 = ${tok}
r stage = 2
}
.elsif(${stage}==2)
{
r prev2 = ${tok}
r stage = 3
}
.elsif(${stage}==3)
{
.if($spat("${tok}","*UserRange*")!=0)
{
r baseaddr = prev1 - prev2
r baseaddrlen = prev2
r stage = 5
}
.else
{
r baseaddr = prev1
r baseaddrlen = ${tok}
r stage = 4
}
}
.elsif(${stage}==4)
{
r stage = 5
}
.elsif(${stage}==5)
{
$$for each block with ReadWriteExecute protection, check MZ / PE
r @$t0 = baseaddr
.if(@$t0!=0)
{
.printf "base %x\n", @$t0
.pagein /p ${processes_tok} @$t0
g
$$!address @$t0
.if($vvalid(@$t0, 2)==1)
{
.printf "valid base address\n"
.printf "PE_header %x\n", @$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew)
.if($vvalid(@$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew), 2)==1)
{
.printf "valid PE_header address\n"
.if(wo(@$t0)==0x5a4d & dwo(@$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew))==0x454E)
{
$$We can find MZ / NE images, we ignore them
}
.elsif(wo(@$t0)==0x5a4d & dwo(@$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew))==0x4550)
{
$$if MZ and PE signatures, valid pe header
.printf "valid PE_header\n"
r isPossiblePE = 1
}
.else
{
$$if not MZ or not PE signature, but msdos message is found, its a possible pe header
r temp = 0
.foreach (tok2 { s -a @$t0 L 0x80 "This program cannot " })
{
r temp = 1
}
.if(${temp}==1)
{
.printf "possible pe header\n"
r isPossiblePE = 1
r isDosMessageBased = 1
}
}
}
.else
{
.printf "not valid PE_header address\n"
}
}
.else
{
.printf "not valid base address\n"
}
$$if we have found a possible PE in a memory zone with ReadWriteExecute protection, we will check if the base address is in the list of loaded module
.if(${isPossiblePE}==1)
{
.printf "is possible module %x\n", @$t0
$$ r temp = 0
$$ $$search for valid from "is not valid address"
$$ .foreach (tok3 { .shell -ci "!lmi @$t0" findstr /N /O "valid.address" })
$$ {
$$ r temp = ${temp} + 1
$$ }
$$ .printf "%x\n", ${temp}
$$
$$ We are going to disable !lmi check. I have found malware that is unmapping
$$ the main module of a executable when it does process hollowing, and it loads
$$ its injected PE in that address. In this cases, !lmi is not answering not valid
$$ address for that address. However !address command is containing the path
$$ of the module (for example Section [\Windows\explorer.exe]) when the module is
$$ a valid loaded module, and it wont contain the path when it is an injected module
$$ in RWE mem. So, we will skip !lmi check, and we will check !address results
$$ vvvvvvvv DISABLE !LMI CHECK vvvvvvvvv
r temp = 4
$$ ^^^^^^^^ DISABLE !LMI CHECK ^^^^^^^^^
$$ "is not valid address" was found
.if(${temp} > 3)
{
$$ there are some modules that !lmi command is answering: is not valid address, however they seems to be valid loaded modules (not interesing for us).
$$ However if we consults information about the address with !address we find things as:
$$ Memory Usage: Section [\WINDOWS\System32\blablabla.mui] (it happens usually with .mui files, but not only with them
$$ We will discard results of !address with .dll], .mui] and .exe]
r temp = 0
.foreach (tok4 { .shell -ci "!address @$t0" findstr /N /O /R /I "\.mui\]" })
{
r temp = ${temp} + 1
}
r temp2 = 0
.foreach (tok5 { .shell -ci "!address @$t0" findstr /N /O /R /I "\.dll\]" })
{
r temp2 = ${temp2} + 1
}
r temp3 = 0
.foreach (tok6 { .shell -ci "!address @$t0" findstr /N /O /R /I "\.exe\]" })
{
r temp3 = ${temp3} + 1
}
.printf "search !address .mui %x\n", ${temp}
.printf "search !address .dll %x\n", ${temp2}
.printf "search !address .exe %x\n", ${temp3}
.if(${temp} < 4 and ${temp2} < 4 and ${temp3} < 4)
{
.if(${isDosMessageBased}==0)
{
.printf /D "<b>---------------------------------------------------------------------------</b>\n"
.printf /D "<b>Process: ${processes_tok} base: %x -> Possible injected or unpacked PE</b>\n", @$t0
.printf /D "<b>---------------------------------------------------------------------------</b>\n"
}
.else
{
.printf /D "-------------------------------------------------------------------------------------------------------------------------------------\n"
.printf /D "Process: ${processes_tok} base: %x -> Possible injected or unpacked PE, based on the dos header message: This program cannot...\n", @$t0
.printf /D "--------------------------------------------------------------------------------------------------------------------------------------\n"
}
.printf "xxxx1\n"
r pe = @$t0 + poi(@$t0 + @@(#FIELD_OFFSET(nt!_IMAGE_DOS_HEADER, e_lfanew)))
.printf "xxxx2 %x\n", ${pe}
r fileheader = ${pe} + @@(#FIELD_OFFSET(nt!_IMAGE_NT_HEADERS, FileHeader))
.printf "xxxx3 %x\n", ${fileheader}
r optionalheader = ${pe} + @@(#FIELD_OFFSET(nt!_IMAGE_NT_HEADERS, OptionalHeader))
.printf "xxxx4 %x\n", ${optionalheader}
r nsections = poi(${fileheader} + @@(#FIELD_OFFSET(nt!_IMAGE_FILE_HEADER, NumberOfSections )))&0xffff
.printf "xxxx5 %x\n", ${nsections}
r sections = ${pe} + @@c++(sizeof(nt!_IMAGE_NT_HEADERS))
.printf "xxxx6 %x\n", ${sections}
$$ reach the end of the PE file (last section rva + last section vsize)
r lastsect = ${sections} + ( ${nsections} - 1 ) * @@c++(sizeof(nt!_IMAGE_SECTION_HEADER))
r @$t1 = @$t0 + @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) ${lastsect} )->VirtualAddress ) + @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) ${lastsect} )->Misc.VirtualSize )
r @$t2 = @$t0 + @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) ${lastsect} )->VirtualAddress ) + @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) ${lastsect} )->SizeOfRawData )
r @$t3 = @$t0
.if (@$t2 > @$t1) { r @$t1 = @$t2 }
$$limit to dump = 0x100000 bytes
.if (@$t1-@$t0 > 0x100000)
{
r @$t1 = @$t0+0x100000
}
.printf "xxxx7"
$$ before dumping the PE, we are going to modify sections' raw offset = virtual address for coherency in the dumped PE when we analyze it with IDA or any other tool
r $t2 = ${sections}
r lastsect = ${sections} + ( ${nsections} ) * @@c++(sizeof(nt!_IMAGE_SECTION_HEADER))
.while (@$t2 < ${lastsect})
{
ed (@$t2+@@( #FIELD_OFFSET(nt!_IMAGE_SECTION_HEADER, PointerToRawData))) @@c++((( nt!_IMAGE_SECTION_HEADER *)@$t2)->VirtualAddress)
.if( @@c++((( nt!_IMAGE_SECTION_HEADER *)@$t2)->SizeOfRawData) < @@c++((( nt!_IMAGE_SECTION_HEADER *)@$t2)->Misc.VirtualSize))
{
ed (@$t2+@@( #FIELD_OFFSET(nt!_IMAGE_SECTION_HEADER, SizeOfRawData))) @@c++((( nt!_IMAGE_SECTION_HEADER *)@$t2)->Misc.VirtualSize)
}
.else
{
ed (@$t2+@@( #FIELD_OFFSET(nt!_IMAGE_SECTION_HEADER, Misc))) @@c++((( nt!_IMAGE_SECTION_HEADER *)@$t2)->SizeOfRawData)
}
r $t2 = @$t2 + @@c++(sizeof(nt!_IMAGE_SECTION_HEADER))
}
$$update sizeofimage
.block
{
ed (${optionalheader} + @@(#FIELD_OFFSET(nt!_IMAGE_OPTIONAL_HEADER, SizeOfImage))) (@$t1-@$t0)&0x7fffffff
}
.printf "xxxx8"
$$update imagebase
.block
{
ed (${optionalheader} + @@(#FIELD_OFFSET(nt!_IMAGE_OPTIONAL_HEADER, ImageBase))) ${baseaddr}
}
.printf "xxxx9"
.printf "dumping file address start %x end %x", @$t0, @$t1
.while (@$t0 < @$t1)
{
.printf "paging in: %x\n", @$t0
.pagein /p ${processes_tok} @$t0
g
r @$t0 = @$t0 + 0x1000;
};
.foreach /pS 4 (baseaddr_tok { ? baseaddr })
{
.foreach /pS 4 (baseaddrlen_tok { ? baseaddrlen })
{
.printf "${processes_tok}_${baseaddr_tok}_${baseaddrlen_tok}.pedmp"
.writemem ${$arg1}\${processes_tok}_${baseaddr_tok}_${baseaddrlen_tok}.pedmp @$t3 L (@$t1 - @$t3)
}
}
}
}
}
r stage = 0
}
}
}
r stage = 0
}
}
.logopen ${$arg1}\dump_injected_pe_rwemem.end
.printf "end"
.logclose
ad stage
ad temp
ad temp2
ad temp3
ad isPossiblePE
ad isDosMessageBased
ad pe
ad fileheader
ad optionalheader
ad sections
ad nsections
ad lastsect
ad prev1
ad prev2
ad baseaddr
ad baseaddrlen
}
================================================
FILE: WinDbg/dump_injected_pe_rwemem_fast.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<dump_injected_pe_rwemem_fast.wdbg <destination directory>
$$
$$ This windbg script will walk the results of !address command for each process in the debuggee machine,
$$ searching for RWE memory containing PE files (based on the analysis of PE header).
$$
$$ When a PE file in RWE memory is found, the script will dump it. In addition to dump it, it will fix
$$ some fields of PE header: imagebase will be set to the address where the PE is loaded, and
$$ section[i].PointerToRawData = section[i].VirtualAddress (because we are dumping a mapped PE to disk and,
$$ if we want to analyze the dumped PE with a disassembler for example, we need to fix the sections).
$$
$$ The difference with the script dump_injected_pe_rwemem.wdbg (the non fast version) it is this script is
$$ not paging-in each page before trying to dump a PE file. Usually pages will be there, but it could fail
$$ to dump the entire file if a page is not mapped.
$$
$$ Anyway, for debugging, i recommend to disable swapping, for having pages always in memory.
$$
$$.sympath SRV*c:\symcache*http://msdl.microsoft.com/download/symbols
$$.reload
.logopen ${$arg1}\dump_injected_pe_rwemem_fast.start
.printf "start"
.logclose
aS stage @$t19
aS temp @$t18
aS temp2 @$t17
aS temp3 @$t16
aS isPossiblePE @$t15
aS isDosMessageBased @$t14
aS pe @$t13
aS fileheader @$t12
aS optionalheader @$t11
aS sections @$t10
aS nsections @$t9
aS lastsect @$t8
aS prev1 @$t7
aS prev2 @$t6
aS baseaddr @$t5
aS baseaddrlen @$t4
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
r stage = 2
.printf "xxxx"
.foreach (processes_tok { !process 0 0 })
{
.if($scmp("${processes_tok}","PROCESS")==0)
{
.if(${stage}==2)
{
$$stage==2 is used to skip the first apparition of PROCESS string in the results of !process 0 0
r stage = 0
}
.else
{
r stage = 1
}
}
.elsif(${stage}==1)
{
.printf /D "<b>Analyzing process ${processes_tok}</b>\n"
r stage = 0
.process /p /r ${processes_tok}
$$search for memory blocks with ReadWriteExecute protection
$$careful:
$$ when the baseaddress is over 0x10000000 findstr tokens will be:
$$ 93:7640:20010000
$$ 2002c000
$$ 1c000
$$ UserRange
$$ ...
$$ however if the addess is under 0x10000000:
$$ 25:1364:
$$ 60000
$$ 61000
$$ 1000
$$ UserRange
$$ The reason for this its windbg puts spaces before the base address when it hasnt 8 characters to complete 8 characters, but if the address is
$$ ???????? then it doesnt put spaces. We need to have in mind both case, and this is the reason of the stages of the next code
$$
.foreach (tok { .shell -ci "!address" findstr /N /O /R /C:"UserRange.*ExecuteReadWrite" /C:"UserRange.*ReadWriteExecute" })
{
r isPossiblePE = 0
r isDosMessageBased = 0
.printf "${tok}\n"
.if($spat("${tok}","*:*:*")!=0)
{
r stage = 1
}
.elsif(${stage}==1)
{
r prev1 = ${tok}
r stage = 2
}
.elsif(${stage}==2)
{
r prev2 = ${tok}
r stage = 3
}
.elsif(${stage}==3)
{
.if($spat("${tok}","*UserRange*")!=0)
{
r baseaddr = prev1 - prev2
r baseaddrlen = prev2
r stage = 5
}
.else
{
r baseaddr = prev1
r baseaddrlen = ${tok}
r stage = 4
}
}
.elsif(${stage}==4)
{
r stage = 5
}
.elsif(${stage}==5)
{
$$for each block with ReadWriteExecute protection, check MZ / PE
r @$t0 = baseaddr
.if(@$t0!=0)
{
.printf "base %x\n", @$t0
$$.pagein /p ${processes_tok} @$t0
$$g
$$!address @$t0
.if($vvalid(@$t0, 2)==1)
{
.printf "valid base address\n"
.printf "PE_header %x\n", @$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew)
.if($vvalid(@$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew), 2)==1)
{
.printf "valid PE_header address\n"
.if(wo(@$t0)==0x5a4d & dwo(@$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew))==0x454E)
{
$$We can find MZ / NE images, we ignore them
}
.elsif(wo(@$t0)==0x5a4d & dwo(@$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew))==0x4550)
{
$$if MZ and PE signatures, valid pe header
.printf "valid PE_header\n"
r isPossiblePE = 1
}
.else
{
$$if not MZ or not PE signature, but msdos message is found, its a possible pe header
r temp = 0
.foreach (tok2 { s -a @$t0 L 0x80 "This program cannot " })
{
r temp = 1
}
.if(${temp}==1)
{
.printf "possible pe header\n"
r isPossiblePE = 1
r isDosMessageBased = 1
}
}
}
.else
{
.printf "not valid PE_header address\n"
}
}
.else
{
.printf "not valid base address\n"
}
$$if we have found a possible PE in a memory zone with ReadWriteExecute protection, we will check if the base address is in the list of loaded module
.if(${isPossiblePE}==1)
{
.printf "is possible module %x\n", @$t0
$$ r temp = 0
$$ $$search for valid from "is not valid address"
$$ .foreach (tok3 { .shell -ci "!lmi @$t0" findstr /N /O "valid.address" })
$$ {
$$ r temp = ${temp} + 1
$$ }
$$ .printf "%x\n", ${temp}
$$
$$ We are going to disable !lmi check. I have found malware that is unmapping
$$ the main module of a executable when it does process hollowing, and it loads
$$ its injected PE in that address. In this cases, !lmi is not answering not valid
$$ address for that address. However !address command is containing the path
$$ of the module (for example Section [\Windows\explorer.exe]) when the module is
$$ a valid loaded module, and it wont contain the path when it is an injected module
$$ in RWE mem. So, we will skip !lmi check, and we will check !address results
$$ vvvvvvvv DISABLE !LMI CHECK vvvvvvvvv
r temp = 4
$$ ^^^^^^^^ DISABLE !LMI CHECK ^^^^^^^^^
$$ "is not valid address" was found
.if(${temp} > 3)
{
$$ there are some modules that !lmi command is answering: is not valid address, however they seems to be valid loaded modules (not interesing for us).
$$ However if we consults information about the address with !address we find things as:
$$ Memory Usage: Section [\WINDOWS\System32\blablabla.mui] (it happens usually with .mui files, but not only with them
$$ We will discard results of !address with .dll], .mui] and .exe]
r temp = 0
.foreach (tok4 { .shell -ci "!address @$t0" findstr /N /O /R /I "\.mui\]" })
{
r temp = ${temp} + 1
}
r temp2 = 0
.foreach (tok5 { .shell -ci "!address @$t0" findstr /N /O /R /I "\.dll\]" })
{
r temp2 = ${temp2} + 1
}
r temp3 = 0
.foreach (tok6 { .shell -ci "!address @$t0" findstr /N /O /R /I "\.exe\]" })
{
r temp3 = ${temp3} + 1
}
.printf "search !address .mui %x\n", ${temp}
.printf "search !address .dll %x\n", ${temp2}
.printf "search !address .exe %x\n", ${temp3}
.if(${temp} < 4 and ${temp2} < 4 and ${temp3} < 4)
{
.if(${isDosMessageBased}==0)
{
.printf /D "<b>---------------------------------------------------------------------------</b>\n"
.printf /D "<b>Process: ${processes_tok} base: %x -> Possible injected or unpacked PE</b>\n", @$t0
.printf /D "<b>---------------------------------------------------------------------------</b>\n"
}
.else
{
.printf /D "-------------------------------------------------------------------------------------------------------------------------------------\n"
.printf /D "Process: ${processes_tok} base: %x -> Possible injected or unpacked PE, based on the dos header message: This program cannot...\n", @$t0
.printf /D "--------------------------------------------------------------------------------------------------------------------------------------\n"
}
.printf "xxxx1\n"
r pe = @$t0 + poi(@$t0 + @@(#FIELD_OFFSET(nt!_IMAGE_DOS_HEADER, e_lfanew)))
.printf "xxxx2 %x\n", ${pe}
r fileheader = ${pe} + @@(#FIELD_OFFSET(nt!_IMAGE_NT_HEADERS, FileHeader))
.printf "xxxx3 %x\n", ${fileheader}
r optionalheader = ${pe} + @@(#FIELD_OFFSET(nt!_IMAGE_NT_HEADERS, OptionalHeader))
.printf "xxxx4 %x\n", ${optionalheader}
r nsections = poi(${fileheader} + @@(#FIELD_OFFSET(nt!_IMAGE_FILE_HEADER, NumberOfSections )))&0xffff
.printf "xxxx5 %x\n", ${nsections}
r sections = ${pe} + @@c++(sizeof(nt!_IMAGE_NT_HEADERS))
.printf "xxxx6 %x\n", ${sections}
$$ reach the end of the PE file (last section rva + last section vsize)
r lastsect = ${sections} + ( ${nsections} - 1 ) * @@c++(sizeof(nt!_IMAGE_SECTION_HEADER))
r @$t1 = @$t0 + @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) ${lastsect} )->VirtualAddress ) + @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) ${lastsect} )->Misc.VirtualSize )
r @$t2 = @$t0 + @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) ${lastsect} )->VirtualAddress ) + @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) ${lastsect} )->SizeOfRawData )
r @$t3 = @$t0
.if (@$t2 > @$t1) { r @$t1 = @$t2 }
$$limit to dump = 0x100000 bytes
.if (@$t1-@$t0 > 0x100000)
{
r @$t1 = @$t0+0x100000
}
.printf "xxxx7"
$$ before dumping the PE, we are going to modify sections' raw offset = virtual address for coherency in the dumped PE when we analyze it with IDA or any other tool
r $t2 = ${sections}
r lastsect = ${sections} + ( ${nsections} ) * @@c++(sizeof(nt!_IMAGE_SECTION_HEADER))
.while (@$t2 < ${lastsect})
{
ed (@$t2+@@( #FIELD_OFFSET(nt!_IMAGE_SECTION_HEADER, PointerToRawData))) @@c++((( nt!_IMAGE_SECTION_HEADER *)@$t2)->VirtualAddress)
.if( @@c++((( nt!_IMAGE_SECTION_HEADER *)@$t2)->SizeOfRawData) < @@c++((( nt!_IMAGE_SECTION_HEADER *)@$t2)->Misc.VirtualSize))
{
ed (@$t2+@@( #FIELD_OFFSET(nt!_IMAGE_SECTION_HEADER, SizeOfRawData))) @@c++((( nt!_IMAGE_SECTION_HEADER *)@$t2)->Misc.VirtualSize)
}
.else
{
ed (@$t2+@@( #FIELD_OFFSET(nt!_IMAGE_SECTION_HEADER, Misc))) @@c++((( nt!_IMAGE_SECTION_HEADER *)@$t2)->SizeOfRawData)
}
r $t2 = @$t2 + @@c++(sizeof(nt!_IMAGE_SECTION_HEADER))
}
$$update sizeofimage
.block
{
ed (${optionalheader} + @@(#FIELD_OFFSET(nt!_IMAGE_OPTIONAL_HEADER, SizeOfImage))) (@$t1-@$t0)&0x7fffffff
}
.printf "xxxx8"
$$update imagebase
.block
{
ed (${optionalheader} + @@(#FIELD_OFFSET(nt!_IMAGE_OPTIONAL_HEADER, ImageBase))) ${baseaddr}
}
.printf "xxxx9"
.printf "dumping file address start %x end %x", @$t0, @$t1
.foreach /pS 4 (baseaddr_tok { ? baseaddr })
{
.foreach /pS 4 (baseaddrlen_tok { ? baseaddrlen })
{
.printf "${processes_tok}_${baseaddr_tok}_${baseaddrlen_tok}.pedmp"
.writemem ${$arg1}\${processes_tok}_${baseaddr_tok}_${baseaddrlen_tok}.pedmp @$t3 L (@$t1 - @$t3)
}
}
}
}
}
r stage = 0
}
}
}
r stage = 0
}
}
.logopen ${$arg1}\dump_injected_pe_rwemem_fast.end
.printf "end"
.logclose
ad stage
ad temp
ad temp2
ad temp3
ad isPossiblePE
ad isDosMessageBased
ad pe
ad fileheader
ad optionalheader
ad sections
ad nsections
ad lastsect
ad prev1
ad prev2
ad baseaddr
ad baseaddrlen
}
================================================
FILE: WinDbg/dump_main_module.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<c:\tools\@scripts\windbg\dump_main_module.wdbg <targetdir> <modname with ext> <modname without ext>
.logopen ${$arg1}\dump_main_module.start
.printf "start"
.logclose
aS stage @$t19
aS base @$t18
aS end @$t17
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload /f
}
.block
{
r stage = 2
.printf "xxxx"
.foreach (processes_tok { !process /m ${$arg2} 0 0 })
{
.if($scmp("${processes_tok}","PROCESS")==0)
{
.if(${stage}==2)
{
$$stage==2 is used to skip the first apparition of PROCESS string in the results of !process 0 0
r stage = 0
}
.else
{
r stage = 1
}
}
.elsif(${stage}==1)
{
.printf /D "<b>Dumping main module for process ${processes_tok}</b>\n"
r stage = 0
.process /i ${processes_tok}
g
.reload /f
.foreach (lmi_tok { lmi m ${$arg3} })
{
r stage = stage + 1
.if(${stage}==5)
{
.printf "${lmi_tok}\r\n"
r base = ${lmi_tok}
}
.if(${stage}==6)
{
.printf "${lmi_tok}\r\n"
r end = ${lmi_tok}
}
}
.if (${end} - ${base} > 0x100000) { r end = ${base} + 0x100000 }
.foreach /pS 4 (baseaddr_tok { ? ${base} })
{
.foreach /pS 4 (endaddr_tok { ? ${end} })
{
.printf "[${processes_tok}] ${baseaddr_tok} - ${endaddr_tok}\r\n"
.block
{
$$>a<c:\tools\@scripts\windbg\secure_writemem.wdbg ${baseaddr_tok} ${endaddr_tok} ${processes_tok} ${$arg1} mainmod.pedmp
}
}
}
r stage = 0
}
}
}
.logopen ${$arg1}\dump_main_module.end
.printf "end"
.logclose
================================================
FILE: WinDbg/dump_pe.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<c:\tools\@scripts\windbg\dump_pe.wdbg <base_address> <process> <path>
$$ it uses t0, t1 and t2
$$.sympath SRV*c:\symcache*http://msdl.microsoft.com/download/symbols
$$.reload
.printf "dump pe base address %x, process %x\n", ${$arg1}, ${$arg2}
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$ change the context to the target process and page in the page containing PE headers
.process /i ${$arg2}
g
$$.reload /user
.pagein /p ${$arg2} ${$arg1}
g
.block
{
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$ define some useful aliases
ad /q ${/v:$imagebase};
ad /q ${/v:$pe_header};
ad /q ${/v:$file_header};
ad /q ${/v:$optional_header};
ad /q ${/v:$number_of_sections};
ad /q ${/v:$sections};
aS /x ${/v:$imagebase} ( ${$arg1} )
aS /x ${/v:$pe_header} ( $imagebase + ( poi( $imagebase + @@( #FIELD_OFFSET( nt!_IMAGE_DOS_HEADER, e_lfanew ) ) ) ) )
aS /x ${/v:$file_header} ( $pe_header + @@( #FIELD_OFFSET( nt!_IMAGE_NT_HEADERS, FileHeader ) ) )
aS /x ${/v:$optional_header} ( $pe_header + @@( #FIELD_OFFSET( nt!_IMAGE_NT_HEADERS, OptionalHeader ) ) )
aS /x ${/v:$number_of_sections} ( poi ( $file_header + @@( #FIELD_OFFSET( nt!_IMAGE_FILE_HEADER, NumberOfSections ) ) ) & 0xffff )
aS /x ${/v:$sections} ( $pe_header + @@c++( sizeof ( nt!_IMAGE_NT_HEADERS ) ) )
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$ we are going to set sections' raw offset = virtual address for coherency in the dumped PE
r $t0 = $sections
r $t1 = 0
r $t2 = $number_of_sections
.while (@$t1 < @$t2)
{
r $t3 = $sections + @$t1 * @@c++(sizeof(nt!_IMAGE_SECTION_HEADER))
$$.printf "%x\n", @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) @$t3 )->VirtualAddress )
$$.printf "%x\n", @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) @$t3 )->PointerToRawData )
r $t4 = @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) @$t3 )->VirtualAddress )
r $t5 = @$t3 + @@( #FIELD_OFFSET( nt!_IMAGE_SECTION_HEADER, PointerToRawData ) )
ed $t5 $t4
r $t1 = @$t1 + 1
}
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$ now we are going to page in all the pages of the PE file in memory
r $t0 = $sections + ( $number_of_sections - 1 ) * @@c++(sizeof(nt!_IMAGE_SECTION_HEADER))
r $t1 = $imagebase + @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) @$t0 )->VirtualAddress ) + @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) @$t0 )->Misc.VirtualSize )
r $t2 = $imagebase + @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) @$t0 )->VirtualAddress ) + @@c++( ( ( nt!_IMAGE_SECTION_HEADER * ) @$t0 )->SizeOfRawData )
.if ($t2 > $t1) { r $t1 = $t2 }
r $t0 = $imagebase
? $t0
? $t1
.while (@$t0 < @$t1)
{
.printf "paging in: %x\n", @$t0
.pagein /p ${$arg2} @$t0
g
r $t0 = @$t0 + 0x1000;
};
? $t1 - $imagebase
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$ finally we are going to write the entire PE file to disk
.writemem ${$arg3} $imagebase L $t1 - $imagebase
}
================================================
FILE: WinDbg/dump_process_symbols_to_file.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$
$$ $$>a<dump_process_symbols_to_file.wdbg <path> <proc>
$$
$$ This simple script will dump to a file all the symbols of the given process.
$$ If you dump a PE from memory, it could have variables pointing to symbols (for example, api addresses that it got with GetProcAddress, etc...).
$$ It is useful to have a list of pairs (symbol, address) because in this way if we open the dumped PE with IDA we can search for that addresses
$$ and set a name for the variable containing them.
$$
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
.process /i ${$arg2}
g
.block
{
.reload
}
.block
{
.reload /user
}
.block { .shell -i- -ci "x *!*" findstr "!">${$arg1} }
}
================================================
FILE: WinDbg/dump_process_symbols_to_file_preselect.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$
$$ $$>a<dump_process_symbols_to_file_preselect.wdbg <path> <proc>
$$
$$ This simple script will dump to a file symbols of a preselection of common dlls of the given process.
$$ If you dump a PE from memory, it could have variables pointing to symbols (for example, api addresses that it got with GetProcAddress, etc...).
$$ It is useful to have a list of pairs (symbol, address) because in this way if we open the dumped PE with IDA we can search for that addresses
$$ and set a name for the variable containing them.
$$
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
.process /i ${$arg2}
g
.block
{
.reload
}
.block
{
.reload /user
}
r $t0 = 0; .foreach ( tok { lm m kernel32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "kernel32";
.block { .shell -i- -ci "x kernel32!*" findstr "!">${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m user32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "user32";
.block { .shell -i- -ci "x user32!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m advapi32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "advapi32";
.block { .shell -i- -ci "x advapi32!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m ws2_32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "ws2_32";
.block { .shell -i- -ci "x ws2_32!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m wininet } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "wininet";
.block { .shell -i- -ci "x wininet!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m ntdll } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "ntdll";
.block { .shell -i- -ci "x ntdll!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m crypt32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "crypt32";
.block { .shell -i- -ci "x crypt32!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m dnsapi } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "dnsapi";
.block { .shell -i- -ci "x dnsapi!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m shlwapi } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "shlwapi";
.block { .shell -i- -ci "x shlwapi!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m winhttp } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "winhttp";
.block { .shell -i- -ci "x winhttp!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m sysdm } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "sysdm";
.block { .shell -i- -ci "x sysdm!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m rpcrt4 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "rpcrt4";
.block { .shell -i- -ci "x rpcrt4!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m iphlpapi } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "iphlpapi";
.block { .shell -i- -ci "x iphlpapi!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m powrprof } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "powrprof";
.block { .shell -i- -ci "x powrprof!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m gdi32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "gdi32";
.block { .shell -i- -ci "x gdi32!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m msvcrt } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "msvcrt";
.block { .shell -i- -ci "x msvcrt*!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m msvcp } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "msvcp";
.block { .shell -i- -ci "x msvcp*!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m winmm } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "winmn";
.block { .shell -i- -ci "x winmm!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m gdiplus } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "gdiplus";
.block { .shell -i- -ci "x gdiplus!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m psapi } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "psapi";
.block { .shell -i- -ci "x psapi!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m urlmon } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "urlmon";
.block { .shell -i- -ci "x urlmon!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m netapi } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "netapi";
.block { .shell -i- -ci "x netapi!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m ole32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "netapi";
.block { .shell -i- -ci "x ole32!*" findstr "!">>${$arg1} };
}
}
================================================
FILE: WinDbg/dump_process_symbols_to_file_preselect_curproc.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$
$$ $$>a<dump_process_symbols_to_file_preselect.wdbg <path>
$$
$$ This simple script will dump to a file symbols of a preselection of common dlls of the given process.
$$ If you dump a PE from memory, it could have variables pointing to symbols (for example, api addresses that it got with GetProcAddress, etc...).
$$ It is useful to have a list of pairs (symbol, address) because in this way if we open the dumped PE with IDA we can search for that addresses
$$ and set a name for the variable containing them.
$$
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
.block
{
.reload
}
.block
{
.reload /user
}
r $t0 = 0; .foreach ( tok { lm m kernel32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "kernel32";
.block { .shell -i- -ci "x kernel32!*" findstr "!">${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m user32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "user32";
.block { .shell -i- -ci "x user32!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m advapi32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "advapi32";
.block { .shell -i- -ci "x advapi32!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m ws2_32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "ws2_32";
.block { .shell -i- -ci "x ws2_32!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m wininet } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "wininet";
.block { .shell -i- -ci "x wininet!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m ntdll } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "ntdll";
.block { .shell -i- -ci "x ntdll!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m crypt32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "crypt32";
.block { .shell -i- -ci "x crypt32!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m dnsapi } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "dnsapi";
.block { .shell -i- -ci "x dnsapi!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m shlwapi } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "shlwapi";
.block { .shell -i- -ci "x shlwapi!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m winhttp } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "winhttp";
.block { .shell -i- -ci "x winhttp!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m sysdm } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "sysdm";
.block { .shell -i- -ci "x sysdm!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m rpcrt4 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "rpcrt4";
.block { .shell -i- -ci "x rpcrt4!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m iphlpapi } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "iphlpapi";
.block { .shell -i- -ci "x iphlpapi!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m powrprof } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "powrprof";
.block { .shell -i- -ci "x powrprof!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m gdi32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "gdi32";
.block { .shell -i- -ci "x gdi32!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m msvcrt } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "msvcrt";
.block { .shell -i- -ci "x msvcrt*!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m msvcp } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "msvcp";
.block { .shell -i- -ci "x msvcp*!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m winmm } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "winmn";
.block { .shell -i- -ci "x winmm!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m gdiplus } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "gdiplus";
.block { .shell -i- -ci "x gdiplus!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m psapi } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "psapi";
.block { .shell -i- -ci "x psapi!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m urlmon } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "urlmon";
.block { .shell -i- -ci "x urlmon!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m netapi } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "netapi";
.block { .shell -i- -ci "x netapi!*" findstr "!">>${$arg1} };
}
r $t0 = 0; .foreach ( tok { lm m ole32 } ){ r $t0 = @$t0 + 1; };
.if( $t0 > 8 )
{
.printf "netapi";
.block { .shell -i- -ci "x ole32!*" findstr "!">>${$arg1} };
}
}
================================================
FILE: WinDbg/find_injected_pe_rmem.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<c:\tools\@scripts\windbg\find_injected_pe_rmem.wdbg
$$.sympath SRV*c:\symcache*http://msdl.microsoft.com/download/symbols
$$.reload
aS stage @$t19
aS temp @$t18
aS temp2 @$t17
aS temp3 @$t16
aS isPossiblePE @$t15
aS isDosMessageBased @$t14
aS prev1 @$t7
aS prev2 @$t6
aS baseaddr @$t5
aS baseaddrlen @$t4
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
r stage = 2
.printf "xxxx"
.foreach (processes_tok { !process 0 0 })
{
.if($scmp("${processes_tok}","PROCESS")==0)
{
.if(${stage}==2)
{
$$stage==2 is used to skip the first apparition of PROCESS string in the results of !process 0 0
r stage = 0
}
.else
{
r stage = 1
}
}
.elsif(${stage}==1)
{
.printf /D "<b>Analyzing process ${processes_tok}</b>\n"
r stage = 0
.process /i ${processes_tok}
g
.block
{
.reload /user
}
$$search for memory blocks
$$careful:
$$ when the baseaddress is over 0x10000000 findstr tokens will be:
$$ 93:7640:20010000
$$ 2002c000
$$ 1c000
$$ UserRange
$$ ...
$$ however if the addess is under 0x10000000:
$$ 25:1364:
$$ 60000
$$ 61000
$$ 1000
$$ UserRange
$$ The reason for this its windbg puts spaces before the base address when it hasnt 8 characters to complete 8 characters, but if the address is
$$ ???????? then it doesnt put spaces. We need to have in mind both case, and this is the reason of the stages of the next code
$$
.foreach (tok { .shell -ci "!address" findstr /N /O "UserRange.*Read" })
{
r isPossiblePE = 0
r isDosMessageBased = 0
.printf "${tok}\n"
.if($spat("${tok}","*:*:*")!=0)
{
r stage = 1
}
.elsif(${stage}==1)
{
r prev1 = ${tok}
r stage = 2
}
.elsif(${stage}==2)
{
r prev2 = ${tok}
r stage = 3
}
.elsif(${stage}==3)
{
.if($spat("${tok}","*UserRange*")!=0)
{
r baseaddr = prev1 - prev2
r baseaddrlen = prev2
r stage = 5
}
.else
{
r baseaddr = prev1
r baseaddrlen = ${tok}
r stage = 4
}
}
.elsif(${stage}==4)
{
r stage = 5
}
.elsif(${stage}==5)
{
$$for each block, check MZ / PE
r @$t0 = baseaddr
.if(@$t0!=0)
{
.printf "base %x\n", @$t0
.pagein /p ${processes_tok} @$t0
g
$$!address @$t0
.if($vvalid(@$t0, 2)==1)
{
.printf "valid base address\n"
.printf "pe %x\n", @$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew)
.if($vvalid(@$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew), 2)==1)
{
.printf "valid pe header address\n"
.if(wo(@$t0)==0x5a4d & dwo(@$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew))==0x454E)
{
$$We can find MZ / NE images, we ignore them
}
.elsif(wo(@$t0)==0x5a4d & dwo(@$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew))==0x4550)
{
$$if MZ and PE signatures, valid pe header
.printf "valid pe header\n"
r isPossiblePE = 1
}
.else
{
$$if not MZ or not PE signature, but msdos message is found, its a possible pe header
r temp = 0
.foreach (tok2 { s -a @$t0 L 0x80 "This program cannot " })
{
r temp = 1
}
.if(${temp}==1)
{
.printf "possible pe header\n"
r isPossiblePE = 1
r isDosMessageBased = 1
}
}
}
}
$$if we have found a possible PE in a memory zone with ReadWriteExecute protection, we will check if the base address is in the list of loaded module
.if(${isPossiblePE}==1)
{
.printf "is possible module %x\n", @$t0
r temp = 0
$$search for valid from "is not valid address"
.foreach (tok3 { .shell -ci "!lmi @$t0" findstr /N /O "valid.address" })
{
r temp = ${temp} + 1
}
.printf "%x\n", ${temp}
$$ "is not valid address" was found
.if(${temp} > 3)
{
$$ there are some modules that !lmi command is answering: is not valid address, however they seems to be valid loaded modules (not interesing for us).
$$ However if we consults information about the address with !address we find things as:
$$ Memory Usage: Section [\WINDOWS\System32\blablabla.mui] (it happens usually with .mui files, but not only with them
$$ We will discard results of !address with .dll], .mui] and .exe]
r temp = 0
.foreach (tok4 { .shell -ci "!address @$t0" findstr /N /O /R /I "\.mui\]" })
{
r temp = ${temp} + 1
}
r temp2 = 0
.foreach (tok5 { .shell -ci "!address @$t0" findstr /N /O /R /I "\.dll\]" })
{
r temp2 = ${temp2} + 1
}
r temp3 = 0
.foreach (tok6 { .shell -ci "!address @$t0" findstr /N /O /R /I "\.exe\]" })
{
r temp3 = ${temp3} + 1
}
.printf "search !address .mui %x\n", ${temp}
.printf "search !address .dll %x\n", ${temp2}
.printf "search !address .exe %x\n", ${temp3}
.if(${temp} < 4 and ${temp2} < 4 and ${temp3} < 4)
{
.if(${isDosMessageBased}==0)
{
.printf /D "<b>---------------------------------------------------------------------------</b>\n"
.printf /D "<b>Process: ${processes_tok} base: %x -> Possible injected or unpacked PE</b>\n", @$t0
.printf /D "<b>---------------------------------------------------------------------------</b>\n"
}
.else
{
.printf /D "-------------------------------------------------------------------------------------------------------------------------------------\n"
.printf /D "Process: ${processes_tok} base: %x -> Possible injected or unpacked PE, based on the dos header message: This program cannot...\n", @$t0
.printf /D "--------------------------------------------------------------------------------------------------------------------------------------\n"
}
}
}
}
r stage = 0
}
}
}
r stage = 0
}
}
ad stage
ad temp
ad temp2
ad temp3
ad isPossiblePE
ad isDosMessageBased
ad prev1
ad prev2
ad baseaddr
ad baseaddrlen
}
================================================
FILE: WinDbg/find_injected_pe_rwemem.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<find_injected_pe_rwemem.wdbg
$$
$$ This windbg script will walk the results of !address command for each process in the debuggee machine,
$$ searching for RWE memory containing PE files (based on the analysis of PE header).
$$
$$.sympath SRV*c:\symcache*http://msdl.microsoft.com/download/symbols
$$.reload
aS stage @$t19
aS temp @$t18
aS temp2 @$t17
aS temp3 @$t16
aS isPossiblePE @$t15
aS isDosMessageBased @$t14
aS prev1 @$t7
aS prev2 @$t6
aS baseaddr @$t5
aS baseaddrlen @$t4
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
r stage = 2
.printf "xxxx"
.foreach (processes_tok { !process 0 0 })
{
.printf "${processes_tok}\n"
.if($scmp("${processes_tok}","PROCESS")==0)
{
.if(${stage}==2)
{
$$stage==2 is used to skip the first apparition of PROCESS string in the results of !process 0 0
r stage = 0
}
.else
{
r stage = 1
}
}
.elsif(${stage}==1)
{
.printf /D "<b>Analyzing process ${processes_tok}</b>\n"
r stage = 0
.process /i ${processes_tok}
g
.block
{
.reload /user
}
$$search for memory blocks with ReadWriteExecute protection
$$careful:
$$ when the baseaddress is over 0x10000000 findstr tokens will be:
$$ 93:7640:20010000
$$ 2002c000
$$ 1c000
$$ UserRange
$$ ...
$$ however if the addess is under 0x10000000:
$$ 25:1364:
$$ 60000
$$ 61000
$$ 1000
$$ UserRange
$$ The reason for this its windbg puts spaces before the base address when it hasnt 8 characters to complete 8 characters, but if the address is
$$ ???????? then it doesnt put spaces. We need to have in mind both case, and this is the reason of the stages of the next code
$$
.foreach (tok { .shell -ci "!address" findstr /N /O /R /C:"UserRange.*ExecuteReadWrite" /C:"UserRange.*ReadWriteExecute" })
{
r isPossiblePE = 0
r isDosMessageBased = 0
.printf "${tok}\n"
.if($spat("${tok}","*:*:*")!=0)
{
r stage = 1
}
.elsif(${stage}==1)
{
r prev1 = ${tok}
r stage = 2
}
.elsif(${stage}==2)
{
r prev2 = ${tok}
r stage = 3
}
.elsif(${stage}==3)
{
.if($spat("${tok}","*UserRange*")!=0)
{
r baseaddr = prev1 - prev2
r baseaddrlen = prev2
r stage = 5
}
.else
{
r baseaddr = prev1
r baseaddrlen = ${tok}
r stage = 4
}
}
.elsif(${stage}==4)
{
r stage = 5
}
.elsif(${stage}==5)
{
$$for each block with ReadWriteExecute protection, check MZ / PE
r @$t0 = baseaddr
.if(@$t0!=0)
{
.printf "base %x\n", @$t0
.pagein /p ${processes_tok} @$t0
g
$$!address @$t0
.if($vvalid(@$t0, 2)==1)
{
.printf "valid base address\n"
.printf "pe %x\n", @$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew)
.if($vvalid(@$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew), 2)==1)
{
.printf "valid pe header address\n"
.if(wo(@$t0)==0x5a4d & dwo(@$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew))==0x454E)
{
$$We can find MZ / NE images, we ignore them
}
.elsif(wo(@$t0)==0x5a4d & dwo(@$t0+@@c++(((nt!_IMAGE_DOS_HEADER * )@$t0)->e_lfanew))==0x4550)
{
$$if MZ and PE signatures, valid pe header
.printf "valid pe header\n"
r isPossiblePE = 1
}
.else
{
$$if not MZ or not PE signature, but msdos message is found, its a possible pe header
r temp = 0
.foreach (tok2 { s -a @$t0 L 0x80 "This program cannot " })
{
r temp = 1
}
.if(${temp}==1)
{
.printf "possible pe header\n"
r isPossiblePE = 1
r isDosMessageBased = 1
}
}
}
}
$$if we have found a possible PE in a memory zone with ReadWriteExecute protection, we will check if the base address is in the list of loaded module
.if(${isPossiblePE}==1)
{
.printf "is possible module %x\n", @$t0
r temp = 0
$$search for valid from "is not valid address"
.foreach (tok3 { .shell -ci "!lmi @$t0" findstr /N /O "valid.address" })
{
r temp = ${temp} + 1
}
.printf "%x\n", ${temp}
$$ "is not valid address" was found
.if(${temp} > 3)
{
$$ there are some modules that !lmi command is answering: is not valid address, however they seems to be valid loaded modules (not interesing for us).
$$ However if we consults information about the address with !address we find things as:
$$ Memory Usage: Section [\WINDOWS\System32\blablabla.mui] (it happens usually with .mui files, but not only with them
$$ We will discard results of !address with .dll], .mui] and .exe]
r temp = 0
.foreach (tok4 { .shell -ci "!address @$t0" findstr /N /O /R /I "\.mui\]" })
{
r temp = ${temp} + 1
}
r temp2 = 0
.foreach (tok5 { .shell -ci "!address @$t0" findstr /N /O /R /I "\.dll\]" })
{
r temp2 = ${temp2} + 1
}
r temp3 = 0
.foreach (tok6 { .shell -ci "!address @$t0" findstr /N /O /R /I "\.exe\]" })
{
r temp3 = ${temp3} + 1
}
.printf "search !address .mui %x\n", ${temp}
.printf "search !address .dll %x\n", ${temp2}
.printf "search !address .exe %x\n", ${temp3}
.if(${temp} < 4 and ${temp2} < 4 and ${temp3} < 4)
{
.if(${isDosMessageBased}==0)
{
.printf /D "<b>---------------------------------------------------------------------------</b>\n"
.printf /D "<b>Process: ${processes_tok} base: %x -> Possible injected or unpacked PE</b>\n", @$t0
.printf /D "<b>---------------------------------------------------------------------------</b>\n"
}
.else
{
.printf /D "-------------------------------------------------------------------------------------------------------------------------------------\n"
.printf /D "Process: ${processes_tok} base: %x -> Possible injected or unpacked PE, based on the dos header message: This program cannot...\n", @$t0
.printf /D "--------------------------------------------------------------------------------------------------------------------------------------\n"
}
}
}
}
r stage = 0
}
}
}
r stage = 0
}
}
ad stage
ad temp
ad temp2
ad temp3
ad isPossiblePE
ad isDosMessageBased
ad prev1
ad prev2
ad baseaddr
ad baseaddrlen
}
================================================
FILE: WinDbg/find_injected_pe_search.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<find_injected_pe_search.wdbg
$$
$$ This windbg script will search for PE headers. Later, it checks !lmi results for checking of the found PE header matchs a loaded module.
$$ If not, it reports that PE (assuming it could be an unpacked or injected PE).
$$
$$.sympath SRV*c:\symcache*http://msdl.microsoft.com/download/symbols
$$.reload
$$.foreach ( header {s -[1]d 0x0 L?0xffffffff 0x00905a4d} )
$${
$$ .echo "****HEADER****"
$$ !lmi header
$$}
$$.foreach /ps 0x10 ( header {s 0 L?0x80000000 4d 5a 90 00} )
$${
$$ .echo "****HEADER****"
$$ !lmi header
$$}
aS stage @$t19
aS temp @$t18
aS baseSearch @$t17
aS stop @$t16
aS temp2 @$t15
aS temp3 @$t14
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
r stage = 2
.foreach (processes_tok { !process 0 0 })
{
.if($scmp("${processes_tok}","PROCESS")==0)
{
.if(${stage}==2)
{
$$stage==2 is used to skip the first apparition of PROCESS string in the results of !process 0 0
r stage = 0
}
.else
{
r stage = 1
}
}
.elsif(${stage}==1)
{
.printf /D "<b>Analyzing process ${processes_tok}</b>\n"
r stage = 0
.process /i ${processes_tok}
g
.block
{
.reload
}
.block
{
.reload /user
}
r ${stop} = 0
r ${baseSearch} = 0
.while(${stop} == 0)
{
.printf "searching base %x\n", ${baseSearch}
r @$t0 = ${baseSearch}
.foreach /ps 0x10 ( header {s @$t0 L?0x10000000 4d 5a 90 00} )
{
r temp = 0
r @$t0 = ${header}
.printf "MZ at ${header}\n"
.if(poi(${header}+3c) < 0x2ff)
{
.if(poi(${header}+poi(${header}+3c)) == 0x00004550)
{
.printf "PE signature found\n"
$$search for valid from: is not valid address
.foreach (tok { .shell -ci "!lmi ${header}" findstr /N /O /R /I "valid.address" })
{
r temp = ${temp} + 1
}
.printf "search valid address %x\n", ${temp}
$$ "is not valid address was found"
.if(${temp} > 3)
{
$$ there are some modules that !lmi command is answering: is not valid address, however they seems to be valid loaded modules (not interesing for us).
$$ However if we consults information about the address with !address we find things as:
$$ Memory Usage: Section [\WINDOWS\System32\blablabla.mui] (it happens usually with .mui files, but not only with them
$$ We will discard results of !address with .dll], .mui] and .exe]
r temp = 0
.foreach (tok2 { .shell -ci "!address ${header}" findstr /N /O /R /I "\.mui\]" })
{
r temp = ${temp} + 1
}
r temp2 = 0
.foreach (tok3 { .shell -ci "!address ${header}" findstr /N /O /R /I "\.dll\]" })
{
r temp2 = ${temp2} + 1
}
r temp3 = 0
.foreach (tok4 { .shell -ci "!address ${header}" findstr /N /O /R /I "\.exe\]" })
{
r temp3 = ${temp3} + 1
}
.printf "search !address .mui %x\n", ${temp}
.printf "search !address .dll %x\n", ${temp2}
.printf "search !address .exe %x\n", ${temp3}
.if(${temp} < 4 and ${temp2} < 4 and ${temp3} < 4)
{
.printf /D "<b>---------------------------------------------------------------------------</b>\n"
.printf /D "<b>Process: ${processes_tok} base: %x -> Possible injected or unpacked PE</b>\n", @$t0
.printf /D "<b>---------------------------------------------------------------------------</b>\n"
}
}
}
.else
{
.printf "PE signature not found\n"
}
}
.else
{
.printf "Invalid lfanew\n"
}
}
.if(${baseSearch} >= 0x70000000)
{
.printf "next process\n"
r ${stop} = 1
}
r ${baseSearch} = ${baseSearch} + 0x10000000
}
}
}
ad stage
ad temp
ad baseSearch
ad stop
ad temp2
ad temp3
}
================================================
FILE: WinDbg/find_objects_by_name.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<c:\tools\@scripts\windbg\find_objects_by_name.wdbg <process> <type> <name matching word>
$$
$$ Types: Event, Section, File, Port, Directory, SymbolicLink, Mutant, WindowStation, Semaphore, Key, Token, Process, Thread, Desktop, IoCompletion, Timer, Job, and WaitablePort
$$
$$ If process == 0, all processes
$$ If type == All, search all types
$$ If name matching word == *, any name
aS stage @$t19
aS lastproc @$t18
aS lastobject @$t17
aS lasttypevalid @$t16
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
$$ STAGE == 1 last token was PROCESS
$$ STAGE == 2 last token was Directory
$$ STAGE == 3 last token was Object:
$$ STAGE == 4 last token was Name:
$$ STAGE == 5 last token was Type:
r stage = 0
.foreach (handle_tok { !handle 0 3 ${$arg1} ${$arg2} })
{
$$ .printf "token = ${handle_tok}\n"
.if($scmp("\${handle_tok}","\\")==0)
{
}
.else
{
.if(${stage}==1)
{
.if($scmp("${handle_tok}","HANDLE")!=0)
{
$$ .printf "stage1 ${handle_tok}\n"
r ${lastproc} = ${handle_tok}
}
r stage = 0
}
.elsif(${stage}==2)
{
$$ if Directory found, skip next Object: word
$$ .printf "stage2\n"
r stage = 0
}
.elsif(${stage}==3)
{
$$ .printf "stage3 ${handle_tok}\n"
r ${lastobject} = ${handle_tok}
r stage = 0
}
.elsif(${stage}==4)
{
$$ .printf "stage4\n"
.if(${lasttypevalid}==1)
{
r @$t0 = 0
.if($scmp("*","${$arg3}")==0)
{
r @$t0 = 4
}
.else
{
.foreach (toktemp { .shell -ci "!object ${lastobject}" findstr /N /O "${$arg3}" })
{
r @$t0 = @$t0 + 1
}
}
.if(@$t0 > 3)
{
.printf "Process %x ", ${lastproc}
!object ${lastobject}
}
}
r stage = 0
}
.elsif(${stage}==5)
{
r stage = 6
}
.elsif(${stage}==6)
{
$$ .printf "stage6 ${handle_tok}\n"
r lasttypevalid = 0
.if($scmp("${handle_tok}","${$arg2}")==0)
{
r lasttypevalid = 1
}
.if($scmp("All","${$arg2}")==0)
{
r lasttypevalid = 1
}
r stage = 0
}
.elsif($scmp("${handle_tok}","PROCESS")==0)
{
$$ .printf "PROCESS\n"
r stage = 1
}
.elsif($scmp("${handle_tok}","Directory")==0)
{
$$ .printf "Image:\n"
r stage = 2
}
.elsif($scmp("${handle_tok}","Object:")==0)
{
$$ .printf "Object:\n"
r stage = 3
}
.elsif($scmp("${handle_tok}","Name:")==0)
{
$$ .printf "Name:\n"
r stage = 4
}
.elsif($scmp("${handle_tok}","Type:")==0)
{
$$ .printf "Type:\n"
r stage = 5
}
}
}
ad stage
ad lastproc
ad lastobject
ad lasttypevalid
}
================================================
FILE: WinDbg/hang_exited_processes.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<c:\tools\@scripts\windbg\hang_exited_processes.wdbg <dir start/end files>
.logopen ${$arg1}\hang_exited_processes.start
.printf "start"
.logclose
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
r @$t0 = nt!KeDelayExecutionThread
r @$t1 = nt!NtTerminateProcess
$$push [interval]
eb @$t1 0x68
ed @$t1+1 @$t1+0x24
$$push FALSE <no alertable>
eb @$t1+5 0x68
ed @$t1+6 0x0
$$push UserMode
eb @$t1+0xa 0x68
ed @$t1+0xb 0x1
$$push 0 <fake ret addr>
eb @$t1+0xf 0x68
ed @$t1+0x10 0x0
$$push nt!KeDelayExecutionThread / ret
eb @$t1+0x14 0x68
ed @$t1+0x15 @$t0
eb @$t1+0x19 0xc3
$$interval
ed @$t1+0x24 0x00000000
ed @$t1+0x28 0xf0000000
.logopen ${$arg1}\hang_exited_processes.end
.printf "end"
.logclose
================================================
FILE: WinDbg/isx64.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
aS x64arch $t0
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
$$is x64?
r x64arch = 0;
.foreach( tok { .effmach } )
{
.if($scmp("${tok}","x64")==0)
{
r x64arch = 1;
.break;
};
};
}
================================================
FILE: WinDbg/lengthDisassembler.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
u ${$arg1} L 1
r @$t0 = 0
.foreach(myVariable {u ${$arg1} L 1})
{
r @$t0 = @$t0 + 1
.if(@$t0==3)
{
.if($spat("${myVariable}", "??")==1){.printf "1"}
.if($spat("${myVariable}", "????")==1){.printf "2"}
.if($spat("${myVariable}", "??????")==1){.printf "3"}
.if($spat("${myVariable}", "????????")==1){.printf "4"}
.if($spat("${myVariable}", "??????????")==1){.printf "5"}
.if($spat("${myVariable}", "????????????")==1){.printf "6"}
.if($spat("${myVariable}", "??????????????")==1){.printf "7"}
.if($spat("${myVariable}", "????????????????")==1){.printf "8"}
.if($spat("${myVariable}", "??????????????????")==1){.printf "9"}
}
}
================================================
FILE: WinDbg/load_code_to_kernel_memory.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<c:\tools\@scripts\windbg\load_code_to_kernel_memory.wdbg <src code> <mem size> <offset start routine>
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload /f
}
$$set a breakpoint on a common function that is executed frequently (NtCreateFile for example) for hijacking the thread for a while
ba e1 nt!NtCreateFile
g
.printf "${$arg1}"
.printf "${$arg2}"
.printf "${$arg3}"
bc *
$$save original esp register and stack parameters that we are going to overwrite when calling ExAllocatePool and PsCreateSystemThread
r @$t19 = (poi esp)
r @$t18 = (poi esp+4)
r @$t17 = (poi esp+8)
r @$t16 = (poi esp+c)
r @$t15 = (poi esp+10)
r @$t14 = (poi esp+14)
r @$t13 = (poi esp+18)
r @$t12 = (poi esp+1c)
r @$t11 = esp
$$change the stack with the parameters that we need for ExAllocatePool
ed (esp+4) 0
ed (esp+8) ${$arg2}
$$hijack the thread running on NtCreateFile to execute ExAllocatePool
u nt!ExAllocatePool
dd esp
r eip = nt!ExAllocatePool
$$steps until the ret instruction is found. We cant execute step out (gu) because we would get a 0x30 bugcheck, this is the reason:
$$ "This bug check occurs when there's a stack underflow detected when restoring context coming out of a trap. There's a check to
$$ validate that the current ESP is less than the ESP saved in the trap frame. If current_esp < saved_esp, bugcheck. "
.while (1)
{
p
r @$t10 = (poi eip)
r @$t10 = @$t10 & 0x000000ff
.if (@$t10 == 0xc2)
{
.break
}
}
r @$t0 = eax
.printf "allocated mem: %x\n", @$t0
$$load code from file to allocated memory
$$careful: .readmem will read blocks of 0x1000 bytes. For example, if your file to load has 0x2800 bytes, .readmem will load 0x2000 bytes only.
$$You will need to complete the file with 0x800 additional trash bytes to load the full code
.readmem ${$arg1} @$t0
$$ @$t1 = allocated mem membase, code is read
r @$t1 = @$t0
$$Now we are going to create a kernel thread at @$t1 + arg3 (membase + startroutine_offset)
$$ NTSTATUS PsCreateSystemThread(
$$ _Out_ PHANDLE ThreadHandle,
$$ _In_ ULONG DesiredAccess,
$$ _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
$$ _In_opt_ HANDLE ProcessHandle,
$$ _Out_opt_ PCLIENT_ID ClientId,
$$ _In_ PKSTART_ROUTINE StartRoutine,
$$ _In_opt_ PVOID StartContext
$$ );
ed (esp+1c) 0
ed (esp+18) @$t1+${$arg3}
ed (esp+14) 0
ed (esp+10) 0
ed (esp+c) 0
ed (esp+8) 0
$$ThreadHandle inout, we use the memory of the parameter StartContext that we dont need
ed (esp+4) (esp+1c)
$$set a breakpoint where the thread is going to be created
ba e1 @$t1+${$arg3}
u nt!PsCreateSystemThread
dd esp
r eip = nt!PsCreateSystemThread
$$again steps until ret instruction is found
.while (1)
{
p
r @$t10 = (poi eip)
r @$t10 = @$t10 & 0x000000ff
.if (@$t10 == 0xc2)
{
.break
}
}
$$restore original registers and stack to continue the execution with no problems
r eip = nt!NtCreateFile
r esp = @$t11
ed esp @$t19
ed (esp+4) @$t18
ed (esp+8) @$t17
ed (esp+c) @$t16
ed (esp+10) @$t15
ed (esp+14) @$t14
ed (esp+18) @$t13
ed (esp+1c) @$t12
g
================================================
FILE: WinDbg/load_swish.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
!load C:\tools\@scripts\windbg\SwishDbgExt-master\Debug\x64\SwishDbgExt.dll
================================================
FILE: WinDbg/log_processes.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<log_processes.wdbg <destination directory>
.logopen ${$arg1}\log_processes.start
.logclose
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.logopen ${$arg1}\processes_wdbglog.txt
!process 0 0
.logclose
.logopen ${$arg1}\log_processes.end
.logclose
================================================
FILE: WinDbg/monitoring_breakpoints.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<c:\tools\@scripts\windbg\monitoring_breakpoints.wdbg
.block
{
.sympath SRV*c:\symcache*http://msdl.microsoft.com/download/symbols
.reload
}
$$.process /i ${$arg1}
$$g
$$.reload /user
.block
{
bp /p ${$arg1} kernel32!CreateFileA ".printf /D \"<b>%x - CreateFileA('%ma')</b>\\n\", poi($csp), poi($csp+4) ; gc"
bp /p ${$arg1} kernel32!CreateFileW ".printf /D \"<b>%x - CreateFileW('%mu')</b>\\n\", poi($csp), poi($csp+4) ; gc"
bp /p ${$arg1} advapi32!RegOpenKeyA ".printf /D \"<b>%x - RegOpenKeyA(%x,%ma)</b>\\n\", poi($csp), poi($csp+4), poi($csp+2*4) ; gc"
bp /p ${$arg1} advapi32!RegOpenKeyW ".printf /D \"<b>%x - RegOpenKeyW(%x,%mu)</b>\\n\", poi($csp), poi($csp+4), poi($csp+2*4) ; gc"
bp /p ${$arg1} advapi32!RegCreateKeyA ".printf /D \"<b>%x - RegCreateKeyA(%x,%ma)</b>\\n\", poi($csp), poi($csp+4), poi($csp+2*4) ; gc"
bp /p ${$arg1} advapi32!RegCreateKeyW ".printf /D \"<b>%x - RegCreateKeyW(%x,%mu)</b>\\n\", poi($csp), poi($csp+4), poi($csp+2*4) ; gc"
bp /p ${$arg1} advapi32!RegSetValueA ".printf /D \"<b>%x - RegSetValueA(%x,%ma)</b>\\n\", poi($csp), poi($csp+4), poi($csp+2*4) ; gc"
bp /p ${$arg1} advapi32!RegSetValueW ".printf /D \"<b>%x - RegSetValueW(%x,%mu)</b>\\n\", poi($csp), poi($csp+4), poi($csp+2*4) ; gc"
bp /p ${$arg1} advapi32!RegSetValueExA ".printf /D \"<b>%x - RegSetValueExA(%x,%ma)</b>\\n\", poi($csp), poi($csp+4), poi($csp+2*4) ; gc"
bp /p ${$arg1} advapi32!RegSetValueExW ".printf /D \"<b>%x - RegSetValueExW(%x,%mu)</b>\\n\", poi($csp), poi($csp+4), poi($csp+2*4) ; gc"
bp /p ${$arg1} kernel32!LoadLibraryW ".printf /D \"<b>%x - LoadLibraryW('%mu')</b>\\n\", poi($csp), poi($csp+4) ; gc"
bp /p ${$arg1} kernel32!WriteProcessMemory ".printf /D \"<b>%x - WriteProcessMemory(%x, %x, %ma)-></b>\", poi($csp), poi($csp+4), poi($csp+4*2), poi($csp+3*4); $$>a<$$>a<c:\\tools\\@scripts\\windbg\\show_proc_from_handle.wdbg poi($csp+4);.printf \"\\n\"; gc"
bp /p ${$arg1} kernel32!CreateRemoteThread ".printf /D \"<b>%x - CreateRemoteThread(%x, %x)-></b>\", poi($csp), poi($csp+4), poi($csp+4*4); $$>a<c:\\tools\\@scripts\\windbg\\show_proc_from_handle.wdbg poi($csp+4);.printf \"\\n\"; gc"
bp /p ${$arg1} kernel32!CreateProcessA ".printf /D \"<b>%x - CreateProcessA(%ma,%ma)</b>\\n\", poi($csp), poi($csp+4), poi($csp+2*4) ; gc"
bp /p ${$arg1} kernel32!CreateProcessW ".printf /D \"<b>%x - CreateProcessW(%mu,%mu)</b>\\n\", poi($csp), poi($csp+4), poi($csp+2*4) ; gc"
bp /p ${$arg1} wininet!InternetOpenA ".printf /D \"<b>%x - InternetOpenA()</b>\\n\", poi($csp); gc"
bp /p ${$arg1} wininet!InternetOpenW ".printf /D \"<b>%x - InternetOpenW()</b>\\n\", poi($csp); gc"
bp /p ${$arg1} wininet!InternetOpenUrlA ".printf /D \"<b>%x - InternetOpenA(%ma,%ma)</b>\\n\", poi($csp), poi($csp+2*4), poi($csp+3*4) ; gc"
bp /p ${$arg1} wininet!InternetOpenUrlW ".printf /D \"<b>%x - InternetOpenW(%mu,%mu)</b>\\n\", poi($csp), poi($csp+2*4), poi($csp+3*4) ; gc"
$$bp /p ${$arg1} kernel32!CreateWaitableTimerW ".printf /D \"<b>%x - CreateWaitableTimerW('%mu')</b>\\n\", poi($csp), poi($csp+3*4) ; gc"
$$bp /p ${$arg1} kernel32!GetTickCount "$$ .printf /D \"<b>%x - GetTickCount()</b>\\n\" , poi($csp); r eax = 0 ; r eip = poi($csp) ; r esp = esp + 4 ; gc"
$$bp /p ${$arg1} kernel32!Sleep ".printf /D \"<b>%x - Sleep(%ds)</b>\\n\", poi($csp), poi($csp+4)/0n1000 ; r eip = poi($csp) ; r esp = esp + 8 ; gc"
bp /p ${$arg1} kernel32!ExitProcess ".printf /D \"<b>%x - ExitProcess()</b>\\n\", poi($csp); gc"
bp /p ${$arg1} kernel32!VirtualProtect ".printf /D \"<b>%x - VirtualProtect(%p, %x, %x)</b>\\n\", poi($csp), poi($csp+4), poi($csp+2*4), poi($csp+3*4) ; gc"
bp /p ${$arg1} kernel32!VirtualProtectEx ".printf /D \"<b>%x - VirtualProtectEx(%p, %x, %x)-></b>\", poi($csp), poi($csp+2*4), poi($csp+3*4), poi($csp+4*4) ; $$>a<c:\\tools\\@scripts\\windbg\\show_proc_from_handle.wdbg poi($csp+4);.printf \"\\n\"; gc"
bp /p ${$arg1} advapi32!CryptGenKey ".printf /D \"<b>%x - CryptGenKey(%x, %x)</b>\\n\", poi($csp), poi($csp+2*4), poi($csp+3*4) ; gc"
bp /p ${$arg1} advapi32!CryptImportKey ".printf /D \"<b>%x - CryptImportKey(%p, %x, %x)</b>\\n\", poi($csp), poi($csp+2*4), poi($csp+3*4), poi($csp+5*4); gc"
bp /p ${$arg1} advapi32!CryptDecrypt ".printf /D \"<b>%x - CryptDecrypt(%x, %p, %p)</b>\\n\", poi($csp), poi($csp+4*4), poi($csp+5*4), poi($csp+6*4); gc"
bp /p ${$arg1} advapi32!CryptEncrypt ".printf /D \"<b>%x - CryptEncrypt(%x, %p, %p, %x)</b>\\n\", poi($csp), poi($csp+4*4), poi($csp+5*4), poi($csp+6*4), poi($csp+7*4); gc"
$$bp /p ${$arg1} kernel32!CreateToolhelp32Snapshot ".printf /D \"<b>%x - CreateToolhelp32Snapshot(%x, %x)</b>\\n\", poi($csp), poi($csp+4), poi($csp+2*4) ; gc"
$$bp /p ${$arg1} kernel32!Process32First ".printf /D \"<b>%x - Process32First()</b>\\n\", poi($csp); gc"
$$bp /p ${$arg1} kernel32!Process32Next ".printf /D \"<b>%x - Process32Next()</b>\\n\", poi($csp); gc"
$$bp /p ${$arg1} kernel32!Thread32First ".printf /D \"<b>%x - Thread32First()</b>\\n\", poi($csp); gc"
$$bp /p ${$arg1} kernel32!Thread32Next ".printf /D \"<b>%x - Thread32Next()</b>\\n\", poi($csp); gc"
$$bp /p ${$arg1} kernel32!Module32First ".printf /D \"<b>%x - Module32First()</b>\\n\", poi($csp); gc"
$$bp /p ${$arg1} kernel32!Module32Next ".printf /D \"<b>%x - Module32Next()</b>\\n\", poi($csp); gc"
sxd sse
}
================================================
FILE: WinDbg/pagein_range.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$
$$ $$>a<pagein_range.wdbg <start_address> <end_address> <process>
$$
$$ Page into memory a range of memory of the given process
$$
r $t0 = ${$arg1};
.while (@$t0 < ${$arg2})
{
.if($vvalid(@$t0, 1)==0)
{
.printf "paging in: %x\n", @$t0
.pagein /p ${$arg3} @$t0
g
.if($vvalid(@$t0, 1)==0)
{
.printf "!!!failed to page in: %x\n", @$t0
}
}
.else
{
.printf "already paged in: %x\n", @$t0
}
r $t0 = @$t0 + 0x1000;
};
================================================
FILE: WinDbg/remove_device_acls.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
aS stage @$t19
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
r stage = 0
.foreach( tok { !devobj "${$arg1}" } )
{
.printf "${tok}\r\n"
.if(${stage}==1)
{
.echo ${tok}
dt _DEVICE_OBJECT ${tok}
r $t0 = ${tok}
dt _SECURITY_DESCRIPTOR @@c++( ( ( nt!_DEVICE_OBJECT * ) @$t0 )->SecurityDescriptor )
ep @@c++( ( ( nt!_DEVICE_OBJECT * ) @$t0 )->SecurityDescriptor ) + @@c++( #FIELD_OFFSET( _SECURITY_DESCRIPTOR, Sacl ) ) 0
ep @@c++( ( ( nt!_DEVICE_OBJECT * ) @$t0 )->SecurityDescriptor ) + @@c++( #FIELD_OFFSET( _SECURITY_DESCRIPTOR, Dacl ) ) 0
dt _SECURITY_DESCRIPTOR @@c++( ( ( nt!_DEVICE_OBJECT * ) @$t0 )->SecurityDescriptor )
.break
}
.if(${stage}==0)
{
.if($scmp("${tok}","object")==0)
{
r stage = 1
}
}
}
================================================
FILE: WinDbg/search_bytes_all_processes.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<search_bytes_all_processes.wdbg <byte1> <byte2> ... <byteN> (max 16 bytes)
$$
$$ This script is useful for search a max of 16 given bytes through all the running processes
$$
aS stage @$t19
aS temp @$t18
aS baseSearch @$t17
aS stop @$t16
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
r stage = 2
.foreach (processes_tok { !process 0 0 })
{
.if($scmp("${processes_tok}","PROCESS")==0)
{
.if(${stage}==2)
{
$$stage==2 is used to skip the first apparition of PROCESS string in the results of !process 0 0
r stage = 0
}
.else
{
r stage = 1
}
}
.elsif(${stage}==1)
{
.printf /D "<b>Analyzing process ${processes_tok}</b>\n"
r stage = 0
.process /i ${processes_tok}
g
.block
{
.reload
}
.block
{
.reload /user
}
r ${stop} = 0
r ${baseSearch} = 0
.while(${stop} == 0)
{
.printf "searching base %x\n", ${baseSearch}
r @$t0 = ${baseSearch}
s @$t0 L?0x10000000 ${$arg1} ${$arg2} ${$arg3} ${$arg4} ${$arg5} ${$arg6} ${$arg7} ${$arg8} ${$arg9} ${$arg10} ${$arg11} ${$arg12} ${$arg13} ${$arg14} ${$arg15} ${$arg16}
.if(${baseSearch} >= 0x70000000)
{
.printf "next process\n"
r ${stop} = 1
}
r ${baseSearch} = ${baseSearch} + 0x10000000
}
}
}
ad stage
ad temp
ad baseSearch
ad stop
}
================================================
FILE: WinDbg/search_bytes_target_process.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<search_string_target_process.wdbg <proc> <byte1> <byte2> .. <byteN>
$$
$$ This script is useful for search a max of 16 given bytes in the given process
$$
aS baseSearch @$t17
aS stop @$t16
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
.process /i ${$arg1}
g
.block
{
.reload
}
.block
{
.reload /user
}
r ${stop} = 0
r ${baseSearch} = 0
.while(${stop} == 0)
{
.printf "searching base %x\n", ${baseSearch}
r @$t0 = ${baseSearch}
s @$t0 L?0x10000000 ${$arg2} ${$arg3} ${$arg4} ${$arg5} ${$arg6} ${$arg7} ${$arg8} ${$arg9} ${$arg10} ${$arg11} ${$arg12} ${$arg13} ${$arg14} ${$arg15} ${$arg16} ${$arg17}
.if(${baseSearch} >= 0x70000000)
{
r ${stop} = 1
}
r ${baseSearch} = ${baseSearch} + 0x10000000
}
ad baseSearch
ad stop
}
================================================
FILE: WinDbg/search_string_all_processes.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<search_string_all_processes.wdbg <string>
$$
$$ This script is useful for search a given string through all the running processes
$$
aS stage @$t19
aS temp @$t18
aS baseSearch @$t17
aS stop @$t16
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
r stage = 2
.foreach (processes_tok { !process 0 0 })
{
.if($scmp("${processes_tok}","PROCESS")==0)
{
.if(${stage}==2)
{
$$stage==2 is used to skip the first apparition of PROCESS string in the results of !process 0 0
r stage = 0
}
.else
{
r stage = 1
}
}
.elsif(${stage}==1)
{
.printf /D "<b>Analyzing process ${processes_tok}</b>\n"
r stage = 0
.process /i ${processes_tok}
g
.block
{
.reload
}
.block
{
.reload /user
}
r ${stop} = 0
r ${baseSearch} = 0
.while(${stop} == 0)
{
.printf "searching base %x\n", ${baseSearch}
r @$t0 = ${baseSearch}
s -a @$t0 L?0x10000000 "${$arg1}"
.if(${baseSearch} >= 0x70000000)
{
.printf "next process\n"
r ${stop} = 1
}
r ${baseSearch} = ${baseSearch} + 0x10000000
}
}
}
ad stage
ad temp
ad baseSearch
ad stop
}
================================================
FILE: WinDbg/search_string_target_process.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<search_string_target_process.wdbg <proc> <string>
$$
$$ This script is useful for search a given string in a given process
$$
aS baseSearch @$t17
aS stop @$t16
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.block
{
.process /i ${$arg1}
g
.block
{
.reload
}
.block
{
.reload /user
}
r ${stop} = 0
r ${baseSearch} = 0
.while(${stop} == 0)
{
.printf "searching base %x\n", ${baseSearch}
r @$t0 = ${baseSearch}
s -a @$t0 L?0x10000000 "${$arg2}"
.if(${baseSearch} >= 0x70000000)
{
r ${stop} = 1
}
r ${baseSearch} = ${baseSearch} + 0x10000000
}
ad baseSearch
ad stop
}
================================================
FILE: WinDbg/secure_writemem.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<secure_writemem.wdbg <start> <end> <process> <targetdir> <ext>
$$
$$ this script tries to dump a range of memory. If its not possible to dump a part of the range, that part if filled with random data
$$ (really its filled with "\x11\x11\x11......\x11\x20\x0d\x0a" (total length 0x1000 for each page filled), but we must not assume it
$$ will always contain this value
.printf "secure_writemem.wdbg\r\n"
.printf "${$arg1}\r\n"
.printf "${$arg2}\r\n"
.printf "${$arg3}\r\n"
.printf "${$arg4}\r\n"
.printf "${$arg5}\r\n"
aS swmbase @$t18
aS swmend @$t17
aS swmtemp @$t16
.block
{
.printf "secure_writemem_1\r\n"
r ${swmbase} = ${$arg1}
r ${swmend} = ${$arg2}
r ${swmtemp} = ${swmbase}
.printf "secure_writemem_2\r\n"
.foreach /pS 4 (swmlen_tok { ? (${swmend}-${swmbase}) })
{
.printf "secure_writemem_3\r\n"
.block
{
.shell -x del ${$arg4}\${$arg3}_${$arg1}_${swmlen_tok}.${$arg5}
}
.printf "secure_writemem_4\r\n"
.while (${swmtemp} < ${swmend})
{
.printf "paging in: %x\n", ${swmtemp}
.pagein /p ${$arg3} ${swmtemp}
g
.foreach /pS 4 (swmtemp_tok { ? swmtemp })
{
.printf "secure_writemem_temp_${swmtemp_tok}.bin > ${$arg3}_${$arg1}_${len_tok}.${$arg5}\n"
.if($vvalid(${swmtemp}, 0x1000)==1)
{
.writemem ${$arg4}\secure_writemem_temp_${swmtemp_tok}.bin ${swmtemp} L 0x1000
.shell -x type ${$arg4}\secure_writemem_temp_${swmtemp_tok}.bin >> ${$arg4}\${$arg3}_${$arg1}_${swmlen_tok}.${$arg5} && del ${$arg4}\secure_writemem_temp_${swmtemp_tok}.bin
}
.else
{
.printf "invalid address: %x\n", ${swmtemp}
.shell -x echo >> ${$arg4}\${$arg3}_${$arg1}_${swmlen_tok}.${$arg5} && x
}
}
r ${swmtemp} = ${swmtemp} + 0x1000
}
.printf "secure_writemem_5\r\n"
}
}
.printf "secure_writemem_6\r\n"
================================================
FILE: WinDbg/show_address_info.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<show_address_info.wdbg <address> <process>
$$
aS stage @$t19
aS basealloc @$t18
aS stop @$t17
aS baseSearch @$t16
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
.process /i ${$arg2}
g
.reload
}
.block
{
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$print info related to the address
.printf /D "<b>\nAddress info for address %x</b>\n", ${$arg1}
.printf /D "<b>--------------------------------------------</b>\n"
!address ${$arg1}
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$print info related to the heap (if the address belongs to a heap)
.printf /D "<b>\nHeap related info to address %x</b>\n", ${$arg1}
.printf /D "<b>--------------------------------------------</b>\n"
!heap -p -a ${$arg1}
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$search for references to the arg1 address
.printf /D "<b>\nReferences to address %x</b>\n", ${$arg1}
.printf /D "<b>--------------------------------------------</b>\n"
r ${stop} = 0
r ${baseSearch} = 0
.while(${stop} == 0)
{
$$.printf "searching %x\n", ${baseSearch}
r @$t0 = ${baseSearch}
r @$t1 = ${$arg1}
s @$t0 L 10000000 (@$t1 & ff) ((@$t1 & ff00) >> 8) ((@$t1 & ff0000) >> 10) ((@$t1 & ff000000) >> 18)
.if(${baseSearch} >= 0x70000000)
{
r ${stop} = 1
}
r ${baseSearch} = ${baseSearch} + 10000000
}
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$ use !heap extension to guess the base of the allocated memory related to the arg1 address
r stage = 0
r basealloc = -1
.foreach ( tok {!heap -p -a ${$arg1}} )
{
.if(${stage} == 2)
{
r basealloc = ${tok}
.break
}
.if(${stage} == 1)
{
.if($scmp("${tok}0","state0")==0)
{
r stage = 2
}
}
.if(${stage} == 0)
{
.if($scmp("${tok}0","HEAP_ENTRY0")==0)
{
r stage = 1
}
}
}
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$search for references to the allocated memory base
.if(${basealloc} != -1)
{
.printf /D "<b>\nReferences to allocation base %x</b>\n", ${basealloc}+8
.printf /D "<b>--------------------------------------------</b>\n"
r ${stop} = 0
r ${baseSearch} = 0
.while(${stop} == 0)
{
$$.printf "searching %x\n", ${baseSearch}
r @$t0 = ${baseSearch}
r @$t1 = ${basealloc}+8
s @$t0 L 10000000 (@$t1 & ff) ((@$t1 & ff00) >> 8) ((@$t1 & ff0000) >> 10) ((@$t1 & ff000000) >> 18)
.if(${baseSearch} >= 70000000)
{
r ${stop} = 1
}
r ${baseSearch} = ${baseSearch} + 10000000
}
}
ad stage
ad basealloc
ad stop
ad baseSearch
}
================================================
FILE: WinDbg/show_pe_headers.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<c:\tools\@scripts\windbg\show_pe_headers.wdbg <base_address> <process>
$$.sympath SRV*c:\symcache*http://msdl.microsoft.com/download/symbols
$$.reload
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$ change the context to the target process and page in the page containing PE headers
.process /i ${$arg2}
g
$$.reload /user
.pagein /p ${$arg2} ${$arg1}
g
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$ define some useful aliases
ad /q ${/v:$imagebase};
ad /q ${/v:$pe_header};
ad /q ${/v:$file_header};
ad /q ${/v:$optional_header};
ad /q ${/v:$number_of_sections};
ad /q ${/v:$sections};
aS /x ${/v:$imagebase} ( ${$arg1} )
aS /x ${/v:$pe_header} ( $imagebase + ( poi( $imagebase + @@( #FIELD_OFFSET( nt!_IMAGE_DOS_HEADER, e_lfanew ) ) ) ) )
aS /x ${/v:$file_header} ( $pe_header + @@( #FIELD_OFFSET( nt!_IMAGE_NT_HEADERS, FileHeader ) ) )
aS /x ${/v:$optional_header} ( $pe_header + @@( #FIELD_OFFSET( nt!_IMAGE_NT_HEADERS, OptionalHeader ) ) )
aS /x ${/v:$number_of_sections} ( poi ( $file_header + @@( #FIELD_OFFSET( nt!_IMAGE_FILE_HEADER, NumberOfSections ) ) ) & 0xffff )
aS /x ${/v:$data_directory} ( $optional_header + @@( #FIELD_OFFSET( nt!_IMAGE_OPTIONAL_HEADER, DataDirectory ) ) )
aS /x ${/v:$sections} ( $pe_header + @@c++( sizeof ( nt!_IMAGE_NT_HEADERS ) ) )
.printf "DOS HEADER:\n"
.printf "----------:\n"
dt nt!_IMAGE_DOS_HEADER $imagebase
.printf "NT HEADERS:\n"
.printf "----------:\n"
dt nt!_IMAGE_NT_HEADERS $pe_header
.printf "FILE HEADER:\n"
.printf "-----------:\n"
dt nt!_IMAGE_FILE_HEADER $file_header
.printf "OPTIONAL HEADER:\n"
.printf "---------------:\n"
dt nt!_IMAGE_OPTIONAL_HEADER $optional_header
.printf "DATA DIRECTORY 0 EXPORT:\n"
.printf "-----------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 0
.printf "DATA DIRECTORY 1 IMPORT:\n"
.printf "-----------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 1
.printf "DATA DIRECTORY 2 RESOURCE:\n"
.printf "-------------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 2
.printf "DATA DIRECTORY 3 EXCEPTION:\n"
.printf "--------------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 3
.printf "DATA DIRECTORY 4 SECURITY:\n"
.printf "-------------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 4
.printf "DATA DIRECTORY 5 FIXUPS:\n"
.printf "-----------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 5
.printf "DATA DIRECTORY 6 DEBUG:\n"
.printf "----------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 6
.printf "DATA DIRECTORY 7 DESCRIPTION:\n"
.printf "----------------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 7
.printf "DATA DIRECTORY 8 MIPS GP:\n"
.printf "------------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 8
.printf "DATA DIRECTORY 9 TLS:\n"
.printf "--------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 9
.printf "DATA DIRECTORY 10 LOAD CONFIG:\n"
.printf "-----------------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 10
.printf "DATA DIRECTORY 11 BOUND IMPORT:\n"
.printf "------------------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 11
.printf "DATA DIRECTORY 12 IMPORT TABLE:\n"
.printf "------------------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 12
.printf "DATA DIRECTORY 13 DELAY IMPORT:\n"
.printf "------------------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 13
.printf "DATA DIRECTORY 14 COM RUNTIME:\n"
.printf "-----------------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 14
.printf "DATA DIRECTORY 15 RESERVED:\n"
.printf "--------------------------:\n"
dt nt!_IMAGE_DATA_DIRECTORY $data_directory + @@c++( sizeof ( nt!_IMAGE_DATA_DIRECTORY ) ) * 15
r $t0 = $sections
r $t1 = 0
r $t2 = $number_of_sections
.while (@$t1 < @$t2)
{
.printf "SECTION HEADER %d:\n", @$t1
.printf "-----------------:\n"
r $t3 = $sections + @$t1 * @@c++(sizeof(nt!_IMAGE_SECTION_HEADER))
dt -b nt!_IMAGE_SECTION_HEADER @$t3
r $t1 = @$t1 + 1
}
================================================
FILE: WinDbg/show_proc_from_handle.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<show_proc_from_handle.wdbg <handle>
$$
$$.sympath SRV*c:\symcache*http://msdl.microsoft.com/download/symbols
$$.reload
r @$t0 = ${$arg1}
.if(@$t0!=0 & @$t0!=0xffffffff)
{
r @$t1 = 0;
.foreach (tok { !handle @$t0 })
{
.if(@$t1==1)
{
r @$t1 = 0;
.printf /D "<b>${tok} "
.foreach (tok2 { !process ${tok} 0 })
{
.if(@$t1==1)
{
.printf /D "${tok2}\n</b>"
.break;
}
.elsif($scmp("${tok2}","Image:")==0)
{
r @$t1 = 1;
}
}
.break;
}
.elsif($scmp("${tok}","Object:")==0)
{
r @$t1 = 1;
};
};
};
================================================
FILE: WinDbg/symbols.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<symbols.wdbg
$$
.block
{
.sympath "SRV*c:\symcache*https://msdl.microsoft.com/download/symbols;SRV*c:\symcache*https://chromium-browser-symsrv.commondatastorage.googleapis.com;SRV*c:\symcache\*https://symbols.mozilla.org/";
.reload
}
================================================
FILE: WinDbg/write_mem_dump.wdbg
================================================
$$
$$ Author: Javier Vicente Vallejo
$$ Twitter: @vallejocc
$$ Web: http://www.vallejo.cc
$$
$$ $$>a<write_mem_dump.wdbg <destination directory>
.logopen ${$arg1}\write_mem_dump.start
.logclose
.block
{
.sympath "SRV*c:\symcache*http://msdl.microsoft.com/download/symbols";
.reload
}
.dump /f ${$arg1}\memory.dmp
.logopen ${$arg1}\write_mem_dump.end
.logclose
gitextract_j6atbifn/
├── .gitmodules
├── IDA/
│ ├── readme.md
│ ├── set_symbols_for_addresses.py
│ └── stack_strings_deobfuscator_1.py
├── README.md
└── WinDbg/
├── Readme.md
├── anti_antidebug_rdtsc.wdbg
├── break_each_new_process.wdbg
├── change_object_name.wdbg
├── change_process_name.wdbg
├── dump_injected_pe_rwemem.wdbg
├── dump_injected_pe_rwemem_fast.wdbg
├── dump_main_module.wdbg
├── dump_pe.wdbg
├── dump_process_symbols_to_file.wdbg
├── dump_process_symbols_to_file_preselect.wdbg
├── dump_process_symbols_to_file_preselect_curproc.wdbg
├── find_injected_pe_rmem.wdbg
├── find_injected_pe_rwemem.wdbg
├── find_injected_pe_search.wdbg
├── find_objects_by_name.wdbg
├── hang_exited_processes.wdbg
├── isx64.wdbg
├── lengthDisassembler.wdbg
├── load_code_to_kernel_memory.wdbg
├── load_swish.wdbg
├── log_processes.wdbg
├── monitoring_breakpoints.wdbg
├── pagein_range.wdbg
├── remove_device_acls.wdbg
├── search_bytes_all_processes.wdbg
├── search_bytes_target_process.wdbg
├── search_string_all_processes.wdbg
├── search_string_target_process.wdbg
├── secure_writemem.wdbg
├── show_address_info.wdbg
├── show_pe_headers.wdbg
├── show_proc_from_handle.wdbg
├── symbols.wdbg
└── write_mem_dump.wdbg
SYMBOL INDEX (1 symbols across 1 files) FILE: IDA/set_symbols_for_addresses.py function binarySearch (line 25) | def binarySearch(alist, item):
Condensed preview — 40 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (177K chars).
[
{
"path": ".gitmodules",
"chars": 217,
"preview": "[submodule \"standalone/RevealPE\"]\n\tpath = standalone/RevealPE\n\turl = https://github.com/vallejocc/RevealPE.git\n[submodul"
},
{
"path": "IDA/readme.md",
"chars": 2192,
"preview": "\n# set_symbols_for_addresses\n\nThis scripts asks you for a file containing pairs address - symbol. It walks all segments "
},
{
"path": "IDA/set_symbols_for_addresses.py",
"chars": 4681,
"preview": "################################################\n##\n## Author: Javier Vicente Vallejo\n## Twitter: @vallejocc\n## Web: htt"
},
{
"path": "IDA/stack_strings_deobfuscator_1.py",
"chars": 5167,
"preview": "################################################\n##\n## Author: Javier Vicente Vallejo\n## Twitter: @vallejocc\n## Web: htt"
},
{
"path": "README.md",
"chars": 51,
"preview": "Useful Scripts for helping in reverse engeenering.\n"
},
{
"path": "WinDbg/Readme.md",
"chars": 7820,
"preview": "\n$$>a<dump_injected_pe_rwemem.wdbg <destination directory>\n--------------------------------------------------------\n\nThi"
},
{
"path": "WinDbg/anti_antidebug_rdtsc.wdbg",
"chars": 1683,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\n$$ $$>a<anti_antidebug_rd"
},
{
"path": "WinDbg/break_each_new_process.wdbg",
"chars": 399,
"preview": "\n\n$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<break_each_ne"
},
{
"path": "WinDbg/change_object_name.wdbg",
"chars": 2078,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\n$$ $$>a<change_object_nam"
},
{
"path": "WinDbg/change_process_name.wdbg",
"chars": 2080,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\n$$ $$>a<change_process_na"
},
{
"path": "WinDbg/dump_injected_pe_rwemem.wdbg",
"chars": 19105,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\n$$ $$>a<dump_injected_pe_r"
},
{
"path": "WinDbg/dump_injected_pe_rwemem_fast.wdbg",
"chars": 18974,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\n$$ $$>a<dump_injected_"
},
{
"path": "WinDbg/dump_main_module.wdbg",
"chars": 2327,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<c:\\tools\\@scripts\\w"
},
{
"path": "WinDbg/dump_pe.wdbg",
"chars": 3137,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<c:\\tools\\@scrip"
},
{
"path": "WinDbg/dump_process_symbols_to_file.wdbg",
"chars": 884,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$\n$$ $$>a<dump_process_sym"
},
{
"path": "WinDbg/dump_process_symbols_to_file_preselect.wdbg",
"chars": 5615,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$\n$$ $$>a<dump_process_sym"
},
{
"path": "WinDbg/dump_process_symbols_to_file_preselect_curproc.wdbg",
"chars": 5572,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$\n$$ $$>a<dump_process_sym"
},
{
"path": "WinDbg/find_injected_pe_rmem.wdbg",
"chars": 10309,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<c:\\tools\\@scrip"
},
{
"path": "WinDbg/find_injected_pe_rwemem.wdbg",
"chars": 10639,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\n$$ $$>a<find_injected_pe_r"
},
{
"path": "WinDbg/find_injected_pe_search.wdbg",
"chars": 6007,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\n$$ $$>a<find_injected_pe_s"
},
{
"path": "WinDbg/find_objects_by_name.wdbg",
"chars": 4082,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<c:\\tools\\@scrip"
},
{
"path": "WinDbg/hang_exited_processes.wdbg",
"chars": 867,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<c:\\tools\\@scrip"
},
{
"path": "WinDbg/isx64.wdbg",
"chars": 406,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\naS x64arch $t0\n\n.block\n{\n "
},
{
"path": "WinDbg/lengthDisassembler.wdbg",
"chars": 838,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\nu ${$arg1} L 1\n\nr @$t0 = 0"
},
{
"path": "WinDbg/load_code_to_kernel_memory.wdbg",
"chars": 3295,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\n$$ $$>a<c:\\tools\\@scripts\\"
},
{
"path": "WinDbg/load_swish.wdbg",
"chars": 168,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n!load C:\\tools\\@scripts\\win"
},
{
"path": "WinDbg/log_processes.wdbg",
"chars": 406,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\n$$ $$>a<log_processes.wdb"
},
{
"path": "WinDbg/monitoring_breakpoints.wdbg",
"chars": 5548,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<c:\\tools\\@scripts\\w"
},
{
"path": "WinDbg/pagein_range.wdbg",
"chars": 612,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$\n$$ $$>a<pagein_range"
},
{
"path": "WinDbg/remove_device_acls.wdbg",
"chars": 1031,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\naS stage @$t19\n\n.block\n{\n "
},
{
"path": "WinDbg/search_bytes_all_processes.wdbg",
"chars": 2076,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<search_bytes_al"
},
{
"path": "WinDbg/search_bytes_target_process.wdbg",
"chars": 1107,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<search_string_t"
},
{
"path": "WinDbg/search_string_all_processes.wdbg",
"chars": 1891,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<search_string_a"
},
{
"path": "WinDbg/search_string_target_process.wdbg",
"chars": 936,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<search_string_t"
},
{
"path": "WinDbg/secure_writemem.wdbg",
"chars": 6448,
"preview": "$$\r\n$$ Author: Javier Vicente Vallejo\r\n$$ Twitter: @vallejocc\r\n$$ Web: http://www.vallejo.cc\r\n$$\r\n$$ $$>a<secure_wri"
},
{
"path": "WinDbg/show_address_info.wdbg",
"chars": 3266,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<show_address_in"
},
{
"path": "WinDbg/show_pe_headers.wdbg",
"chars": 4812,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<c:\\tools\\@scrip"
},
{
"path": "WinDbg/show_proc_from_handle.wdbg",
"chars": 938,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<show_proc_from_"
},
{
"path": "WinDbg/symbols.wdbg",
"chars": 350,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n$$ $$>a<symbols.wdbg\n$$"
},
{
"path": "WinDbg/write_mem_dump.wdbg",
"chars": 375,
"preview": "$$\n$$ Author: Javier Vicente Vallejo\n$$ Twitter: @vallejocc\n$$ Web: http://www.vallejo.cc\n$$\n\n$$ $$>a<write_mem_dump.wd"
}
]
About this extraction
This page contains the full source code of the vallejocc/Reverse-Engineering-Arsenal GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 40 files (144.9 KB), approximately 42.3k tokens, and a symbol index with 1 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.