#!/bin/sh
: ; exec klone $0 "$@"
; The above line allows not to embed the exact path of the klone executable
;;Skeleton of a typical klone script
(setq args (getopts "USAGE: %0 [options] hexstring files...
grep-like program, but searches for string (in hxa) in files
Shows somes bytes of context around matches, ^ meaning start of file
and $ end of file.
Can also patch files by setting strings at offsets, and be used as a minimal
hex editor
Output is:
filename: hex_offset / dec_offset: left_context string right_context
"
("-li" () LI "string must be a decimal number that will be searched for in
files as a little endian (intel order) 4-byte number")
("-bi" () BI "string must be a decimal number that will be searched for in
files as a big endian (motorola order) 4-byte number")
("-a" () ASC "string is taken as ascii text")
("-e" () EVAL "ascii string is evaluated to expand klone string escapes: \\xXX
for char of ascii code XX in hexa, \\n, \\e, \\r, etc...")
("-c" N context "number of context bytes shown around matches.
default to as much as fit in 80 columns")
("-p" () printables "shows also, underneath matches, the printable characters
Unprintable chars are show as undeline \"_\" characters")
("-s" N replace-at "do not search for string, instead sets the string at
offset N in files, overwriting contents there.
N can be in decimal or hexa (prefixed by 0x)")
("-r" repstring replace-all "replace all occurences of string by repstring\n(hexadecimal string)")
("-R" repstring replace-all-eval "replace all occurences of string by repstring\n(klone-evaluated ascii string)")
("-re" () REGEXP "hexstring is a regexp. Actually, not a true regexp, but a regexp\non the ascii representation: to match a number in brackets,\nsay 3c3[0-9]3e, <[0-9]> wont work\nThis work only to search, not to replace")
("-l" () LIST "string must be a number (decimal or 0x-prefixed hexa, only
lists memory at the location given by string")
("-m" N modulo "string must start at a multiple of N (4, 8...)")
("-q" () quiet "no output. Only return value can be used")
("-v" () verbose "verbose")
))
(if (not args) (getopts :usage))
(defvar found (list))
(setq context (if context (Int context) ()))
(if replace-all-eval (setq replace-all
(read (open (+ "\"" replace-all-eval "\"") :type :string)))
)
(if modulo (setq modulo (Int modulo)))
(defun main (&aux
)
(if LI (setq s (itole (Int (get args 0))))
BI (setq s (itobe (Int (get args 0))))
ASC (setq s (get args 0))
EVAL (setq s (read (open (+ "\"" (get args 0) "\"") :type :string)))
LIST (setq s (dec-or-hex (get args 0)))
REGEXP (setq s (get args 0))
t (setq s (decode-hex (get args 0)))
)
(if LIST (dolist (file (subseq args 1))
(list-places s file))
)
(if replace-at (doreplace s replace-at (subseq args 1) t))
(if replace-all (doreplace-all s (decode-hex replace-all) (subseq args 1)))
(verbose? "Searching for %0" (encode-hex s))
(dolist (file (subseq args 1))
(if REGEXP
(hexgrep-re s file (open file :error ()) (> (length args) 2) ())
(hexgrep s file (open file :error ()) (> (length args) 2) ())
)
)
(if (= 1 (length args))
(if REGEXP
(hexgrep-re s "stdin" *standard-input* () ())
(hexgrep s "stdin" *standard-input* () ())
)
)
(exit (if found 0 1))
)
(defun doreplace (s replace files must-exit? &aux offset fd)
(setq offset (dec-or-hex replace))
(verbose? "Setting %0 in files at offset %1" (encode-hex s) offset)
(dolist (file files)
(setq fd (open file :if-exists :overwrite :direction :io))
(if (or (< offset 0)
(> (+ offset (length s)) (get (file-stats file) 'size)))
(print-exit 1 "offset %1 is not inside file %0! aborting.\n" file offset)
)
(verbose? "Putting string %2 at offset %1 in file %0" file offset
(encode-hex s)
)
(file-position fd offset)
(write-chars s () fd)
(close fd)
)
(if must-exit? (exit 0))
)
(defun doreplace-all (s replace files &aux (found found))
(if (> (length replace) (length s))
(print-exit 1 "Error: trying to replace a string %r0 by a longer one: %r1 !\n" s replace)
)
(verbose? "Replacing %0 by %1" (encode-hex s) (encode-hex replace))
(dolist (file files)
(setq found (list))
(with (verbose () quiet t)
(hexgrep s file (open file :error ()) () ())
)
(if found
(with (fd (open file :if-exists :overwrite :direction :io))
(dolist (pos found)
(verbose? "%0: %1 / %2: replacing %3 by %4"
file (itox pos) pos (encode-hex s) (encode-hex replace)
)
(file-position fd pos)
(write-chars replace () fd)
)
(close fd)
)
)
)
(exit 0)
)
(defun list-places (pos file &aux
(s (make-string 24))
(fd (open file :error ()))
(context 0)
)
(hexgrep s file fd (> (length args) 2) pos)
(exit 0)
)
(defun hexgrep (s file fd show-filename? &optional one-pos &aux
buffer
(pos 0)
hexpad decpad
room-for-context
(context context)
(loop-cond (if one-pos
'(if (= t one-pos) () (progn (setq pos one-pos) (setq one-pos t) pos))
'(setq pos (seek buffer s pos))
))
)
(if (not fd) (progn
(print-format *standard-error* "Error: cannot open file %0\n" file)
(exit 1)
))
(setq buffer (String fd))
(setq hexpad (length (itox (length buffer))))
(setq decpad (length (String (length buffer))))
(setq room-for-context (/ (- 79 (+ (if show-filename? (+ (length file) 2) 0)
hexpad 3 decpad 2 2 (* 2 (length s)))) 4)
)
(setq context (if context context (max 0 room-for-context)))
(while (eval loop-cond)
(if (not (and modulo (/= 0 (mod pos modulo)))) (progn
(lappend found pos)
(if (not quiet) (progn
(if show-filename?
(print-format "%0: " file)
)
(print-format "%4%1 / %5%2: %3\n"
file (itox pos) pos (hexrep buffer pos s)
(make-string (- hexpad (length (itox pos))))
(make-string (- decpad (length (String pos))))
)
(if printables
(print-format "%0%1\n"
(make-string (+
(if show-filename? (+ (length file) 2) 0)
hexpad 3 decpad 2))
(printable-rep (hexrep buffer pos s))
))
))
(incf pos (length s))
)
(incf pos)
)
)
)
;; Same, but search on ascii encoding of file
(defun hexgrep-re (s file fd show-filename? &optional one-pos &aux
buffer
(pos 0)
(ascpos 0)
hexpad decpad
room-for-context
(context context)
(re (regcomp s))
len
)
(if (not fd) (progn
(print-format *standard-error* "Error: cannot open file %0\n" file)
(exit 1)
))
(setq buffer (String fd))
(setq ascbuffer (encode-hex buffer))
(setq hexpad (length (itox (length buffer))))
(setq decpad (length (String (length buffer))))
(setq room-for-context (/ (- 79 (+ (if show-filename? (+ (length file) 2) 0)
hexpad 3 decpad 2 2 (length s))) 4)
)
(setq context (if context context (max 0 room-for-context)))
(while (regexec re ascbuffer ascpos)
(setq ascpos (0 (get re 0)))
(if (/= 0 (mod ascpos 2)) (progn ;not on a byte start, ignore
(incf ascpos)
)
(and modulo (/= 0 (mod (setq pos (/ascpos 2)) modulo)))
(incf ascpos 2)
(progn
(setq pos (/ ascpos 2))
(setq len (/ (- (1 (get re 0)) (0 (get re 0))) 2))
(if (not quiet) (progn
(if show-filename?
(print-format "%0: " file)
)
(print-format "%4%1 / %5%2: %3\n"
file (itox pos) pos (hexrep buffer pos len)
(make-string (- hexpad (length (itox pos))))
(make-string (- decpad (length (String pos))))
)
(if printables
(print-format "%0%1\n"
(make-string (+
(if show-filename? (+ (length file) 2) 0)
hexpad 3 decpad 2))
(printable-rep (hexrep buffer pos len))
))
))
(setq ascpos (* 2 (/ (1 (get re 0)) 2)))
)
))
)
;; shows string and its context
(defun hexrep (buffer pos s &aux
(res (copy ""))
(len (if (typep s Number) s (length s)))
)
(if (<= pos context)
(nconc res "^")
)
(nconc res (encode-hex (subseq buffer (max 0 (- pos context)) pos)))
(nconc res " ")
(nconc res (encode-hex (subseq buffer pos (+ pos len))))
(nconc res " ")
(nconc res (encode-hex
(subseq buffer (+ pos len)
(min (length buffer) (+ pos context len))
)))
(if (>= (+ pos context len) (length buffer))
(nconc res "$")
)
res
)
(defun printable-rep (s &aux
(res (copy ""))
upper-half-byte
out
)
(dolist (c s)
(setq out #\ )
(if (seek encode-hex-values c)
(if upper-half-byte
(progn
(setq out (printable-rep-char
(+ upper-half-byte (seek encode-hex-values c))))
(setq upper-half-byte ())
)
(setq upper-half-byte (* 16 (seek encode-hex-values c)))
))
(put res -1 out)
)
res
)
(defun printable-rep-char (c)
(if (and (>= c #\ ) (< c 127))
c
#\_
)
)
(defun itole (n &aux (res (copy "")) (mul 1))
(dotimes 4
(put res -1 (logand 0xff n))
(setq n (logshift n -8))
)
res
)
(defun itobe (n &aux (res (copy "")) (shift -24))
(dotimes 4
(put res -1 (logand 0xff (logshift n shift)))
(incf shift 8)
)
res
)
;; decodes a number decimal or 0x_hex
(defun dec-or-hex (s)
(if (match "^0x" s)
(xtoi s)
(Int s)
))
(defun decode-hex-error (s)
(print-format "Error: string %0 is not hexadecimal!\n" s)
(exit 1)
)
(setq decode-hex-values (Hashtable '(
#\0 0 #\1 1 #\2 2 #\3 3 #\4 4 #\5 5 #\6 6 #\7 7 #\8 8 #\9 9
#\a 10 #\b 11 #\c 12 #\d 13 #\e 14 #\f 15
#\A 10 #\B 11 #\C 12 #\D 13 #\E 14 #\F 15
)))
(defun decode-hex (s &aux (i 0) (res (copy "")))
(while (< i (length s))
(if (= i (- (length s) 1))
(decode-hex-error s)
)
(put res -1
(+ (* 16 (get decode-hex-values (i s) '(decode-hex-error s)))
(get decode-hex-values ((+ i 1) s) '(decode-hex-error s))
))
(incf i 2)
)
res
)
(setq encode-hex-values '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9
#\a #\b #\c #\d #\e #\f))
(defun encode-hex (s &aux (res (copy "")))
(dolist (i s)
(put res -1 ((/ i 16) encode-hex-values))
(put res -1 ((mod i 16) encode-hex-values))
)
res
)
(main)
;;; EMACS MODES
;;; Local Variables: ***
;;; mode:lisp ***
;;; End: ***
syntax highlighted by Code2HTML, v. 0.9.1