1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-10 17:59:04 +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-*- # 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. o A dozen or so small patches to Nmap and NmapFE by Kris Katterjohn.
4.20ALPHA7 4.20ALPHA7

View File

@@ -174,8 +174,8 @@ class NmapOps {
int interactivemode; int interactivemode;
int ping_group_sz; int ping_group_sz;
int generate_random_ips; /* -iR option */ int generate_random_ips; /* -iR option */
FingerPrint **reference_FPs1; /* Used in the old OS scan system. */ FingerPrintDB *reference_FPs1; /* Used in the old OS scan system. */
FingerPrint **reference_FPs; /* Used in the new OS scan system. */ FingerPrintDB *reference_FPs; /* Used in the new OS scan system. */
u16 magic_port; u16 magic_port;
unsigned short magic_port_set; /* Was this set by user? */ unsigned short magic_port_set; /* Was this set by user? */
int num_ping_synprobes; int num_ping_synprobes;

View File

@@ -197,6 +197,13 @@ typedef struct FingerTest {
struct FingerTest *next; struct FingerTest *next;
} FingerPrint; } 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 { struct timeout_info {
int srtt; /* Smoothed rtt estimate (microseconds) */ int srtt; /* Smoothed rtt estimate (microseconds) */
int rttvar; /* Rout trip time variance */ int rttvar; /* Rout trip time variance */

View File

@@ -31,7 +31,7 @@
# are used when there are no perfect matches to determine which OS # are used when there are no perfect matches to determine which OS
# fingerprint matches a target machine most closely. # fingerprint matches a target machine most closely.
MatchPoints 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) 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) 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) 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) 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) 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) 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) 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(DFI=40%T=20%TG=20%TOSI=100%CD=100%SI=100%DLI=100) IE(R=50%DFI=40%T=20%TG=20%TOSI=100%CD=100%SI=100%DLI=100)
# Device type: switch. Running: 3Com embedded. # 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 # OS details: 3Com Superstack 3, 3300XM switch , Boot PROM Version: 1.00, Software Version: 2.69, Hardware Version: 0

