CAR HACKING VILLAGE - CAN Signal Extraction from OpenXC with Radare2
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 322 | |
Author | ||
License | CC Attribution 3.0 Unported: 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. | |
Identifiers | 10.5446/39847 (DOI) | |
Publisher | ||
Release Date | ||
Language |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
DEF CON 26132 / 322
18
27
28
40
130
134
164
173
177
178
184
190
192
202
203
218
219
224
231
233
234
235
237
249
252
255
268
274
287
289
290
295
297
298
299
302
306
309
312
315
316
00:00
Reverse engineeringMessage passingComputer fileAndroid (robot)CASE <Informatik>FlagMobile appMereologySampling (statistics)Functional (mathematics)Utility softwareSymbol tableSpecial unitary groupHash functionOpen sourceFirmwareComputing platformAreaMathematical analysisBit rateGroup actionSuite (music)Binary codeError messageFile formatMultiplication signSoftware developerSlide ruleBackupHoaxTranslation (relic)Computer hardwareoutputMobile WebBitRAIDSoftwareBinary fileLecture/Conference
02:13
Binary codeComputer fileFirmwareRAIDMetadataSymbol tableDisassemblerMathematical analysisInformationProbability density functionOnline helpControl flow graphAsynchronous Transfer ModeFunctional (mathematics)Multiplication signSlide ruleBinary fileVisualization (computer graphics)Radical (chemistry)Control flowLetterpress printingCAN busBitLine (geometry)Digital photographyOpen setGraph (mathematics)Game controllerRight angleEaster egg <Programm>Medizinische InformatikLecture/ConferenceMeeting/Interview
04:11
Multiplication signYouTubeFirmwareLoop (music)MereologyRAIDArmCAN busData structureBlogStructural loadOnline helpBootingReverse engineeringComputer fileAddress spaceArrow of timeRun time (program lifecycle phase)Storage area networkLink (knot theory)Right angleProcess (computing)Letterpress printingECosLecture/Conference
06:13
Reverse engineeringMappingBitThumbnailHexagonRoundness (object)Data structureLetterpress printingRevision controlLevel (video gaming)Computer architectureComputer fileAddress spaceCache (computing)ArmOpen setLecture/ConferenceMeeting/Interview
06:51
Computer fileConfiguration spaceRAIDAddress spaceHexagonOpen sourceFlash memoryLengthFirmwareDifferent (Kate Ryan album)Projective planeRevision controlBitFunctional (mathematics)Binary fileBinary codeSheaf (mathematics)Multiplication signBootingBuildingCodeRun time (program lifecycle phase)ProgrammschleifeRaw image formatCAN busScripting languageFlagVirtualizationThumbnailVirtual memoryLoop (music)Group actionLevel (video gaming)Machine codeMetropolitan area networkStructural loadState of matterRight angleComputer architectureArc (geometry)Crash (computing)Bit ratePoint (geometry)Power (physics)System callSurgeryError messageKey (cryptography)
09:59
QuadrilateralCrash (computing)String (computer science)Goodness of fitComputer fileOrdinary differential equationDifferent (Kate Ryan album)Ring (mathematics)Level (video gaming)CodeAddress spaceMatching (graph theory)Data structureBackupMappingStress (mechanics)InformationRight angleSlide ruleSemiconductor memoryInstance (computer science)Core dumpLecture/ConferenceMeeting/Interview
11:49
FirmwareProjective planeComputer filePosition operatorPoint (geometry)Address spaceString (computer science)Scripting languageEntire functionPatch (Unix)Function (mathematics)Array data structureQuadrilateralHash functionLevel (video gaming)Set (mathematics)Goodness of fitHexagonAreaAxiom of choiceSheaf (mathematics)Parameter (computer programming)Type theoryMaxima and minimaControl flowProof theoryCodeUniform resource locatorRight angleProcess (computing)Insertion lossLine (geometry)Semiconductor memoryBit rate2 (number)Reading (process)Lecture/ConferenceMeeting/Interview
14:11
Demo (music)Data structureComputer fileStatement (computer science)Pointer (computer programming)String (computer science)BitLetterpress printingInformationDot productPoisson-KlammerGoodness of fitRoundness (object)File formatType theoryMacro (computer science)Partial differential equationEmailRight angleMappingAddress spaceQuadrilateralMessage passingFrequencyProjective planeArray data structureRow (database)DivisorOperator (mathematics)Instance (computer science)Position operatorDampingLine (geometry)Heat transferForm (programming)NumberIdentifiabilityFunction (mathematics)Control flowView (database)Semiconductor memoryStructural loadClique-widthCAN busElectronic mailing listMetropolitan area networkWebsiteMultiplication signACIDGame theoryPoint (geometry)Personal identification numberFlagOntologyDisk read-and-write headRange (statistics)MultiplicationBit rateAssembly languageThumbnailError messageParsingBus (computing)Binary fileBoss CorporationForestLevel (video gaming)Direction (geometry)Lecture/Conference
21:54
Address spaceStatement (computer science)Macro (computer science)File formatBoss CorporationDisassemblerAreaLetterpress printingLecture/Conference
22:13
Array data structureView (database)Level (video gaming)Semiconductor memorySlide ruleDemo (music)Data structureBoss CorporationString (computer science)Goodness of fitAddress spaceVideo gameKeyboard shortcutLecture/ConferenceMeeting/Interview
Transcript: English(auto-generated)
00:00
So, I'm going to be talking to you about RAID Air 2. It has a lot of pronunciations. People tell me that I'm probably saying it wrong. I'm just going to keep saying RAID Air 2. I'm sorry, that's what I'm going to do. It's going to be about 25 minutes. I'm talking about OpenXC, which is Ford's platform for moving canned messages over Bluetooth to, like, mobile apps, and it's kind of neat. Because OpenXC is open
00:23
source, we can actually look at the firmware and kind of skip a lot of the discovery parts and just focus on reverse engineering with RAID Air. So it's going to be a whole bunch of command line stuff. The talk is kind of in the Gen 2 quadrant. There's going to be just a lot of commands, and, you know, I'll have the slides up. You can refer to the commands later. I'll try to describe what I'm doing too, but it's not going to
00:43
be Ubuntu style. It's going to be kind of like just fly, you know, go. So, we'll see. First up, OpenXC. You know, this is a cool platform that kind of takes canned messages and translates them into message pack format, which is like JSON, and it lets you build these mobile apps. For iOS recently, for Android, it's actively developed. It's
01:05
pretty fun. I recommend you go check it out. They give you hardware platforms and the software development, so you can do some cool stuff with it. RAID Air. So RAID Air 2 is like this reverse engineering suite of tools. There's a whole bunch of command line utilities you can use, like RA bin and RA hash. I'm going to be talking almost
01:23
exclusively about R2, which is like the reverse engineering part of it. So I'll show you a little bit of RAID Air. I really wanted to do everything like command line style, but this is actually my backup deck because the command line stuff just didn't work. But I'm going to do this style of like here's a fake command prompt, and I'm going to kind of scroll down. So a RAID Air session, you might open
01:43
up a file. It's like an ELF file from binary samples. And then you want to analyze it. So AA. Every command in RAID Air starts with like hierarchical letters. So A is for analysis commands and AA is just like do the basic analysis. Okay, it says great. I looked at all the flags and I found some stuff. Thanks. S is for seek. So I'm going to like seek to the function called main. There I did it. And then more
02:04
analysis commands. AX is for listing cross references. So here I'm just listing all the cross references that happen to be referring to main, and you can see that the analysis actually completed because of a whole bunch of cross references. This is great. And this is usually the experience you get with RAID Air, especially on like ELF files and PE files and stuff. It's really great at finding all that
02:22
metadata in the symbol table and then helping you kind of walk your way through the binary. Now, when you're dealing with a raw firmware file, just like a bin file, you don't actually get all those nice symbol exports, right? So you can open up in RAID Air. You can ask it to do the analysis. It'll say, great, I analyze zero things for you because I don't know where main is, right? There's nothing there. There's no metadata. So you have to
02:42
actually kind of teach RAID Air about where the file goes, do some searching for functions. And first Easter egg, RAID Air has Clippy. Clippy is installed, so you can actually get Clippy to tell you things if you want a little bit of help. He likes to help. RAID Air also does really great control flow graph
03:03
visualizations in the terminal. So this is actually like text mode using UTF-8 lines, and all the disassembly for every disassembly flavor that it supports. It'll do this kind of control flow layout, which is gorgeous. It's VV to get there. I'm not going to be showing you disassembly because this talk is really about extracting data. So we're going to be looking at how to
03:22
pretty print CAN signal information inside OpenXC. So not disassembly, more like data stuff. But if you want to just have great control flow graphs for free, check out RAID Air. It's great. Okay, gonna be a lot of commands. I put together this summary of all the commands that I'm going to use. I will also show this slide at the end, so you know you'll have lots of time to take
03:42
pictures for it. It'll probably be in a PDF somewhere as well. You can see that the commands are all really hierarchical, right? All the analysis commands start with A, all the printing commands start with P, information for the binary starts with I, and then there's two different things. And the most important one is really the question mark. You can use
04:00
question mark as a postfix, and if you put it at the end of any letter basically, it'll tell you what that letter is as a command. And the help information in the command line for RAID Air 2 is really great. So if you know that to put question mark at the end, you can actually walk your way through RAID Air pretty well. The other thing is question mark first is more like an echo command. So you can question mark E will echo like
04:20
actual echo command line, the normal stuff. Question mark V shows you like hexadecimal values. Question mark capital E shows you Clippy. We saw that already. So to recap, just to make sure we know what we're talking about, OpenXC takes these JSON files as part of its build and turns them into these CAN signal structures, okay? And what we're going to do is go
04:43
through the RAID Air commands necessary to actually go backwards and get the information out of the CAN signal structures that are in the firmware. They're going to kind of reverse that arrow. This is reverse engineering, right? Okay. This isn't the first time OpenXC has been reversed. I've given some talks about doing this with IDA Pro. IDA Pro is
05:00
very expensive and this is now free. Around the same time I did the SANS talk, someone who had the handle BSMT put out a blog post detailing how to do the steps with some IDA Pro Python scripts. So BSMT's blog has like a fully automated way to do this using IDA Pro, which is kind of cool. Also, you know, I'm not the person who invented firmware reverse
05:21
engineering. I'm totally standing on the shoulders of giants and there are many. I really appreciate the help of Craig Heffner and his blog post. I recommend all of his work. And then finally, I didn't invent Radair. You know, that's done by Trufé or Pancake, Pancake Knopcode. He actually has a talk here, this YouTube link, where he takes the MD380,
05:41
the Tyterra radios, and he reverses the ARM firmware in the Tyterra radio. It's a firmware blob. It's using a lot of the same techniques. So I'm really building on all this previous work here, okay? We don't have a whole lot of time, so I'm really just going to do the yellow stuff, right? I'm going to assume, okay, we already know that we've got the firmware in our hand. You know, that's always a hard step with the device you just got, maybe getting the firmware. We already know the
06:02
architecture. We already know the load address. We're just going to assume that stuff and we're going to go do this yellow stuff, load it into R2. We're going to assume that we figured out where the loader loop is, you know, where the C runtime stuff does the copying, and we'll just do a copy for ourselves and then we'll do the pretty printing of the structures. Okay. So first up, we want to load this thing. So we already know because we're smart and we did
06:22
some reverse engineering already that the thing can get loaded at hex 10,000, all right? If you wanted to do this in a one-off R2 command, you can do that with a dash M, as I'm showing here, M for the mapped address, hex 10,000. The architecture, which is A, dash A is arm. The size, like the bit width, is 16 for thumb. And
06:43
this is a bit of a weird round hole or a square peg in a round hole because thumb isn't necessarily 60-bit addressing and that will actually bite us when we're doing data printing, but you have to say B16 to get it to disassemble in thumb. And then you just give it the file. And this is the file that I got from the open source build of VI firmware from OpenXC. Okay. Now, we
07:03
don't necessarily want to just do the command line version of this because we're going to keep adding stuff in. It's kind of like when you build a project in IDA Pro, you have like an IDB, right? Rather than work with an IDB, we're going to have this like config file for RAID Air and we're going to keep building in little commands. So instead of that one line command, we're going to start with like this
07:20
R2 script file and I'm going to set the architecture. It's the E command is setting config values. And under the hood, when you do those command line switches for dash A and dash B, that's really what's happening is just setting these config values. Okay. So then I'm going to say, okay, the bin address is hex 10,000. I'm going to call something flash, the F flash. So F is the flag command.
07:43
In RAID Air, you can name any address by just calling it a flag. So F flash is hex 10,000. And then O is for open files. So O-N, I'm mapping a new file, the VI firmware file at the address flash, which I just called 10,000. Okay. And then I'm setting this thing VA. With
08:02
RAID Air, it knows the difference between file addresses and virtual memory addresses. And now that I've mapped the file, we need to tell RAID Air to switch into that virtual address space because I've just mapped it, right? So now I'm going to start referring to things by hex 10,000 instead of zero. Zero would be the file offset. Hex 10,000 is the virtual offset, right? So
08:21
I'm going to switch to VA. And then lastly, the capital S, I'm adding a section. So I'm creating a section for the flash, giving executable bits. And dollar S is the size of the file. And that's the kind of RAID Air magic that you just kind of get used to. And you write this once in the config file, and then you just kind of move on, right? Okay. So what else we got to do? We know that there's a loader loop
08:41
in this firmware. Loader loops are for C runtime. You know, it takes like your segment that's packed up into the binary file and puts it into the writable data segment, for example. That's what we're going to do here. We're going to emulate that. We know that it does a copy from 32EBC up to this million C8 hex address. And we know the length. So we're going to
09:01
define data as this million C8. And then we're going to go and make another section. And this time you'll notice that the offset is different, right? Because the sections, when you make them, they refer to the file offset. So I'm doing it, I'm making the section start from 22, not 32. But it'll be mapped at 32 because that's the map address, right? And
09:20
then the last command, now we have everything set up. We actually know enough to use radar's, like, search for preludes. You know, like, push, you know, every time you start a function, it's always the same little push and set up of the stack. And radar knows how to search for that. So you can go AAP. It'll search through all the code sections that you define, and it'll define functions that it recognizes as preludes. And that's a really
09:41
great command for raw binaries. So at this point, we've mapped the code. And we have search for preludes. And PowerPoint crashed. I don't know. Let's try again. We might have to drink. Okay. Can
10:03
you see that? Oh, good. And it crashed again. This is great. I should have made backup
10:22
strats. That's what I really needed. Okay. Right. So we have everything kind of mapped. I'm going to try to present. No, I'm not going to present this slide because it's just going to crash again. We have everything mapped, okay? But you can see here what I'm showing you. When you
10:42
just kind of dump the memory, you're not seeing, for instance, that there's a string defined here. Like if it found the string, it would say str.signal1. It would have identified the string, radar would have. And when you do the disassembly, like right here, instead of seeing disassembly of opcodes, you actually see that it was data. Like
11:02
if you've used IDA Pro before, there's a difference between saying it's code or saying it's a quad D word. What we want to see here to actually look at the data structures is turn this stuff into data. So it's not enough that we've mapped it. We have to actually somehow massage the information to get it together. So in this slide, which I'm sure won't crash PowerPoint. Good. I'm showing you
11:23
that you can ask radar what it knows about strings. So here I'm saying, hey, radar, IZ, what strings do you know? And it says nothing, nothing at all. But you can ask radar to search the entire file and just do like a string match, just like, you know, GNU strings does. And it says, I know all these strings, but if you look at the addresses, they're all in
11:40
22, right? They're not in the data segment that we created. They're in the wrong base offset. So the problem we have now is we have to actually take all those strings and map them where we want them to go. Probably with a patch to radar, you could fix this because IZ should do all data segments. But when you make a data segment, it doesn't recognize it. It only sees it when it comes from an L file. So this happens a lot when
12:01
you're using radar. And the solution is always just scripted, right? So I like Python. You could script it with Ruby. I know that there's like, you know, choices like VI, Emacs, Ruby, Python. But R2Pipe lets you script radar. Very simple Python script. I'm looking at the output of IIZ, and then I'm just adding that hex 10,000 to the base so that I can redefine
12:21
all the strings, right? So then when I'm done that, they're all redefined. All right. We need to groom the data too, right? Because we know that when we looked at that data section in memory, we saw disassembly. We didn't see quad words. Craig Hefner wrote a really nice script for IDA Pro called Codatify. When you're looking at firmware files, it's really useful to use this because it just kind of like grooms the entire data
12:42
segment, makes everything a quad word, sets everything to an offset type, and then what you get is like nice aligned data that automatically has cross references. So when you just start looking at xrefs and start looking at data segments, it just starts popping out at you. With radar, you don't get that automatically, but we can script it, right? And what we're doing here is just walking the entire area of
13:02
that data segment we created, defining everything as a quad word, and then creating a data reference of the value of that quad word. So you might be left with a bunch of like false positives, but it's better to have a bunch of false positives than have none of the cross references at all. So here's a look at what our like project file is now. You can
13:20
see we have the data segment, all the stuff we did before is in gray and I'm highlighting in white how you actually execute those Python scripts. So radar has this weird, has this shebang idiom, right? So you go hash bang or exclamation point Python to run my Python script and then I give it the two arguments. I'm just, all I'm doing, my project file now includes those two scripts that we defined. Now I know we look at a lot
13:41
of strings and text and stuff, so I just wanted to give you a funny picture. Take a break, but there will be more commands. Okay, more commands. So we're loading stage three, everything is all set. We know from our previous research that can signals, the whole array starts at this particular address. Just let's assume that you can figure it out by looking at the cross refs. If we go to that
14:02
point and we say, hey radar, what cross refs are there? It says, okay, good. Now we have cross refs. See, codatify went, set everything as a quad word and made data cross refs. And there's the proof. We actually have our data cross references. And if we disassemble at that location, we don't see disassembly, we now see nice quad word
14:20
statements, which is kind of like what you'd see in IDA Pro after you set everything to the data type, right? This helps us like inspect the data better. Lastly, there's this really nice command called PXR. It does telescoping. So if anyone uses like PwnBug or any of those GDB add-ons, telescoping is where you take like the value on your stack and you just keep treating it like a pointer until you can't treat it like a pointer anymore. So you get
14:42
this view of memory as it telescopes, right? And PXR does that for you automatically. So you can see PXR is telling us, yep, this particular address is pointing to something in data and that's pointing to something in data, good, that's all set. And we can see that we actually are referring to a string signal one, and it's actually starting to map to that structure really well, right? What we would really love to
15:00
be able to do is say, okay, if this signal is called tire pressure, what can message is that in, right? That's kind of like what we would like to know, right? If you had some vehicle and you wanted to figure out where's the tire pressure signal, that's what you'd like to know. And this kind of telescoping isn't telling us enough. We want to go backwards to get all that mapping information. Okay. What
15:21
we really want is types, right? Radair actually knows how to parse header files and how to pretty print types. So we could eventually tell it, hey, you know, we know this is a printer, a pointer to a can message, and we know that this is a pointer to a string, can you just pretty print that for me so I can see the ID in the string right next to each other? The answer is yes, you can. The first problem you run
15:41
into is if you rent load a raw binary file, you don't get any basic types, right? So here I'm asking Radair, hey, I opened that raw binary file, what types do you know about? I'm saying T enter. It says nothing. I don't know any types. Okay, so what if I ask you to give me the types in Radair commands because the star postfix turns any output from instead
16:01
just printing like the information in human form to printing it in Radair commands so you can just reuse it. It says, nope, sorry, I don't know any Radair transfer types either. The work around is you just open any elf file that's close to what you want to do and then use that T star command to tell that instance of Radair to dump all its type information to a file, which I'm doing with like a redirect
16:21
here at the bottom, right? And then when I go to the other instance of Radair, I can load that up, see the dot, I can load the types 32 file. So I got all the basic info and I'm like great, I got the basic types, I'll just go and load the header file from OpenXC, it'll be fine, right? No, that's not fine. Segfault. But we can fix that. You take the header file, you massage the header file a little bit, I won't bore
16:41
you with that, you just kind of like, you know, fix the types and all of a sudden we have all the info. Okay, so here's our new project file, we made some more flags, see the F commands, we're saying these are addresses that have important names. I'm doing dot types 32 to load in that basic type information and then I'm just loading a can util.h file which I've massaged into something that doesn't make
17:01
Radair Segfault, which is useful. Now I have to set the bit width back to 32. When you're doing disassembly it needs to be 16 to recognize thumb, but when you're doing the data inspection you want the pointers to all be 32 wide and if you leave it as 16 it treats pointers as half words, right? And then you're, you just don't even see the data. So we set it back to 32 to continue. All right, where are we at?
17:23
We can go to that can signal start array, I can ask Radair what type information do you know about this can signal type and it prints back this PF thing. So what it's telling me is this is a PF, a print format command for dumping that can signal structure, right? Now it's quite a long
17:41
string, you know, you have this kind of PZBBFFF and then a whole bunch of names, okay? If we just do that straight up, things don't really line up properly and we don't get stuff like, instead of seeing the address of that can message, we really would just like to see the arbitration ID in that message, right? That's the mapping we want to go for. And you notice that factor doesn't even make
18:01
sense. You can't have a factor of zero because if you take a number and you multiply it by zero, you're not going to get that number back. It's a useless can signal. So something has gone wrong. There's a lot of format commands, like a lot of them. Each one of them does something subtly different. If you're going to start doing this kind of thing, I recommend starting with that header file because then at least you don't have to just like grok the entire format. You
18:21
can tweak one format at a time. And what I did was PF question question to get this list. Okay? So we have that signal start. I'm going to first fix the can message type, which is the thing that the signal structure refers to because what I want to see is the arbitration ID. And that's really the only thing I care about in this message structure. See, it's
18:41
got an arbitrary, a bus, an ID, a format, frequency and stuff. I don't care about the rest. I just want that ID. So I know that the print format statement has PDE and all that stuff. I'm going to ignore things and you can ignore stuff with colons and dots. So I'm going to change it to, you know, colon X dot colon, colon, that whole statement you see there with the PF and all of a sudden all I see
19:01
is the ID. Great. That's all I want, right? So I'm going to make a format identifier, like a custom format identifier. I call it just the can message arbitration ID, please. And it'll remember that. And I can use that command. So now what I said is print me the current thing as a pointer to the structure. And I got back
19:21
exactly what I wanted. I got just the arbitration ID. So I'm sitting here at the top of the array, sitting on a pointer to this structure that has the arbitration ID. And I asked it to print me just the ID. We're good. So now we can build up the rest. Another break. I know I'm doing a lot of text. Here you go. Funny picture. Okay. All
19:41
right. So back to the can signal structure. That's a really, really big print format statement and it misaligned. So you look at the one that I'm highlighting in blue. There was an alignment problem in the can signal structure. And I had to add those two dots between the N1 and the F and I've told it now, okay, I've got two pointers, two bytes, two things you should ignore and then a whole bunch of floats. And the
20:01
good news is when you do that, it's exactly the info you want. So we have our bit position, we have our bit size. We have a factor that makes sense. We don't have like a pointer to the message with the arbitration ID yet. We need to get that to work. The problem is you can print either a pointer to a string in a format specifier or a pointer
20:21
to a structure in a format specifier, but you cannot print both a pointer to a structure and a pointer to a string in a format specifier. It just doesn't work for some reason. So more workarounds, what is print twice, right? But this gives us a chance to talk about macros, right? So Redair has this idea of macros. See the little round
20:40
brackets. So I define this thing called just print something and it's, you know, doing one print command, comma another print command. And then I can just call that macro by putting in around brackets later, we're off to the races. Okay. So we load this thing up. I can now say invoke that macro, see the round brackets on print signal. And
21:01
then I'm using the iterate operator. So I'm telling Redair to run that command on every address between the steps. It's just like Python's range operator, this address to that address with this step. And that means I can actually print each thing in a row in that whole array and we get what we're looking for, right? So you got the message structure pointer, which is just
21:21
showing you the arbitration ID right next to the bit position and the bit size and the factors and the min and max values. And that's all the really key information that you needed, right? If you compare like what's in the JSON from the build to what you can now pretty print with Redair, all the info you want is right there, right? So we're good. Now, because I like pain and
21:42
we're not over time, let's do a live demo. What do you think? Live demo? All right, let's do this. So actually I'm in the right folder. This is good. All right, we're going to invoke Redair. I have created the final stage, final boss. Okay. We have our macro
22:00
defined. I've also introduced a new command here with CF. CF lets you do like what you do in IDA, where you set an address to be formatted a particular way. So you can tell it, here's that print format statement and just make it persistent on this address. So I can go through all of memory and make it formatted so that when I do the disassembly, I can actually see the arrays. Here
22:21
we go. Final boss stage like that. Good. We'll seek to can signals start. No, we won't. Why not? Come on live demo. Ah, I did the
22:42
wrong stage, guys. Here we go. Here we go. Final boss. Yep. All right. So we formatted memory. Sorry. For those of you who can't see my keyboard, I'm typing V and then I hit the letter P to go to the disassembly view.
23:00
And when we're looking at disassembly, you know, we see the D words and when we're properly aligned in every place, we see the nice structures. And this is all the way through that array of can signals. So we did it. Are there any questions? I know that was really fast and a lot of commands. This
23:21
is the reference for commands. Let me put it up again so you guys can take some, some pictures. I'll definitely get the slides out as well. Hey, thanks for your attention, guys. I'm gonna have another.