Dissecting media file formats with Kaitai Struct
CC Attribution 2.0 Belgium:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Yakshin, Mikhail (GreyCat)
Media file formats grow progressively more and more complex every year andsupporting them all requires tremendous effort of all the FOSS developers.It's a problem that concerns not only low-level library developers, but higherlevel software as well: for example, audio sequencer or video editor developerwill still need solid understanding of underlying media file format structureto be able to debug any problems with it (like non-standard chunks inserted bysome properitary software). We'd want to present Kaitai Struct, a newfree/open source solution for file format dissecting, visualization andparsing. It is "write one - run everywhere" solution, where one needs tospecify declarative file format spec once, and then compile it into ready-madeparsing library in a large variety of supported target languages. And ourvisualization tools make Kaitai Struct work like "Wireshark for media files". Kaitai Struct started as an in-house tool in 2014 and was initially releasedas open-source project to public at March-April, 2016, supporting only 2target languages: Java and Ruby. Since then, we've collected 400+ stars aGitHub, hundreds of praising testimonials, got about a dozen of contributors,implemented support for 8 languages, got a handful of useful tools, likeconsole visualizer, GUI visualizer, [WebIDE], etc. Kaitai Struct is frequently compared to proprietary template-enabled hexeditors (like 010 Editor, Synalize It! or Hexinator), but goes one stepforward: it's not only about highlighting entities in hex dump, but also itcan automatically generate working API from spec, which accelerates work offile formats considerably and greatly reduces human factor errors whendeveloping parsers by hand. One's guaranteed to get exactly the same parsingresult both in visualizer and using the compiled API. And, what's important,it's free and open source. Some other comparable projects include BinPAC (but it's C++ only), Preon(which is Java-only), PADS (which targets only C & Haskell), and Construct(Python only). In comparison, Kaitai Struct offers cross-language support, andincludes visualization tools. For media file dissection, we have a growing collection of well-known mediafile formats (including MP4 / QuickTime .mov, AVI, GIF, JPEG, PNG, TIFF, etc),and other interesting file formats (like executables, byte-code, networkprotocols, etc, etc). We hope that open media software developers would findKaitai Struct to be a helpful ally in their arsenal of tools to deal with thediverse world of modern file formats.
 So please welcome on stage Michael, aka Greyhunt, who will introduce us with an exciting project called Kitehouse Tracks, the dissecting media files. Hello everyone and thank you for joining me today. My name is Michael the Action and I'd like to talk about dissecting media file formats today with the tool named Kitehouse Tracks. So what's the idea? Media file formats grow complex and more complex every day. Media software developers have to deal with the multitude of different media file formats. Some of them are well documented but still pretty complex to process. Some of them are proprietary and undocumented and need to be reverse engineered. It's even more complicated task and requires quite a few hurdles to jump. For example, one need to do the proper black box reverse engineer to be included into free and open source project without major legal problems. One need to do lots of testing, making some hypothesis or proving them right or wrong and doing some decisions, mapping some proprietary format step by step, exploring it and doing some kind of description or specification of such problem. So the mission that such a developer must undertake is going from by-representation of file format in a stream, loading into memory and sometimes going back from memory to a stream. That is, we have some kind of stream that needs to be dissected into some objects laid out in memory, usually in some kind of tree or graph of objects. Typical development workflow for such a process involves writing some part in code with certain programming language. Then you write some extra debugging code to ensure that it actually works because you need to somehow prove that it works. You either dump it to the screen, check some assertions, run with it with a debugger or something like that. Then you just basically debug it till you drop because parsing binary formats is not exactly a trivial task. There are quite a few pit holes to overcome like going over some boundary inside some soft structure, dealing with N-DNS, dealing with byte alignment, dealing with a few other things like assertions checks, badly specified formats, some special cases, conditional reading, writing, et cetera, et cetera. As soon as you finish such a big task, you get some sort of parsing library that loads of objects from stream to memory. But what then, if you want to support some other programming language, you just basically need to redo the whole process from the start, doing basically the same code in some other programming language imperatively. Actually almost every media format library I've encountered has these dumping tools. On this slide, at least at some of them, then they're not just for random reason. They're for reason. They're needed by developers of these two libraries to debug their tools and to see if they really work. Just to say that errors in file format libraries can be really devastatingly dangerous. Almost every such error, such as buffer loss, such as reading beyond some part of structure, interpreting something wrongly because of human errors in writing the code, et cetera, et cetera, are almost always remotely explotable. They frequently provide arbitrary code execution, especially if we're talking about buffer over flow in libraries written in languages such as C. They leak information. They usually can lead to denial of service errors. For example, in Lib.png since 2010, there were 22 vulnerabilities and quite a few of them are really dangerous. In Lib.png, for example, there are full vulnerabilities, but they're still dangerous as well. If we'll revert to the start and see what format file format specifications exist, we'll discover that there is no single universal access standard. If we'll take a look at the documents provided by file format authors, there are quite a few things invented to describe by file format, such as, for example, C structures, as we see here with ELF headers, such as intricate tables, as we see here with network tables, such as even more intricate tables, as we see here with some random page describing Microsoft Word document format that maps some bytes, bits, and try to explain its values. Network protocol engineers have something better to rescue. They've got Wireshark that is universal accepted to be a tool of trade that allows to dissect the packets and see what's inside in some kind of three format. Basically, you have the dump. You can point at any byte in the dump and see what values in protocol in the packets it corresponds to and vice versa. But what about the same stuff for media files? It's a bit complicated. There are quite a few proprietary tools available, such as one-to-one editor or hexinator, or signalized it, as some of you may be familiar with. But generally, there is no universal accepted or at least a tool that supports enough popular formats to dissect and to build upon. Basically, we've tried to follow this whole and go actually one step ahead of it. I'd like to present a KiteListrack project, which is declarative file format specification language. All the words in this phrase are actually meaningful. The emphasis on the declarative, it means that we do not actually specify how to read the format, but we specify what is inside the format. It's harder to implement in some cases, but it gives us quite a few advantages. I'd like to show a bit further in the presentation. We can compile our case-wide file that we've set up with the file format specification and to read the main parsers libraries in quite a few target program and languages that I'll demonstrate further. Quite as well, we can visualize, dump, and debug all these file format specifications using several tools that were built around the KiteListrack project, such as visualization tools, such as WebID that I'll demonstrate further as well. Case-wide format is YAML-based, and that's actually a good thing because it's very easy to write your own tools. For example, it's quite a snap to write a tool that would embed one case-wide file into another case-wide file. It's generally a matter of writing a script in five or ten lines, and it's quite easy. It's large but not least. It's free and liberal. We use GPLv3 for the compiler and actually, generated code uses some runtime libraries that we supply as well, and they are MIT or RAPH2 licensed. So even if the compiler is GPL, it's possible to use the proceedings of the compiler and proprietary products as well. We support eight target languages right now. It's C++, C-Shop, Java, JavaScript, Pro, PHP, Python, and Ruby. As a bonus, we support output to graphies. I'll demonstrate it further. It's quite interesting site project as well. As experimental features, right now, we are developing Swiss support. We're developing support for exporting case-wide files as WI-SHARC dists to be able to load the same declared formats into the WI-SHARC interface and see there, and some quite a few other probably interesting target formats. So how does it look? The natural API generated by KITAS struct looks something like that. Here we have demonstration of GIF file, GIF file, all loaded. Generally, the KITAS struct file declares the tree of objects. Here for example, we have the header, the logical screen descriptor, global collo table, et cetera, et cetera. And generally, it goes down to traversing this tree of objects from some start that, for example, this code in Java starts with GIF.fromFile that loads some... That parses some GIF data from the file. And then you just do this file dot something, dot something, dot something, and extract the data right away. For example, this is one liner that shows the screen width and height, which is actually there, dimensions with visible fragment of GIF image right away in one line of code. This is our web ID. Probably it's much better to just demonstrate it right away. This is probably now the main working place of a developer that wants to get his or her hands dirty with KITAS struct. Here we have simultaneously an editor to see and edit the KSY file. It's on upper left corner. In upper right corner, there is a hex dump of some loaded file. Here we have a Microsoft ADI file, and it's corresponding from a description. As you may have seen in such editors like 101 editor or Hexinator or other preparator editors, it's possible to select any byte in the stream and go exactly to some value in the object tree in lower bottom corner to see what this byte corresponds to. And as well, one can traverse open and close arbitrary objects in the object tree as well and see how it looks like. It's fully interactive, changing a single symbol in the KSY code, recompiles everything conservatively, and tries to implement rate-to-rate, reparts the file in a new way that you've just specified. So, for example, if you add some lines of code that add parsing of some new field, it would just appear right away. You don't need to basically just do anything. For those who want some more console hardcore style parsing, there is also a console visualizer. Here we have the GPAC file loaded into it. It doesn't look just as flash as the Web 1, but it works just as well. It doesn't feature an separate editor, of course. You're expected to have your own editor on console or whatever you want to use in some other window, so it focuses just on visualization. You have the same tree, you have the same binary dump, you can traverse it and see if the file specification you just entered matches what you expect to see or not. This is how our KSY files look like. Basically it's YAML, it allows us to set up some fields, some field types, and that is important because it's declarative and not imperative. On the left we see our declarative specification. On the right we see what it compiles usually to into some kind of imperative code. We do not have things like while loops, we do not have things like direct ifs, any conditional controls, any jumps, any code flow that is immediate in imperative implementations. We just describe the file structures. If there are some repetitions, we enter it as repetitions. If there are some conditional parsing, we enter it as conditional parsing. It brings up quite a few possibilities, interesting possibilities that are possible. We have quite a few built-in data types such as integers, floats, unaligned bit integers, and bit fields, strings, robot arrays, enums, and of course we allow to define user defined data types. We have sequential parsing, parsing holds one by one in sequence. We have autoforder parsing, something called instances, so you can seek in the file actually to do some parsing of other parts of file by some index or offset. We have calculated attributes to ease representation of something that we got from the file in some more popular form. We got checking for magic signatures such as fixed content, counter, for example, headers. We have conditional parsing. We have type switching on a condition, something like switch. We have repetitions until the end of stream, repetitions in predefined number of iterations, or until some condition in net. We have powerful expression language that could be used almost everywhere. That's a good thing because it actually compiles into direct expression code in some other languages. For example, this one shows how we can parse the attribute named full length that allows us to specify unsigned integer 4 bytes long in the first place. Then we parse as many elements as we need, calculating the number of elements as full length minus 4 divided by 6. So it is how it compiles to C++. This is how it compiles to Python. You can see that the code is quite different. It's how it compiles to JavaScript. Another difference is that, for example, JavaScript doesn't have the integer division, so we end it with a must.floor. That's another one that I wanted to demonstrate interesting stuff about the graphless visualization. Basically we compile stuff to graphless. This is what we've got. It's a human readable diagram that one can parse to his colleagues, one can parse to other people to just take a look at the format and implement it, for example, in some other programs. We've got a ground repository of formats, including tons of formats right now. It's quite interesting. You can find it at our GitHub page, see for yourself if there will be anything interesting for you there. There are quite a few image file formats, video file formats, audio file formats, archives, documents, executables, file systems, et cetera, et cetera. Thanks for your attention. I'd like to see if any questions or rises. There was one example of length here, and you had length minus four, five plus six. I was wondering what happens if the length is three, so I would get negative, so would it catch that and tell me this input is straight? The question was about handling incorrect values in complex expression language expressions. So basically there is no internal checking in kind of abstract. It just compiles the expression as I showed you. And in runtime, it will probably arise some sort of exception or error. And this would be specific to a particular target language that you compile this code to. Please. So you have mostly container formats in that list, which are generally roughly byte-stream based. What about codecs that have bit-stream fields that are quite complicated, like based on popular codes or go-long codes or various different, they have multiple complex bit-streams that we'd like to analyze. Is it possible to write in your definition language where you post these bit-streams? Yes, since, yeah, sorry. The question was about parsing bit-streams with more complex codecs like Huff and codes, etc., etc. So yeah, since version 10.6, we have support for reading bit-streams. It's slowly growing. Probably it's not very optimal to parse bit-streams per se to fulfill simple operations like unpacking something or uncompressing something. Probably it's more efficient to use some special processing onto the whole byte stream here, but it also can be done, I don't see any major problems here. Could you repeat it a bit louder? Right now, the API, repeating the question, yeah? The question was about reading something from a stream, not from the whole file from the disk. Right now, the API allows to basically do reading parsing from two sources from a file on disk or from arbitrary array memory. If you can organize the parser in some way that would be, for example, chunk based, that would parse one chunk and then stop, it's no problem to go with this chunk based stream that you would somehow buffer in memory and add to this buffer again and again and recall the parser. So I guess that would be it. There are several possible, the question was about adding connotations to YAML file to have more readable representation of whatever is going on there. There are quite a few possibilities to do so. We allow to add some annotations to fields to be parsed as doc strings into the target code. So for example, if you load it into ID, you just see whatever the comment for the field are. We allow in Web ID there are several syntaxes that allows you to mark up some formatting for the representation, for example, choosing the hex representation, binary representation, or decimal representation, et cetera, et cetera. And last but not least, you can do a calculator to values that allow you to represent something in more human readable way as well. Thanks. The bit flags are parsed using the bit parsing syntax usually and you usually get them as separate fields that you can basically touch in every way you want to. Okay. Well, thank you. I'm sorry. There are a lot of questions, but I'm sure you are available after the talk. We'll answer them. Okay. Good. Thanks, everyone. Thanks.
 So please welcome on stage Michael, aka Greyhunt, who will introduce us with an exciting project  called Kitehouse Tracks, the dissecting media files.  Hello everyone and thank you for joining me today.  My name is Michael the Action and I'd like to talk about dissecting media file formats  today with the tool named Kitehouse Tracks.  So what's the idea?  Media file formats grow complex and more complex every day.  Media software developers have to deal with the multitude of different media file formats.  Some of them are well documented but still pretty complex to process.  Some of them are proprietary and undocumented and need to be reverse engineered.  It's even more complicated task and requires quite a few hurdles to jump.  For example, one need to do the proper black box reverse engineer to be included into free  and open source project without major legal problems.  One need to do lots of testing, making some hypothesis or proving them right or wrong  and doing some decisions, mapping some proprietary format step by step, exploring it and doing  some kind of description or specification of such problem.  So the mission that such a developer must undertake is going from by-representation of  file format in a stream, loading into memory and sometimes going back from memory to a  stream.  That is, we have some kind of stream that needs to be dissected into some objects laid  out in memory, usually in some kind of tree or graph of objects.  Typical development workflow for such a process involves writing some part in code with certain  programming language.  Then you write some extra debugging code to ensure that it actually works because you  need to somehow prove that it works.  You either dump it to the screen, check some assertions, run with it with a debugger or  something like that.  Then you just basically debug it till you drop because parsing binary formats is not  exactly a trivial task.  There are quite a few pit holes to overcome like going over some boundary inside some  soft structure, dealing with N-DNS, dealing with byte alignment, dealing with a few other  things like assertions checks, badly specified formats, some special cases, conditional reading,  writing, et cetera, et cetera.  As soon as you finish such a big task, you get some sort of parsing library that loads  of objects from stream to memory.  But what then, if you want to support some other programming language, you just basically  need to redo the whole process from the start, doing basically the same code in some other  programming language imperatively.  Actually almost every media format library I've encountered has these dumping tools.  On this slide, at least at some of them, then they're not just for random reason.  They're for reason.  They're needed by developers of these two libraries to debug their tools and to see  if they really work.  Just to say that errors in file format libraries can be really devastatingly dangerous.  Almost every such error, such as buffer loss, such as reading beyond some part of structure,  interpreting something wrongly because of human errors in writing the code, et cetera,  et cetera, are almost always remotely explotable.  They frequently provide arbitrary code execution, especially if we're talking about buffer over  flow in libraries written in languages such as C.  They leak information.  They usually can lead to denial of service errors.  For example, in Lib.png since 2010, there were 22 vulnerabilities and quite a few of  them are really dangerous.  In Lib.png, for example, there are full vulnerabilities, but they're still dangerous as well.  If we'll revert to the start and see what format file format specifications exist, we'll  discover that there is no single universal access standard.  If we'll take a look at the documents provided by file format authors, there are quite a  few things invented to describe by file format, such as, for example, C structures, as we  see here with ELF headers, such as intricate tables, as we see here with network tables,  such as even more intricate tables, as we see here with some random page describing  Microsoft Word document format that maps some bytes, bits, and try to explain its values.  Network protocol engineers have something better to rescue.  They've got Wireshark that is universal accepted to be a tool of trade that allows to dissect  the packets and see what's inside in some kind of three format.  Basically, you have the dump.  You can point at any byte in the dump and see what values in protocol in the packets  it corresponds to and vice versa.  But what about the same stuff for media files?  It's a bit complicated.  There are quite a few proprietary tools available, such as one-to-one editor or hexinator, or  signalized it, as some of you may be familiar with.  But generally, there is no universal accepted or at least a tool that supports enough popular  formats to dissect and to build upon.  Basically, we've tried to follow this whole and go actually one step ahead of it.  I'd like to present a KiteListrack project, which is declarative file format specification  language.  All the words in this phrase are actually meaningful.  The emphasis on the declarative, it means that we do not actually specify how to read  the format, but we specify what is inside the format.  It's harder to implement in some cases, but it gives us quite a few advantages.  I'd like to show a bit further in the presentation.  We can compile our case-wide file that we've set up with the file format specification  and to read the main parsers libraries in quite a few target program and languages that I'll  demonstrate further.  Quite as well, we can visualize, dump, and debug all these file format specifications  using several tools that were built around the KiteListrack project, such as visualization  tools, such as WebID that I'll demonstrate further as well.  Case-wide format is YAML-based, and that's actually a good thing because it's very easy  to write your own tools.  For example, it's quite a snap to write a tool that would embed one case-wide file into  another case-wide file.  It's generally a matter of writing a script in five or ten lines, and it's quite easy.  It's large but not least.  It's free and liberal.  We use GPLv3 for the compiler and actually, generated code uses some runtime libraries  that we supply as well, and they are MIT or RAPH2 licensed.  So even if the compiler is GPL, it's possible to use the proceedings of the compiler and  proprietary products as well.  We support eight target languages right now.  It's C++, C-Shop, Java, JavaScript, Pro, PHP, Python, and Ruby.  As a bonus, we support output to graphies.  I'll demonstrate it further.  It's quite interesting site project as well.  As experimental features, right now, we are developing Swiss support.  We're developing support for exporting case-wide files as WI-SHARC dists to be able to load  the same declared formats into the WI-SHARC interface and see there, and some quite a  few other probably interesting target formats.  So how does it look?  The natural API generated by KITAS struct looks something like that.  Here we have demonstration of GIF file, GIF file, all loaded.  Generally, the KITAS struct file declares the tree of objects.  Here for example, we have the header, the logical screen descriptor, global collo table,  et cetera, et cetera.  And generally, it goes down to traversing this tree of objects from some start that,  for example, this code in Java starts with GIF.fromFile that loads some...  That parses some GIF data from the file.  And then you just do this file dot something, dot something, dot something, and extract  the data right away.  For example, this is one liner that shows the screen width and height, which is actually  there, dimensions with visible fragment of GIF image right away in one line of code.  This is our web ID.  Probably it's much better to just demonstrate it right away.  This is probably now the main working place of a developer that wants to get his or her  hands dirty with KITAS struct.  Here we have simultaneously an editor to see and edit the KSY file.  It's on upper left corner.  In upper right corner, there is a hex dump of some loaded file.  Here we have a Microsoft ADI file, and it's corresponding from a description.  As you may have seen in such editors like 101 editor or Hexinator or other preparator  editors, it's possible to select any byte in the stream and go exactly to some value in  the object tree in lower bottom corner to see what this byte corresponds to.  And as well, one can traverse open and close arbitrary objects in the object tree as well  and see how it looks like.  It's fully interactive, changing a single symbol in the KSY code, recompiles everything  conservatively, and tries to implement rate-to-rate, reparts the file in a new way that you've  just specified.  So, for example, if you add some lines of code that add parsing of some new field, it  would just appear right away.  You don't need to basically just do anything.  For those who want some more console hardcore style parsing, there is also a console visualizer.  Here we have the GPAC file loaded into it.  It doesn't look just as flash as the Web 1, but it works just as well.  It doesn't feature an separate editor, of course.  You're expected to have your own editor on console or whatever you want to use in some  other window, so it focuses just on visualization.  You have the same tree, you have the same binary dump, you can traverse it and see if  the file specification you just entered matches what you expect to see or not.  This is how our KSY files look like.  Basically it's YAML, it allows us to set up some fields, some field types, and that is  important because it's declarative and not imperative.  On the left we see our declarative specification.  On the right we see what it compiles usually to into some kind of imperative code.  We do not have things like while loops, we do not have things like direct ifs, any conditional  controls, any jumps, any code flow that is immediate in imperative implementations.  We just describe the file structures.  If there are some repetitions, we enter it as repetitions.  If there are some conditional parsing, we enter it as conditional parsing.  It brings up quite a few possibilities, interesting possibilities that are possible.  We have quite a few built-in data types such as integers, floats, unaligned bit integers,  and bit fields, strings, robot arrays, enums, and of course we allow to define user defined  data types.  We have sequential parsing, parsing holds one by one in sequence.  We have autoforder parsing, something called instances, so you can seek in the file actually  to do some parsing of other parts of file by some index or offset.  We have calculated attributes to ease representation of something that we got from the file in  some more popular form.  We got checking for magic signatures such as fixed content, counter, for example, headers.  We have conditional parsing.  We have type switching on a condition, something like switch.  We have repetitions until the end of stream, repetitions in predefined number of iterations,  or until some condition in net.  We have powerful expression language that could be used almost everywhere.  That's a good thing because it actually compiles into direct expression code in some other  languages.  For example, this one shows how we can parse the attribute named full length that allows  us to specify unsigned integer 4 bytes long in the first place.  Then we parse as many elements as we need, calculating the number of elements as full  length minus 4 divided by 6.  So it is how it compiles to C++.  This is how it compiles to Python.  You can see that the code is quite different.  It's how it compiles to JavaScript.  Another difference is that, for example, JavaScript doesn't have the integer division, so we end  it with a must.floor.  That's another one that I wanted to demonstrate interesting stuff about the graphless visualization.  Basically we compile stuff to graphless.  This is what we've got.  It's a human readable diagram that one can parse to his colleagues, one can parse to  other people to just take a look at the format and implement it, for example, in some other  programs.  We've got a ground repository of formats, including tons of formats right now.  It's quite interesting.  You can find it at our GitHub page, see for yourself if there will be anything interesting  for you there.  There are quite a few image file formats, video file formats, audio file formats, archives,  documents, executables, file systems, et cetera, et cetera.  Thanks for your attention.  I'd like to see if any questions or rises.  There was one example of length here, and you had length minus four, five plus six.  I was wondering what happens if the length is three, so I would get negative, so would  it catch that and tell me this input is straight?  The question was about handling incorrect values in complex expression language expressions.  So basically there is no internal checking in kind of abstract.  It just compiles the expression as I showed you.  And in runtime, it will probably arise some sort of exception or error.  And this would be specific to a particular target language that you compile this code  to.  Please.  So you have mostly container formats in that list, which are generally roughly byte-stream  based.  What about codecs that have bit-stream fields that are quite complicated, like based on  popular codes or go-long codes or various different, they have multiple complex bit-streams  that we'd like to analyze.  Is it possible to write in your definition language where you post these bit-streams?  Yes, since, yeah, sorry.  The question was about parsing bit-streams with more complex codecs like Huff and codes,  etc., etc.  So yeah, since version 10.6, we have support for reading bit-streams.  It's slowly growing.  Probably it's not very optimal to parse bit-streams per se to fulfill simple operations like unpacking  something or uncompressing something.  Probably it's more efficient to use some special processing onto the whole byte stream here,  but it also can be done, I don't see any major problems here.  Could you repeat it a bit louder?  Right now, the API, repeating the question, yeah?  The question was about reading something from a stream, not from the whole file from the  disk.  Right now, the API allows to basically do reading parsing from two sources from a file  on disk or from arbitrary array memory.  If you can organize the parser in some way that would be, for example, chunk based, that  would parse one chunk and then stop, it's no problem to go with this chunk based stream  that you would somehow buffer in memory and add to this buffer again and again and recall  the parser.  So I guess that would be it.  There are several possible, the question was about adding connotations to YAML file to  have more readable representation of whatever is going on there.  There are quite a few possibilities to do so.  We allow to add some annotations to fields to be parsed as doc strings into the target  code.  So for example, if you load it into ID, you just see whatever the comment for the field  are.  We allow in Web ID there are several syntaxes that allows you to mark up some formatting  for the representation, for example, choosing the hex representation, binary representation,  or decimal representation, et cetera, et cetera.  And last but not least, you can do a calculator to values that allow you to represent something  in more human readable way as well.  Thanks.  The bit flags are parsed using the bit parsing syntax usually and you usually get them as  separate fields that you can basically touch in every way you want to.  Okay.  Well, thank you.  I'm sorry.  There are a lot of questions, but I'm sure you are available after the talk.  We'll answer them.  Okay.  Good.  Thanks, everyone.  Thanks.
