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
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)
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
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 - #
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
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.