Board logo

标题: [格式转换] ascode 的 encode/decode 算法 [打印本页]

作者: CrLf    时间: 2012-12-26 01:34     标题: ascode 的 encode/decode 算法

本帖最后由 CrLf 于 2013-1-20 05:29 编辑

在旧帖里有人提到 Herbert Kleebauer 的 decode 算法是在代码段前缀一个解码部分,以便对真正的代码进行解释。
虽然没有费劲跟踪这位前辈的作品,但是在此框架下可以很容易地整出一堆大同小异的 encode/decode 算法,我的算法是借鉴 ansi 的编码形式,用两个字节的低位来保存一个完整的字节。而为了尽量避免生成的数据太大,对于值在 0x21~0x2F 和 0x50~0x7E 范围内的字节保留原样(扩展为两个字节也可以解码表示),取 0x2f 和 0x50 为上下限的目的是可以完全用大写字母和数字来表示任意字节,这样可以较容易地简单解码以及目测实际的机器码。

解码部分也用 ascode 写成,本来可以稍微短一点(稍微...稍微短那么一点点...嗯嗯),但为了便于计算偏移量,所以长度凑整:
  1. @echo off&chcp 437|GRAFTABL>nul 936
  2. @(echo RQPUWP]UZV_BBBB5``PY(E1(E6^)E8^)E=(EA^)EI=
  3. echo SX3CP^<P}pE^< ~r%%ooQRY2AY0@G uO0EOB?_]XYZ
  4. echo K8OO4LLM21)>err.com
  5. ::生成一个 com
  6. echo %errorlevel%
  7. ::显示原始错误码
  8. err.com
  9. ::调用生成的 com
  10. echo %errorlevel%
  11. ::显示调用后的错误码,若变为 255 则说明调用成功
  12. pause
复制代码
------------------------------------------------------------------------------------------------
2012/12/27 更新内容:
   1.修正遗漏 0x50 字符的 bug;
   2.扩大不进行转换的字符范围,稍微降低了生成的 com 的体积膨胀率(更新前生成的 com 体积理论值为 (原体积*0.82+80) 字节,更新后为 (原体积*0.76+80) 字节);
   3.忽略控制字符,生成的数据可在 0x30 ~ 0x4f 范围之外的字符前后添加控制字符,以便断行或加 tab
   4.解码部分的长度从 0x50 扩到 0x60
------------------------------------------------------------------------------------------------
2012/12/28 更新内容:
   1.考虑到转换后的数据一般较长,故取消头部 :: 字符,废弃对标签调用法的支持,转而在解码部分中间插入断行,以使代码更清楚整洁
   2.为了压缩代码,取消昨日的第二项更新内容,甚至建议对任何字节都使用双字节来表示,2 楼更新的 encode 代码就是如此,这样便于断行和消除特殊字符;
   3.避开大部分的特殊字符,现在仅有 % < ) 这三个字符需转义
   4.由于对代码进行精简,解码部分的长度从 0x60 恢复到 0x50
------------------------------------------------------------------------------------------------
2012/12/30 更新内容:
   修正了一个原先不知道原因的可能导致解码错误的逻辑隐患(感谢 qzwqzw 前辈指出问题所在)
------------------------------------------------------------------------------------------------
2013/01/20 更新内容:
   修正了一个原先被忽略的逻辑小错误
------------------------------------------------------------------------------------------------
PS:多次简化及修正后,算法趋于完善,也似乎已无可简化,也许不会再更新了。留下两个遗憾,一个是没能找到简单实现用 ABCDEF 字符来映射对应 hex 的算法,另一个是没有实现在不需要预知代码长度的情况下将解码后的代码段生成到 IP:0100 处
作者: CrLf    时间: 2012-12-26 01:44

本帖最后由 CrLf 于 2013-1-21 15:08 编辑

