#162045 - yellowstar - Sun Aug 24, 2008 1:19 am
Does anybody know what the URL of the ARM946E-S reference manual is? For the instruction set and ect. And the ARM7TDMI manual, for the instruction set?
Last edited by yellowstar on Mon Aug 25, 2008 12:26 am; edited 1 time in total
#162046 - chuckstudios - Sun Aug 24, 2008 1:28 am
#162069 - yellowstar - Sun Aug 24, 2008 9:14 pm
I didn't see any ARM architecture/instruction set description in those documents.(ARM9 atleast)
All I saw was reference to a book. I want a .pdf, or something I can download/view on Internet.
#162072 - kusma - Sun Aug 24, 2008 9:38 pm
Sounds like you're looking for the ARM ARM.
#162073 - yellowstar - Sun Aug 24, 2008 11:28 pm
Thanks kusma, that's exactly what I was looking for. Nothing about swile in this either, just swi<condition>... Does swile mean execute this swi instruction only if the two values compared with last, if like say: val1 <= val2?(This is in the Arm9 loader, the first piece of assembly run before anything else)
#162074 - kusma - Sun Aug 24, 2008 11:46 pm
yellowstar wrote: |
Thanks kusma, that's exactly what I was looking for. Nothing about swile in this either, just swi<condition>... Does swile mean execute this swi instruction only if the two values compared with last, if like say: val1 <= val2?(This is in the Arm9 loader, the first piece of assembly run before anything else) |
swile = swi + "le" (less or equal) condition, yeah.
#162076 - yellowstar - Mon Aug 25, 2008 12:25 am
In this loader, I see this:(I'm reversing the Nintendo Spot client, from the TSUTAYA Japanese game store) (I'm using a dissassembly from dsd, not obj-dump. Obj-dump seems to produce a different dissassembly, at least for the addresses in the left column. dsd seems to use addresses in the .nds, while obj-dump seems to do addresses for where it would be for a address when running on DS, like in RAM.)
*Thumb assembly disassembled as ARM removed*
These oprands for the swile instructions don't look anything like any id in the BIOS/swi table... They look more look addresses to jump to directly in the BIOS, or jump to assembly that then jumps to the actual assembly to be executed. Is this correct? Any idea what these do? I don't have a ARM9 BIOS dump... :\ I can't dump it myself either, I still don't have a flash card. At 0x00004C5C, I see more andeq instructions. Is this the end of the loader? Is the actual assembly for the binary,(not the loader)
the assembly that follows these many nop/andeq instructions?(This assembly begins at 00007000)
Last edited by yellowstar on Mon Dec 15, 2008 9:15 pm; edited 1 time in total
#162077 - tepples - Mon Aug 25, 2008 1:24 am
Are you sure those aren't Thumb?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#162080 - yellowstar - Mon Aug 25, 2008 2:58 am
tepples wrote: |
Are you sure those aren't Thumb? |
I don't know for sure. Isn't dsd and obj-dump supposed to detect Thumb/ARM automatically?
#162081 - Cydrak - Mon Aug 25, 2008 4:05 am
yellowstar wrote: |
I don't know for sure. Isn't dsd and obj-dump supposed to detect Thumb/ARM automatically? |
The ARM/THUMB distinction is made at runtime. That is, there's no flag in the instructions themselves. The compiler and linker have to keep track, but the info is probably stripped when building the .NDS. So how would you suggest to do it?
Since you have is a raw binary, the disassembler knows nothing. Teasing apart the pile of code and data is your job. ;-) Of course it can be done, but "reversing" is serious and somewhat specialised work. Expect to spend a lot of time, even if you're comfortable with assembler...
Anyhow, that's definitely THUMB, but not very interesting code. (See ARM ARM sec 6.2 and look up 0x4770 in the table. If you know what the result is for--as well you should if you're trying this--you'll soon learn to like it. :P)
#162082 - yellowstar - Mon Aug 25, 2008 4:28 am
Cydrak wrote: |
yellowstar wrote: | I don't know for sure. Isn't dsd and obj-dump supposed to detect Thumb/ARM automatically? |
The ARM/THUMB distinction is made at runtime. That is, there's no flag in the instructions themselves. The compiler and linker have to keep track, but the info is probably stripped when building the .NDS. So how would you suggest to do it?
Since you have is a raw binary, the disassembler knows nothing. Teasing apart the pile of code and data is your job. ;-) Of course it can be done, but "reversing" is serious and somewhat specialised work. Expect to spend a lot of time, even if you're comfortable with assembler...
|
I thought there was a flag in some instructions signifying whether or not the instruction, and the following instructions, were switched to ARM/Thumb instruction sets? I read that in ARM ARM... Wouldn't Nintendo be stuck with with just Thumb/ARM if the instruction set flag was stripped from the instructions, by there assembler/linker?(The processor wouldn't know whether the code was ARM/Thumb)
EDIT:
But I guess those instructions aren't present at the start of the Arm9 binary in this loader... If those flags really are stripped, that would mean tedious work trying to find if each opcode is ARM, or Thumb...(If those instruction sets are mixed together)
#162084 - Cydrak - Mon Aug 25, 2008 5:39 am
The processor doesn't "know" either. It boots in ARM mode, and the BIOS code starts that way too, so it works out. Specific instructions (mainly BX) can switch in and out of THUMB. It's usually done as part of a function call.
ARM and THUMB are rarely ever mixed within a function, since a switch is required.
Distinguishing ARM, THUMB and data isn't really the hardest part, if you've written much assembler. Data, or code in the opposite mode should just "feel" wrong. It looks repetitive or random, and often does things that are useless or would obviously crash. (There's three or four of those in your snippet alone!)
#162107 - yellowstar - Mon Aug 25, 2008 10:46 pm
Cydrak wrote: |
Distinguishing ARM, THUMB and data isn't really the hardest part, if you've written much assembler. Data, or code in the opposite mode should just "feel" wrong. It looks repetitive or random, and often does things that are useless or would obviously crash. (There's three or four of those in your snippet alone!) |
Which instructions? I wonder what ldrmib, andcs, and stcne do... I can't find anything in ARM ARM... What does the mib condition mean?(Yeah, this isn't the correct dissassembly, but since these seem to be real instructions, and and I can't finding anything in ARM ARM, I'd like to know what they do)
So I basically need to determine the instruction set for each function, and then manually disassemble the opcode for the correct instruction set. Wonder if there's any free disassemblers for NDS/Armv5 that can disassemble on select regions of opcodes for a selected instruction set... I tried using this to disassemble the whole .nds, but that didn't have a perfect dissassembly, and it's not open-source either.
#162109 - Cearn - Mon Aug 25, 2008 11:52 pm
yellowstar wrote: |
Cydrak wrote: |
Distinguishing ARM, THUMB and data isn't really the hardest part, if you've written much assembler. Data, or code in the opposite mode should just "feel" wrong. It looks repetitive or random, and often does things that are useless or would obviously crash. (There's three or four of those in your snippet alone!) |
Which instructions? I wonder what ldrmib, andcs, and stcne do... I can't find anything in ARM ARM... What does the mib condition mean?
|
Like swile, it's not ldrmib, andcs, or stcne instructions, but ldr-mi-b, and-cs and stc-ne, meaning 'load byte is negative', 'and if carry set' and store coprocessor if not equal'. You'll only find the stem of the instructions in an instruction set, without adornments, much like you would only find the infinitive of a verb in a dictionary and not its conjugations. GBATek has a useful summary of the instructions, and how they are composed.
yellowstar wrote: |
(Yeah, this isn't the correct dissassembly, but since these seem to be real instructions, and and I can't finding anything in ARM ARM, I'd like to know what they do)
|
Instructions are just an interpretation of binary data. Any set of words can be disassembled to (mostly) valid instructions -- you could even disassemble a bitmap or sound-file into ARM instructions, or vice versa. However, what you get from such an attempt will basically be meaningless unless your chosen interpretation matches the one intended by the author. This is an important point to realize when dealing with binary data manually.
Like Cydrak mentioned, you can generally tell if you have the right interpretation once the stuff starts to make some kind of sense. Your data, when interpreted as ARM code, doesn't. In Thumb, it looks like a set of functions for calling software interrupts. Something like this:
Code: |
...
swi 0x05
bx lr
swi 0x06
bx lr
swi 0x09
bx lr
...
|
yellowstar wrote: |
So I basically need to determine the instruction set for each function, and then manually disassemble the opcode for the correct instruction set. Wonder if there's any free disassemblers for NDS/Armv5 that can disassemble on select regions of opcodes for a selected instruction set... I tried using this to disassemble the whole .nds, but that didn't have a perfect dissassembly, and it's not open-source either. |
I haven't tried this yet, but it seems obj-dump can be set to produce Thumb code with the -M switch (look it up in the obj-dump manual. You could also try loading it as data in an emulator and see what its disassembler makes of it. Desmume's disassembler, for example, lets you switch the view between instruction sets, although it unfortunately doesn't seem to let you save it.
#162110 - yellowstar - Tue Aug 26, 2008 12:29 am
How do I convert an .nds to an .elf, for using obj-dump? Obj-dump can only use .elf - I tried using .nds before, and I got error "file format not recognized".(That is, does anyone know of a tool to convert the Arm7/9 binaries in an .nds to .elf?)
#162111 - tepples - Tue Aug 26, 2008 12:47 am
Cydrak wrote: |
yellowstar wrote: | I don't know for sure. Isn't dsd and obj-dump supposed to detect Thumb/ARM automatically? |
The ARM/THUMB distinction is made at runtime. That is, there's no flag in the instructions themselves. The compiler and linker have to keep track, but the info is probably stripped when building the .NDS. So how would you suggest to do it? |
Some NES emulators can log which bytes are opcodes and which are data. So why not make a GBA or DS emulator that can log which mode a piece of code was executed in?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#162112 - tepples - Tue Aug 26, 2008 12:50 am
Try extracting the ARM9 segment and playing with it in arm-eabi-objcopy, taking note of its --set-start option.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#162117 - Cydrak - Tue Aug 26, 2008 2:14 am
tepples wrote: |
Some NES emulators can log which bytes are opcodes and which are data. So why not make a GBA or DS emulator that can log which mode a piece of code was executed in? |
Whoa! So this is really a viable option for most people? ;-) I guess patching Desmume is still easier than static analysis...
My (rhetorical) question was how an automatic tool like objdump, which obviously has no compile-time or run-time information could be expected to do it (as yellowstar assumed). The emulator solution requires a human to play through the game. While there's nothing wrong with that--IDA is interactive too, and an amazing tool if the free version is any indication--it's quite another thing, isn't it? >_>
</verbose>
yellowstar wrote: |
How do I convert an .nds to an .elf, for using obj-dump? |
By hand, I imagine? Well, you shouldn't need to. After extracting the ARM9 binary with ndstool (-x, -9 options IIRC), tell arm-eabi-objdump to use "binary" format and "arm" architecture (-b, -m switches respectively). "-M force-thumb" and "-M reg-names-std" may also be helpful.
At this point, if you have more than (say) 32-64KB of binary you won't want to disassemble it all--it will be quite unwieldy and old computers tend to choke on it. Instead, just give objdump the address range you want to work with. I haven't done much with large binaries... but as for finding the right spots, all I can say is: take notes, keep a good eye out, and spend some quality time with a hex editor.
#162119 - yellowstar - Tue Aug 26, 2008 2:30 am
Cydrak wrote: |
After extracting the ARM9 binary with ndstool (-x, -9 options IIRC)
At this point, if you have more than (say) 32-64KB of binary you won't want to disassemble it all--it will be quite unwieldy and old computers tend to choke on it. Instead, just give objdump the address range you want to work with. |
Those ndstool parameters didn't do anything. Not a really big problem, I can add some code to Wmb Asm to dump the Arm7/9.(And write custom software to do it, if the need ever arises)
EDIT:
I have successfully dumped the Arm7/9 binaries.
#162122 - yellowstar - Tue Aug 26, 2008 2:57 am
Code: |
arm-eabi-objdump -b -m -M force-thumb -d -S arm9.bin >arm9.s
arm-eabi-objdump: arm9.bin: Invalid bfd target
|
#162135 - Kyoufu Kawa - Tue Aug 26, 2008 7:13 pm
Me, I use IDA. Once you declare a certain offset to contain ARM or Thumb code, it'll spread out through the branches and use the off-by-one rule to determine when to switch. Of course, this works best if you hit crt0 or the main() routine.
So yeah, I like IDA.
#162147 - yellowstar - Tue Aug 26, 2008 10:06 pm
IDA Pro costs ~$500... And I'm not risking losing my reverse engineering progress, assembly comments, ect, on a 30-day trial.
#162150 - tepples - Tue Aug 26, 2008 10:48 pm
We already saw what happened to MoonShell. The developer switched to a trial version of a non-libre non-gratis compiler, and when it expired, MoonShell fell unmaintained.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#162178 - chishm - Wed Aug 27, 2008 1:53 pm
yellowstar wrote: |
Code: |
arm-eabi-objdump -b -m -M force-thumb -d -S arm9.bin >arm9.s
arm-eabi-objdump: arm9.bin: Invalid bfd target
|
|
You almost had it. I'd write the correct code, but it seems I already have.
_________________
http://chishm.drunkencoders.com
http://dldi.drunkencoders.com
#162192 - yellowstar - Wed Aug 27, 2008 8:40 pm
Thanks chishm, that seems to have worked.
*Thumb dissassembly removed*
Now I need to lookup these instructions and begin reversing. :-)
Two more questions. I have attempted to disassemble Wii Nintendo Channel earlier. CPU is a PowerPC processor, Broadway, based on the Ganecube's Gekko, based on PPC 750, or some model in that range. I know I have the correct .dol. I converted the .dol to .elf with doltool. I attempted to disassemble the binary with obj-dump. However, I see ... in some places throughout the disassembly. And I see another ... at the end of the disassembly. What does the ... from obj-dump mean? Also, would anyone happen to know the link to the ARM926EJ-S ARM?(It's the hidden processor in the Wii, known as Starlet, which runs IOS.)
Last edited by yellowstar on Mon Dec 15, 2008 9:17 pm; edited 1 time in total
#162194 - Cydrak - Wed Aug 27, 2008 9:53 pm
yellowstar wrote: |
Now I need to lookup these instructions and begin reversing. :-) |
Actually, if you have to look them up, then I think you need to spend a few solid months learning to read and write ARM assembler. It may also help to disassemble small programs you compiled yourself. This way you can recognize what the compiler does to different sorts of code, and see what the programmer might have intended.
So, have you written much ASM? If not, let me put this another way. What you're proposing would be like trying to comprehend, say, a French poem--without having ever spoke French, much less lived anywhere near France. Sure, you could try, but it would not only be highly confusing; understanding a few words is worlds away from understanding the metaphors and cultural context. So, if you succeeded, you'd still spend the extra time to study and learn some French in the process, whether you wanted to or not.
Yeah, the challenge is interesting, but why make it next to impossible? What you're trying to do sounds like no fun at all. :|
Btw, I think the code near the bottom (which wasn't in your original listing) uses ARM instructions.
#162202 - yellowstar - Thu Aug 28, 2008 12:04 am
Cydrak wrote: |
yellowstar wrote: | Now I need to lookup these instructions and begin reversing. :-) |
Actually, if you have to look them up, then I think you need to spend a few solid months learning to read and write ARM assembler. It may also help to disassemble small programs you compiled yourself. This way you can recognize what the compiler does to different sorts of code, and see what the programmer might have intended.
So, have you written much ASM? If not, let me put this another way. What you're proposing would be like trying to comprehend, say, a French poem--without having ever spoke French, much less lived anywhere near France. Sure, you could try, but it would not only be highly confusing; understanding a few words is worlds away from understanding the metaphors and cultural context. So, if you succeeded, you'd still spend the extra time to study and learn some French in the process, whether you wanted to or not.
|
I hardly wrote any assembler at all... I wrote a tiny amount of asm, but that isn't really enough. I successfully reversed engineered a x86 crack me, learning the instructions as I went, but that was easy since the program was simple and it was rather obvious where code which checks the key input by the user was.(IDA Free, which is restricted to only x86, program flow map) Of course NDS reverse engineering is way different, and this is actually an real challenge, not something easy.(There are much harder crack mes on that web site, but I got bored of it - I wanted to RE console/hand held software, not PC stuff.)
#162206 - yellowstar - Thu Aug 28, 2008 2:04 am
Here's the dissassembly of the section mentioned earlier disassembled in ARM:
*dasm removed*
This appears to be valid ARM asm. Dissassembling starting at 0x84e seems to produce nonsense. What's this ip register? I never heard of a ip register on ARM. I couldn't find anything in ARM ARM either. I know about the x86 ip instruction pointer register, however. What is the ARM ip register used for?
Here's the dissassembly that appears to be nonsense, imo.
*dasm removed*
EDIT:
Apparently address 0x4000000 is REG_DISPCNT... So is ip a general purpose register, or something else?
Last edited by yellowstar on Mon Dec 15, 2008 9:19 pm; edited 1 time in total
#162210 - tepples - Thu Aug 28, 2008 3:12 am
Every register but r13-r15 (sp, lr, pc) is "general purpose".
'ip' is the Cispa word for "past", but on ARM it means r12, one of the five general-purpose registers that a procedure is allowed to change before returning. (The others are r0-r3, which hold arguments.)
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#162230 - Cearn - Thu Aug 28, 2008 10:42 am
Cydrak wrote: |
yellowstar wrote: | Now I need to lookup these instructions and begin reversing. :-) |
Actually, if you have to look them up, then I think you need to spend a few solid months learning to read and write ARM assembler. It may also help to disassemble small programs you compiled yourself. This way you can recognize what the compiler does to different sorts of code, and see what the programmer might have intended.
So, have you written much ASM? If not, let me put this another way. What you're proposing would be like trying to comprehend, say, a French poem--without having ever spoke French, much less lived anywhere near France. Sure, you could try, but it would not only be highly confusing; understanding a few words is worlds away from understanding the metaphors and cultural context. So, if you succeeded, you'd still spend the extra time to study and learn some French in the process, whether you wanted to or not. |
Cydrak's righ ... no, let's do this differently.
Cydrak heeft gelijk. Een tekst begrijpen is meer dan alleen de woorden kunnen lezen. Als je de grammatica of algemene uitdrukkingen niet kent weet je eigenlijk nog niks. Dit geldt nog sterker voor assembly->C conversie. Zelf als je beide talen goed begrijpt is het een lastig karwei om te zien wat er gaande is, omdat een C-expressie vaak meerdere instructies in beslag neemt en omdat de context van de functies en data waarschijnlijk nog onduidelijk zal zijn.
yellowstar wrote: |
This appears to be valid ARM asm. Dissassembling starting at 0x84e seems to produce nonsense. What's this ip register? I never heard of a ip register on ARM. I couldn't find anything in ARM ARM either. |
IP, and a few other identifiers, are aliases that GCC likes to use.
Code: |
std | alt
-----+-----
r0 | a1
r1 | a2
r2 | a3
r3 | a4
r4 | v1
r5 | v2
r6 | v3
r7 | v4
r8 | v5
r9 | v6
r10 | v7 / sl
r11 | v8 / fp
r12 | ip
r13 | sp
r14 | lr
r15 | pc
|
Don't rely on just one document; as you can see it might not have all the information, or be too technical for what you're doing. Also look at the Arm Architecture Procedure Call Standard, and Writing Efficient C for ARM and, oh what the hell why not, Tonc's chapter on assembly. If you don't already have a printed copy of the ARM/Thumb quickreference sheets, I'd advise you to get one of those too.
Also compile some of the C code you have with -save-temps (and with optimization at -O1 or -O2 but without -g; the extra debugging information makes things hard to read), which will make GCC output the generated assembly for the C files in your project. Examining this gives you a better understanding of the things you may come across.
yellowstar wrote: |
Here's the disassembly that appears to be nonsense, imo.
Code: | 0000084e <.data+0x84e>:
84e: c3010000 movwgt r0, #4096 ; 0x1000
852: c208e3a0 andgt lr, r8, #-2147483646 ; 0x80000002
856: 00b6e58c adceqs lr, r6, ip, lsl #11
85a: 0000e1dc ldreqd lr, [r0], -ip |
|
What you have here is a frame shift. ARM instructions are always put on word boundaries. You can't start in the middle of an instruction and expect to see something sensible.
It's unfortunate that everything here uses decimal numbers. In hex, it's clearer what memory the code is accessing. I'm guessing the code does something like this:
Code: |
00000850 <.data+0x850>:
850: e3a0c301 mov ip, #67108864 @ REG_BASE
854: e58cc208 str ip, [ip, #0x0208] @ REG_IME= 0
.LwaitForVDraw:
858: e1dc00b6 ldrh r0, [ip, #0x0006] @ REG_VCOUNT
85c: e3500000 cmp r0, #0 ; 0x0
860: 1afffffc bne .LwaitForVDraw
864: eb000097 bl 0xac8
@ --- Init sp_svc ---
868: e3a00013 mov r0, #0x13
86c: e121f000 msr CPSR_c, r0 @ cpuMode= SWI (irq off)
870: e59f00f0 ldr r0, .L968
874: e2800dff add r0, r0, #16320 @ 0x3fc0
878: e1a0d000 mov sp, r0 @ Set sp_svc
@ --- Init sp_irq ---
87c: e3a00012 mov r0, #0x12
880: e121f000 msr CPSR_c, r0 @ cpuMode= IRQ
884: e59f00dc ldr r0, .L968
888: e2800dff add r0, r0, #16320 @ 0x3fc0
88c: e2400040 sub r0, r0, #64 @ 0x40
890: e240d004 sub sp, r0, #4 @ set sp_iqr
894: e31d0004 tst sp, #4
898: 024dd004 subeq sp, sp, #4 @ Align to 8
@ --- Init sp_usr ---
89c: e59f10c8 ldr r1, .L968+4 @
8a0: e0401001 sub r1, r0, r1
8a4: e3a0001f mov r0, #0x1F
8a8: e12ff000 msr CPSR_fsxc, r0 @ cpuMode= User
.L968:
.word ??? // Base sp_irq address ?
.word ??? // Base sp_usr address ?
|
#162258 - yellowstar - Thu Aug 28, 2008 8:35 pm
Cearn wrote: |
yellowstar wrote: | This appears to be valid ARM asm. Dissassembling starting at 0x84e seems to produce nonsense. What's this ip register? I never heard of a ip register on ARM. I couldn't find anything in ARM ARM either. |
IP, and a few other identifiers, are aliases that GCC likes to use.
Code: | std | alt
-----+-----
r0 | a1
r1 | a2
r2 | a3
r3 | a4
r4 | v1
r5 | v2
r6 | v3
r7 | v4
r8 | v5
r9 | v6
r10 | v7 / sl
r11 | v8 / fp
r12 | ip
r13 | sp
r14 | lr
r15 | pc
|
Don't rely on just one document; as you can see it might not have all the information, or be too technical for what you're doing. Also look at the Arm Architecture Procedure Call Standard, and Writing Efficient C for ARM and, oh what the hell why not, Tonc's chapter on assembly. If you don't already have a printed copy of the ARM/Thumb quickreference sheets, I'd advise you to get one of those too.
|
Thanks, Cearn and tepples.
Cearn wrote: |
Also compile some of the C code you have with -save-temps (and with optimization at -O1 or -O2 but without -g; the extra debugging information makes things hard to read), which will make GCC output the generated assembly for the C files in your project. Examining this gives you a better understanding of the things you may come across.
|
I'll do that.
Cearn wrote: |
It's unfortunate that everything here uses decimal numbers. In hex, it's clearer what memory the code is accessing. I'm guessing the code does something like this:
|
I reversed engineered that assembly myself. I didn't have the time to reverse engineer the rest of that since I was reading an ARM ASM tutorial, and other reasons.(I happened to find an old gba arm asm tutorial when I googled for address 0x4000000)
Last edited by yellowstar on Mon Dec 15, 2008 9:20 pm; edited 1 time in total
#162439 - yellowstar - Wed Sep 03, 2008 12:01 am
Is there a manual somewhere explaining the Arm9/7 co-processor opcodes used in the mcr/mrc ARM instructions? What do Arm9 co-procsssor opcodes 0, 1, 2, 3, and 4 do?(I didn't find anything in the Arm9 manual linked to at the start of this topic)
(I'll try reverse engineering a devkitARM .nds after I reverse engineer these official loaders)
*dasm removed*
Last edited by yellowstar on Mon Dec 15, 2008 9:20 pm; edited 1 time in total
#162441 - Cearn - Wed Sep 03, 2008 12:28 am
It's in the DDI0201 paper chuckstudios linked to, but it's well hidden. The coprocessor you're looking for is CP15, which has to do with the TCM, cache and protection unit among other things. You can find an overview in Section 2.3 and other tidbits some of the chapters after it.
#162445 - yellowstar - Wed Sep 03, 2008 1:17 am
Thanks Cearn.
#162503 - yellowstar - Thu Sep 04, 2008 1:23 am
*dasm removed*
How are additional co-processor registers in mrc/mcr handled? These are the ID Code Register, and the Control Register. R0 is zero prior to this, so it can't be some kind of assembly pointer... And R0 and the Co-processor registers are 32-bit, so the Co-processor registers couldn't be squeezed into r0 directly... Is this moving cr1 into r0, then left-shifting r0 by cr0's value, or is it moving/loading cr1 into r0, then adding cr0 to r0, and storing the result in r0?
Last edited by yellowstar on Mon Dec 15, 2008 9:21 pm; edited 1 time in total
#163632 - yellowstar - Tue Oct 07, 2008 2:59 am
ARM ARM on SPR mode bits wrote: |
A2.5.7 The mode bits
M[4:0] are the mode bits. These determine the mode in which the processor operates. Their interpretation
is shown in Table A2-2.
Table A2-2 The mode bits
M[4:0] Mode Accessible registers
0b10000 User PC, R14 to R0, CPSR
0b10001 FIQ PC, R14_fiq to R8_fiq, R7 to R0, CPSR, SPSR_fiq
0b10010 IRQ PC, R14_irq, R13_irq, R12 to R0, CPSR, SPSR_irq
0b10011 Supervisor PC, R14_svc, R13_svc, R12 to R0, CPSR, SPSR_svc
0b10111 Abort PC, R14_abt, R13_abt, R12 to R0, CPSR, SPSR_abt
0b11011 Undefined PC, R14_und, R13_und, R12 to R0, CPSR, SPSR_und
0b11111 System PC, R14 to R0, CPSR (ARMv4 and above)
|
Is this real hex? Or is it values of the bits for each mode, with 0b pre-appended to each of these? Are the following comments I added to the Nintendo Spot Arm7 dissassembly correct?
*dasm removed*
Last edited by yellowstar on Mon Dec 15, 2008 9:22 pm; edited 1 time in total
#163634 - tepples - Tue Oct 07, 2008 6:28 am
Some languages (not standard C) use the prefix 0b for binary in the same way that C uses 0x for hexadecimal.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.
#163661 - yellowstar - Wed Oct 08, 2008 3:41 am
Does anyone know what addresses 0x4708434 - 0x4708594 and 0xFCffff3A - 0xFD00009A are used for? What values are stored at these addresses?
*dasm removed*
Last edited by yellowstar on Mon Dec 15, 2008 9:23 pm; edited 1 time in total
#163678 - Cearn - Wed Oct 08, 2008 5:33 pm
Are you sure you're reading it right? None of those addresses make much sense, so make sure you have your PC-relative offsets right.
For example, this
Code: |
40: e59f0090 ldr r0, [pc, #144] ; Sets r0 to 0xE0846005
44: e1a0d000 mov sp, r0 ; Sets mode IRQ's sp to r0, |
would set the stack to an odd value, which is definitely not allowed.
#163679 - Miked0801 - Wed Oct 08, 2008 5:39 pm
Dunno, but how are you determining that r0 and r1 are being loaded with those funky values? I'm guessing those are pool loads, but just checking.
0x04708434 would be well into mirrored I/O ports on Arm7 or Arm9
0xFCFFFF3a should be access restricted. It's very close to ARM-9 BIOS though which starts at 0xFFFF0000. Copying 160 bytes makes me think that this is the bootup logo copy code, though I'm making a wild ass guess.
#163687 - yellowstar - Wed Oct 08, 2008 11:44 pm
Miked0801 wrote: |
Dunno, but how are you determining that r0 and r1 are being loaded with those funky values? |
I used the address two instructions down from the instruction reading pc, then I added that address to the offset being used. Since XVI32 apparently handles everything in the data inspector as signed values, there is no unsigned option, I used a tool I wrote to find the hex and decimal of the value of the addresses I found from adding these addresses and offsets.
Miked0801 wrote: |
I'm guessing those are pool loads, but just checking.
|
Well, all/most of the addresses loaded from seem to be inside a procedure... It's in Thumb however, and I haven't attempted disassembling it. I haven't disassembled or reversed engineered anything other than what I posted, so I'm not 100% sure.
Miked0801 wrote: |
Copying 160 bytes makes me think that this is the bootup logo copy code, though I'm making a wild ass guess. |
I said 0x160 bytes. In decimal that would be 352 bytes.
#163707 - Miked0801 - Thu Oct 09, 2008 3:45 pm
So no debugger info - no direct register inspection is possible?
#163719 - Maxxie - Thu Oct 09, 2008 6:55 pm
I'd guess that you did get the wrong content for the registers.
0xFCFFFF3a only makes sense when it is used in indirect addressing, which the code you think uses this address doesn't do.
0xFCFFFF3A is -0x020000C4 which would make sense for a code address or offset to the arm7 bios or arm9 I/DTCM?
Actually the previous SP sets also point to the point that you are having the wrong content: 0xE0846005 is more likely an ARM opcode then it is the correct SP.
The code at 0x44 should grab the value from 0x44+0x08+0x90 = 0xDC
0x5C from 0xE0
0x60 from 0xE4
_________________
Trying to bring more detail into understanding the wireless hardware
#163722 - Cearn - Thu Oct 09, 2008 7:15 pm
The PC-relative loads seem to come from CCh onward. Could you post the data between, say, C0h and F0h? That might shed some light on things.
#163731 - yellowstar - Thu Oct 09, 2008 10:38 pm
Apparently I was obtaining the addresses from the wrong offset... I was adding the address two instructions below using the ldr instruction, plus the hex offset in the comment for the ldr instruction. (The comment was added by obj-dump) However, since obj-dump apparently attempts to calculate the offset automatically, so I only need to obtain the data at that offset, I got the wrong offset. However even obj-dump seemed to have failed at calculating the offset - for that Supervisor ldr sp instruction, it calculated 0xd4, while Maxxie calculated 0xdc. The former address contained at obj-dump's calculated offset wasn't aligned correctly, while Maxxie's was: 0x40000. I manually calculated this, and I got the same offset as obj-dump. The difference between Maxxie's offset and obj-dump is 8 bytes. Maxxie, I noticed that you started with address of the first instruction after the ldr sp instruction, not the actual ldr instruction. If this wasn't a mistake, why is this needed?(For ldr instructions loading into sp) *link removed* Here's the first 0x158 bytes of this Arm7 binary.
Last edited by yellowstar on Mon Dec 15, 2008 9:24 pm; edited 1 time in total
#163747 - Maxxie - Fri Oct 10, 2008 7:46 am
yellowstar wrote: |
Maxxie, I noticed that you started with address of the first instruction after the ldr sp instruction, not the actual ldr instruction. |
That was a mistake, sorry.
It is the pc of the ldr instruction (= address of the ldr instruction + prefetch size) + offset.
_________________
Trying to bring more detail into understanding the wireless hardware
#163771 - yellowstar - Fri Oct 10, 2008 8:22 pm
I now get 0xc0ff8003 for Supervisor's sp, and 0x80ff8003 for IRQ's sp. I assume these are wrong since they are not 32-bit aligned, so what's the correct addresses, and where do those come from? Does anyone know what address used in the following dissassembly, do/are used for?(I tried finding some of the addresses in gbatek, but I didn't really find anything...)
*dasm removed*
Last edited by yellowstar on Mon Dec 15, 2008 9:25 pm; edited 2 times in total
#163774 - Maxxie - Fri Oct 10, 2008 8:45 pm
yellowstar wrote: |
I now get 0xc0ff8003 for Supervisor's sp, |
Wrong endianess:
0xc0, 0xff, 0x80, 0x03 (in byte order) is 0x0380ffc0 (as little endian word) which is a valid and very plausible address on the arm7.
_________________
Trying to bring more detail into understanding the wireless hardware
#163777 - Cearn - Fri Oct 10, 2008 8:59 pm
yellowstar wrote: |
I now get 0xc0ff8003 for Supervisor's sp, and 0x80ff8003 for IRQ's sp. I assume these are wrong since they are not 32-bit aligned, so what's the correct addresses, and where do those come from? Does anyone know what address used in the following dissassembly, do/are used for?(I tried finding some of the addresses in gbatek, but I didn't really find anything...) |
Words and halfwords already have the endianness taken care of. 0380ff00 really is 0x0380ff00 and not 0x00FF8003.
The addresses do seem to relate to special areas. A cursory glance at GBATek gave me this:
Code: |
023x:xxxx : Debugger RAM (?)
027F:Fxxx : shared main RAM / personal data
0280:xxxx : ???
037F:xxxx : shared WRAM
0380:Fxxx : ARM7-WRAM, stack locations
FFFF:xxxx : BIOS
|
#163779 - yellowstar - Fri Oct 10, 2008 10:27 pm
Ah, obj-dump doesn't output "address: <exact hex data from input>", as I thought, it automatically outputs the the hex data as an 32-bit hex number. So manually decoding that data in the dissassembly is unneeded, which was producing those invalid addresses. The dissassembly in my previous post has been updated.
I found the answer to that mcr/mrc question.
gbatek wrote: |
ARM CP15 Overview
...
CP15 Opcodes
CP15 can be accessed via MCR and MRC opcodes, with Pn=P15, and <cpopc>=0.
MCR{cond} P15,0,Rd,Cn,Cm,<cp> ;move from ARM to CP15
MRC{cond} P15,0,Rd,Cn,Cm,<cp> ;move from CP15 to ARM
Rd can be any ARM register in range R0-R14, R15 should not be used with P15.
Cn,Cm,<cp> are used to select a CP15 register, eg. C0,C0,0 = Main ID Register.
Other coprocessor opcodes (CDP, LDC, STC) cannot be used with P15.
CP15 Register List
Register Expl.
C0,C0,0 Main ID Register (R)
C0,C0,1 Cache Type and Size (R)
C0,C0,2 TCM Physical Size (R)
C1,C0,0 Control Register (R/W, or R=Fixed)
C2,C0,0 PU Cachability Bits for Data/Unified Protection Region
C2,C0,1 PU Cachability Bits for Instruction Protection Region
C3,C0,0 PU Write-Bufferability Bits for Data Protection Regions
C5,C0,0 PU Access Permission Data/Unified Protection Region
C5,C0,1 PU Access Permission Instruction Protection Region
C5,C0,2 PU Extended Access Permission Data/Unified Protection Region
C5,C0,3 PU Extended Access Permission Instruction Protection Region
C6,C0..C7,0 PU Protection Unit Data/Unified Region 0..7
C6,C0..C7,1 PU Protection Unit Instruction Region 0..7
C7,Cm,Op2 Cache Commands and Halt Function (W)
C9,C0,0 Cache Data Lockdown
C9,C0,1 Cache Instruction Lockdown
C9,C1,0 TCM Data TCM Base and Virtual Size
C9,C1,1 TCM Instruction TCM Base and Virtual Size
C13,Cm,Op2 Misc Process ID registers
C15,Cm,Op2 Misc Implementation Defined and Test/Debug registers
|
#164455 - yellowstar - Sun Nov 02, 2008 7:42 pm
What's the machine code for an infinite loop? A branch branching to itself, to the same branch instruction?(The hex for each of the 4 ARM bytes?)(iDeas doesn't immediately stop on a BKPT instruction...)
#164457 - Maxxie - Sun Nov 02, 2008 10:44 pm
In arm mode:
0xEAFFFFFE: B PC + (-2*4)
Is
@here: B here
_________________
Trying to bring more detail into understanding the wireless hardware
#164459 - yellowstar - Mon Nov 03, 2008 3:23 am
Thanks Maxxie, that worked.
#164763 - yellowstar - Tue Nov 18, 2008 9:29 pm
What happens when software writes to REG_IF, outside of any interrupt handler? Does the interrupts specified by the bits written, get "triggered"?(IRQ handler being executed, handling these "interrupts"?)
EDIT:
Does it matter with REG_IF writing from software, for the IPC Recv FIFO Not Empty bit, if the REG_IE bit for IPC Recv FIFO Not Empty interrupt is not yet enabled in REG_IE, but it is already enabled in IPCFIFOCNT?
#164772 - Miked0801 - Wed Nov 19, 2008 3:05 pm
Pretty sure as soon as the IF bits are set, the app will jump to interrupt.
#164839 - yellowstar - Mon Nov 24, 2008 9:46 pm
There's some compressed server URL embedded in this Arm9 binary. I attempted to decompress it with lzo1x, but an access violation occurred. I'd like to reverse engineer the decompression by code finding a reference to the code which uses it, (Kind of like IDA's XREF) but when searching for the URL's address in the file minus 0x4000,(I also tried adding 0x02000000 to this address, but didn't find anything at all) I can only find the attempted dissassembly of the URL in both ARM/Thumb disassembles.
EDIT: I attempted to find the 32-bit value used for a string in one of my homebrew programs, and succeeded. I tried searching for various addresses of the compressed URL minus 0x4000, but didn't find anything. So, never mind... Guess the code that loads the compressed URL's address is obfuscated and doesn't directly load this address...
#164880 - yellowstar - Sat Nov 29, 2008 6:08 pm
Could someone check what I did wrong with this C++ implementation of L_9a0 in the Arm9 loader? I checked it many times over many days, and still couldn't find the fix...
*Arm9 self-decompression dasm removed*
Code: |
#include <stdio.h>
#include <strings.h>
#include <malloc.h>
bool move_ram = 0;
bool gen_ram = 0;
char *arm9_filename = NULL;
unsigned char *RAM = NULL;
void L_9a0(unsigned int start, size_t length, unsigned int clearval);
void L_98c(unsigned int start, size_t length, unsigned int clearval)
{
unsigned int *ptr;
ptr = (unsigned int*)start;
for(size_t i=0; i<length; i+=4)
{
*ptr++ = clearval;
}
L_9a0(start, length, clearval);
}
void L_9a0(unsigned int start, unsigned int length, unsigned int clearval)
{
unsigned char *source, *dest;
unsigned char val;
int a, b;
int counter;
if(clearval==0)return;
length += clearval;
dest = (unsigned char*)((int)RAM + (int)0x12C344);
source = (unsigned char*)((int)RAM + (int)0xD5F5B);
start = (unsigned int)((int)RAM + (int)0x4000);
while((int)source > (int)start)
{
source = (unsigned char*)((int)source - 1);
val = *source;
counter = 8;
while(counter>=0)
{
counter--;
if(counter<0)break;
if((int)source<=(int)start)
{
return;
}
if(val & 0x80)
{
source = (unsigned char*)((int)source - 1);
a = *source;
source = (unsigned char*)((int)source - 1);
b = *source;
b |= a<<8;
b &= ~0xF000;
b += 2;
a += 32;
while(a>=16)
{
dest--;
*dest = *((unsigned char*)((int)dest + (int)b));
a-=16;
}
}
else
{
dest--;
source = (unsigned char*)((int)source - 1);
*dest = *source;
}
val = val << 1;
}
}
}
void L_a4c()
{
unsigned int *start = (unsigned int*)((int)RAM + (int)0x12c320);
unsigned int *stop_addr = (unsigned int*)((int)RAM + (int)0x12c344);
unsigned int *source = (unsigned int*)((int)RAM + (int)0xb5800);
unsigned int *dest, val1, end, val2;
while((unsigned int)start != (unsigned int)stop_addr)
{
val1 = *start;
start++;
val2 = *start;
start++;
end = (val1 - 0x02000000) + (val2 - 0x02000000);
dest = ((unsigned int*)((int)RAM + ((int)val1 - 0x02000000)));
while(end > (unsigned int)dest)
{
val2 = *source;
source++;
*dest = val2;
dest++;
}
val2 = *start;
start++;
end = (unsigned int)dest + ((int)val2 - 0x02000000);
while(end > (unsigned int)dest)
{
*dest = 0;
dest++;
}
}
}
int GetFileLength(FILE* _pfile)
{
int l_iSavPos, l_iEnd;
l_iSavPos = ftell(_pfile);
fseek(_pfile, 0, SEEK_END);
l_iEnd = ftell(_pfile);
fseek(_pfile, l_iSavPos, SEEK_SET);
return l_iEnd;
}
int main(int argc, char *argv[])
{
FILE *file = NULL;
int filesize = 0;
printf("Arm9loader");
if(argc==1)
{
printf(" application by yellowstar 10/26/08\n");
printf("C++ conversion of the memory moving assembly in the official Arm9 loaders.\n");
printf("Usage:\n");
printf("-g<arm9.bin>: Generate the required ram.bin from the Arm9 binary specified.\n");
printf("-m: Move memory around like the official Arm9 loader does, from ram.bin, and write the new arragment to new_ram.bin.\n");
}
else
{
printf("\n");
move_ram = 0;
gen_ram = 0;
RAM = (unsigned char*)malloc(0x400000);
if(RAM==NULL)
{
printf("Failed to allocate memory.\n");
return 1;
}
memset(RAM, 0, 0x400000);
for(int argi = 1; argi<argc; argi++)
{
if(strcmp(argv[argi], "-m")==0)
{
move_ram = 1;
}
if(argv[argi][0] == '-' && argv[argi][1] == 'g')
{
gen_ram = 1;
arm9_filename = (char*)malloc(256);
if(arm9_filename==NULL)
{
printf("Failed to allocate memory.\n");
return 1;
}
memset(arm9_filename, 0, 256);
strcpy(arm9_filename, &argv[argi][2]);
file = fopen(arm9_filename, "rb");
if(file==NULL)
{
printf("Failed to open file %s.\n", arm9_filename);
return 1;
}
filesize = GetFileLength(file);
fread(RAM, 1, filesize, file);
fclose(file);
file = fopen("ram.bin", "wb");
fwrite(RAM, 1, 4000000, file);
fclose(file);
free(arm9_filename);
}
}
if(!gen_ram)
{
file = fopen("ram.bin", "rb");
if(file!=NULL)
{
fread(RAM, 1, 4000000, file);
fclose(file);
}
else
{
printf("Failed to open ram.bin.\n");
return 1;
}
}
if(move_ram)
{
L_9a0(0x02000bb4, 0x400, 0x020d5f64);
//L_a4c();//Commented out because this program crashes with this uncommented, because the wrong data is being used from the modified memory.
//memset((void*)((int)RAM + 0xb5800), 0, 0x207a0);
file = fopen("new_ram.bin", "wb");
fwrite(RAM, 1, 0x400000, file);
fclose(file);
}
}
free(RAM);
return 0;
}
|
Last edited by yellowstar on Mon Dec 15, 2008 9:27 pm; edited 1 time in total
#164881 - Maxxie - Sat Nov 29, 2008 6:46 pm
r0 is of type pointer on function entry, as it is used to load r1,r2 from it via the ldmia r0,{}
So it is not a clear value and the first check is only
if (pointer == NULL) return;
Also r1, r2 are NO parameter, their content is trashed with the ldmia.
I'd advise to remove the comments and start over on this particular function.
_________________
Trying to bring more detail into understanding the wireless hardware
#164884 - yellowstar - Sat Nov 29, 2008 7:48 pm
Maxxie wrote: |
Also r1, r2 are NO parameter, their content is trashed with the ldmia.
|
When beginning execution from L_9a0, they are parameters for some memory clearing assembly. However with this ref I'm dealing with, those aren't parameters at all.
#164885 - Cearn - Sat Nov 29, 2008 8:37 pm
This should be close:
Code: |
void sub_9A0(u32 *arg0)
{
//# NOTE: arg0 probably not really a u32-pointer, but rather
//# a struct passed by reference or something. In any case, this is
//# what it says.
u8 *foo= (u8*)arg0;
u32 header= arg0[-2]; // Compression header ???
u8 *dst= foo+arg0[-1]; // r2
u8 *src= foo-(header>>24); // r3
u8 *end= foo-(header&0xFFFFFF) // r1
u8 *orgDst= dst; // Save the original destination for later.
int iBlock=0;
u32 blockFlags=0;
while(src > end)
{
// Prepare for a new compression block.
if(iBlock==0)
blockFlags= *--src;
if(blockFlags & 0x80) // Reverse LZ77 compressed.
{
u16 hdr= src[-1]<<8 | src[-2];
u16 offset= (hdr&0xFFF)+2, len= (hdr>>4)+2;
src += 2;
while(len--)
{
dst[-1]= dst[offset];
--dst;
}
}
else // Normal byte stream.
*--dst = *--src;
blockFlags <<= 1;
}
//# TODO: cache flush from`end' to `orgDst'
} |
Basically, it's a reverse-LZ77 decompresser. There may be some off-by-one errors in there (I always mess those up), this is basically it. The entry parts will have to be modified a little, of course, as that part of the routine was missing from the assembly.
Sidenote: you really, really don't have to spam your code with type-casts. In most cases, it's unnecessary and makes code more difficult to write and read. They are almost never needed for integers, and only rarely for pointers.
For example, this:
Code: |
source = (unsigned char*)((int)source - 1);
a = *source;
|
just means this:
The meaning of the latter is immediately obvious (though somewhat odd due to the negative offset); the former ... not so much.
#164886 - Cearn - Sat Nov 29, 2008 8:45 pm
Cearn wrote: |
This should be close:
Code: | void sub_9A0(u32 *arg0)
{
//# NOTE: arg0 probably not really a u32-pointer, but rather
//# a struct passed by reference or something. In any case, this is
//# what it says.
u8 *foo= (u8*)arg0;
u32 header= arg0[-2]; // Compression header ???
u8 *dst= foo+arg0[-1]; // r2
u8 *src= foo-(header>>24); // r3
u8 *end= foo-(header&0xFFFFFF) // r1
u8 *orgDst= dst; // Save the original destination for later.
int iBlock=0;
u32 blockFlags=0;
while(src > end)
{
// Prepare for a new compression block.
if(iBlock==0)
{
blockFlags= *--src;
iBlock= 8;
}
iBlock--;
if(blockFlags & 0x80) // Reverse LZ77 compressed.
{
u16 hdr= src[-1]<<8 | src[-2];
u16 offset= (hdr&0xFFF)+2, len= (hdr>>4)+2;
src += 2;
while(len--)
{
dst[-1]= dst[offset];
--dst;
}
}
else // Normal byte stream.
*--dst = *--src;
blockFlags <<= 1;
}
//# TODO: cache flush from`end' to `orgDst'
} |
Basically, it's a reverse-LZ77 decompresser. There may be some off-by-one errors in there (I always mess those up), this is basically it. The entry parts will have to be modified a little, of course, as that part of the routine was missing from the assembly.
Sidenote: you really, really don't have to spam your code with type-casts. In most cases, it's unnecessary and makes code more difficult to write and read. They are almost never needed for integers, and only rarely for pointers.
For example, this:
Code: | source = (unsigned char*)((int)source - 1);
a = *source;
| just means this:
The meaning of the latter is immediately obvious (though somewhat odd due to the negative offset); the former ... not so much. |
#164891 - chishm - Sun Nov 30, 2008 1:13 am
I take it this is the code executed by the ARM9 binary to decompress itself? Here's my version, written over a year ago:
sourceData should point to the start of the ARM9 binary and headerBottom should be the size of it. The compressed data "header" is actually a trailer, as the data is decompressed from the tail to the head, in place in memory.
Code: |
unsigned int get_decompressed_data_size (const unsigned char* sourceData, unsigned int headerBottom) {
unsigned int decompressedLength;
decompressedLength = (((unsigned int*)sourceData)[headerBottom / 4 - 1]) + headerBottom;
return decompressedLength;
}
void decompress_ARM9_binary (const unsigned char* sourceData, unsigned int headerBottom, unsigned char* destData) {
int srcPos, destPos;
int controlByte;
int runsLeft;
unsigned int srcLength, destLength;
int srcStart;
srcLength = ((unsigned int*)sourceData)[headerBottom / 4 - 2];
destLength = (((unsigned int*)sourceData)[headerBottom / 4 - 1]) + headerBottom;
srcPos = headerBottom - (srcLength >> 24);
destPos = destLength;
srcStart = headerBottom - (srcLength & 0x00FFFFFF);
while (srcPos > srcStart) {
controlByte = sourceData[--srcPos];
for (runsLeft = 0x08; (runsLeft > 0) && (srcPos >= 0) && (destPos >=0); --runsLeft) {
if (controlByte & 0x80) {
// copy data run from decompressed buffer
int runRepeats, runLength;
unsigned char temp;
runRepeats = sourceData[--srcPos];
runLength = ((sourceData[--srcPos] | (runRepeats << 8)) & 0x0fff) + 0x02;
runRepeats = (runRepeats >> 4) + 0x2;
for ( ; runRepeats >= 0; runRepeats-= 0x1) {
temp = destData[destPos + runLength];
destData[--destPos] = temp;
}
} else {
// copy byte from source compressed data
destData[--destPos] = sourceData[--srcPos];
}
controlByte = controlByte << 1;
}
}
while ((srcPos >= 0) && (destPos >= 0)) {
destData[--destPos] = sourceData[--srcPos];
}
}
|
_________________
http://chishm.drunkencoders.com
http://dldi.drunkencoders.com
#164921 - yellowstar - Mon Dec 01, 2008 8:16 pm
Thanks chishm. My L_a4c implementation crashed again, and the procedure at 0x5120 didn't change at all. But the destination region is nowhere near 0x5120, however when viewing that RAM address with iDeas it shows that procedure was changed by 9a0... However I found these URLs thanks to chishm's decompressor implementation.
chishm wrote: |
Code: |
while ((srcPos >= 0) && (destPos >= 0)) {
destData[--destPos] = sourceData[--srcPos];
}
|
|
What's this for? The assembly after the decompression code only flushes cache, it doesn't write anything...
Cearn wrote: |
Sidenote: you really, really don't have to spam your code with type-casts. In most cases, it's unnecessary and makes code more difficult to write and read. They are almost never needed for integers, and only rarely for pointers.
For example, this:
Code: |
source = (unsigned char*)((int)source - 1);
a = *source;
|
just means this:
The meaning of the latter is immediately obvious (though somewhat odd due to the negative offset); the former ... not so much.
|
I used to have source--; ,where those source = (unsigned char*)((int)source - 1); are in that code post. I switched some of those to source = (unsigned char*)((int)source - 1); as I thought switching it *might* help,(I was stumped as to why my implementation wouldn't work, and I still am) but that didn't help, and I didn't bother switching it back before that post. However I have now switched it back, and changed the implementation some, and now it crashes...
EDIT:
Chishm, I noticed another thing... At the end of the decompressed binary decompressed by your implementation, there's a bunch of zeroes. However, viewing that memory with iDeas reveals data used by L_a4c... However I checked the values of your implementation's variables, and the values were identical to the values I reverse engineered, so the flaw is elsewhere...
#164927 - yellowstar - Tue Dec 02, 2008 1:20 am
I finally got my implementation working. It appears Nintendo tweaked the decompressor since last year. My code outputs the correct data at both the end of the binary, and 0x5120, while chishm's failed at both.
Code: |
void L_9a0(unsigned int start, unsigned int length, unsigned int clearval)
{
unsigned char *source, *dest;
unsigned char val;
unsigned char temp;
int a, b;
int counter;
if(clearval==0)return;
length += clearval;
dest = (unsigned char*)((int)RAM + (int)0x12C344);
source = (unsigned char*)((int)RAM + (int)0xD5F5B);
start = (unsigned int)((int)RAM + (int)0x4000);
while((int)source > (int)start)
{
val = *--source;
counter = 8;
while(counter>=0)
{
counter--;
if(counter<0)break;
if((int)source<=(int)start)
{
return;
}
if(val & 0x80)
{
a = (int)*--source;
b = (int)*--source;
b |= a<<8;
b &= ~0xF000;
b += 2;
a += 32;
while(a>=0)
{
temp = dest[b];
*--dest = temp;
a-=16;
}
}
else
{
*--dest = *--source;
}
val = val << 1;
}
}
}
|
#164928 - yellowstar - Tue Dec 02, 2008 5:18 am
What's the data at 0x023801b0 - 0x0238f308? The Arm7 is copying the data from that region to 0x037f8000 - 0x03807158, then branching to 0x037f8000. After some debugging, I had both CPUs enter infinite loops, then checked the memory at 0x023801b0, and there was still data at that region. I guess Nintendo's bootloaders write this data to that region before booting, and emulators write that data to that region also?(Which use dumps of Nintendo's data?) Where could I find this data in the DeSmuMe source?
#165048 - yellowstar - Sun Dec 07, 2008 9:03 pm
I've found this assembly, but I don't know what mode it's switching to... I don't see any mode listed for this setting in ARM ARM...
*dasm removed*
Last edited by yellowstar on Mon Dec 15, 2008 9:29 pm; edited 1 time in total
#165051 - Maxxie - Sun Dec 07, 2008 10:04 pm
It's getting and setting the interrupt and fast interrupt flags of the psr. (If set, the cpu ignores (fast)interrupt request untill they are cleared again)
It doesn't change the mode or other flags.
Parameter is only r0 and supposed to be in the right format (all 0 except a possible bit6,7) r1 is trashed immediately on entry.
_________________
Trying to bring more detail into understanding the wireless hardware
#165052 - yellowstar - Sun Dec 07, 2008 10:33 pm
Maxxie wrote: |
It doesn't change the mode or other flags.
|
Why do you say that? :\
A msr CPSR_c can write to the mode bits, and I thought it was writing bits 01111 to the mode bits... r0 on entry is bits 01111110... Doesn't the mode change on Arm9 when modes not defined in ARM ARM are attempted to be written to PSR? ARM ARM says the results are unpredictable if you do that...
Maxxie wrote: |
Parameter is only r0 and supposed to be in the right format (all 0 except a possible bit6,7) r1 is trashed immediately on entry. |
That r1 = 0x40 line meant that r1 = 0x40 after the procedure is executed, not before. All the parameters are listed on the REF line only.(Didn't bother removing that r1 = 0x40 from the post...)
#165061 - Maxxie - Mon Dec 08, 2008 1:42 pm
yellowstar wrote: |
Maxxie wrote: |
It doesn't change the mode or other flags.
|
Why do you say that? :\
A msr CPSR_c can write to the mode bits, and I thought it was writing bits 01111 to the mode bits... r0 on entry is bits 01111110... Doesn't the mode change on Arm9 when modes not defined in ARM ARM are attempted to be written to PSR? ARM ARM says the results are unpredictable if you do that...
|
Please check the r0 (which bits are those you posted, the upper, the lower?) if you really have other bits set then those in 0xC0.
(thats why i said "and supposed to be in the right format")
The code wouldn't make much sense if there are mode parts included in the parameter (You really wouldn't want to OR modes) Actually i am pretty sure you will find that the return value of this function is saved and passed as the new argument in the next call.
It is a pretty standard code to to make sure non atomic access to data is not interrupted by the very same cpu in IRQ or FIQ. This is to prevent racing situations at accessing data used by the main flow and an interrupt handler.
_________________
Trying to bring more detail into understanding the wireless hardware
#165071 - yellowstar - Mon Dec 08, 2008 9:01 pm
My bad, apparently the potential bl is being beq'd past, so this procedure is never executed...
#165234 - yellowstar - Sun Dec 14, 2008 1:00 am
How does binary addition work, at the bits level? Recently I attempted to patch this Arm9 binary to branch to the end of the binary in memory, patch an instruction, then branch back were it would have branched normally. However I'm not sure how to do it... How does one calculate the immediate_24 value when branching backwards? I also tried injecting the loader along with the decompressed Arm9 binary into an .nds, but No$Gba though the .nds was encrypted, although I tried fixing the Secure CRC16... Where does that 0x023801b0 - 0x0238f308 data come from? Is the the firmware, the BIOS, or whatever is first executed when the DS boots?
#165238 - Maxxie - Sun Dec 14, 2008 9:10 am
1) http://en.wikipedia.org/wiki/Half_adder
2) Should work for forward and backwards jumps:
Code: |
s32 byteOffset = jmpTarget - (jmpSource + 8) ;
if (abs(byteOffset) >= BIT(26)) throw "Branch: Out of range"
u32 B_opcode = (condition << 28) | 0x0A000000 | ((byteOffset >> 2) & 0x00FFFFFF ;
|
where jmpSource is the address where the branch is stored. jmpSource + 8 is the PC at the time of its execution.
3) dont know
_________________
Trying to bring more detail into understanding the wireless hardware
#165244 - yellowstar - Sun Dec 14, 2008 9:34 pm
Thanks Maxxie.
#165270 - wintermute - Mon Dec 15, 2008 8:43 pm
Further posts containing disassembly of copyrighted code in this thread will result in a lock.
_________________
devkitPro - professional toolchains at amateur prices
devkitPro IRC support
Personal Blog
#165271 - yellowstar - Mon Dec 15, 2008 9:31 pm
wintermute wrote: |
Further posts containing disassembly of copyrighted code in this thread will result in a lock. |
Sorry about that... I may be close to finishing loader RE though, and I wasn't planning on posting anything about non-loader code anyway. All of the dis-assemblies in all of my posts have been removed.
#165487 - yellowstar - Mon Dec 22, 2008 11:49 pm
Could someone whom knows Perl adapt this(disasm.pl) for ARM?(I don't know perl, but I'd like to have a flow chart for this Nintendo Spot client... Not for finding main though, I already found that.)
#165677 - yellowstar - Sat Jan 03, 2009 2:54 am
Everyone, what methods do you use for finding main when reverse engineering? I thought I found it, but apparently the software is still running in System mode in that procedure... Does anyone know what's the first libc procedure called/executed in official software?(Where it is/a sort of "call stack" to find it? Like say, procedure A calls B, calls L_xxxx, calls ..., calls libc_xxx) (I'm asking because it's taking me a long time to RE the loader...)
#165679 - Maxxie - Sat Jan 03, 2009 12:15 pm
Why would main() not run in system mode on the DS?
Callstacks on an arm are a lot more difficult under our arm assemblies then they are on a x86. You need to parse the stack, register and code to locate the call remainders. (The x86 allways put a CALL return address on stack, the arm not. The x86 abi has a base pointer point to the stackframe's base, the arm abi doesn't)
_________________
Trying to bring more detail into understanding the wireless hardware
#165711 - yellowstar - Mon Jan 05, 2009 12:00 am
I thought on entry to main, the CPU mode would be User... By "call stack", I meant a listing on what calls what, ect, based on disassembles, not literally the stack.
#165715 - Maxxie - Mon Jan 05, 2009 8:27 am
yellowstar wrote: |
I thought on entry to main, the CPU mode would be User... |
There is no need to, no higher instance exists to control it. And often the requirement of full access rights prevents the use of user mode. system is nothing other then user mode with priviledges.
system != svc
_________________
Trying to bring more detail into understanding the wireless hardware
#166216 - yellowstar - Mon Feb 02, 2009 5:57 am
yellowstar wrote: |
What's the data at 0x023801b0 - 0x0238f308? The Arm7 is copying the data from that region to 0x037f8000 - 0x03807158, then branching to 0x037f8000. |
Never mind, solved. Apparently in the .nds header 0x023801b0 is the Arm7 execution and copy to address... I assumed it was 0x03000000...
#167801 - yellowstar - Sat Mar 28, 2009 6:42 pm
What's the bytecode for a blx from 0x023fee00, to 0x023bfe00? Maxxie's code failed to generate it this properly. With an adjusted opcode from his code, viewing 0x023fee00 under iDeas, results in this: "blx 0x023ce00".(Generated bytecode is 0xfaff03fe)(Arm blx)