Инструмент
Так как я не смог найти инструмент, отвечающий моим требованиям, я написал его сам на golang. Я называю это bima (для двоичного соответствия). Это не красиво, но это сделало работу:
package main
import (
"bytes"
"encoding/hex"
"fmt"
"gopkg.in/alecthomas/kingpin.v1"
"io"
"log"
"math"
"os"
)
var (
debug = kingpin.Flag("debug", "Enable debug mode.").Short('d').Bool()
bsize = kingpin.Flag("blocksize", "Blocksize").Short('b').Default("126976").Int()
debugDetail = kingpin.Flag("debugdetail", "Debug Detail").Short('v').Default("10").Int()
matchCommand = kingpin.Command("match", "Match a value")
matchCommandValue = matchCommand.Arg("value", "The value (Hex Encoded e.g.: 616263 == abc)").Required().String()
matchCommandFile = matchCommand.Arg("file", "The file").Required().String()
)
func main() {
kingpin.Version("0.1")
mode := kingpin.Parse()
if *bsize <= 0 {
log.Fatal("The blocksize has to be larger than 0")
}
if *debugDetail <= 0 {
log.Fatal("The Debug Detail has to be larger than 0")
}
if mode == "match" {
searchBytes, err := hex.DecodeString(*matchCommandValue)
if err != nil {
log.Fatal(err)
}
scanFile(searchBytes, *matchCommandFile)
}
}
func scanFile(search []byte, path string) {
searchLength := len(search)
blocksize := *bsize
f, err := os.Open(path)
if err != nil {
log.Fatal(err)
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
log.Fatal(err)
}
filesize := fi.Size()
expectedRounds := int(math.Ceil(float64(filesize-int64(searchLength))/float64(blocksize)) + 1)
if expectedRounds <= 0 {
expectedRounds = 1
}
data := make([]byte, 0, blocksize+searchLength-1)
data2 := make([]byte, 0, blocksize+searchLength-1)
offset := make([]byte, searchLength-1)
//reading the len of the slice or less (but not the cap)
readCount, err := f.Read(offset)
if err == io.EOF {
fmt.Println("The files seems to be empty")
return
} else if err != nil {
log.Fatal(err)
}
data = append(data, offset...)
buffer := make([]byte, blocksize)
var blockpos int
var idx int
blockpos = 0
lastLevel := -1
roundLevel := 0
idxOffset := 0
for round := 0; ; round++ {
if *debug {
roundLevel = ((round * 100) / expectedRounds)
if (roundLevel%*debugDetail == 0) && (roundLevel > lastLevel) {
lastLevel = roundLevel
fmt.Fprintln(os.Stderr, "Starting round", round+1, "of", expectedRounds, "--", ((round * 100) / expectedRounds))
}
}
//At EOF, the count will be zero and err will be io.EOF
readCount, err = f.Read(buffer)
if err != nil {
if err == io.EOF {
if *debug {
fmt.Fprintln(os.Stderr, "Done - Found EOF")
}
break
}
fmt.Println(err)
return
}
data = append(data, buffer[:readCount]...)
data2 = data
idxOffset = 0
for {
idx = bytes.Index(data2, search)
if idx >= 0 {
fmt.Println(blockpos + idxOffset + idx)
if idx+searchLength < len(data2) {
data2 = data2[idx+searchLength:]
idxOffset += idx
} else {
break
}
} else {
break
}
}
data = data[readCount:]
blockpos += readCount
}
}
История
Для полноты вот что я сделал, чтобы решить мою проблему:
Сначала я использовал hexedit, чтобы узнать, что все файлы db имеют одинаковый заголовок. В шестнадцатеричном коде это выглядит так: 0102030463584d0b0000004b62574c41
Поэтому я использовал свой инструмент, чтобы найти все вхождения в моем файле sda.image:
./bima match 0102030463584d0b0000004b62574c41 ./sda.image >DBfiles.txt
Для 64 ГБ это заняло около 8 минут, и я думаю, что ограничивающим фактором был жесткий диск.
В результате получилось около 1200 вхождений, которые я извлек из образа с помощью dd. Поскольку я не знал точного размера файлов, я просто извлек куски по 20 000 байт:
for f in $(cat DBfiles.txt); do
dd if=sda.image of=$f.dunno bs=1 ibs=1 skip=$f count=20000
done
Теперь у меня было около 1200 файлов и нужно было найти нужные. На первом этапе я ищу файлы passwd (passwd.DCD и passwd.DCL). позже я сделал то же самое для файлов реестра. Поскольку заголовок файлов содержит имя, я просто искал пароль:
for f in *.dunno; do
if [ "$(cat $f | head -c 200 | grep "passwd" | wc -l)" == "1" ]; then
echo "$f" | sed 's/\.$//g' >> passwd_files.list
fi
done
Поскольку куски были больше, чем файлы, мне пришлось искать конец каждого файла вручную. Я внес исправления с помощью Curses Hexedit.
Во время этого процесса я мог видеть, что заголовок каждого файла содержит либо dcl_logk
, либо dcd_logk
. Так что я знал, какие из файлов были файлами DCL, а какие — файлами DCD.
В конце концов у меня был каждый файл до десяти раз, и мне нужно было решить, какую версию я хочу использовать. В общем я взял самый большой файл. После помещения файлов в каталог DB нового сервера ejabberd и его перезапуска все учетные записи снова восстанавливаются. :-)
person
JepZ
schedule
19.07.2015
grep
вместе с его элементом управления строкой контекста и шаблоном поиска, который ранее был экранирован регулярным выражением? - person arkascha   schedule 18.07.2015grep -U -b -A 20 "$(printf $'\x64\x6f\x6f')" /dev/loop0
, но он только сказал мне, что это совпадение, но не где, и не вывел никакого содержимого. - person JepZ   schedule 18.07.2015