这里给出 encode 代码,拖动写好的 com 到 encode.bat 上进行操作:
  1. @echo off&setlocal enabledelayedexpansion
  2. set compress=true
  3. ::设置是否压缩
  4. if "%~1"=="" echo 无文件&pause&exit/b
  5. set "s1=%~s1"
  6. set list=0123456789JKLMNO
  7. for %%a in (0 1 2 3 4 5 6 7 8 9 A B C D E F) do (
  8. set hb=!list:~0x%%a,1!
  9. for %%b in (0 1 2 3 4 5 6 7 8 9 A B C D E F) do (
  10. set lb=!list:~0x%%b,1!
  11. set "hex%%a%%b=!hb!!lb!"
  12. if %compress%==true (
  13. if 0x%%a%%b geq 0x50 if 0x%%a%%b leq 0x7e set "hex%%a%%b="
  14. for %%c in (5E 7C) do if %%a%%b==%%c set "hex%%a%%b=!hb!!lb!"
  15. )
  16. )
  17. )
  18. ::获取 hex 表
  19. for %%a in ("%~1") do cmd/c exit/b %%~za
  20. if !errorlevel! gtr 0x3030 (
  21. echo 文件太长
  22. pause&exit/b
  23. )
  24. ::判断文件长度
  25. set cx=!=exitcode:~-4!
  26. echo d100l!cx!^&echo q|debug !s1! >"%~n1.bat"
  27. ::获取文件 hex
  28. set m=0
  29. (for /f %%a in ('find /n /v ""^<"%~n1.bat"') do (
  30. set "var="&set /p "var="
  31. if "!var::=!" neq "!var!" (
  32. set "var=!var:-= !"
  33. set "str=!var:~61!"
  34. set "var=!var:~11,48!"
  35. for %%b in (!var!) do (
  36. if defined hex%%b (set "com=!com!!hex%%b!") else set "com=!com!!str:~,1!"
  37. set "str=!str:~1!"
  38. if "!com:~39!" neq "" (
  39. set /a m+=1
  40. set "com!m!=!com!"
  41. set "com="
  42. )
  43. )
  44. )
  45. ))<%~n1.bat"
  46. if defined com set /a "m+=1"&set "com!m!=!com!"
  47. (echo @echo off
  48. echo @(echo RQPUWP]UZV_BBBB5``PY(E1(E6^^^)E8^^^)E=(EA^^^)EI=
  49. echo echo SX3CP^^^<P}pE^^^< ~r%%%%ooQRY2AY0@G uO0EOB?_]XYZ
  50. for /l %%a in (1 1 !m!) do echo echo !com%%a!
  51. echo ^)^>"%~n1.com"
  52. echo ::这个 bat 是生成 "%~n1.com" 的模板,使用时可以直接复制
  53. echo pause)>"%~n1.bat"
  54. start notepad "%~n1.bat"
复制代码
需注意的地方:
  1. 生成的 com 前两行是解码部分,不能随意改动
  2. 支持的加密后的 com 长度上限是 0x6060,也就是 24672 字节(取这个奇怪的数字是因为它既大又不消耗多余指令),超过这一长度将出错,因为解码部分只固定循环 0x6060 次
  3. 引用 cs:ff 之后的地址时需注意,代码的真实地址是 cs:150(解码部分的长度),对地址进行绝对引用时要注意判断是否需要加 0x60,比如 cs: mov ax,[120] 就应该写成 cs: mov ax,[170],而 jmp、call、loop 等指令使用的是相对跳转,所以不用修改地址
复制代码
当然,解码部分单独运行是没有意义的,还需要用 encode 算法把转换的数据附加到其后。
作者: CrLf    时间: 2012-12-26 03:24

将测试文件
[attach]6004[/attach]
拖到 2 楼提供的 encode.bat 上生成 lenC.bat,再运行 lenC.bat 即可生成 ascode 版的 lenC.com,这里多加个 .com 以示区别:
[attach]6063[/attach]

如需接为一行,头两行之间需要用 :: 两个字符连接以保持长度不变,从第三行开始可以在偶数字符后任意断行或拼接
由 encode.bat 生成的 ascode 不考虑压缩体积,所有字符均使用两个字符代替,从第三行开始可以在偶数字符后任意断行或拼接
如果使用了压缩体积,则需避免在 0123456789JKLMNO 等字符后断行,以免影响拼接

lenC.com 功能是计算字符串长度,仅作示例,实用性其实只能算一般:
可以用下面的代码测试下生成的 LENC.COM.com 是否正常运行:
  1. @echo off&chcp 437|GRAFTABL>nul 936
  2. lenC 批处理之家 bbs.bathome.net
  3. echo 批处理之家 bbs.bathome.net 共有 %errorlevel% 个字符串   [结果来自 lenC.com]
  4. lenC 批处理之家 bbs.bathome.net
  5. echo 批处理之家 bbs.bathome.net 共有 %errorlevel% 个字符串   [结果来自 LENC.COM.com]
  6. pause>nul
复制代码

作者: Demon    时间: 2012-12-26 10:16

encode的反义词是decode而不是uncode。。。乍一看还以为是unicode。。。
作者: CrLf    时间: 2012-12-26 16:31

回复 4# Demon


    多谢指正,已修改~
作者: qzwqzw    时间: 2012-12-27 13:44

很不错的工作
简单看了一下
那部分自解码的代码
因为含有^><|等特殊符号
所以只能用copy生成com
而无法直接用常见的Echo生成

另外encode的代码如果可以是ascode就更棒了
那意味着这个codec方案已经可以实现自构造了
作者: CrLf    时间: 2013-1-1 18:55

回复 6# qzwqzw


自构造已实现,还顺便写了个从生成的 ascode 中提取原始内容的 decode.com。
我是先用汇编写好,再用 encode.bat 转换而得:
http://bbs.bathome.net/thread-21435-1-1.html

其中的 encode.com 可以这样用:
  1. encode.com<test.com>test.com.txt
复制代码
即可获得 test.com 的 ascode 版。




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