154
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, want to see the results from this match. if shortcircuit is zero,
it does all the tests, otherwise it returns when the first one it does all the tests, otherwise it returns when the first one
fails. If you want details of the match process printed, pass n 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 onzero for 'verbose'. If points is non-null, it is examined to
name (SEQ, T1, etc) to have that extra info printed. If you pass 0 find the number of points for each test in the fprint AVal and use
for verbose, you might as well pass NULL for testGroupName as it that the increment num_subtests and num_subtests_succeeded
won't be used. */ appropriately. If it is NULL, each test is worth 1 point. In that
static int AVal_match(struct AVal *reference, struct AVal *fprint, 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,
unsigned long *num_subtests_succeeded, int shortcut, unsigned long *num_subtests_succeeded, int shortcut,
int verbose, const char *testGroupName) { int verbose, const char *testGroupName) {
struct AVal *current_ref; struct AVal *current_ref;
struct AVal *current_fp; struct AVal *current_fp;
struct AVal *current_points;
unsigned int number, number1; unsigned int number, number1;
unsigned int val; unsigned int val;
char *p, *q, *q1; /* OHHHH YEEEAAAAAHHHH!#!@#$!% */ 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 andexp, orexp, expchar, numtrue;
int testfailed; int testfailed;
int subtests = 0, subtests_succeeded=0; int subtests = 0, subtests_succeeded=0;
int pointsThisTest = 1;
for(current_ref = reference; current_ref; current_ref = current_ref->next) { for(current_ref = reference; current_ref; current_ref = current_ref->next) {
current_fp = getattrbyname(fprint, current_ref->attribute); 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; if (q) p = q + 1;
} while(q); } while(q);
if (numtrue == 0) testfailed=1; 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 (testfailed) {
if (shortcut) { if (shortcut) {
if (num_subtests) *num_subtests += subtests; if (num_subtests) *num_subtests += subtests;
return 0; return 0;
} }
if (verbose) 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->attribute, current_fp->value,
current_ref->value); current_ref->value, pointsThisTest, (pointsThisTest == 1)? "point" : "points");
} else subtests_succeeded++; } else subtests_succeeded += pointsThisTest;
/* Whew, we made it past one Attribute alive , on to the next! */ /* Whew, we made it past one Attribute alive , on to the next! */
} }
if (num_subtests) *num_subtests += subtests; 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 /* Compares 2 fingerprints -- a referenceFP (can have expression
attributes) with an observed fingerprint (no expressions). If attributes) with an observed fingerprint (no expressions). If
verbose is nonzero, differences will be printed. The comparison 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, double compare_fingerprints(FingerPrint *referenceFP, FingerPrint *observedFP,
int verbose) { FingerPrint *MatchPoints, int verbose) {
FingerPrint *currentReferenceTest; FingerPrint *currentReferenceTest;
struct AVal *currentObservedTest; struct AVal *currentObservedTest;
struct AVal *currentTestMatchPoints;
unsigned long num_subtests = 0, num_subtests_succeeded = 0; unsigned long num_subtests = 0, num_subtests_succeeded = 0;
unsigned long new_subtests, new_subtests_succeeded; unsigned long new_subtests, new_subtests_succeeded;
assert(referenceFP); assert(referenceFP);
@@ -1288,7 +1303,13 @@ double compare_fingerprints(FingerPrint *referenceFP, FingerPrint *observedFP,
currentObservedTest = gettestbyname(observedFP, currentReferenceTest->name); currentObservedTest = gettestbyname(observedFP, currentReferenceTest->name);
if (currentObservedTest) { if (currentObservedTest) {
new_subtests = new_subtests_succeeded = 0; 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); &new_subtests, &new_subtests_succeeded, 0, verbose, currentReferenceTest->name);
num_subtests += new_subtests; num_subtests += new_subtests;
num_subtests_succeeded += new_subtests_succeeded; 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; return (num_subtests)? (num_subtests_succeeded / (double) num_subtests) : 0;
} }
/* Takes a fingerprint and looks for matches inside reference_FPs[]. /* Takes a fingerprint and looks for matches inside the passed in
The results are stored in FPR (which must point to an instantiated reference fingerprint DB. The results are stored in in FPR (which
FingerPrintResults class) -- results will be reverse-sorted by must point to an instantiated FingerPrintResults class) -- results
accuracy. No results below accuracy_threshhold will be included. will be reverse-sorted by accuracy. No results below
The max matches returned is the maximum that fits in a accuracy_threshhold will be included. The max matches returned is
FingerPrintResults class. */ the maximum that fits in a FingerPrintResults class. */
void match_fingerprint(FingerPrint *FP, FingerPrintResults *FPR, void match_fingerprint(FingerPrint *FP, FingerPrintResults *FPR,
FingerPrint **reference_FPs, double accuracy_threshold) { FingerPrintDB *DB, double accuracy_threshold) {
int i; int i;
double FPR_entrance_requirement = accuracy_threshold; /* accuracy must be double FPR_entrance_requirement = accuracy_threshold; /* accuracy must be
at least this big at least this big
to be added to the to be added to the
list */ list */
FingerPrint **reference_FPs = DB->prints;
FingerPrint *current_os; FingerPrint *current_os;
double acc; double acc;
int state; int state;
@@ -1332,7 +1354,7 @@ void match_fingerprint(FingerPrint *FP, FingerPrintResults *FPR,
current_os = reference_FPs[i]; current_os = reference_FPs[i];
skipfp = 0; 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); */ /* 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) { if (acc >= FPR_entrance_requirement || acc == 1.0) {
@@ -1615,7 +1637,7 @@ do {
one is the same */ one is the same */
if (i == numFPs - 1 || !currentFPs[i+1] || if (i == numFPs - 1 || !currentFPs[i+1] ||
strcmp(currentFPs[i]->name, currentFPs[i+1]->name) != 0 || 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) NULL, 1, 0, NULL) ==0)
{ {
changed = 1; 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 **current;
FingerPrint *c, *d; FingerPrint *c, *d;
struct AVal *avc; struct AVal *avc;
@@ -1950,21 +1973,47 @@ void free_fingerprint_file(FingerPrint **FPs) {
} }
} }
free(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) { FingerPrintDB *parse_fingerprint_file(char *fname) {
FingerPrint **FPs; FingerPrintDB *DB = NULL;
FingerPrint *current; FingerPrint *current;
FILE *fp; FILE *fp;
int max_records = 4096; int max_records = 4096;
char line[512]; char line[512];
int numrecords = 0; int numrecords = 0;
int lineno = 0; int lineno = 0;
bool parsingMatchPoints = false;
int classno = 0; /* Number of Class lines dealt with so far */ int classno = 0; /* Number of Class lines dealt with so far */
DB = (FingerPrintDB *) safe_zalloc(sizeof(FingerPrintDB));
char *p, *q; /* OH YEAH!!!! */ 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"); fp = fopen(fname, "r");
if (!fp) fatal("Unable to open Nmap fingerprint file: %s", fname); if (!fp) fatal("Unable to open Nmap fingerprint file: %s", fname);
@@ -1978,29 +2027,39 @@ while(fgets(line, sizeof(line), fp)) {
fparse: 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); fprintf(stderr, "Parse error on line %d of nmap-os-fingerprints file: %s\n", lineno, line);
continue; continue;
} }
p = line + 12; current = (FingerPrint *) safe_zalloc(sizeof(FingerPrint));
while(*p && isspace((int) *p)) p++;
q = strpbrk(p, "\n#"); if (parsingMatchPoints) {
if (!p) fatal("Parse error on line %d of fingerprint: %s", lineno, line); current->OS_name = NULL;
DB->MatchPoints = current;
} else {
DB->prints[numrecords] = current;
p = line + 12;
while(*p && isspace((int) *p)) p++;
q = strpbrk(p, "\n#");
if (!p) fatal("Parse error on line %d of fingerprint: %s", lineno, line);
while(isspace(*(--q))) while(isspace(*(--q)))
; ;
if (q < p) fatal("Parse error on line %d of fingerprint: %s", lineno, line); 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);
FPs[numrecords]->OS_name = (char *) cp_alloc(q - p + 2); current->OS_name[q - p + 1] = '\0';
memcpy(FPs[numrecords]->OS_name, p, q - p + 1); }
FPs[numrecords]->OS_name[q - p + 1] = '\0';
current = FPs[numrecords];
current->line = lineno; current->line = lineno;
classno = 0; classno = 0;
@@ -2039,23 +2098,24 @@ while(fgets(line, sizeof(line), fp)) {
current->results = str2AVal(p); 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])); */
numrecords++; if (!parsingMatchPoints)
numrecords++;
if (numrecords >= max_records) if (numrecords >= max_records)
fatal("Too many OS fingerprints -- 0verflow"); fatal("Too many OS fingerprints -- 0verflow");
} }
fclose(fp); fclose(fp);
FPs[numrecords] = NULL; DB->prints[numrecords] = NULL;
return FPs; return DB;
} }
FingerPrint **parse_fingerprint_reference_file(char *dbname) { FingerPrintDB *parse_fingerprint_reference_file(char *dbname) {
char filename[256]; char filename[256];
if (nmap_fetchfile(filename, sizeof(filename), dbname) == -1){ 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); fatal("OS scan requested but I cannot find %s file. It should be in %s, ~/.nmap/ or .", dbname, NMAPDATADIR);
} }
return parse_fingerprint_file(filename); return parse_fingerprint_file(filename);
} }
/* This function takes an array of "numSamples" IP IDs and analyzes /* This function takes an array of "numSamples" IP IDs and analyzes

View File

@@ -114,6 +114,7 @@
/* We won't even consider matches with a lower accuracy than this */ /* We won't even consider matches with a lower accuracy than this */
#define OSSCAN_GUESS_THRESHOLD 0.85 #define OSSCAN_GUESS_THRESHOLD 0.85
/********************** STRUCTURES ***********************************/ /********************** STRUCTURES ***********************************/
/* moved to global_structures.h */ /* 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 complete since it is used by scripts such as scripts/fingerwatch for
which some partial fingerpritns are OK. */ which some partial fingerpritns are OK. */
FingerPrint *parse_single_fingerprint(char *fprint_orig); 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 /* Compares 2 fingerprints -- a referenceFP (can have expression
attributes) with an observed fingerprint (no expressions). If attributes) with an observed fingerprint (no expressions). If
verbose is nonzero, differences will be printed. The comparison 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, double compare_fingerprints(FingerPrint *referenceFP, FingerPrint *observedFP,
int verbose); FingerPrint *MatchPoints, int verbose);
/* Takes a fingerprint and looks for matches inside reference_FPs[]. /* Takes a fingerprint and looks for matches inside the passed in
The results are stored in in FPR (which must point to an instantiated reference fingerprint DB. The results are stored in in FPR (which
FingerPrintResults class) -- results will be reverse-sorted by must point to an instantiated FingerPrintResults class) -- results
accuracy. No results below accuracy_threshhold will be included. will be reverse-sorted by accuracy. No results below
The max matches returned is the maximum that fits in a accuracy_threshhold will be included. The max matches returned is
FingerPrintResults class. */ the maximum that fits in a FingerPrintResults class. */
void match_fingerprint(FingerPrint *FP, FingerPrintResults *FPR, 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 */ /* 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 */