1
0
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:
fyodor
2006-09-25 09:08:56 +00:00
parent aee73b577b
commit f1440dfc89
6 changed files with 141 additions and 64 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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
View File

@@ -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);

View File

@@ -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 */