mirror of
https://github.com/nmap/nmap.git
synced 2025-12-11 02:09:03 +00:00
Nmap now does better OS detection guesses when there isn't an exact match because it uses the point system (MatchPoints) now given in nmap-os-db
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# Nmap Changelog ($Id$); -*-text-*-
|
||||
|
||||
o Nmap 2nd generation OS detection now has a more sophisticated
|
||||
mechanism for guessing a target OS when there is no exact match in the
|
||||
database (see http://insecure.org/nmap/osdetect/osdetect-guess.html )
|
||||
|
||||
o A dozen or so small patches to Nmap and NmapFE by Kris Katterjohn.
|
||||
|
||||
4.20ALPHA7
|
||||
|
||||
@@ -174,8 +174,8 @@ class NmapOps {
|
||||
int interactivemode;
|
||||
int ping_group_sz;
|
||||
int generate_random_ips; /* -iR option */
|
||||
FingerPrint **reference_FPs1; /* Used in the old OS scan system. */
|
||||
FingerPrint **reference_FPs; /* Used in the new OS scan system. */
|
||||
FingerPrintDB *reference_FPs1; /* Used in the old OS scan system. */
|
||||
FingerPrintDB *reference_FPs; /* Used in the new OS scan system. */
|
||||
u16 magic_port;
|
||||
unsigned short magic_port_set; /* Was this set by user? */
|
||||
int num_ping_synprobes;
|
||||
|
||||
@@ -197,6 +197,13 @@ typedef struct FingerTest {
|
||||
struct FingerTest *next;
|
||||
} FingerPrint;
|
||||
|
||||
/* This structure contains the important data from the fingerprint
|
||||
database (nmap-os-db or nmap-os-fingerprints) */
|
||||
typedef struct FingerPrintDB {
|
||||
FingerPrint **prints;
|
||||
FingerPrint *MatchPoints;
|
||||
} FingerPrintDB;
|
||||
|
||||
struct timeout_info {
|
||||
int srtt; /* Smoothed rtt estimate (microseconds) */
|
||||
int rttvar; /* Rout trip time variance */
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
# are used when there are no perfect matches to determine which OS
|
||||
# fingerprint matches a target machine most closely.
|
||||
MatchPoints
|
||||
SEQ(SP=30%GCD=50%ISR=30%TI=100%II=100%TS=100)
|
||||
SEQ(SP=30%GCD=75%ISR=30%TI=100%II=100%SS=80%TS=100)
|
||||
OPS(O1=20%O2=20%O3=20%O4=20%O5=20%O6=20)
|
||||
WIN(W1=15%W2=15%W3=15%W4=15%W5=15%W6=15)
|
||||
ECN(R=100%DF=20%T=20%TG=20%W=15%O=15%CC=100%Q=20)
|
||||
@@ -42,8 +42,8 @@ T4(R=100%DF=20%T=20%TG=20%W=30%S=20%A=20%F=30%O=10%RD=20%Q=20)
|
||||
T5(R=100%DF=20%T=20%TG=20%W=30%S=20%A=20%F=30%O=10%RD=20%Q=20)
|
||||
T6(R=100%DF=20%T=20%TG=20%W=30%S=20%A=20%F=30%O=10%RD=20%Q=20)
|
||||
T7(R=100%DF=20%T=20%TG=20%W=30%S=20%A=20%F=30%O=10%RD=20%Q=20)
|
||||
U1(DF=20%T=20%TG=20%TOS=100%IPL=100%UN=100%RIPL=100%RID=100%RIPCK=100%RUCK=100%RUL=100%RUD=100)
|
||||
IE(DFI=40%T=20%TG=20%TOSI=100%CD=100%SI=100%DLI=100)
|
||||
U1(R=50%DF=20%T=20%TG=20%TOS=100%IPL=100%UN=100%RIPL=100%RID=100%RIPCK=100%RUCK=100%RUL=100%RUD=100)
|
||||
IE(R=50%DFI=40%T=20%TG=20%TOSI=100%CD=100%SI=100%DLI=100)
|
||||
|
||||
# Device type: switch. Running: 3Com embedded.
|
||||
# OS details: 3Com Superstack 3, 3300XM switch , Boot PROM Version: 1.00, Software Version: 2.69, Hardware Version: 0
|
||||
|
||||
132
osscan.cc
132
osscan.cc
@@ -1176,16 +1176,20 @@ static struct AVal *gettestbyname(FingerPrint *FP, const char *name) {
|
||||
want to see the results from this match. if shortcircuit is zero,
|
||||
it does all the tests, otherwise it returns when the first one
|
||||
fails. If you want details of the match process printed, pass n
|
||||
onzero for 'verbose'. In that case, you may also pass in the group
|
||||
name (SEQ, T1, etc) to have that extra info printed. If you pass 0
|
||||
for verbose, you might as well pass NULL for testGroupName as it
|
||||
won't be used. */
|
||||
static int AVal_match(struct AVal *reference, struct AVal *fprint,
|
||||
onzero for 'verbose'. If points is non-null, it is examined to
|
||||
find the number of points for each test in the fprint AVal and use
|
||||
that the increment num_subtests and num_subtests_succeeded
|
||||
appropriately. If it is NULL, each test is worth 1 point. In that
|
||||
case, you may also pass in the group name (SEQ, T1, etc) to have
|
||||
that extra info printed. If you pass 0 for verbose, you might as
|
||||
well pass NULL for testGroupName as it won't be used. */
|
||||
static int AVal_match(struct AVal *reference, struct AVal *fprint, struct AVal *points,
|
||||
unsigned long *num_subtests,
|
||||
unsigned long *num_subtests_succeeded, int shortcut,
|
||||
int verbose, const char *testGroupName) {
|
||||
struct AVal *current_ref;
|
||||
struct AVal *current_fp;
|
||||
struct AVal *current_points;
|
||||
unsigned int number, number1;
|
||||
unsigned int val;
|
||||
char *p, *q, *q1; /* OHHHH YEEEAAAAAHHHH!#!@#$!% */
|
||||
@@ -1194,6 +1198,8 @@ static int AVal_match(struct AVal *reference, struct AVal *fprint,
|
||||
int andexp, orexp, expchar, numtrue;
|
||||
int testfailed;
|
||||
int subtests = 0, subtests_succeeded=0;
|
||||
int pointsThisTest = 1;
|
||||
|
||||
|
||||
for(current_ref = reference; current_ref; current_ref = current_ref->next) {
|
||||
current_fp = getattrbyname(fprint, current_ref->attribute);
|
||||
@@ -1250,17 +1256,24 @@ static int AVal_match(struct AVal *reference, struct AVal *fprint,
|
||||
if (q) p = q + 1;
|
||||
} while(q);
|
||||
if (numtrue == 0) testfailed=1;
|
||||
subtests++;
|
||||
if (points) {
|
||||
current_points = getattrbyname(points, current_ref->attribute);
|
||||
if (!current_points) fatal("%s: Failed to find point amount for test %s.%s", __FUNCTION__, testGroupName? testGroupName : "", current_ref->attribute);
|
||||
pointsThisTest = strtol(current_points->value, &endptr, 10);
|
||||
if (pointsThisTest < 1)
|
||||
fatal("%s: Got bogus point amount (%s) for test %s.%s", __FUNCTION__, current_points->value, testGroupName? testGroupName : "", current_ref->attribute);
|
||||
}
|
||||
subtests += pointsThisTest;
|
||||
if (testfailed) {
|
||||
if (shortcut) {
|
||||
if (num_subtests) *num_subtests += subtests;
|
||||
return 0;
|
||||
}
|
||||
if (verbose)
|
||||
printf("%s.%s: \"%s\" NOMATCH \"%s\"\n", testGroupName,
|
||||
printf("%s.%s: \"%s\" NOMATCH \"%s\" (%d point%s)\n", testGroupName,
|
||||
current_ref->attribute, current_fp->value,
|
||||
current_ref->value);
|
||||
} else subtests_succeeded++;
|
||||
current_ref->value, pointsThisTest, (pointsThisTest == 1)? "point" : "points");
|
||||
} else subtests_succeeded += pointsThisTest;
|
||||
/* Whew, we made it past one Attribute alive , on to the next! */
|
||||
}
|
||||
if (num_subtests) *num_subtests += subtests;
|
||||
@@ -1271,11 +1284,13 @@ static int AVal_match(struct AVal *reference, struct AVal *fprint,
|
||||
/* Compares 2 fingerprints -- a referenceFP (can have expression
|
||||
attributes) with an observed fingerprint (no expressions). If
|
||||
verbose is nonzero, differences will be printed. The comparison
|
||||
accuracy (between 0 and 1) is returned). */
|
||||
accuracy (between 0 and 1) is returned). If MatchPoints is not NULL, it is
|
||||
a special "fingerprints" which tells how many points each test is worth. */
|
||||
double compare_fingerprints(FingerPrint *referenceFP, FingerPrint *observedFP,
|
||||
int verbose) {
|
||||
FingerPrint *MatchPoints, int verbose) {
|
||||
FingerPrint *currentReferenceTest;
|
||||
struct AVal *currentObservedTest;
|
||||
struct AVal *currentTestMatchPoints;
|
||||
unsigned long num_subtests = 0, num_subtests_succeeded = 0;
|
||||
unsigned long new_subtests, new_subtests_succeeded;
|
||||
assert(referenceFP);
|
||||
@@ -1288,7 +1303,13 @@ double compare_fingerprints(FingerPrint *referenceFP, FingerPrint *observedFP,
|
||||
currentObservedTest = gettestbyname(observedFP, currentReferenceTest->name);
|
||||
if (currentObservedTest) {
|
||||
new_subtests = new_subtests_succeeded = 0;
|
||||
AVal_match(currentReferenceTest->results, currentObservedTest,
|
||||
if (MatchPoints) {
|
||||
currentTestMatchPoints = gettestbyname(MatchPoints, currentReferenceTest->name);
|
||||
if (!currentTestMatchPoints)
|
||||
fatal("%s: Failed to locate test %s in MatchPoints directive of fingerprint file", __FUNCTION__, currentReferenceTest->name);
|
||||
} else currentTestMatchPoints = NULL;
|
||||
|
||||
AVal_match(currentReferenceTest->results, currentObservedTest, currentTestMatchPoints,
|
||||
&new_subtests, &new_subtests_succeeded, 0, verbose, currentReferenceTest->name);
|
||||
num_subtests += new_subtests;
|
||||
num_subtests_succeeded += new_subtests_succeeded;
|
||||
@@ -1299,20 +1320,21 @@ double compare_fingerprints(FingerPrint *referenceFP, FingerPrint *observedFP,
|
||||
return (num_subtests)? (num_subtests_succeeded / (double) num_subtests) : 0;
|
||||
}
|
||||
|
||||
/* Takes a fingerprint and looks for matches inside reference_FPs[].
|
||||
The results are stored in FPR (which must point to an instantiated
|
||||
FingerPrintResults class) -- results will be reverse-sorted by
|
||||
accuracy. No results below accuracy_threshhold will be included.
|
||||
The max matches returned is the maximum that fits in a
|
||||
FingerPrintResults class. */
|
||||
/* Takes a fingerprint and looks for matches inside the passed in
|
||||
reference fingerprint DB. The results are stored in in FPR (which
|
||||
must point to an instantiated FingerPrintResults class) -- results
|
||||
will be reverse-sorted by accuracy. No results below
|
||||
accuracy_threshhold will be included. The max matches returned is
|
||||
the maximum that fits in a FingerPrintResults class. */
|
||||
void match_fingerprint(FingerPrint *FP, FingerPrintResults *FPR,
|
||||
FingerPrint **reference_FPs, double accuracy_threshold) {
|
||||
FingerPrintDB *DB, double accuracy_threshold) {
|
||||
|
||||
int i;
|
||||
double FPR_entrance_requirement = accuracy_threshold; /* accuracy must be
|
||||
at least this big
|
||||
to be added to the
|
||||
list */
|
||||
FingerPrint **reference_FPs = DB->prints;
|
||||
FingerPrint *current_os;
|
||||
double acc;
|
||||
int state;
|
||||
@@ -1332,7 +1354,7 @@ void match_fingerprint(FingerPrint *FP, FingerPrintResults *FPR,
|
||||
current_os = reference_FPs[i];
|
||||
skipfp = 0;
|
||||
|
||||
acc = compare_fingerprints(current_os, FP, 0);
|
||||
acc = compare_fingerprints(current_os, FP, DB->MatchPoints, 0);
|
||||
|
||||
/* error("Comp to %s: %li/%li=%f", o.reference_FPs1[i]->OS_name, num_subtests_succeeded, num_subtests, acc); */
|
||||
if (acc >= FPR_entrance_requirement || acc == 1.0) {
|
||||
@@ -1615,7 +1637,7 @@ do {
|
||||
one is the same */
|
||||
if (i == numFPs - 1 || !currentFPs[i+1] ||
|
||||
strcmp(currentFPs[i]->name, currentFPs[i+1]->name) != 0 ||
|
||||
AVal_match(currentFPs[i]->results,currentFPs[i+1]->results, NULL,
|
||||
AVal_match(currentFPs[i]->results,currentFPs[i+1]->results, NULL, NULL,
|
||||
NULL, 1, 0, NULL) ==0)
|
||||
{
|
||||
changed = 1;
|
||||
@@ -1927,7 +1949,8 @@ FingerPrint *parse_single_fingerprint(char *fprint_orig) {
|
||||
}
|
||||
|
||||
|
||||
void free_fingerprint_file(FingerPrint **FPs) {
|
||||
void free_fingerprint_file(FingerPrintDB *DB) {
|
||||
FingerPrint **FPs = DB->prints;
|
||||
FingerPrint **current;
|
||||
FingerPrint *c, *d;
|
||||
struct AVal *avc;
|
||||
@@ -1950,21 +1973,47 @@ void free_fingerprint_file(FingerPrint **FPs) {
|
||||
}
|
||||
}
|
||||
free(FPs);
|
||||
|
||||
if (DB->MatchPoints) {
|
||||
for(c = DB->MatchPoints; c; c=d){
|
||||
d = c->next;
|
||||
if(c->name)
|
||||
free((void*)c->name); //strdup
|
||||
if(c->results){
|
||||
for(avc = c->results; avc; avc = avd) {
|
||||
avd = avc->next;
|
||||
if(avc->attribute)
|
||||
free(avc->attribute);
|
||||
}
|
||||
free(c->results);
|
||||
}
|
||||
free(c);
|
||||
}
|
||||
free(DB->MatchPoints);
|
||||
}
|
||||
free(DB);
|
||||
}
|
||||
|
||||
|
||||
FingerPrint **parse_fingerprint_file(char *fname) {
|
||||
FingerPrint **FPs;
|
||||
FingerPrintDB *parse_fingerprint_file(char *fname) {
|
||||
FingerPrintDB *DB = NULL;
|
||||
FingerPrint *current;
|
||||
FILE *fp;
|
||||
int max_records = 4096;
|
||||
char line[512];
|
||||
int numrecords = 0;
|
||||
int lineno = 0;
|
||||
bool parsingMatchPoints = false;
|
||||
|
||||
int classno = 0; /* Number of Class lines dealt with so far */
|
||||
|
||||
DB = (FingerPrintDB *) safe_zalloc(sizeof(FingerPrintDB));
|
||||
|
||||
char *p, *q; /* OH YEAH!!!! */
|
||||
|
||||
FPs = (FingerPrint **) safe_zalloc(sizeof(FingerPrint *) * max_records);
|
||||
if (!DB) fatal("non-allocated DB passed to %s", __FUNCTION__);
|
||||
|
||||
DB->prints = (FingerPrint **) safe_zalloc(sizeof(FingerPrint *) * max_records);
|
||||
|
||||
fp = fopen(fname, "r");
|
||||
if (!fp) fatal("Unable to open Nmap fingerprint file: %s", fname);
|
||||
@@ -1978,11 +2027,23 @@ while(fgets(line, sizeof(line), fp)) {
|
||||
|
||||
fparse:
|
||||
|
||||
if (strncasecmp(line, "FingerPrint", 11)) {
|
||||
if (strncasecmp(line, "FingerPrint", 11) == 0) {
|
||||
parsingMatchPoints = false;
|
||||
} else if (strncasecmp(line, "MatchPoints", 11) == 0) {
|
||||
if (DB->MatchPoints) fatal("Found MatchPoints directive on line %d of %s even though it has previously been seen in the file", lineno, fname);
|
||||
parsingMatchPoints = true;
|
||||
} else {
|
||||
fprintf(stderr, "Parse error on line %d of nmap-os-fingerprints file: %s\n", lineno, line);
|
||||
continue;
|
||||
}
|
||||
|
||||
current = (FingerPrint *) safe_zalloc(sizeof(FingerPrint));
|
||||
|
||||
if (parsingMatchPoints) {
|
||||
current->OS_name = NULL;
|
||||
DB->MatchPoints = current;
|
||||
} else {
|
||||
DB->prints[numrecords] = current;
|
||||
p = line + 12;
|
||||
while(*p && isspace((int) *p)) p++;
|
||||
|
||||
@@ -1994,13 +2055,11 @@ while(fgets(line, sizeof(line), fp)) {
|
||||
|
||||
if (q < p) fatal("Parse error on line %d of fingerprint: %s", lineno, line);
|
||||
|
||||
FPs[numrecords] = (FingerPrint *) safe_zalloc(sizeof(FingerPrint));
|
||||
current->OS_name = (char *) cp_alloc(q - p + 2);
|
||||
memcpy(current->OS_name, p, q - p + 1);
|
||||
current->OS_name[q - p + 1] = '\0';
|
||||
}
|
||||
|
||||
FPs[numrecords]->OS_name = (char *) cp_alloc(q - p + 2);
|
||||
memcpy(FPs[numrecords]->OS_name, p, q - p + 1);
|
||||
FPs[numrecords]->OS_name[q - p + 1] = '\0';
|
||||
|
||||
current = FPs[numrecords];
|
||||
current->line = lineno;
|
||||
classno = 0;
|
||||
|
||||
@@ -2039,17 +2098,18 @@ while(fgets(line, sizeof(line), fp)) {
|
||||
current->results = str2AVal(p);
|
||||
}
|
||||
}
|
||||
/* printf("Read in fingerprint:\n%s\n", fp2ascii(FPs[numrecords])); */
|
||||
/* printf("Read in fingerprint:\n%s\n", fp2ascii(DB->prints[numrecords])); */
|
||||
if (!parsingMatchPoints)
|
||||
numrecords++;
|
||||
if (numrecords >= max_records)
|
||||
fatal("Too many OS fingerprints -- 0verflow");
|
||||
}
|
||||
fclose(fp);
|
||||
FPs[numrecords] = NULL;
|
||||
return FPs;
|
||||
DB->prints[numrecords] = NULL;
|
||||
return DB;
|
||||
}
|
||||
|
||||
FingerPrint **parse_fingerprint_reference_file(char *dbname) {
|
||||
FingerPrintDB *parse_fingerprint_reference_file(char *dbname) {
|
||||
char filename[256];
|
||||
if (nmap_fetchfile(filename, sizeof(filename), dbname) == -1){
|
||||
fatal("OS scan requested but I cannot find %s file. It should be in %s, ~/.nmap/ or .", dbname, NMAPDATADIR);
|
||||
|
||||
30
osscan.h
30
osscan.h
@@ -114,6 +114,7 @@
|
||||
|
||||
/* We won't even consider matches with a lower accuracy than this */
|
||||
#define OSSCAN_GUESS_THRESHOLD 0.85
|
||||
|
||||
/********************** STRUCTURES ***********************************/
|
||||
|
||||
/* moved to global_structures.h */
|
||||
@@ -128,26 +129,31 @@ char *fp2ascii(FingerPrint *FP);
|
||||
complete since it is used by scripts such as scripts/fingerwatch for
|
||||
which some partial fingerpritns are OK. */
|
||||
FingerPrint *parse_single_fingerprint(char *fprint_orig);
|
||||
FingerPrint **parse_fingerprint_file(char *fname);
|
||||
FingerPrint **parse_fingerprint_reference_file(char *dbname);
|
||||
|
||||
void free_fingerprint_file(FingerPrint **FPs);
|
||||
/* These functions take a file/db name and open+parse it, returning an
|
||||
(allocated) FingerPrintDB containing the results. They exit with
|
||||
an error message in the case of error. */
|
||||
FingerPrintDB *parse_fingerprint_file(char *fname);
|
||||
FingerPrintDB *parse_fingerprint_reference_file(char *dbname);
|
||||
|
||||
void free_fingerprint_file(FingerPrintDB *DB);
|
||||
|
||||
/* Compares 2 fingerprints -- a referenceFP (can have expression
|
||||
attributes) with an observed fingerprint (no expressions). If
|
||||
verbose is nonzero, differences will be printed. The comparison
|
||||
accuracy (between 0 and 1) is returned) */
|
||||
accuracy (between 0 and 1) is returned). If MatchPoints is not NULL, it is
|
||||
a special "fingerprints" which tells how many points each test is worth. */
|
||||
double compare_fingerprints(FingerPrint *referenceFP, FingerPrint *observedFP,
|
||||
int verbose);
|
||||
FingerPrint *MatchPoints, int verbose);
|
||||
|
||||
/* Takes a fingerprint and looks for matches inside reference_FPs[].
|
||||
The results are stored in in FPR (which must point to an instantiated
|
||||
FingerPrintResults class) -- results will be reverse-sorted by
|
||||
accuracy. No results below accuracy_threshhold will be included.
|
||||
The max matches returned is the maximum that fits in a
|
||||
FingerPrintResults class. */
|
||||
/* Takes a fingerprint and looks for matches inside the passed in
|
||||
reference fingerprint DB. The results are stored in in FPR (which
|
||||
must point to an instantiated FingerPrintResults class) -- results
|
||||
will be reverse-sorted by accuracy. No results below
|
||||
accuracy_threshhold will be included. The max matches returned is
|
||||
the maximum that fits in a FingerPrintResults class. */
|
||||
void match_fingerprint(FingerPrint *FP, FingerPrintResults *FPR,
|
||||
FingerPrint **reference_FPs, double accuracy_threshold);
|
||||
FingerPrintDB *DB, double accuracy_threshold);
|
||||
|
||||
/* Returns true if perfect match -- if num_subtests & num_subtests_succeeded are non_null it updates them. if shortcircuit is zero, it does all the tests, otherwise it returns when the first one fails */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user