Article 12822 of alt.sources:
Path: nntpd.lkg.dec.com!crl.dec.com!crl.dec.com!bloom-beacon.mit.edu!paperboy.osf.org!bone.think.com!blanket.mitre.org!agate!news.mindlink.net!van-bc!vanbc.wimsey.com!not-for-mail
From: markh@vanbc.wimsey.com (Mark C. Henderson)
Newsgroups: alt.sources
Subject: unstrip for Solaris 2.4/SPARC -- version 1.0
Date: 16 Sep 1995 10:49:56 -0700
Organization: Online at Wimsey Information Services Inc - Vancouver, Canada
Lines: 614
Message-ID: <43f2o4$svh@vanbc.wimsey.com>
NNTP-Posting-Host: vanbc.wimsey.com

Not much change from the BETA versions. Consider this the first real release.

#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.1).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `!/bin/sh' line above, then type `sh FILE'.
#
# Made on 1995-09-02 23:28 PDT by <mch@ratatosk>.
# Source directory was `/usr/local/src/un'.
#
# Existing files will *not* be overwritten unless `-c' is specified.
#
# This shar contains:
# length mode       name
# ------ ---------- ------------------------------------------
#   2030 -r-------- README
#  15473 -r--r--r-- unstrip.c
#    264 -r-------- Makefile
#
touch -am 1231235999 $$.touch >/dev/null 2>&1
if test ! -f 1231235999 && test -f $$.touch; then
  shar_touch=touch
else
  shar_touch=:
  echo
  echo 'WARNING: not restoring timestamps.  Consider getting and'
  echo "installing GNU \`touch', distributed in GNU File Utilities..."
  echo
fi
rm -f 1231235999 $$.touch
#
# ============= README ==============
if test -f 'README' && test X"$1" != X"-c"; then
  echo 'x - skipping README (file already exists)'
else
  echo 'x - extracting README (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'README' &&
$Header: /home/mch/src/un/README,v 1.2 1995/09/03 03:34:01 mch Exp $
X
unstrip version 1.0
X
Unstrip is a program which can often recover some symbolic information
from a strippped dynamically linked Solaris 2.4 SPARC executable and
make it available to common Solaris debuggers (gdb, adb, dbx). It can
only recover the symbolic information in the dynamic symbol table, but
it can usually recover enough to be useful.
X
It turns out that it is relatively simple to do this. The program makes
copies of the dynamic string table and the dynamic symbol table, and
adds these copies to the section header table.  It also takes care of
some additional complications when the section header string table is
missing or doesn't contain .symtab and .strtab.
X
Unstrip is intended to aid in reverse engineering efforts. For example
it can be used to help figure out what is going on when a stripped
binary dumps core. Often the name of the routine is helpful in trying
to trace the problem.
X
A similar program by P.Kranenburg <pk@cs.few.eur.nl> is available for 
Sun OS 4.1.x from 
ftp://ftp.netcom.com/pub/he/henderso/unstrip.shar.gz
ftp://ftp.wimsey.com/pub/crypto/netcom-mirror/unstrip.shar.gz
X
Please note that some earlier versions of gdb and binutils have
problems dealing with the output of unstrip. I have tested the output
of unstrip against:
gdb 4.14 
binutils 2.5.2
/opt/SUNWspro/bin/dbx
adb supplied with Solaris 2.4
nm supplied with Solaris 2.4
X
To compile:
use either gcc or /opt/SUNWspro/bin/cc
simply:
X
gcc -o unstrip unstrip.c
OR
cc -o unstrip unstrip.c
X
Note that using unstrip with gdb 4.14 is pretty pointless, because gdb
is already smart enough to look at the dynamic symbol table in a stripped
dynamically linked binary.
X
Changes
from version 0.21
- code added to deal with case where hdr.e_shstrndx = SHN_UNDEF, i.e.
X  the case where there is no section header string table
- the dynamic string table is now identified in the right way, 
X  i.e. by following the sh_link from the dynamic symbol table 
X  (type ST_DYNSYM)
SHAR_EOF
  $shar_touch -am 0902203495 'README' &&
  chmod 0400 'README' ||
  echo 'restore of README failed'
  shar_count="`wc -c < 'README'`"
  test 2030 -eq "$shar_count" ||
    echo "README: original size 2030, current size $shar_count"
