Board logo

标题: [其他] BAT+JScript+VBScript混排,实现批处理读取二进制文件结构体 [打印本页]

作者: amwfjhh    时间: 2015-5-19 11:24     标题: BAT+JScript+VBScript混排,实现批处理读取二进制文件结构体

本帖最后由 amwfjhh 于 2015-5-19 11:51 编辑

接到一个任务,需要将一些备份资源中的数据导出,其中有许多图片资源与文本描述,而这些图片的编号及其对应的说明则在一个后缀为.db的数据库中,这个数据库有一个专门的应用程序来打开,本想写个脚本来批量完成这事,可是问题来了:如何批量读取数据库里的东西呢?这数据库既不是mysql,也不是sqlserver,更不是PostgreSQL或oracle之类的已知数据库,想调用数据库引擎来读取内容也没法,于是尝试二进制方式读取,用ultraedit打开.db文件,查看它的二进制代码规律,万幸能在其中找到一点蛛丝马迹:
这是它提供的数据库读取程序读出来的内容:
[attach]8713[/attach]
这是ultraedit打开的二进制内容:
[attach]8714[/attach]
可以看到其中规律还是很明显的,数据主体部分应该是用到了一个数据结构体,从其数据库打开程序所看到的关键信息都能在其中找到对应的位置,那我们只需要按照规律读取出来就行了。
  1. 0000080ah: 80 01 D1 AD BB B7 CF B5 CD B3 D5 FD B3 A3 58 CF ; .循环系统正常X?
  2. 0000081ah: DF B1 ED CF D6 00 00 00 00 00 00 00 00 00 00 00 ; 弑硐?..........
  3. 0000082ah: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ; ................
  4. 0000083ah: 00 00 00 00 80 03                               ; .....
复制代码
这是其中一个结构体的信息,可以看到之后的数字为该条目的编号或者其所拥有的章节数,第一个后的文本部分即为数据表格内容,读取流数据,我们可以用adodb.stream来进行,基于在一个脚本内写完所有内容的要求,我们采用BJ混排的方式,将jscript和bat脚本写到同一个文件里,利用cscript的-e参数可将批处理作为jscript脚本来调用,达到自身既是批处理,也是jscript脚本的目的。但这里面有一个麻烦就是,文本信息是用的中文(或说文本)来存储的,但是编号和拥有章节数这两个数据却不是以文本方式存储的,可以直接将其值以字节的方式存储在二进制文件里面,这对于c或c++之类的语言来说完全没问题,但是对于adodb.stream这个组件来说,它要么吐出字符串,要么吐出字节流,而字节流,在jscript中又无法直接处理,且其获取字符串编码的方式charCodeAt(n)则是直接获取的unicode代码,显示对于直接的字节流支持不是很好,那我们要怎么把这个数据取出来呢,VBScript!虽然有了BJ混排后我极力想将其抛弃,但不得不说,在这里,对于字节流的支持,其leftb,ascb等方法却是救命稻草,能完美解决我的问题,于是方案就出来了,bat调用jscript,创建adodb.stream组件,对文件进行读取,在读取编号时,将文件以流方式读取,再创建一个临时的vbs脚本,用于调用ascb方法,对流编码进行读取,代码如下:
  1. @if (0)==(0) echo off
  2. setlocal enabledelayedexpansion
  3. REM set /p "strFile=Input binary file:"
  4. REM set /p "strOffset=Input offset:"
  5. REM set /p "strLen=Input length of data:"
  6.    
  7. set "strFile=a.db"
  8. set "strOffset=2059"
  9. set "strLen=58"
  10.    
  11. for /l %%i in (0 1 6) do (
  12.     set /a offsetNum=!strOffset!+%%i*!strLen!
  13.     set /a offsetTxt=!offsetNum!+3
  14.     set /a offsetNum1=!offsetNum!+52
  15.     echo,!offsetNum!    !offsetTxt! !offsetNum1!
  16.     cscript -nologo -e:jscript %~s0 GetBinNumber "!strFile!" !offsetNum!
  17.     for /f "delims=" %%b in ('cscript -nologo -e:vbscript "$"') do (set "strIndex=%%b") & del /f "$"
  18.     for /f "delims=" %%a in ('cscript -nologo -e:jscript %~s0 GetBinText "!strFile!" !offsetTxt! !strLen!') do (set "strLine=%%a")
  19.     cscript -nologo -e:jscript %~s0 GetBinNumber "!strFile!" !offsetNum1!
  20.     for /f "delims=" %%b in ('cscript -nologo -e:vbscript "$"') do (set "strIndex1=%%b") & del /f "$"
  21.     echo,第 !strIndex! 章, !strLine!, 拥有节数 !strIndex1!
  22.     REM pause>nul
  23. )
  24. pause
  25. GOTO :EOF
  26. @end
  27. var fun = WScript.arguments(0);
  28. switch (fun) {
  29. case "GetBinText":
  30.     CheckArgs(4);
  31.     var fil = WScript.arguments(1);
  32.     var offset = parseInt(WScript.arguments(2));
  33.     var len = parseInt(WScript.arguments(3));
  34.     GetBinText(fil, offset, len);
  35.     break;
  36. case "GetBinNumber":
  37.     CheckArgs(3);
  38.     var fil = WScript.arguments(1);
  39.     var offset = parseInt(WScript.arguments(2));
  40.     GetBinNumber(fil, offset);
  41.     break;
  42.    
  43.     default:break;
  44. }
  45. function CheckArgs(num){
  46.     if (WScript.arguments.length < num){
  47.         WScript.echo("参数不齐");
  48.         WScript.quit();
  49.     }
  50. }
  51. function GetBinText(fil, offset, len){
  52.     var stream;
  53.     var strContent;
  54.    
  55.     stream = new ActiveXObject("adodb.stream");
  56.     stream.open();
  57.     stream.loadFromFile(fil);
  58.     stream.type = 2;
  59.     stream.charset = "gb2312";
  60.     stream.position = offset;
  61.     strContent = stream.readText();
  62.     stream.close();
  63.    
  64.     WScript.echo(strContent);
  65. }
  66. function GetBinNumber(fil, offset){
  67.     var stream, stream1;
  68.     stream = new ActiveXObject("adodb.stream");
  69.     stream.open();
  70.     stream.type = 1;
  71.     stream.loadFromFile(fil);
  72.     stream.position = offset;
  73.    
  74.     //输出序号
  75.     var stream1 = new ActiveXObject("adodb.stream");
  76.     stream1.mode = 3;
  77.     stream1.open();
  78.     stream1.type = 2;
  79.     stream1.charset = "gb2312"
  80.     stream1.writeText("WScript.echo ascb(leftb(\"");
  81.     stream1.position = 0;
  82.     stream1.type = 1;
  83.     stream1.position = 25;
  84.     stream1.write(stream.read(1));
  85.     stream1.position = 0;
  86.     stream1.type = 2;
  87.     stream1.position = 26;
  88.     stream1.writeText("\", 1))");
  89.     stream1.saveToFile("$", 2);
  90.     stream1.close();
  91.     stream.close();
  92. }
