The Autolisp FasFile Format

 

 

Document History:

14.06.04 english corrections - thanks goes to Mark

12.06.04 making of section added

10.06.04 improved readability and added some more comments

25.01.03 - document created

 

 

Fas Format (Streams only)

 

 

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   0D 0A 20 46 41 53 34 2D  46 49 4C 45 20 3B 20 44   .. FAS4-FILE ; D

00000010   6F 20 6E 6F 74 20 63 68  61 6E 67 65 20 69 74 21   o not change it!

00000020   0D 0A 34 32 0D 0A 36 20  24 14 00 00 00 00 09 05   ..42..6 $.......

00000030   00 35 01 04 00 03 0A 09  03 00 33 46 41 53 34 35   .5........3FAS45

00000040   02 02 00 03 0A 09 01 00  33 34 12 00 00 35 02 02   ........34...5..

00000050   00 03 16 24 0D 0A 31 34  38 20 34 20 24 14 01 01   ...$..148 4 $...

00000060   01 00 32 00 32 18 2A 39  01 00 5B 53 3A 3A 53 54   ..2.2.*9..[S::ST

00000070   41 52 54 55 50 00 00 01  01 43 00 00 03 00 0A 32   ARTUP....C.....2

00000080   00 32 2A 2A 39 01 00 55  01 00 04 00 61 73 64 66   .2**9..U....asdf

00000090   5B 53 45 54 56 41 52 00  00 55 01 00 08 00 6D 65   [SETVAR..U....me

000000A0   6E 75 65 63 68 6F 5B 41  4C 45 52 54 00 00 55 01   nuecho[ALERT..U.

000000B0   00 03 00 71 77 65 5C 00  00 43 00 00 06 00 0A 5C   ...qwe\..C.....\

000000C0   00 00 32 00 5B 53 3A 3A  53 54 41 52 54 55 50 00   ..2.[S::STARTUP.

000000D0   00 3A 01 43 03 00 01 00  1C 14 01 00 00 00 09 02   .:.C............

000000E0   00 0A 57 00 00 00 00 09  03 00 06 01 00 09 01 00   ..W.............

000000F0   16 16 00 2C DB 0E FF E4  8A 0A 3B 66 61 73 34 20   ...,..;fas4

00000100   63 72 75 6E 63 68 0A 3B  24 3B 41 37 2F 33 31 2F   crunch.;$;A7/31/

00000110   30 32                                              02

 

FileSignature     Ensure that its a fas-file

MainStream        Contain all commands which are not inside a function

FunctionStream    Contain all Function (Functionname and Commands)

 

Fas Format (Details)

 

 

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00000000   0D 0A 20 46 41 53 34 2D  46 49 4C 45 20 3B 20 44   .. FAS4-FILE ; D

00000010   6F 20 6E 6F 74 20 63 68  61 6E 67 65 20 69 74 21   o not change it!

00000020   0D 0A 34 32 0D 0A 36 20  24 14 00 00 00 00 09 05   ..42..6$.......

00000030   00 35 01 04 00 03 0A 09  03 00 33 46 41 53 34 35   .5........3FAS45

00000040   02 02 00 03 0A 09 01 00  33 34 12 00 00 35 02 02   ........34...5..

00000050   00 03 16 24 0D 0A 31 34  38 20 34 20 24 14 01 01   ...$..1484$...

00000060   01 00 32 00 32 18 2A 39  01 00 5B 53 3A 3A 53 54   ..2.2.*9..[S::ST

00000070   41 52 54 55 50 00 00 01  01 43 00 00 03 00 0A 32   ARTUP....C.....2

00000080   00 32 2A 2A 39 01 00 55  01 00 04 00 61 73 64 66   .2**9..U....asdf

00000090   5B 53 45 54 56 41 52 00  00 55 01 00 08 00 6D 65   [SETVAR..U....me

000000A0   6E 75 65 63 68 6F 5B 41  4C 45 52 54 00 00 55 01   nuecho[ALERT..U.

000000B0   00 03 00 71 77 65 5C 00  00 43 00 00 06 00 0A 5C   ...qwe\..C.....\

000000C0   00 00 32 00 5B 53 3A 3A  53 54 41 52 54 55 50 00   ..2.[S::STARTUP.

000000D0   00 3A 01 43 03 00 01 00  1C 14 01 00 00 00 09 02   .:.C............

000000E0   00 0A 57 00 00 00 00 09  03 00 06 01 00 09 01 00   ..W.............

000000F0   16 16 00 2C DB 0E FF E4  8A 0A 3B 66 61 73 34 20   ...,..;fas4

00000100  63 72 75 6E 63 68 0A 3B  24 3B 41 37 2F 33 31 2F   crunch.;$;A7/31/

00000110   30 32                                              02

 

StreamLength

NumberofStrings         Shows how many Strings are used in the stream

StreamTerminatorChar    Defines the StreamTerminator

StreamTerminator        Shows the end of a Stream

KeyLength               Defines the length of the Key

Keydata                 key to decrypt the stream

 

FileSignature

MainStream

FunctionStream

 

 

Lisp Charinterpretion Table

 

Offset  0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F  -> 0123456789ABCDEF

00     00 00 00 00 00 00 00 00  00 01 01 00 01 01 00 00  -> Tab,LF,NewLine,CR

10     00 00 00 00 00 00 00 00  00 00 01 00 00 00 00 00  -> ................

20     01 0B 0B 0F 0A 0A 0A 0B  0B 0B 0A 0A 0B 0A 0A 0A  ->  !"#$%&'()*+,-./

30     0A 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0B 0A 0A 0A 0B  -> 0123456789:;<=>?

40     0A 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0A 0A 0A 0A 0A  -> @abcdefghijklmno

50     0A 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0B 04 0B 0A 0A  -> pqrstuvwxyz[\]^_

60     0B 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0A 0A 0A 0A 0A  -> `abcdefghijklmno

70     0A 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0B 05 0B 0A 00  -> pqrstuvwxyz{|}~

80     0A 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0A 0A 0A 0A 0A  -> "

90     0A 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0A 0A 0A 0A 0A  -> ''""o--

A0     0A 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0A 0A 0A 0A 0A  ->  "­

B0     0A 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0A 0A 0A 0A 0A  -> "

C0     0A 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0A 0A 0A 0A 0A  ->

D0     0A 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0A 0A 0A 0A 0A  ->

E0     0A 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0A 0A 0A 0A 0A  ->

F0     0A 0A 0A 0A 0A 0A 0A 0A  0A 0A 0A 0A 0A 0A 0A 0A  ->

 

00 Controlchars

01 Whitespaces - Tab,LF,NewLine,CR,1A,Space

04 Blackslash - \

05 Pipe - |

0A Alphanummeric   $0123456789ABCD

0B Limiter - !(),;?{}~

0F Dash - #

 

 

 

Pseudo code for Open a FAS4-FILE

 

Target: Get unencrypted stream data from a fas-file and

        store the Streamdata in the vars MainStream and FunctionStream.

 

// following tasks are done by a Lisp program stored in the

// vllib.dll!WINDLG as string resource

Func fasFileOpen

openFileStream (C:\ACAD\MyLispFile.fas)

MainStream = fasStreamRead()

FunctionStream = fasStreamRead()

End Func

 

 

Func fasStreamRead() String

SkipWhiteSpaceEx

FileSignature= ReadString_until_Whitespace()

Compare fileSignature with FAS4-FILE   ; maybe FAS3-FILE, FAS-FILE,
  FAS2-FILE, #1Y# works too

SkipWhiteSpaceEx

StreamLength= GetLong()

SkipNextChar

 

// following tasks are done by C++ program code stored in

// vl.arx!.text section

SkipWhiteSpace

NumberofStrings = GetLong()

StreamTerminatorChar = SkipWhiteSpace    ; returns first non-whitespacechar

If StreamTerminatorChar =!

   StreamTerminatorChar =GetNextChar()

 

Streamdata = Readstring(length: = StreamLength)

 

If getchar()!=StreamTerminatorChar

   DecryptStream

 

If getchar()!=StreamTerminatorChar

   Error InvalidStreamTerminatorChar

Return (Streamdata)

End Func

 

// just skip whitespaces and comments (starts with ; | end with newline)

Proc SkipWhiteSpaceEx

do

tmpchar = getchar()

if tmpchar==; Skip_until_NewLine (0x0a,0x0d)

while Lisp_Charinterpretion_Table[tmpchar]==1 

            ; in the Array Lisp_Charinterpretion_Table all
  whitespaces are marked with 0x01

End Proc

 

// skip whitespaces and returns first char

Func SkipWhiteSpace() char

do

tmpchar = getchar()

while Lisp_Charinterpretion_Table[tmpchar]==1 

            ; in the Array Lisp_Charinterpretion_Table all
  whitespaces are marked with 0x01

return (tmpchar)

End Func

 

// Checks if fas is encrypted and decrypt its content

Proc DecryptStream

      KeyLength = Stream.Getbyte()

      If KeyLength == 0

         Exit proc

 

      If KeyLength =>0x80

         Error invalid Keylength

 

      Keydata = Stream.GetString(length: = KeyLength)

 

     Do Until FasDataStream.EOF

       

  // Start at the beginning if we reached the end of the Keydata

        If Keydata.EOF Then Keydata.position = 0

 

        KeyNew = Keydata.byte

        value = FasDataStream.byte Xor KeyNew Xor KeyOld

        UnCryptedFasDataStream.addbyte = value

       

        KeyOld = KeyNew

       

     Loop

 

End Proc

 

 

Just for Historical Reason The Making of

 

Everything thing start when I view a fas-file with an ACCII-Viewer.

and I though Whoops were are the strings gone? - They must be compressed or encrypted in some way. 

Well the first and easiest things to look at, when you analyse an unknown (compiled) program, are the strings. Strings are good for getting a feel of whats going on.

 

EAX=0125F93C   EBX=01150000   ECX=C3610B50   EDX=815B2A6C   ESI=815A72EF        
EDI=815B2A6C   EBP=0125FA40   ESP=0125F928   EIP=BFF638D9   o d I s Z a P c     
CS=0137   DS=013F   SS=013F   ES=013F   FS=487F   GS=0000                       
-----esp------------------------------------------dword-------------PROT---(0)--
013F:0125F928 0042F248  00000000  0125F93C  0052D080      H.B.....<.%...R.     ^
013F:0125F938 00001035  61656C50  69206573  7265736E      5...Please inser     v
-----USER32!SetThreadDesktop+0005----------------------------------------PROT32-
0137:BFF638D6  RET       0004                                                  ^
USER32!MessageBoxA                                                             ^
0137:BFF638D9  PUSH      EBP                                                    
0137:BFF638DA  MOV       EBP,ESP                                                
0137:BFF638DC  PUSH      00                                                     
0137:BFF638DE  PUSH      DWORD PTR [EBP+14]                                     
0137:BFF638E1  PUSH      DWORD PTR [EBP+10]                                     
0137:BFF638E4  PUSH      DWORD PTR [EBP+0C]                                     
0137:BFF638E7  PUSH      DWORD PTR [EBP+08]                                     
0137:BFF638EA  CALL      USER32!MessageBoxExA                                   
0137:BFF638EF  POP       EBP                                                    
0137:BFF638F0  RET       0010                                                  v
USER32!MessageBoxExW                                                       < > v
------------------------------------USER32!.text+28D6---------------------------
Break due to BPX USER32!MessageBoxA  (ET=1.63 seconds)                          
:dex 0 esp                                                                      
:                                                                               
 

Ok I started ACAD and set in Numegas Softice Debugger a breakpoint()BPX) to Kernel32!CreateFileA (or maybe it was msvcrt!_lcreat) to see when my Fas-file is opened. After CreateFileA(C:\MyLspFile.fas) I set a breakpoint on the API Kernel32!Readfile to get the memory location where the file content is loaded. Now set a set a Memorybreakpoint (BPM) on the Address where the content is loaded and trace what happened to the data until I got to the location of where the data is decrypted


01A7:619C3BEF  8B750C  MOV       ESI,[EBP+0C]  ;Load Start of crypted code

01A7:619C3BF2  8B4510  MOV       EAX,[EBP+10]  ;Load End of crypted code

01A7:619C3BF5  33FF    XOR       EDI,EDI              ;Keyindex=0

01A7:619C3BF7  3BF0    CMP       ESI,EAX

01A7:619C3BF9  7D29    JGE       619C3C24                   :Did we reached the end

01A7:619C3BFB  8B5508  MOV       EDX,[EBP+08]  ;Load start of Key

01A7:619C3BFE  EB03    JMP       619C3C03

 

Then I analysed the AssemblerCode and tried to understand the algorithm used and after that I rewrote it in VB. I did the same thing when I worked out the FAS Stream File Format. I traced what happened to every single byte of the header. 

After that the FAS-File Resource Decrypter was finished.

 

After we opened and sliced the file into bits and pieces, our minced meatwas ready for the burger maker.

So the next step was analysing the streams.

The first approach was to compare the sourcecode with the Hexcode

LDP.lsp

>>> 

(alert "qwe")

(setvar "menuecho" 877871430)

(setvar "asdf" 4660)

<<<

 

LSP.fas (Hex;decrypted)

 

00000000 0D0A 2046 4153 342D 4649 4C45 203B 2044 .. FAS4-FILE ; D

00000010 6F20 6E6F 7420 6368 616E 6765 2069 7421 o not change it!

00000020 0D0A 310D 0A31 2024 2024 0D0A 3133 3120 ..1..1 $ $..131

00000030 3720 2414 0101 0100 3200 322E 2A39 01007 $.....2.2.*9..

00000040 55010004 0061 7364 665B5345 5456 4152U....asdf[SETVAR

00000050 0000 5501 0008 006D 656E 7565 6368 6F5B..U....menuecho[

00000060 414C 4552 5400 0055 01000300 7177 6501 ALERT..U....qwe.

00000070 0143 0000 0700 0A01 5C00 0043 0000 0100 .C......\..C....

00000080 0A01 4307 0000 001C 1401 0000 0009 0600 ..C.............

00000090 0A090500 3501 0400 030A 0903 0033 4641 ....5........3FA

000000A0 5334 3502 0200 030A 0901 0033 3412 0000 S45........34...

000000B0 3502 0200 0316 1500 3216 E4D8 DE0A 3B66 5.......2.....;f

000000C0 6173 3420 6372 756E 6368 0A3B 243B 4138 as4 crunch.;$;A8

000000D0 2F31 2F30 32                            /1/02          

 

header        

strings       

data          

Prgcode       

Key           

 

Hexdump Interpreted

 

String        Index

asdf          0001

[SETVAR       0002

menuecho      0003

[ALERT        0004

qwe           0005

Strings       5 (should be 7 according to header)

 

 

14 01000000 090600 0A  <- What is String 0006

 

(alert "qwe")

09 0500 3501 0400 03 0A

(  qwe       ALERT)

 

(setvar "menuecho" 877871430)      ;=FAS4

09 0300 33  46415334 3502 0200   03 0A

(  menuecho 877871430     [SETVAR)

 

 

(setvar "asdf" 4660)               ;=0x1234

09 0100 33 341200003502 0200   03

(  asdf    4460          [SETVAR)

 

First everything looked nice, but soon more and more inconsistencies and questions were raised. This approach was based on far too many incorrect assumptions (which I discovered later) so I had to try something different.

On my second try I used the CreateFile-and-breakpoint-on memory-access method to get into the main interpreter loop.

The fas-commands seems to be similar to assembler. First it is important to know whether a byte is a command or a parameter. A fas-command is divided into commandbyte (or opcode) and parameters. The commandbyte specifys what's to do, how long the command is (i.e. how many parameters follow) and where the next command starts.

Streamdata

14 01 00 00 00 09 03 00 0A 57 00 00 00 00 32 00 32 27 2A 39 01 00

Streamdata separate into command and parameters

14 0100 0000

09 0300 0A

57 00000000

32  00

32  27

2A

39  01 00

Screenshot:

Getting the fasfilecommand specifications

 

 

 

I used Microsoft's Visual C++ Debugger for this because while youre in Softice your system is frozen so you cant use Word or other Windows dependant programs

How the parameters are read (i.e. as byte, integer, long) reveals part of their meaning.

After I separated the streamdata into command and parameters I compared them to the sourcecode to get their meaning. 

00047  9  4             push StringVar4(="in_if_b") on stack

00050  35 1 7 3         Execute Command7(=ALERT) with 1 Params from stack; Bitflags: 011

00055  A                pop dummy (decrease stack)

 

With this information I wrote the FAS-File Disassembler.

 



 

The next step will be to recognize structures like if, while, for and then translate the pseudo fas-disassembler code into lisp command which can be recompiled. (-> see fas.structure.htm for more details) But even now you are able to see what a fas-program does and with a hexeditor you can do some small modifications.