fi
# ============= unstrip.c ==============
if test -f 'unstrip.c' && test X"$1" != X"-c"; then
  echo 'x - skipping unstrip.c (file already exists)'
else
  echo 'x - extracting unstrip.c (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'unstrip.c' &&
/* 
X * unstrip version 1.0
X * Author: Mark C. Henderson <markh@wimsey.bc.ca>
X * Placed in the public domain by the author. 2 September 1995.
X *
X * A quick and dirty hack to "unstrip" a Solaris 2.4 ELF SPARC binary
X * This program recovers some symbolic information from the dynamic
X * symbol table of the binary, and creates a new symbol table section
X * with that data.
X *
X * to compile: gcc -o unstrip unstrip.c  (or use /opt/SUNWspro/bin/cc)
X *
X * This can often be useful when reverse engineering dynamically linked
X * binaries, e.g. to find out what is going on when they dump core
X *
X * inspired by unstrip for Sun OS 4.1.x by P.Kranenburg <pk@cs.few.eur.nl>
X *
X * USE AT YOUR OWN RISK
X *
X * BUGS: Possibly several. Please report them to markh@wimsey.bc.ca.
X *
X * N.B. If this program malfunctions it can corrupt the binary you run
X *      it on. Please make backup copies.
X *
X * tested with adb, /opt/SUNWspro/bin/dbx, gdb 4.14, binutils 2.5.2
X * there are some problems with some earlier versions of gdb and binutils
X * 
X * Also note that there is no point in using this with gdb 4.14 as gdb
X * is smart enough to look at the dynamic symbol table (.dynsym) if the
X * binary is stripped
X */
X
static char header[]=
"$Header: /home/mch/src/un/unstrip.c,v 1.7 1995/09/03 03:41:36 mch Exp $";
X
#include <elf.h>
#include <sys/elf_SPARC.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
X
#define MAXSECTIONS 128
X
Elf32_Shdr sht[MAXSECTIONS + 1];
unsigned char   *secnames[MAXSECTIONS + 1];
unsigned char   *symtable;
unsigned char   *stringtable;
unsigned char   *shstringtable = NULL;
unsigned char   *new_shstringtable;
unsigned char   s[] = "\0.strtab\0.symtab\0\0\0\0\0\0\0\0\0\0";
unsigned char   s1[] = "\0.strtab\0.symtab\0.shstrtab\0.dynstr\0.dynsym\0.dynamic\0\0";
/* indices in s1
X *  .strtab         1
X *  .symtab         9
X *  .shstrtab       17
X *  .dynstr         27
X *  .dynsym         35
X *  .dynamic        43
X */
X
main(argc, argv)
int argc;
char    *argv[];
{
X    Elf32_Ehdr hdr, newhdr;
X    Elf32_Shdr newshdr, newshdr1, newshdr2;
X    int need_new_shstringtable = 0;
X    unsigned int    i;
X    int pad = 0, pad1 = 0, pad0 = 0, pad2 = 0;
X    int sto = (-1), sto1 = (-1);
X    int c;
X    FILE * chan;
X    int symflag = 0;
X    size_t size;
X    int dyn = (-1);
X    int stringidx = (-1);
X
X    if (argc < 2) {
X        fprintf(stderr, "usage: %s file\n", argv[0]);
X        exit(1);
X    }
X    chan = fopen(argv[1], "r+");
X
X    if (!chan) {
X        fprintf(stderr, "cannot open %s\n", argv[1]);
X        exit(1);
X    }
X    if (fread(&hdr, sizeof(hdr), 1, chan) < 0) {
X        fprintf(stderr, "not an ELF file\n");
X        exit(1);
X    }
X    if (    (hdr.e_ident[0] != ELFMAG0) || 
X        (hdr.e_ident[1] != ELFMAG1) || 
X        (hdr.e_ident[2] != ELFMAG2) || 
X        (hdr.e_ident[3] != ELFMAG3)     ) {
X        fprintf(stderr, "not an ELF file\n");
X        exit(1);
X    }
X    if (hdr.e_ident[4] != ELFCLASS32) {
X        fprintf(stderr, "not a 32 bit object\n");
X        exit(1);
X    }
X    if (hdr.e_type != ET_EXEC) {
X        fprintf(stderr, "not an executable file\n");
X        exit(1);
X    }
X    if (!hdr.e_shoff) {
X        fprintf(stderr, "file has no section header table\n");
X        exit(1);
X    }
X    if (hdr.e_shnum > MAXSECTIONS) {
X        fprintf(stderr, "%s has %d sections. maximum is %d\n", 
X            argv[1], hdr.e_shnum, MAXSECTIONS);
X        fprintf(stderr, 
X            "increase MAXSECTIONS in unstrip.c and recompile\n");
X        exit(1);
X    }
X    if (fseek(chan, hdr.e_shoff, SEEK_SET) < 0) {
X        fprintf(stderr, "fseek failed\n");
X        exit(1);
X    }
X    for (i = 0; i < hdr.e_shnum ; i++) {
X        fread(&sht[i], sizeof(Elf32_Shdr), 1, chan);
X        if (sht[i].sh_type == SHT_DYNSYM) {
X            dyn = i; 
X            if (sht[i].sh_link != SHN_UNDEF)
X                stringidx = sht[i].sh_link;
X        }
X        if (sht[i].sh_type == SHT_SYMTAB) 
X            symflag++;
X    }
X    if (symflag) {
X        fprintf(stderr, "file is not stripped - already has symbol table \n");
X        exit(1);
X    }
X    if (dyn < 0) {
X        fprintf(stderr, "file is not dynamically linked\n");
X        exit(1);
X    }
X
X    /* get section name string table */
X    if (hdr.e_shstrndx != SHN_UNDEF) {
X        if (fseek(chan, sht[hdr.e_shstrndx].sh_offset, SEEK_SET) < 0) {
X            perror("fseek failed (shstr)"); 
X            exit(1);
X        }
X        if (!(shstringtable = malloc(sht[hdr.e_shstrndx].sh_size))) {
X            fprintf(stderr, "malloc failed\n"); 
X            exit(1);
X        }
X        if (fread(shstringtable, 1, sht[hdr.e_shstrndx].sh_size, chan)
X             < sht[hdr.e_shstrndx].sh_size) {
X            perror("fread failed (str)"); 
X            exit(1);
X        }
X    } else
X        shstringtable = NULL;  /* no section name string table */
X
X    /* now get the section names themselves */
X    if (shstringtable) {
X        for (i = 0; i < hdr.e_shnum ; i++) {
X            int len = strlen(&shstringtable[sht[i].sh_name]);
X            secnames[i] = malloc(len + 1);
X            strncpy(secnames[i], &shstringtable[sht[i].sh_name], len + 1);
X        }
X    } 
X
X    /* get dynamic symbol table */
X    if (sht[dyn].sh_size) {
X        if (fseek(chan, sht[dyn].sh_offset, SEEK_SET) < 0) {
X            perror("fseek failed (dyn)"); 
X            exit(1);
X        }
X        if (!(symtable = malloc(sht[dyn].sh_size))) {
X            fprintf(stderr, "malloc failed\n"); 
X            exit(1);
X        }
X        if (fread(symtable, 1, sht[dyn].sh_size, chan) < sht[dyn].sh_size) {
X            perror("fread failed (dyn)"); 
X            exit(1);
X        }
X    } else {
X        fprintf(stderr, "dynamic symbol table has zero size\n");
X        exit(1);
X    }
X
X    /* get string table */
X    if ((stringidx >= 0) && sht[stringidx].sh_size) {
X        if (fseek(chan, sht[stringidx].sh_offset, SEEK_SET) < 0) {
X            perror("fseek failed (str)"); 
X            exit(1);
X        }
X        if (!(stringtable = malloc(sht[stringidx].sh_size))) {
X            fprintf(stderr, "malloc failed\n"); 
X            exit(1);
X        }
X        if (fread(stringtable, 1, sht[stringidx].sh_size, chan)
X             < sht[stringidx].sh_size) {
X            perror("fread failed (str)"); 
X            exit(1);
X        }
X    } else {
X        fprintf(stderr, "warning: string table has zero size\n");
X        stringidx = (-1);   /* just to be sure */
X        exit(1);
X    }
X
X    /* find offset to .symtab and .strtab in section name string table */
X    if (shstringtable) {
X        for (i = 0; i < sht[hdr.e_shstrndx].sh_size - 8; i++) {
X            if (!strncmp(&shstringtable[i], ".symtab", 8)) {
X                sto = i;
X                break;
X            }
X        }
X        for (i = 0; i < sht[hdr.e_shstrndx].sh_size - 8; i++) {
X            if (!strncmp(&shstringtable[i], ".strtab", 8)) {
X                sto1 = i;
X                break;
X            }
X        }
X    }
X    else {
X        /* no shstringtable - so we try to create one */
X
X        new_shstringtable = malloc(sizeof(s1) + 2);
X        memset(new_shstringtable,0,sizeof(s1) + 2);
X        memcpy(new_shstringtable,s1,sizeof(s1));
X        newshdr2.sh_name = 17;  /* locn of .shstrtab */
X        sto = 9;                /* locn of .symtab */ 
X        sto1 = 1;               /* locn of .strtab */
X        sht[dyn].sh_name = 35;  /* locn of .dynsym */
X        if (stringidx >= 0) 
X            sht[stringidx].sh_name = 27;  /* locn of .dynstr */
X        newshdr2.sh_type = SHT_STRTAB;
X        newshdr2.sh_flags = 0;
X        newshdr2.sh_size = sizeof(s1) + 2;
X        newshdr2.sh_link = 0;
X        newshdr2.sh_info = 0;
X        newshdr2.sh_addralign = 8;  /* just in case */
X        newshdr2.sh_entsize = 0;
X        fseek(chan, 0, SEEK_END);
X        newshdr2.sh_offset = ftell(chan);
X        if (newshdr2.sh_addralign > 1) {
X            pad0 = newshdr2.sh_addralign - (newshdr2.sh_offset
X                 % newshdr2.sh_addralign);
X            newshdr2.sh_offset += pad0;
X        } else
X            pad0 = 0;
X        for (i = 0; i < pad0; i++)
X            putc(0, chan);
X        if (ftell(chan) != newshdr2.sh_offset) {
X            fprintf(stderr, "internal error (new_shstringtable-0)\n");
X            exit(1);
X        }
X        if (fwrite(new_shstringtable, (unsigned int)newshdr2.sh_size, 
X            1, chan) < 1) {
X            perror("fwrite failed (new_shstringtable-0)");
X            exit(1);
X        }
X
X        /* fill in .dynamic */
X        for (i = 0; i < hdr.e_shnum; i++)
X            if (sht[i].sh_type == SHT_DYNAMIC)
X                sht[i].sh_name = 43;
X
X        hdr.e_shstrndx = hdr.e_shnum;
X        memcpy(&sht[hdr.e_shnum],&newshdr2,sizeof(Elf32_Shdr));
X        hdr.e_shnum++;
X    }
X
X    if (sto < 0 || sto1 < 0) {
X        /* This is annoying. Sometimes the sh_stringtable doesn't contain
X           one or both of .symtab or .strtab */
X        need_new_shstringtable = 1;
X        new_shstringtable = malloc(sht[hdr.e_shstrndx].sh_size + 24 );
X        if (!new_shstringtable) {
X            fprintf(stderr, "malloc failed\n"); 
X            exit(1);
X        }
X        memcpy(new_shstringtable, shstringtable, 
X            sht[hdr.e_shstrndx].sh_size);
X        for (i = 0; i < 19 ; i++)
X            *(new_shstringtable + sht[hdr.e_shstrndx].sh_size + i) = s[i];
X
X        memcpy(&newshdr2, &sht[hdr.e_shstrndx], sizeof(newshdr2));
X        newshdr2.sh_size += 19;
X        sto1 = sht[hdr.e_shstrndx].sh_size + 1;
X        sto = sht[hdr.e_shstrndx].sh_size + 9;
X        fseek(chan, 0, SEEK_END);
X        newshdr2.sh_offset = ftell(chan);
X        if (newshdr2.sh_addralign > 1) {
X            pad0 = newshdr2.sh_addralign - (newshdr2.sh_offset
X                 % newshdr2.sh_addralign);
X            newshdr2.sh_offset += pad0;
X        } else
X            pad0 = 0;
X        for (i = 0; i < pad0; i++)
X            putc(0, chan);
X        if (ftell(chan) != newshdr2.sh_offset) {
X            fprintf(stderr, "internal error (new_shstringtable)\n");
X            exit(1);
X        }
X        if (fwrite(new_shstringtable, (unsigned int)newshdr2.sh_size, 
X            1, chan) < 1) {
X            perror("fwrite failed (new_shstringtable)");
X            exit(1);
X        }
X    }
X
X    if (stringidx < 0) {
X
X        memcpy(&newhdr, &hdr, sizeof(newhdr));
X        memcpy(&newshdr, &sht[dyn], sizeof(newshdr));
X
X        /* go to end of file */
X        fseek(chan, 0, SEEK_END);
X        newhdr.e_shoff = ftell(chan);
X
X        /* get new offset for section header table */
X        if (newhdr.e_shoff % 8)
X            pad2 = 8 - (newhdr.e_shoff % 8);
X        else
X            pad2 = 0;
X        newhdr.e_shoff += pad2;
X        for (i = 0; i < pad2; i++)
X            putc(0, chan);
X
X        newhdr.e_shnum++;
X
X        /* write new section header table */
X        for (i = 0; i < hdr.e_shnum ; i++) {
X            if (need_new_shstringtable && i == hdr.e_shstrndx) {
X                if (fwrite(&newshdr2, sizeof(Elf32_Shdr), 1, chan) < 1) {
X                    perror("fwrite failed");
X                    exit(1);
X                }
X            } else {
X                if (fwrite(&sht[i], sizeof(Elf32_Shdr), 1, chan) < 1) {
X                    perror("fwrite failed");
X                    exit(1);
X                }
X            }
X        }
X        newshdr.sh_type = SHT_SYMTAB;
X        if (newshdr.sh_addralign > 1)
X            pad = newshdr.sh_addralign - ((newhdr.e_shoff + newhdr.e_shnum 
X                *newhdr.e_shentsize) % (newshdr.sh_addralign));
X        else
X            pad = 0;
X
X        newshdr.sh_offset = (newhdr.e_shoff + newhdr.e_shnum * 
X            newhdr.e_shentsize) + pad;
X        if (sto >= 0)
X            newshdr.sh_name = sto;
X        if (fwrite(&newshdr, sizeof(Elf32_Shdr), 1, chan) < 1) {
X            perror("fwrite failed (newshdr)");
X            exit(1);
X        }
X        for (i = 0; i < pad; i++)
X            putc(0, chan);
X        if (fwrite(symtable, newshdr.sh_size, 1, chan) < 1) {
X            perror("fwrite failed (symtable)");
X            exit(1);
X        }
X        rewind(chan);
X        if (fwrite(&newhdr, sizeof(newhdr), 1, chan) < 1) {
X            perror("fwrite failed (elfhdr)");
X            exit(1);
X        }
X    } else {
X        memcpy(&newhdr, &hdr, sizeof(newhdr));
X        memcpy(&newshdr, &sht[dyn], sizeof(newshdr));
X        memcpy(&newshdr1, &sht[stringidx], sizeof(newshdr1));
X
X        /* go to end of file */
X        fseek(chan, 0, SEEK_END);
X        newhdr.e_shoff = ftell(chan);
X
X        /* get new offset for section header table */
X        if (newhdr.e_shoff % 8)
X            pad2 = 8 - (newhdr.e_shoff % 8);
X        else
X            pad2 = 0;
X        newhdr.e_shoff += pad2;
X        for (i = 0; i < pad2; i++)
X            putc(0, chan);
X
X        newhdr.e_shnum += 2;  /* two new sections */
X
X        /* write new section header table */
X        for (i = 0; i < hdr.e_shnum ; i++) {
X            if (need_new_shstringtable && i == hdr.e_shstrndx) {
X                if (fwrite(&newshdr2, sizeof(Elf32_Shdr), 1, chan) < 1) {
X                    perror("fwrite failed");
X                    exit(1);
X                }
X            } else {
X                if (fwrite(&sht[i], sizeof(Elf32_Shdr), 1, chan) < 1) {
X                    perror("fwrite failed");
X                    exit(1);
X                }
X            }
X        }
X        newshdr.sh_type = SHT_SYMTAB;
X        newshdr.sh_flags = 0;
X        newshdr1.sh_type = SHT_STRTAB;  /* redundant */
X        newshdr.sh_link = newhdr.e_shnum - 1;
X        newshdr.sh_flags = 0;
X        newshdr1.sh_flags = 0;
X        if (sto >= 0)
X            newshdr.sh_name = sto;
X        if (sto1 >= 0)
X            newshdr1.sh_name = sto1;
X
X        newshdr.sh_offset = newhdr.e_shoff + newhdr.e_shnum 
X            *newhdr.e_shentsize;
X
X        if (newshdr.sh_addralign > 1) {
X            pad = newshdr.sh_addralign - (newshdr.sh_offset 
X                 % newshdr.sh_addralign);
X            newshdr.sh_offset += pad;
X        }
X        newshdr1.sh_offset = newshdr.sh_offset + newshdr.sh_size;
X        if (newshdr1.sh_addralign > 1) {
X            pad1 = newshdr1.sh_addralign - (newshdr1.sh_offset 
X                 % newshdr1.sh_addralign);
X            newshdr1.sh_offset += pad1;
X        }
X        if (fwrite(&newshdr, sizeof(Elf32_Shdr), 1, chan) < 1) {
X            perror("fwrite failed (newshdr)");
X            exit(1);
X        }
X        if (fwrite(&newshdr1, sizeof(Elf32_Shdr), 1, chan) < 1) {
X            perror("fwrite failed (newshdr1)");
X            exit(1);
X        }
X        for (i = 0; i < pad ; i++)
X            putc(0, chan);
X
X        if (ftell(chan) != newshdr.sh_offset) {
X            fprintf(stderr, "internal error (1)\n");
X            fprintf(stderr, "%08x != %08x\n", ftell(chan), newshdr.sh_offset);
X            exit(1);
X        }
X
X        if (fwrite(symtable, newshdr.sh_size, 1, chan) < 1) {
X            perror("fwrite failed (symtable)");
X            exit(1);
X        }
X        for (i = 0; i < pad1 ; i++)
X            putc(0, chan);
X
X        if (ftell(chan) != newshdr1.sh_offset) {
X            fprintf(stderr, "internal error (2)\n");
X            fprintf(stderr, "%08x != %08x\n", ftell(chan), newshdr1.sh_offset);
X            exit(1);
X        }
X        if (fwrite(stringtable, newshdr1.sh_size, 1, chan) < 1) {
X            perror("fwrite failed (symtable)");
X            exit(1);
X        }
X        rewind(chan);
X        if (fwrite(&newhdr, sizeof(newhdr), 1, chan) < 1) {
X            perror("fwrite failed (elfhdr)");
X            exit(1);
X        }
X    }
X    fclose(chan);
}
X
X
SHAR_EOF
  $shar_touch -am 0902204195 'unstrip.c' &&
  chmod 0444 'unstrip.c' ||
  echo 'restore of unstrip.c failed'
  shar_count="`wc -c < 'unstrip.c'`"
  test 15473 -eq "$shar_count" ||
    echo "unstrip.c: original size 15473, current size $shar_count"
