PDA

Показать полную графическую версию : [решено] Поиск hex значения в файле, замена значения на заданное, сохранение файла


Страниц : [1] 2

9119
02-08-2021, 15:36
Добрый.

Сейчас делаю так:
Есть файл, скажем B2DD.tmp. около 20МБ.
Открываю его в hex редакторе, нахожу нужное а именно (35 32 88 65 59 18 01).
Важно! сама искомая комбинация выглядит так: 08 3А 35 32 88 65 59 18 01, но почему то по полному запросу не находит.
Меняю полную найденную строку на 08 8A XX XX XX XX XX XX XX. Сохраняю файл.


Можно ли это проделать в powershell? без использования hex-редактора.
Или с помощью любой другой консольной утилиты. (если можно с примерами)
Спасибо.

Iska
02-08-2021, 15:56
Можно ли это проделать в powershell? »
Можно.

Или с помощью любой другой консольной утилиты. »
Можно.

Есть файл, скажем B2DD.tmp. около 20МБ. »
Важно! сама искомая комбинация выглядит так: 08 3А 35 32 88 65 59 18 01, но почему то по полному запросу не находит. »
Упакуйте файл в архив и выложите на вменяемый обменник или облако.

9119
02-08-2021, 16:23
Упакуйте файл в архив »
https://drive.google.com/file/d/12yOb4RxWfrSvMrL_qrp2ta8F7ymKAjSN/view?usp=sharing

alpap
03-08-2021, 02:44
у меня вышло пока так:
PowerShell

$file = 'F0D7.tmp'
$tmp = 'tmp.txt'
$slineHex = '35 32 88 65 59 18'

certutil -f -encodeHex $file $tmp|Out-Null

$cont = Switch -Regex -File $tmp {
" 08 3a $slineHex" {$_ -Replace ' 08 3a', ' 08 8A'}
Default {$_}
}
$cont|Set-Content $tmp

certutil -f -decodeHex $tmp $file|Out-Null

но выполнение на приведенном файле ой как не быстро.

9119
03-08-2021, 10:15
у меня вышло пока так »
не до конца разобрался в коде...
вижу часть замены 08 3a на 08 8A, а где остальная замена 35 32 88 65 59 18 на нужное мне XX XX XX XX XX XX ?
как-то так?:

$my_data = 'XX XX XX XX XX XX
XX XX XX XX XX XX
XX XX XX XX XX XX'
$stroke = '0'


$cont = Switch -Regex -File $tmp {
" 08 3a $slineHex" {$_ -Replace ' 08 3a', ' 08 8A' -Replace ' $slineHex', ' ($my_data)[$stroke]'}
Default {$_}
}

p.s. как я понял файл декодируется в txt (метров 100 наверное будет).. потом правится и сохраняется.
Есть ли более быстрая возможность замены?(как это делают hex редакторы.. скорость в данном случае для меня имеет значение). Возможно сторонние консольные утилиты .


p.s.s как привел выше... комбинаций для замены будет несколько (скажем 20). и после выполнения, скрипт должен становится на паузу, а переменная $stroke должна увеличиваться после каждого выполнения на +1 (тем самым получая следующую строку $my_data в следующем цикле).

YuS_2
03-08-2021, 10:29
Есть ли более быстрая возможность замены? »
$file = 'F0D7.tmp'
$fout = 'F0D7_1.tmp'
[byte[]]$fin = 0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01
[byte]$rep = 0x8A

$bytes = [io.file]::readallbytes($file)
$i = -1
$a = -join $fin
do {
$i = [byte[]]::indexof($bytes,$fin[0],($i+1))
[byte[]]$tmp = @()
for ($k = $i; $k -lt ($i+$fin.count);$k++){
$tmp += $bytes[$k]
}
$b = -join $tmp
} while ($a -ne $b)
$bytes[($i+1)] = $rep
[io.file]::writeallbytes($fout, $bytes)

но почему то по полному запросу не находит. »
Патамушта "А" - это совсем не "A" :)

9119
03-08-2021, 10:44
$file = 'F0D7.tmp'
$fout = 'F0D7_1.tmp'
$list = '0x08 0x8a 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0x21
0x08 0x8a 0xXX 0xXX 0xXX 0xXX 0xXX 0xXX 0x22'
$number = '0'

[byte[]]$fin = 0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01
[byte]$rep = ($list)[$number]

