Last week I tried to prepare some dll/process hash control for remote nodes, idea was to list dll and proces hash trough snapshot and see if certain hashes are there.
I've done that plenty of times with v6 and I'll do it with v6 even now but my v6 licence is not valid any more, so have to do it in v7, it should be almost same ...
In fact it is not and it is a bit of nightmare ....
In theory after sweep all processes and dll are listed with its hash, so it should be easy to use condition or filter to point to node name where hash is found. As for precaution since there is a hash set field in sweep view I've created hash sets from hash list and added that new has set to hash library. Fot this I've used Lance Mueler script with
detailed instruitions.
Up to here all works fine, than surprises started. In "sweep analysis" there is no hash set info, you can check for hash value but "restriction" wizard is so awkwardly done that you have to add one by one hash value, it is nonsense since it take ages to load any set of hashes into ...
maybe it can be edited in source or by enscript but there is nothing in documentation ..
zero for that ... one essential functionality crippled but implementation
So what to try next ?
All data is also stored in L01 file related to sweep so it can be seen and analyzed trough Encase in theory. If you load that L01 file, in its record view you'll notice that hash value for process or dll is there but a hash set filed is empty ... god knows why. Form documentation I've got idea that I'll have info in that hahs set field if dll hash in in one of the sets in case library .. but nothing, probably a bug.
Also by some strange ideas in v7 there are no filters and no conditions for records view so you can't do search for values as in old v6. Again some very essential functionality is disabled and whole process crippled by inadequate interface. Almost as someone was trying to sabotage product, In such form v7 is practically unusable without heavy enscripting, To be worse a lot of examples and explanation in documentation is missing too.
In despair I asked a few questions around but not much use of answers, just to check examples.
I've located the example from enscript programming for sweep dll and modified it minimally to node name, hash value and do same for present processes also. There was also one bitter disappoint, dll object and process object has hash and has set info included but it is changed in v7 and changes are undocumented so nothing for elegant solution too.
At the end I decided to use simple fgrep from cygwin to find lines in console file which match hash
since the sweep enscript puts output into console. iconv command was used to convert utf-16 console text files into utf-8 encoding which fgrep handles perfectly.
Here is code for small cygwin bash script
-------------------------------------------------------------------------------------------------------------------
#!/usr/bin/bash
#############################################
#fast and dirty for finding something in encase log files
#since a lot of things in encase v7 sweep does not work or it is not
#doumented
#based on cygwin fgrep utility
#Usage: uhh md5
#takes md5 file name as argument
################################################
#folder with encase logs for user
ENCASE="/cygdrive/c/Users/$USERNAME/Documents/EnCase/logs"
#there are Console[0-9].txt files
#in utf-16, so stardard grep breaks, solution is to use iconv
#iconv -f utf-16 -t utf-8
##################################################
#if no patern file exit 1
#patern file, plain md5 signitures
MD5="$1"
test -f "$MD5" || exit 1
#strange systax to avoid unix-dos path name troubles
for f in $(ls "$ENCASE")
do
echo $f
iconv -f utf-16 -t utf-8 "$ENCASE/$f"| fgrep -i -f "$MD5"
done
---------------------------------------------------------------------------------------------------------------
There are other ways to do similar thing, more elegant but I was simply to tired to experiment more
I forget to add modified example from Enscript manual, here it is
/*-------------------------------------------
DllListClass represents a list of all loaded libraries on a remote node. In order to retreive a list of DLLs, a valid
snapshot object is required. A snapshot object can be created in the following ways:
(1) BatchClass::GetConnection method
(2) ConnectionClass::ReadSnapshot method
Note that the ConnectionClass::SNAPDLL must be used when snapshot objects are created. Also, if ConnectionClass::SNAPHASH
is used, each DLL will have its hash value calculated.
See Also:
SnapshotClass
DllListClas
NodeClass
2015 added extension for hash value and
same for process class
*/
/*
Example: Connect to a remote node and print out a list of all the DLLs loaded in memory.
*/
class MainClass {
SafeClass Safe; //object to connect to SAFE
RoleClass RoleRoot, //list of all roles for a given user
Role; //role user choose to take
NetworkClass SweepNet; //list of remote nodes to connect to
String NetText, //textual list of remote nodes
ClientReturnAddress, //for NODECLIENT connection options
StatusBarName; //name to be displayed in the status bar
int NumConnections, //number of SAFE connections to use
ConnectOptions; // Connection Options: INDIRECT, CLIENTNODELOCAL, CLIENTNODESAFE, NODECLIENT
MainClass() :
Safe(),
RoleRoot(),
Role(),
SweepNet(),
NumConnections = 1,
ConnectOptions = ConnectionClass::CLIENTNODESAFE,
StatusBarName = "Example - Getting DLL Data"
{
}
/**
Entry point of the Enscript
**/
void Main(CaseClass c) {
if (c) {
SystemClass::ClearConsole();
if (Safe.Logon(null) && ShowDiag() == SystemClass::OK) {
Sweep();
SystemClass::Message(0, "Success", String::Format("{0}: Completed Successfully!", StatusBarName));
}
}
else
SystemClass::Message(0, "Error", "Need an open case so that results of registry queries can be added!");
}
/**
This method contains the logic we want to apply to each node on the network
**/
void Process(SnapshotClass snap) {
Console.WriteLine("Processing Machine " + snap.Name());
//in order for the DLL list to not be empty, the BatchClass
//must have been constructed with the ConnectionClass::SNAPDLL or
//ConnectionClass::SNAPHIDDEN
//entrpy is on -1 how to do enteryp in sweep ?
//also how to get hash in set
Console.WriteLine("DLL Count = {0}", snap.DllListRoot().Count());
forall (DllListClass p in snap.DllListRoot()) {
//add to print hash value for dll and node name
Console.WriteLine("DLL loaded: {0} {1} {2}", snap.Name(),p.Name(),
p.HashValue() );
}
/* add proceses too */
Console.WriteLine("Process Count = {0}", snap.ProcessRoot().Count());
forall (ProcessClass p in snap.ProcessRoot()) {
//add to print hash value for process and node name
Console.WriteLine("Process loaded: {0} {1} {2}", snap.Name(), p.Name(),p.HashValue() );
}
}
/**
Display dialogs
**/
int ShowDiag() {
RoleRoot = Safe.RoleRoot();
DialogClass diag();
new NetTextDialogClass(diag, this);
return diag.Wizard();
}
/**
Code that gets connection and snapshot
**/
void ReadNetwork(BatchClass batch, SnapshotClass root) {
String message,
name;
DateClass d();
do {
ConnectionClass conn;
SnapshotClass ss(null);
message = "";
BatchClass::ConnectionTypes reply = batch.GetConnection(conn, ss, name, message, 0);
if (reply == BatchClass::BATCHCONNECT) { //successfully connected to remote node
Process(ss);
SystemClass::StatusInc(1);
root.Insert(ss);
}
else if (reply == BatchClass::BATCHERROR) { //could not connect to remote node. ss object will have the state of the node
d.Now();
Console.WriteLine("Could Not Connect To {0} SAFE Error Message: {1}", name, message);
SystemClass::StatusInc(1);
root.Insert(ss);
}
else if (reply == BatchClass::BATCHWAIT)
SystemClass::Sleep(100);
else if (reply == BatchClass::BATCHFATAL) {
String err = SystemClass::LastError();
Console.WriteLine("The SAFE is not responding: {0}. This Enscript will terminate.", err);
return;
}
} while (reply != BatchClass::BATCHDONE);
}
/** Code that creates a batchclass
**/
void Sweep() {
DateClass now;
SnapshotClass newSnaps = new SnapshotClass(null, "Snapshot");
BatchClass batch(Safe, Role, NumConnections, ConnectionClass::SNAPALL);
if (batch.Add(SweepNet)) {
batch.SetMode(ConnectionClass::Options::Convert(ConnectOptions), ClientReturnAddress);
if (batch.Start()) {
uint machines = batch.TotalMachines();
Console.WriteLine("Scanning {0} using {1}", Plural("node", machines), Plural("connection", batch.ConnectionsUsed()));
SystemClass::StatusRange(StatusBarName, machines);
uint start;
now.Now();
start = now.GetUnix();
ReadNetwork(batch, newSnaps);
now.Now();
Console.WriteLine("Scan completed in {0} seconds", (now.GetUnix() - start));
}
else {
SystemClass::Message(0, "BatchClass error", SystemClass::LastError());
}
}
else {
SystemClass::Message(0, "BatchClass Error", "Unable to add any IPs to the sweep");
}
}
String Plural(const String &str, uint n) {
return String::Format("{0} {1}{2}", n, str, n == 1 ? "" : "s");
}
/**
Turn a string of text into networkclass objects
**/
bool ParseText(String t) {
SweepNet.Close();
bool ret = false;
while (t) {
ret = true;
int end = t.Find("\n");
String line = end < 0 ? t : t.SubString(0, end);
int dash = line.Find("-");
if (dash >= 0) {
IPClass ip1(ExtractIP(line.SubString(0, dash))),
ip2(ExtractIP(line.SubString(dash+1, -1)));
if (ip1 && ip2) {
NetworkClass n(SweepNet, "IP Range", NodeClass::SELECTED);
n.SetStart(ip1);
n.SetStop(ip2);
}
else
NetworkClass n(SweepNet, line, NodeClass::SELECTED);
}
else if (line != "") {
NetworkClass n(SweepNet, line, NodeClass::SELECTED);
}
if (end >= 0)
t.Delete(0, end+1);
else
break;
}
return ret;
}
/**
Check for IPs in nettext
**/
String ExtractIP(const String &s) {
String ret = s;
ret.Trim(" ", String::TRIMSTART | String::TRIMEND);
return ret.IsValidIPAddress() ? ret : "";
}
}
/**
Dialog to choose a role and enter nodes to sweep
**/
class NetTextDialogClass: DialogClass {
MainClass Data;
StaticTextClass SafeTextEdit;
TreeEditClass Tree;
StaticTextClass Help;
StringEditClass NetTextEdit;
NetTextDialogClass(DialogClass diag, MainClass d) :
DialogClass(diag, String::Format("{0} Options", d.StatusBarName)),
Data = d,
SafeTextEdit(this, "", START, 15, 200, 100, 0),
Tree(this, "Choose The Role You Want To Assume", NEXT, START, 200, 100, 0, d.RoleRoot, 0),
Help(this, "Enter IP addresses or machine names on separate\n"
"lines. Enter ranges on separate lines and delimit\n"
"the start and stop address with a dash (\"-\").\n\n"
"Example:\n\n"
"\tlocalhost\n"
"\t192.168.5.5\n"
"\t192.168.0.16-192.168.0.64\n"
"\t192.168.1.1-192.168.3.255\n"
"\tfd00:0:1000:20:0:0:0:100\n",
START, NEXT, 200, 100, REQUIRED),
NetTextEdit(this, "", NEXT, SAME, 200, 100, AUTOVSCROLL | MULTILINE | WANTRETURN, d.NetText, 9999, 0)
{
}
virtual void Setup() {
DialogClass::Setup();
SafeTextEdit.SetText("SAFE:\t\t\t\t" + Data.Safe.Name() +
"\nUser:\t\t\t\t" + Data.Safe.UserName() +
"\n\nTotal Connections:\t\t" + Data.Safe.TotalConnections() +
"\nActive Connections:\t\t" + Data.Safe.ActiveConnections() +
"\nConnections To Use:\t\t" + Data.NumConnections +
"\n\nRemediation Allowed:\t\t" + (Data.Safe.RemediationAllowed() ? "Yes" : "No") +
"\nSnapshot Allowed:\t\t" + (Data.Safe.SnapshotAllowed() ? "Yes" : "No") +
"\n\nSAFE Version:\t\t\t" + Data.Safe.Version()
);
}
virtual void CheckControls() {
DialogClass::CheckControls();
EnableClose(Tree.GetValue().Parent());
}
virtual bool CanClose() {
Output();
bool ret = false;
if (DialogClass::CanClose()) {
Data.Role = RoleClass::TypeCast(Tree.GetValue());
ret = Data.ParseText(Data.NetText);
if (!ret)
ErrorMessage("Please Enter a value in the IP List Text Area.");
}
return ret;
}
}