fi
# ============= Makefile ==============
if test -f 'Makefile' && test X"$1" != X"-c"; then
  echo 'x - skipping Makefile (file already exists)'
else
  echo 'x - extracting Makefile (text)'
  sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
# $Header: /home/mch/src/un/Makefile,v 1.1 1995/09/03 03:41:00 mch Exp $
CC=gcc
#CC=/opt/SUNWspro/bin/cc
CFLAGS=-O
unstrip: unstrip.c
X	$(CC) $(CFLAGS) -o unstrip unstrip.c
X
unstrip.shar.01: unstrip.c README Makefile
X	shar -o unstrip.shar README unstrip.c Makefile
SHAR_EOF
  $shar_touch -am 0902204195 'Makefile' &&
  chmod 0400 'Makefile' ||
  echo 'restore of Makefile failed'
  shar_count="`wc -c < 'Makefile'`"
  test 264 -eq "$shar_count" ||
    echo "Makefile: original size 264, current size $shar_count"
fi
exit 0
-- 
Mark Henderson -- markh@wimsey.bc.ca, henderso@netcom.com, mch@squirrel.com
PGP 1024/C58015E3 fingerprint=21 F6 AF 2B 6A 8A 0B E1 A1 2A 2A 06 4A D5 92 46
cryptography archive maintainer  --  ftp://ftp.wimsey.com/pub/crypto
ftp://ftp.netcom.com/pub/he/henderso/change-sun-hostid-1.5.0.tar.gz