$bytes = [io.file]::readallbytes($file)
$i = -1
$a = -join $fin
do {
$i = [byte[]]::indexof($bytes,$fin[0],($i+1))
[byte[]]$tmp = @()
for ($k = $i; $k -lt ($i+$fin.count);$k++){
$tmp += $bytes[$k]
}
$b = -join $tmp
} while ($a -ne $b)
$bytes[($i+1)] = $rep
[io.file]::writeallbytes($fout, $bytes)

Помогите это положить в цикл, чтобы после каждого выполнения становился на паузу и увеличивал $number на +1.
Чтобы выполнялся Пока ($list)[$number] не равно null; когда переменная пустая (т.е строки закончились) - выдать уведомление что строк нет и стать на паузу.
Спасибо.

YuS_2
03-08-2021, 11:03
чтобы после каждого выполнения становился на паузу и увеличивал $number на +1. »
Это лишнее... я так понимаю, что просто надо получить, вот это:
$file = 'F0D7.tmp'
$fout = 'F0D7_1.tmp'
[byte[]]$fin = 0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01
[byte[]]$rep = 0x8A,0x34,0x31,0x87,0x64,0x58,0x17,0x00

$bytes = [io.file]::readallbytes($file)
$i = -1
$a = -join $fin
do {
$i = [byte[]]::indexof($bytes,$fin[0],($i+1))
[byte[]]$tmp = @()
for ($k = $i; $k -lt ($i+$fin.count);$k++){
$tmp += $bytes[$k]
}
$b = -join $tmp
} while ($a -ne $b)
for ($j,$n=0,$($i+1);$j -lt $rep.count;$n++,$j++){
$bytes[$n] = $rep[$j]
}
[io.file]::writeallbytes($fout, $bytes)

Добавлено:
Либо, чтобы была возможность для простой замены нескольких строк по шаблону, то уравнять шаблон поиска и замены по количеству символов, и подавать эти шаблоны извне:
param(
$file = '.\F0D7.tmp',
$fout = '.\F0D7_1.tmp',
[byte[]]$fin = (0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01),
[byte[]]$rep = (0x08,0x8A,0x34,0x31,0x87,0x64,0x58,0x17,0x00)
)

$bytes = [io.file]::readallbytes($file)
$i = -1
$a = -join $fin
do {
$i = [byte[]]::indexof($bytes,$fin[0],($i+1))
[byte[]]$tmp = @()
for ($k = $i; $k -lt ($i+$fin.count);$k++){
$tmp += $bytes[$k]
}
$b = -join $tmp
} while ($a -ne $b)
for ($j,$n=0,$i;$j -lt $rep.count;$n++,$j++){
$bytes[$n] = $rep[$j]
}
[io.file]::writeallbytes($fout, $bytes)

и запускать можно так:
.\script.ps1 -fin (0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01) -rep (0x08,0x5A,0x54,0x51,0x57,0x54,0x78,0x77,0x10)

9119
03-08-2021, 11:23
YuS_2,
не могу получить желаемое.
Я этого не уточнял в шапке. Что значение не одно.

Делаю так:
$list = Get-content C:\Users\Administrator\Desktop\list.txt
*в файле 10 строк. кроме как ($list)["номер строки"] - не знаю как еще получить нужную строку.
Но если подставляю полученное в [byte[]]$rep - получаю что не может перекодировать полученное в system byte
* строки в txt подогнаны под формат 0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX


как сделать, чтобы в [byte[]]$rep в вашем коде попадала сначала первая строка, потом вторую и т.д?

