Hex Blog
July 9th, 2010, 22:43
In this blog post we are going to demonstrate how the '-S' and '-t' switches (that were introduced in IDA Pro 5.7 ("http://www.hex-rays.com/idapro/57/index.htm")) can be used to run IDC, Python or other supported scripts from the command line as if they were standlone scripts and how to use the idascript utility
http://hexblog.com/ida_pro/pix/idascript_intro.gif
Background
In order to run a script from the command line, IDA Pro needs to know which script to launch. We can specify the script and its argument via the "-S" switch: Or: In case the script does not require a database (for example, it works with the debugger and attaches to existing processes), then IDA Pro will be satisfied with the "-t" (create a temporary database) switch: Where first.idc: If we run IDA Pro with the following command: We notice two things:
In the following section, we will address those two problems with idascript.idc and idascript.py helper scripts and the idascript utility. Running scripts from the command line
In order to print to the console window, we will not use IDC / Message() instead we will write to a file and when IDA Pro exits we will display the contents of that file.
Our second attempt with second.idc: Now let us run IDA Pro: and type afterwards: to get the following output: To simplify this whole process, we wrote a small win32 command line utility called idascript:
Since we will be using LogInit(), LogTerm(), LogWrite() and other helper functions over and over, we moved those common functions to idascript.idc.
The script first.idc can now be rewritten like this:
As for IDAPython, we wrote a small class to redirect all output to idaout.txt: Thus, hello.py can be written like this: Sample scripts
Process list
The sample script listprocs.idc will enumerate all processes and display their ID and name: To test the script, let us suppose we have a few instances of notepad.exe we want to kill: We used here the "ARGV" variable that contains all the parameters passed to IDA Pro via the -S switch, FindProcessByName() utility function and KillProcess() (check procutil.idc)
The trick behind terminating a process is to attach and call StopDebugger(). The following is an excerpt from procutil.idc utility script: Process information
The procinfo.idc script will display thread count, register information and the command line arguments of the process in question: The function GetProcessCommandLine is implemented (using Appcall ("http://hexblog.com/2010/01/practical_appcall_examples_1.html")) like this: Extracting function body
So far we did not really need a specific database to work with. In the following example (funcextract.idc) we will demonstrate how to extract the body of a function from a given database: To test the script, we use idascript utility and pass a database name: Other ideas
There are other ideas that can be implemented to create useful command line tools:
Installing the idascript utility
Please download idascript and the needed scripts from here ("http://hexblog.com/ida_pro/files/idascript_files.zip") and follow these steps:
Comments and suggestions are welcome!
http://hexblog.com/2010/07/running_scripts_from_the_comma.html
http://hexblog.com/ida_pro/pix/idascript_intro.gif
Background
In order to run a script from the command line, IDA Pro needs to know which script to launch. We can specify the script and its argument via the "-S" switch:
Code:
idag -Sfirst.idc mydatabase.idb
Code:
idag -S"first.idc arg1 arg2" mydatabase.idb
Code:
idag -S"first.idc arg1 arg2" -t
Code:
#include
static main()
{
Message("Hello world from IDC!\n";
return 0;
}
Code:
idag -Sfirst.idc -t
Nothing is printed in the console window: This is because the message will show in the output window instead:
http://hexblog.com/ida_pro/pix/idascript_first.gif
(It is possible to save all the text in the output window by using the IDALOG environment variable.)IDA Pro remains open and does not close: To exit IDA Pro when the script finishes, use Exit() IDC function.
In the following section, we will address those two problems with idascript.idc and idascript.py helper scripts and the idascript utility. Running scripts from the command line
In order to print to the console window, we will not use IDC / Message() instead we will write to a file and when IDA Pro exits we will display the contents of that file.
Our second attempt with second.idc:
Code:
extern g_idcutil_logfile;
static LogInit()
{
g_idcutil_logfile = fopen("idaout.txt", "w";
if (g_idcutil_logfile == 0)
return 0;
return 1;
}
static LogWrite(str)
{
if (g_idcutil_logfile != 0)
return fprintf(g_idcutil_logfile, "%s", str);
return -1;
}
static LogTerm()
{
if (g_idcutil_logfile == 0)
return;
fclose(g_idcutil_logfile);
g_idcutil_logfile = 0;
}
static main()
{
LogInit(); // Open log file
LogWrite("Hello world from IDC!\n"; // Write to log file
LogTerm(); // Close log file
Exit(0); // Exit IDA Pro
}
Code:
idag -Ssecond.idc -t
Code:
type idaout.txt
Code:
Hello world from IDC!
Code:
IDAScript 1.0 (c) Hex-Rays - A tool to run IDA Pro scripts from the command line
It can be used in two modes:
a) With a database:
idascript database.idb script.(idc|py|...) [arg1 [arg2 [arg3 [...]]]]
b) With a temporary database:
idascript script.(idc|py|...) [arg1 [arg2 [arg3 [...]]]]
Since we will be using LogInit(), LogTerm(), LogWrite() and other helper functions over and over, we moved those common functions to idascript.idc.
The script first.idc can now be rewritten like this:
Code:
#include
#include "idascript.idc"
static main()
{
InitUtils(); // calls LogInit()
Print(("Hello world from IDC!\n"); // Macro that calls LogWrite()
Quit(0); // calls LogTerm() following by Exit()
}
Code:
import sys
class ToFileStdOut(object):
def __init__(self):
self.outfile = open("idaout.txt", "w"
def write(self, text):
self.outfile.write(text)
def flush(self):
self.outfile.flush()
def isatty(self):
return False
def __del__(self):
self.outfile.close()
sys.stdout = sys.stderr = ToFileStdOut()
Code:
import idc
import idascript
print "Hello world from IDAPython\n"
for i in xrange(1, len(idc.ARGV)):
print "ARGV[%d]=%s" % (i, idc.ARGV)
idc.Exit(0)
Process list
The sample script listprocs.idc will enumerate all processes and display their ID and name:
Code:
#include
#include
static main()
{
InitUtils();
LoadDebugger("win32", 0);
auto q = GetProcessQty(), i;
for (i=0;i=0;i--)
{
auto pid = procs[I];
Print(("killing pid: %X\n", pid));
KillProcess(pid);
}
Quit(0);
}
Code:
D:\idascript>idascript killproc.idc notepad.exe
killing pid: 878
killing pid: 14C8
D:\idascript>
The trick behind terminating a process is to attach and call StopDebugger(). The following is an excerpt from procutil.idc utility script:
Code:
static KillProcess(pid)
{
if (!AttachToProcess(pid))
return 0;
StopDebugger(); // Terminate the current process
// Normally, we should get a PROCESS_EXIT event
GetDebuggerEvent(WFNE_SUSP, -1);
}
The procinfo.idc script will display thread count, register information and the command line arguments of the process in question:
Code:
#include "idascript.idc"
#include "procutil.idc"
static DumpProcessInfo()
{
// Retrieve command line via Appcall
Print(("Command line: %s\n", GetProcessCommandLine()));
// Enum modules
Print(("Module list:\n------------\n");
auto x;
for (x = GetFirstModule();x!=BADADDR;x=GetNextModule(x))
Print(("Module [%08X] [%08X] %s\n", x, GetModuleSize(x), GetModuleName(x)));
Print(("\nThread list:\n------------\n");
for (x=GetThreadQty()-1;x>=0;x--)
{
auto tid = GetThreadId(x);
Print(("Thread [%x]\n", tid));
SelectThread(tid);
Print((" EIP=%08X ESP=%08X EBP=%08X\n", Eip, Esp, Ebp));
}
}
static main()
{
InitUtils();
// Load the debugger
LoadDebugger("win32", 0);
// Get parameters
if (ARGV.count < 2)
QuitMsg(0, "Usage: killproc.idc ProcessName\n";
auto procs = FindProcessByName(ARGV[1]), i;
for (i=procs.count-1;i>=0;i--)
{
auto pid = procs[I];
if (!AttachToProcess(pid))
{
Print(("Could not attach to pid=%x\n", pid));
continue;
}
DumpProcessInfo();
DetachFromProcess();
}
Quit(0);
}
Code:
static GetProcessCommandLine()
{
// Get address of the GetCommandLine API
auto e, GetCmdLn = LocByName("kernel32_GetCommandLineA";
if (GetCmdLn == BADADDR)
return 0;
// Set its prototype for Appcall
SetType(GetCmdLn, "char * __stdcall x();";
try
{
// Retrieve the command line using Appcall
return GetCmdLn();
}
catch (e)
{
return 0;
}
}
So far we did not really need a specific database to work with. In the following example (funcextract.idc) we will demonstrate how to extract the body of a function from a given database:
Code:
#include
#include "idascript.idc"
static main()
{
InitUtils();
if (ARGV.count < 2)
QuitMsg(0, "Usage: funcextract.idc FuncName OutFile";
// Resolve name
auto ea = LocByName(ARGV[1]);
if (ea == BADADDR)
QuitMsg(0, sprintf("Function '%s' not found!", ARGV[1]));
// Get function start
ea = GetFunctionAttr(ea, FUNCATTR_START);
if (ea == BADADDR)
QuitMsg(0, "Could not determine function start!\n";
// size = end - start
auto sz = GetFunctionAttr(ea, FUNCATTR_END) - ea;
auto fp = fopen(ARGV[2], "wb";
if (fp == 0)
QuitMsg(-1, "Failed to create output file\n";
savefile(fp, 0, ea, sz);
fclose(fp);
Print(("Successfully extracted %d byte(s) from '%s'", sz, ARGV[1]));
Quit(0);
}
Code:
D:\idascript>idascript [I]ar.idb funcextract.idc start start.bin
Successfully extracted 89 byte(s) from 'start'
D:\idascript>
There are other ideas that can be implemented to create useful command line tools:
Process memory read/write: Check the rwproc.idc script that allows you to read from the process memory to a file or the other way round.
Associate .IDC with idascript.exe: This allows you to double-click on IDC scripts to run them from the Windows Explorer
Scriptable debugger: Write scripts to debug a certain process and extract needed information...
Installing the idascript utility
Please download idascript and the needed scripts from here ("http://hexblog.com/ida_pro/files/idascript_files.zip") and follow these steps:
Copy idascript.exe to the installation directory of IDA Pro (say %IDA%)
Add IDA Pro directory to the PATH environment variable
Copy idascript.idc and procutil.idc to %IDA%\idc
Copy idascript.py to %IDA%\pythonOptional: Associate *.idc files with idascript.exe
Comments and suggestions are welcome!
http://hexblog.com/2010/07/running_scripts_from_the_comma.html