复制代码
以下为代码运行效果,读出内容与数据无差异
[attach]8715[/attach]

[attach]8716[/attach]
作者: CrLf    时间: 2015-5-19 17:40

本帖最后由 CrLf 于 2015-5-19 17:48 编辑

可以不用临时文件,该方案可以模拟 WSH:http://www.bathome.net/thread-34109-1-1.html

其实这种明显是结构体的数据库,用 c 语言可能更合适:
  1. //tcc 编译后,以 程序名.exe 数据库名.db 查看数据库
  2. #include <stdio.h>
  3. typedef   unsigned char   BYTE;
  4. struct data
  5. {
  6. BYTE head1[3];
  7. BYTE index;
  8. BYTE head1[2];
  9. char text[50];
  10. BYTE head1[1];
  11. BYTE section;
  12. };
  13. main(int argc, char **argv){
  14. int i;
  15. FILE *fp;
  16. struct data db;
  17. for(i=1;i<argc;i++){
  18. fp = fopen(argv[i],"rb");
  19. fseek(fp,22,SEEK_SET);
  20. while(!feof(fp)){
  21. if(!fread(&db,sizeof(db),1,fp))break;
  22. printf("第 %d 章,%s,拥有节数 %d\n",db.index,db.text,db.section);
  23. }
  24. fclose(fp);
  25. }
  26. }
复制代码
之前碰到过另一种文件格式,只有数据头和数据尾标记的,而且编码混杂,c 做起来不方便,最后用 ado 配合 vbs 正则了...
作者: CrLf    时间: 2015-5-19 18:00

如果只需要 text 值的话,也可以暴力点,利用 more 将 \0 转为 \r\n,再用 sed 过滤
  1. more bin.db | sed -r "s/^.{,3}//g;/^$/d" | more
复制代码

作者: amwfjhh    时间: 2015-5-20 01:27

回复 3# CrLf


    c还要去编译一次,哪有批处理这样写出来就直接运行爽,而且这是任务其中一环,文本,数据都要得到,然后还需作其它相应处理,最终将零散的资源再以需要的方式重组。
more处理二进制文件还有你说的附加效果?试试去…
作者: amwfjhh    时间: 2015-5-20 09:06

回复 2# CrLf


    拜读了下基于mshta的三种语言混排方案,新技能GET+1。




欢迎光临 批处理之家 (http://bathome.net./) Powered by Discuz! 7.2