DJ Mogarych
03-08-2021, 11:23
Чуть более лаконичный вариант коллеги alpap (http://forum.oszone.net/post-2963506-4.html)

$file = 'D:\temp\F0D7.tmp'
$tmp = 'D:\temp\tmp.tmp'
$pattern = '08 3a 35 32 88 65 59 18'
$replacement = '08 8a 35 32 88 65 59 18'

certutil -f -encodeHex "$file" "$tmp" > $null
(gc "$tmp") -replace "$pattern","$replacement" |set-content "$tmp"
certutil -f -decodeHex "$tmp" "$file" > $null


Что значение не одно. »
Вообще, одно:

f17050 00 00 35 35 30 6e ff 01 08 3a 35 32 88 65 59 18 ..550n...:52.eY.

9119
03-08-2021, 11:31
DJ Mogarych, я наверное криво объясняю...простите.


Значение которое находится в файле - всегда одно (08 3a 35 32 88 65 59 18)
ЗначениЙ которыми искомое заменяется - НЕ ОДНО! Их список. Скажем 10 шт.
Файлов TMP тоже НЕ ОДИН.
Т.е сначала находим в первом файле 1.tmp значение 08 3a 35 32 88 65 59 18 и меняем на 08 8a XX XX XX XX XX XX 01
Потом берется следующий 2.TMP файл. В нем опять находится все тоже 08 3a 35 32 88 65 59 18, но меняется уже на следлующее значение из списка
08 8a XX XX XX XX XX XX 02.... и так далее пока не закончатся строки в списке.

YuS_2
03-08-2021, 11:32
Что значение не одно. »
Пост выше дополнил, это должно помочь

DJ Mogarych
03-08-2021, 11:40
9119, надо всегда описывать задачу целиком, это избавит от двойной работы.
Как определять соответствие файлов списку, т. е., как понять, в каком файле какое значение в списке применять?

YuS_2
03-08-2021, 11:43
* строки в txt подогнаны под формат 0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX
как сделать, чтобы в [byte[]]$rep в вашем коде попадала сначала первая строка, потом вторую и т.д? »
Чтобы считывать из текстового файла строковые значения и преобразовывать их в массив байтов, можно сделать, что-то типа:
gc test.txt|%{
[byte[]]$a = $_ -split ','
# здесь организовать вызов функции с передачей параметра $a
}

9119
03-08-2021, 11:47
Пост выше дополнил, это должно помочь »
да действительно, так мне проще. Один нюанс.
Стартую ваш скрипт так:
$list = Get-content C:\Users\Administrator\Desktop\list.txt
$number = 0
$my_data = ($list)[$number]
.\find_replace.ps1 -fin (0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01) -rep ($my_data)
$number = $number + 1
$my_data
pause

при запуске получаю следующее:
PS C:\Users\Administrator\Desktop\script_PS> ./start.ps1
find_replace.ps1: C:\Users\Administrator\Desktop\script_PS> ./start.ps1:4
Line |
4 | : 1 -fin (0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01) -rep ($my_data)
| ~~~~~~~~~~
| Cannot process argument transformation on parameter 'rep'. Cannot
| convert value "0x08,0x8a,0x26,0x41,0x07,0x04,0x38,0x40,0x21" to type
| "System.Byte[]". Error: "Cannot convert value
| "0x08,0x8a,0x26,0x41,0x07,0x04,0x38,0x40,0x21" to type "System.Byte".
| Error: "Additional non-parsable characters are at the end of the
| string.""
0x08,0x8a,0x26,0x41,0x07,0x04,0x38,0x40,0x21
Press Enter to continue...:

list.txt:
0x08,0x8a,0x26,0x41,0x07,0x04,0x38,0x40,0x21
0x08,0x8a,0x26,0x41,0x07,0x04,0x38,0x40,0x97
0x08,0x8a,0x26,0x41,0x07,0x04,0x38,0x30,0x12
0x08,0x8a,0x26,0x41,0x07,0x04,0x38,0x40,0x83
0x08,0x8a,0x26,0x41,0x07,0x04,0x38,0x40,0x40
0x08,0x8a,0x26,0x41,0x07,0x04,0x38,0x70,0x10


DJ Mogarych,
пример списка привел, файлы всегда одинаковые по размеру, имеют просто разные имена, но все всегда содержат одну и туже искомую строку:
0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01, которую нужно находить и менять на значение из списка.
Пример файла выкладывал на 1ой странице

YuS_2
03-08-2021, 12:04
при запуске получаю следующее: »
Там должна быть не строка, а массив байтов...
В общем, сделайте лучше так:
Пример для трех файлов и трех шаблонов замены:
function Replace-Bytes {
param(
$file = '.\F0D7.tmp',
$fout = '.\F0D7_1.tmp',
[byte[]]$fin = (0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01),
[byte[]]$rep = (0x08,0x8A,0x34,0x31,0x87,0x64,0x58,0x17,0x00)
)

$bytes = [io.file]::readallbytes($file)
$i = -1
$a = -join $fin
do {
$i = [byte[]]::indexof($bytes,$fin[0],($i+1))
[byte[]]$tmp = @()
for ($k = $i; $k -lt ($i+$fin.count);$k++){
$tmp += $bytes[$k]
}
$b = -join $tmp
} while ($a -ne $b)
for ($j,$n=0,$i;$j -lt $rep.count;$n++,$j++){
$bytes[$n] = $rep[$j]
}
[io.file]::writeallbytes($fout, $bytes)
}

[byte[]]$findb = 0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01
# количество шаблонов должно соответствовать количеству файлов
$files = '.\F0D1.tmp','.\F0D2.tmp','.\F0D3.tmp' # 3 файла
$frep = gc test.txt # здесь в файле содержится 3 строки шаблонов замены

0..2|%{
replace-bytes -file $files[$_] -fout "fout$($_+1).tmp" -fin $findb -rep $([byte[]]($frep[$_] -split ','))
}

YuS_2
03-08-2021, 12:27
Как определять соответствие файлов списку, т. е., как понять, в каком файле какое значение в списке применять? »
9119, Кстати, да... это важное замечание.
Чтобы не путаться в шаблонах и файлах, проще создавать файл в формате CSV, с содержимым типа:
File,Pattern
F0D1.tmp,"0x08,0x5A,0x54,0x51,0x57,0x54,0x78,0x77,0x10"
F0D2.tmp,"0x08,0x8A,0x34,0x31,0x87,0x64,0x58,0x17,0x00"
F0D3.tmp,"0x08,0x9A,0x24,0x21,0x27,0x24,0x28,0x27,0x20"

Функция не меняется, а вот запуск действий, будет выглядеть так:
...
[byte[]]$findb = 0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01
$list = import-csv test.csv

for ($i=0;$i -lt $list.count;$i++){
replace-bytes -file $list.file[$i] -fout "fout$($i+1).tmp" -fin $findb -rep $([byte[]]($list.pattern[$i] -split ','))
}

- в этом случае, не придется вручную указывать количество файлов/шаблонов, скрипт сам посчитает количество и сделает замену, по списку из файла test.csv

9119
03-08-2021, 12:36
YuS_2, с задачей немного не идет это просто.

Суть в том, что изначально есть только список значений для замены в файлах, а списка файлов нет. Файл tmp на момент выполнения скрипта - только один.

Запускается exe, который кладет файл.tmp во временную папку (каждый запуск exe имя файла меняется и это имя каждый раз рандомное я его не знаю!)
Далее я получаю имя этого файла (получаю все файлы tmp и сортирую по дате изменения, беру первый в списке) и кладу в переменную.
нахожу в нем искомое и меняю на строку из txt файла.
Далее запускаю опять этот же exe. Опять появляется новый tmp файл. опять получаю его в переменную. и делаю замены, но беру уже следующую строку из txt файла.... и так далее... пока не закончатся строки в txt файле.

DJ Mogarych
03-08-2021, 12:51
Тогда надо запускать не .exe, а скрипт, который будет запускать .exe, а потом обрабатывать его выхлоп, и писать в отдельный файл номер последней использованной строки.

Что будет, когда строки в файле закончатся?

9119
03-08-2021, 13:02
DJ Mogarych, когда строки закончатся - должно об этом сообщить и стать на паузу.

Для себя все таки максимально удобным считаю вариант запуска основного скрипта с параметрами от ув. YuS_2. http://forum.oszone.net/post-2963519-8.html


Стартовать пытаюсь его скрипт так:
$file = Get-ChildItem -Path "C:\Windows\Temp" -recurse -include *.tmp | Sort-Object LastAccessTime -Descending | Sort-Object -Descending -Property length | Select-Object -First 1 |%{$_.FullName}
$list = Get-content C:\Users\Administrator\Desktop\list.txt
$number = 0
$my_data = ($list)[$number]
pause
.\find_replace.ps1 -file ($file) -fin (0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01) -rep ($my_data)
$number = $number + 1
$my_data
$file
pause

Пожалуйста, помогите правильно передать $my_data в -rep.
чтобы строка передалась как набор байтов, и дальнейший скрипт её "схавал")
Пока получаю это:
find_replace.ps1: C:\Users\Administrator\Desktop\script_PS\start.ps1:6
Line |
6 | : ) -fin (0x08,0x3A,0x35,0x32,0x88,0x65,0x59,0x18,0x01) -rep ($my_data)
| ~~~~~~~~~~
| Cannot process argument transformation on parameter 'rep'. Cannot
| convert value "0x08,0x8a,0x26,0x41,0x07,0x04,0x38,0x40,0x21" to type
| "System.Byte[]". Error: "Cannot convert value
| "0x08,0x8a,0x26,0x41,0x07,0x04,0x38,0x40,0x21" to type "System.Byte".
| Error: "Additional non-parsable characters are at the end of the
| string.""

* может нужно к какому-то виду привести сам список..




© OSzone.net 2001-2012