Dissecting_media_file_formats_with_Kaitai_Struc_0_131.png Dissecting_media_file_formats_with_Kaitai_Struc_132_1055.png Dissecting_media_file_formats_with_Kaitai_Struc_1056_1103.png Dissecting_media_file_formats_with_Kaitai_Struc_1104_1187.png Dissecting_media_file_formats_with_Kaitai_Struc_1188_1319.png Dissecting_media_file_formats_with_Kaitai_Struc_1320_8243.png Dissecting_media_file_formats_with_Kaitai_Struc_8244_9683.png Dissecting_media_file_formats_with_Kaitai_Struc_9684_22943.png Dissecting_media_file_formats_with_Kaitai_Struc_22944_30275.png Dissecting_media_file_formats_with_Kaitai_Struc_30276_30515.png Dissecting_media_file_formats_with_Kaitai_Struc_30516_31823.png Dissecting_media_file_formats_with_Kaitai_Struc_31824_31883.png Dissecting_media_file_formats_with_Kaitai_Struc_31884_31967.png Dissecting_media_file_formats_with_Kaitai_Struc_31968_34451.png Dissecting_media_file_formats_with_Kaitai_Struc_34452_34835.png Dissecting_media_file_formats_with_Kaitai_Struc_34836_35507.png Dissecting_media_file_formats_with_Kaitai_Struc_35508_35615.png Dissecting_media_file_formats_with_Kaitai_Struc_35616_35891.png Dissecting_media_file_formats_with_Kaitai_Struc_35892_35939.png Dissecting_media_file_formats_with_Kaitai_Struc_35940_35999.png Dissecting_media_file_formats_with_Kaitai_Struc_36000_36251.png Dissecting_media_file_formats_with_Kaitai_Struc_36252_36587.png Dissecting_media_file_formats_with_Kaitai_Struc_36588_36671.png Dissecting_media_file_formats_with_Kaitai_Struc_36672_36719.png Dissecting_media_file_formats_with_Kaitai_Struc_36720_37055.png Dissecting_media_file_formats_with_Kaitai_Struc_37056_37175.png Dissecting_media_file_formats_with_Kaitai_Struc_37176_37559.png Dissecting_media_file_formats_with_Kaitai_Struc_37560_37703.png Dissecting_media_file_formats_with_Kaitai_Struc_37704_38171.png Dissecting_media_file_formats_with_Kaitai_Struc_38172_38219.png Dissecting_media_file_formats_with_Kaitai_Struc_38220_38291.png Dissecting_media_file_formats_with_Kaitai_Struc_38292_38471.png Dissecting_media_file_formats_with_Kaitai_Struc_38472_39222.png