|
|
#1 | |
|
Member
![]() Join Date: Jul 2008
Location: Austria
Posts: 121
Likes: 18
Liked 85 Times in 29 Posts
Mentioned: 0 Post(s)
Tagged: 0 Thread(s)
|
Reversing on the PS3 Linux using the GNU tool-chain
Reversing on the PS3 Linux using the GNU tool-chain (by Disane) Prologue Hello and welcome to my brand new tutorial. I know you guys have been waiting for it to come out. As i mentioned on IRC, this tutorial is not talking about any practical hacking of the PS3, but what you can see here (and hopefully learn) can help you in achieving this goal. First of all, I'm assuming that you have some basic computer science knowledge and know a little bit of assembly. Knowing C is also very important in understanding the problem we're going to solve here. In order to try out the code and follow the tutorial, you are going to need a PS3 with Linux installed. You're also going to need the GNU tool-chain for the Cell Processor installed on your PS3 Linux. Without further a due let’s take a look at the documents we are going to need. So, if you haven’t picked up PPU and SPU assembly yet, then you might want to check out the articles and tutorials I’m going to list here: PPC32-64 assembly: IBM Introduction to PowerPC assembly: http://www.ibm.com/developerworks/library/l-ppc/ MacTech PPC assembly introduction: http://www.mactech.com/articles/deve...21balance.html IBM’s article series (1-4) on PowerPC assembly: http://www.ibm.com/developerworks/vi...show_all=false (recommended) Beginners guide to PPC32 assembly: http://www.lightsoft.co.uk/Fantasm/B...rs/begin1.html (optional) MacTech PPC Function Calls: http://www.mactech.com/articles/mact...FunctionCalls/ PPC ABI: http://wall.riscom.net/books/proc/ppc/cwg/a_abi.html IBM developers guide to the PPC architecture: http://www.ibm.com/developerworks/li...w961PowPCGuide PPC compiler writers guide (the ultimate doc for people who want to reverse engineer PPC code): https://www-01.ibm.com/chips/techlib.../$file/cwg.pdf Official PPC32-64 Docs we are going to use in this tutorial: PPC32: https://www-01.ibm.com/chips/techlib...le/6xx_pem.pdf PPC64: https://www-01.ibm.com/chips/techlib....2005jul15.pdf Cell Processor SPU assembly: IBM SPU assembly article series: http://www.ibm.com/developerworks/vi...l+BE+processor (Trust me it’s better than reading boring documents )About the SPU ABI: https://www-01.ibm.com/chips/techlib...cation_1.9.pdf (The second thing you want to read after the article series above.) Cell BE Linux ABI: https://www-01.ibm.com/chips/techlib...UX_ABI_1.2.pdf Cell Processor ABI: https://www-01.ibm.com/chips/techlib...2May08_pub.pdf (This last document is actually a very interesting reading in general (I think this is one of the best manuals I’ve ever read. You are going to need to read the following section Objects, Executables, and SPE Loading starting on page 397.) We are also going to use the following documentations when we are reversing SPU code: SPU language specification: https://www-01.ibm.com/chips/techlib...cation_1.7.pdf SPU Instruction Set Architecture: https://www-01.ibm.com/chips/techlib...an2007_pub.pdf FAQ Another tutorial on reversing, why? Well the answer is very simple. I think we haven't seen a tutorial on reversing SPU code yet. I'm also confident that there are not too many beginner tutorials on reversing PPU code as well. Plus we are doing everything on the PS3 Linux. I also have personal reasons for writing this tutorial. As I mentioned on Twitter, I'm researching methods on how to make use of the Hypervisor and the CellOS-lv2 dump. You can consider this tutorial as a warm up before you jump into the endless ocean of IBMs Hypervisor code. Why do you reverse a program when you have its source code? For learning purposes of course. You can only learn reversing if you view the assembly code of your own programs. So from now on you might want to check out the assembly code that your compiler generates.True reverse engineers are very experienced programmers who know how to debug their own applications and how to optimize them using their assembly knowledge. Our Guinea Pig: The Good old Guessing Game So now that we are through the introduction. Let’s get down and dirty. The first thing we need is an application we can reverse. Well, we could try and reverse existing applications compiled on our PPC64 Linux but that would take too much time and besides this is not a book. So we are going to start with something very basic. The application we are going to reverse today is the all times classic “Guessing Game” written in C. The idea is simple. The computer generates a number between 1 and 10 and the player has to guess which number has the computer generated. Simple, right? In C probably yes but in PPC and SPU assembly this could be a real challenge even for experienced programmers. Well we are not going to write the game in PPC and SPU assembly simply because of two reasons. The first one is that C is portable code. We can compile our C code to run on the PPU or on the SPU. Cool, what’s the other reason? Well the other reason is that this is a tutorial on reversing SPU and PPU code and not an assembly tutorial. About the sample code I’m going to show you. We are not going to use classes or anything fancy this is a classic C (sequential programming 101) so no Object Oriented Programming introduced. No exceptions to invoke or anything fancy. We are going to use one single Game Loop to keep the game going until the player wins (yeah you can’t lose in this game, but you can modify it if you want, the possibilities are endless ) .Alright so let’s fire up Linux on our PS3 and open up the Terminal. I don’t know about you, but I’ve got Ubuntu 10.4 on my machine. Locate a nice spot like ~/Projects/Reversing/guessing_game/ (use cd <Dir_Name> command to get to you home directory then mkdir <Dir_Name> to create the directories you need) and here we can create a text document using gedit or nano (type in gedit or nano). Our C code is the following: Code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void initialize();
void player_guess();
/* the players guess */
int guess;
/* the number the player needs to guess*/
int game_number;
int main(int argc, char* argv)
{
initialize();
/* the game wont stop until the player finds the number */
do
{
player_guess();
}
while(guess != game_number);
/* congratulate the player */
printf("Oh goody, you guessed the number!\n");
printf("the number was indeed %d. \nCongratulations !!!\n", game_number);
/* end... */
return 0;
}
void initialize()
{
printf("Welcome to the Guessing game!\n");
/* initialize random seed: */
srand ( time(NULL) );
/* generate a game number */
game_number = rand() % 10 + 1;
printf("A random number was generated between 1 to 10 for you.\n");
}
void player_guess()
{
printf("Please place your bet: ");
scanf("%d", &guess);
/* the player missed the number, let's help her/him out a little bit */
if(guess > game_number)
{
printf("Nah, sorry you missed it! The number your looking for is actually SMALLER than %d.\n", guess);
}
else if(guess < game_number)
{
printf("Nah, sorry you missed it! The number your looking for is actually LARGER than %d.\n", guess);
}
}
Code:
gcc ppe_guessing_game.c –o PPC32_guessing Code:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void initialize();
void player_guess();
/* variable to the number of the running SPE */
unsigned long long host;
/* the players guess */
int guess;
/* the number the player needs to guess*/
int game_number;
int main(unsigned long long spe, unsigned long long argp, unsigned long long envp)
{
host = spe;
initialize();
/* the game wont stop until the player finds the number */
do
{
player_guess();
}
while(guess != game_number);
/* congratulate the player */
printf("Oh goody, you guessed the number!\n");
printf("the number was indeed %d. \nCongratulations !!!\n", game_number);
/* end... */
return 0;
}
void initialize()
{
printf("Welcome to the Guessing game!\n");
printf("Our host for today is SPE number %d!\n", host);
/* initialize random seed: */
srand ( time(NULL) );
/* generate a game number */
game_number = rand() % 10 + 1;
printf("A random number was generated between 1 to 10 for you.\n");
}
void player_guess()
{
printf("Please place your bet: ");
scanf("%d", &guess);
/* the player missed the number, let's help her/him out a little bit */
if(guess > game_number)
{
printf("Nah, sorry you missed it! The number your looking for is actually SMALLER than %d.\n", guess);
}
else if(guess < game_number)
{
printf("Nah, sorry you missed it! The number your looking for is actually LARGER than %d.\n", guess);
}
}
unsigned long long spe: is the SPU’s ID which is a unique SPU task identifier assigned by the operating system. Which means the OS selects the SPE where the program gets loaded into. unsigned long long argp: is an effective address (EA) in the main storage to application parameters. unsigned long long env: The environment pointer, which is an effective address (EA) in main storage to runtime environment information. I really don’t want to get into how the SPU code is getting loaded and all the other mysterious questions you might raise here. So please refer to this tutorial if you have questions about programming the SPU: http://www.kernel.org/pub/linux/kern...ogramming.html Okay so the SPE version of the guessing game has some little extra. It stores the SPE number in a variable and displays the ID of the SPE when the game starts up. Other than that these two games are all the same. Compile the game with the following command in your Terminal: Code:
spu-gcc spe_guessing_game.c –o SPU_guessing Code:
./PPU_guessing Code:
./SPU_guessing readelf and objdump This section will talk about using readelf, objdump, spu-readelf and spu-objdump . If you haven’t used these tools before then you might want to take a look at the following tutorial on exploring ELF files with readelf and objdump: http://www.linuxforums.org/articles/...jdump_125.html I will only going to show you what kind of arguments we are going to use and tell a little bit about these tools. readelf: So let’s start with the readelf. In this tutorial we are going to use the readelf file to view ELF headers and to detect sections of the executable ELF binary we created when we compiled the simple game above this section. So for starters the readelf arguments in general look like this: readelf –h <File_Name> - allows the user to view the header of the ELF binary. readelf –S <File_Name> - read section headers Navigate to the PPU_guessing binary using the Terminal and let’s type in readelf –h PPU_guessing to view the header of the PPU_guessing game: ![]() Alright, so now we know that we are dealing with PPU code. We know that Data is Big Endian code and it’s an EXEC (Executable File) file. We know that there are 38 Sections (This could probably hold debug information as well). We also see where the entry point to the program is: 0x10000430. Now let’s run readelf –S PPU_guessing .We get the following: ![]() As you can see this has lots info we can use. Like what sections are executable/readable/writable codes (by looking at the Flags column). Also we have the sizes of each section. The ones that are zeroed out are probably not interesting anyways. We also have debug sections you can check out. There’s a major difference between debug ELF executables and non-debug ELF executables. There are less sections in the ELF file and also the executable code itself is shorter, since you don’t have to store every register on the stack to make the value available to read by the debugger (ppu/spu-gdb). The list your seeing now won’t be explained now. We will discuss it in the objdump section of this tutorial. We are actually going to use objdump to view what’s inside of these sections. Now, try and use spu-readelf –h and spu-readelf –S on your SPU_guessing: ![]() ![]() As you can see the spu-elf consists of fewer sections. This means fewer data and code to worry about. Ok, so we have a bunch of sections here but how do we check what’s inside in each section. That’s where the objdump comes into play. objdump: objdump is the main tool we are going to use for reversing. You can look at it as a dump IDA disassembler but don’t be fooled. objdump can be still very powerful. First of all it allows us to view PPU and SPU code. Which IDA currently does not implement is the SPU disassemble and without the proper SPU processor plugin you can’t view/reverse SPU code using IDA. We are going to use objdump for two main things. The first one is viewing data and addresses. The second thing is viewing PPU and SPU code plus the address of each code segment. If you look at the top readelf/spu-readelf –S listings. You should see that there are many sections to disassemble and view. Let’s take a look at the most important sections which we are going to touch in this tutorial. PPU: .init: Holds the process initialization code. Gets called before main(). .text: Executable section. This is where the program code goes. You might think that only main() and the other functions which are used by the application reside in this section. Well, the truth is that you get a lot more than that. More details in the “Reversing PowerPC 32 code” section of this tutorial. .fini: Holds the process termination code. Gets executed when your program exits normally. .rodata: A non writable section. Holds read only data which are strings, mostly. An ASCII code table could come in handy when dealing with .rodata. .bss: Uninitialized or so called “null” data goes here. When the program executes this data gets initialized as 0. .data: Initialized data. .got: The Global Offset Table. Nearly everything gets redirected to this address. It’s very handy to know this address. .plt: The Procedure Linkage Table. The base address for some libc function calls. SPU: note.spu_name: holds the name of the executable. For example: SPU_NAME.SPU_guessing For more informationon the ELF and it’s special sections check out: http://www.skyfree.org/linux/references/ELF_Format.pdf Now, let’s check out some of the sections with objdump. Here’s some of the arguments you can give to objdump: objdump –d <File_Name> ,disassembles the file completely objdump –D <File_Name>, does the same as the previous flag objdump –s <File_Name>,diplay content (hex and string) objdump –S <File_Name>,mix disassembly with content So let’s go ahead and display the .rodata section of the PPU_guessing using the command below: Code:
objdump –s –j .rodata PPU_guessing ![]() Here we can view the read-only data (like strings) which are located in our program. The first column shows the address while the other 4 next to this shows the data in hex (each row holds 16 bytes of data, remember that one character is 1 byte which can be represented with 2 hexadecimal digits). Let’s check out what the following command gives us: Code:
spu-objdump –s –j .rodata SPU_guessing ![]() Now, you might think hex-editing is the same as hacking. Not at all dear Sir, you are wrong. Hacking is about reversing parts of the application which you are interested in or want to abuse (remove nag screens, remove trial counter, make the application fully functional if it’s a shareware). In order to do that, you’ll need to reverse engineer parts of the application and this is the part where assembly knowledge comes in handy. In the next section we are going to dive deep into reversing our PPU_guessing program. Reversing PowerPC 32 code Before we go into detail, there’s something I want to show you. If you look at the .text section using the command below: Code:
objdump –D –j .text PPU_guessing _start: The first thing that gets executed by the Operating System. Also sets up the stack and It’s in fact a wrapper for __libc_start_main. Which is a library function inside libc. It’s interesting to note that we can actually create our own _start function enabling us to make the start code smaller. _do_global_dtors_aux: The destructor function for this program. call_do_global_dtors_aux frame_dummy call_fame_dummy libc_csu_fini libc_csu_init _do_globabl_ctors_aux: The constructor function for this program. call_do_global_ctors_aux srand@plt: srand() _gmon_start__@plt __libc_start_main@plt: initializes the C library and calls main() printf@plt: calls printf() time@plt: calls time() __isoc99_scanf@plt: calls scanf() puts@plt: calls puts() although we know from the source code this wasn’t called specifically you’ll see how the compiler has swapped some of the printf()s to puts(). rand@plt: calls rand() _glink _glink_plt_resolver: resolves linking when the program executes. It’s nice to know some of these (C runtime) functions as well, cause at least we know that they’re not interesting for us to reverse. The good thing is that we know which functions are being called from libc this can make our job a lot easier. Check this out for more info on the above functions: http://www.mentalcases.net/texts/sec...ctionStudy.txt So now that we know what not to reverse there are only 3 functions left. Let’s view them: Code:
1000059c <main>: 1000059c: 94 21 ff e0 stwu r1,-32(r1) 100005a0: 7c 08 02 a6 mflr r0 100005a4: 90 01 00 24 stw r0,36(r1) 100005a8: 93 e1 00 1c stw r31,28(r1) 100005ac: 7c 3f 0b 78 mr r31,r1 100005b0: 90 7f 00 0c stw r3,12(r31) 100005b4: 90 9f 00 08 stw r4,8(r31) 100005b8: 48 00 00 79 bl 10000630 <initialize> 100005bc: 48 00 01 0d bl 100006c8 <player_guess> 100005c0: 3c 00 10 01 lis r0,4097 100005c4: 7c 0b 03 78 mr r11,r0 100005c8: 81 2b 10 30 lwz r9,4144(r11) 100005cc: 3c 00 10 01 lis r0,4097 100005d0: 7c 0b 03 78 mr r11,r0 100005d4: 80 0b 10 2c lwz r0,4140(r11) 100005d8: 7f 89 00 00 cmpw cr7,r9,r0 100005dc: 40 9e ff e0 bne+ cr7,100005bc <main+0x20> 100005e0: 3c 00 10 00 lis r0,4096 100005e4: 30 60 0a 2c addic r3,r0,2604 100005e8: 48 00 03 79 bl 10000960 <puts@plt> 100005ec: 3c 00 10 00 lis r0,4096 100005f0: 31 20 0a 50 addic r9,r0,2640 100005f4: 3c 00 10 01 lis r0,4097 100005f8: 7c 0b 03 78 mr r11,r0 100005fc: 80 0b 10 2c lwz r0,4140(r11) 10000600: 7d 23 4b 78 mr r3,r9 10000604: 7c 04 03 78 mr r4,r0 10000608: 4c c6 31 82 crclr 4*cr1+eq 1000060c: 48 00 03 25 bl 10000930 <printf@plt> 10000610: 38 00 00 00 li r0,0 10000614: 7c 03 03 78 mr r3,r0 10000618: 39 7f 00 20 addi r11,r31,32 1000061c: 80 0b 00 04 lwz r0,4(r11) 10000620: 7c 08 03 a6 mtlr r0 10000624: 83 eb ff fc lwz r31,-4(r11) 10000628: 7d 61 5b 78 mr r1,r11 1000062c: 4e 80 00 20 blr 10000630 <initialize>: 10000630: 94 21 ff e0 stwu r1,-32(r1) 10000634: 7c 08 02 a6 mflr r0 10000638: 90 01 00 24 stw r0,36(r1) 1000063c: 93 e1 00 1c stw r31,28(r1) 10000640: 7c 3f 0b 78 mr r31,r1 10000644: 3c 00 10 00 lis r0,4096 10000648: 30 60 0a 80 addic r3,r0,2688 1000064c: 48 00 03 15 bl 10000960 <puts@plt> 10000650: 38 60 00 00 li r3,0 10000654: 48 00 02 ed bl 10000940 <time@plt> 10000658: 7c 60 1b 78 mr r0,r3 1000065c: 7c 03 03 78 mr r3,r0 10000660: 48 00 02 a1 bl 10000900 <srand@plt> 10000664: 48 00 03 0d bl 10000970 <rand@plt> 10000668: 7c 69 1b 78 mr r9,r3 1000066c: 3c 00 66 66 lis r0,26214 10000670: 60 00 66 67 ori r0,r0,26215 10000674: 7c 09 00 96 mulhw r0,r9,r0 10000678: 7c 0b 16 70 srawi r11,r0,2 1000067c: 7d 20 fe 70 srawi r0,r9,31 10000680: 7c 00 58 50 subf r0,r0,r11 10000684: 54 00 08 3c rlwinm r0,r0,1,0,30 10000688: 54 0b 10 3a rlwinm r11,r0,2,0,29 1000068c: 7c 00 5a 14 add r0,r0,r11 10000690: 7c 00 48 50 subf r0,r0,r9 10000694: 31 20 00 01 addic r9,r0,1 10000698: 3c 00 10 01 lis r0,4097 1000069c: 7c 0b 03 78 mr r11,r0 100006a0: 91 2b 10 2c stw r9,4140(r11) 100006a4: 3c 00 10 00 lis r0,4096 100006a8: 30 60 0a a0 addic r3,r0,2720 100006ac: 48 00 02 b5 bl 10000960 <puts@plt> 100006b0: 39 7f 00 20 addi r11,r31,32 100006b4: 80 0b 00 04 lwz r0,4(r11) 100006b8: 7c 08 03 a6 mtlr r0 100006bc: 83 eb ff fc lwz r31,-4(r11) 100006c0: 7d 61 5b 78 mr r1,r11 100006c4: 4e 80 00 20 blr 100006c8 <player_guess>: 100006c8: 94 21 ff e0 stwu r1,-32(r1) 100006cc: 7c 08 02 a6 mflr r0 100006d0: 90 01 00 24 stw r0,36(r1) 100006d4: 93 e1 00 1c stw r31,28(r1) 100006d8: 7c 3f 0b 78 mr r31,r1 100006dc: 3c 00 10 00 lis r0,4096 100006e0: 30 00 0a d8 addic r0,r0,2776 100006e4: 7c 03 03 78 mr r3,r0 100006e8: 4c c6 31 82 crclr 4*cr1+eq 100006ec: 48 00 02 45 bl 10000930 <printf@plt> 100006f0: 3c 00 10 00 lis r0,4096 100006f4: 30 00 0a f0 addic r0,r0,2800 100006f8: 7c 03 03 78 mr r3,r0 100006fc: 3c 00 10 01 lis r0,4097 10000700: 30 80 10 30 addic r4,r0,4144 10000704: 4c c6 31 82 crclr 4*cr1+eq 10000708: 48 00 02 49 bl 10000950 <__isoc99_scanf@plt> 1000070c: 3c 00 10 01 lis r0,4097 10000710: 7c 0b 03 78 mr r11,r0 10000714: 81 2b 10 30 lwz r9,4144(r11) 10000718: 3c 00 10 01 lis r0,4097 1000071c: 7c 0b 03 78 mr r11,r0 10000720: 80 0b 10 2c lwz r0,4140(r11) 10000724: 7f 89 00 00 cmpw cr7,r9,r0 10000728: 40 9d 00 2c ble- cr7,10000754 <player_guess+0x8c> 1000072c: 3c 00 10 00 lis r0,4096 10000730: 31 20 0a f4 addic r9,r0,2804 10000734: 3c 00 10 01 lis r0,4097 10000738: 7c 0b 03 78 mr r11,r0 1000073c: 80 0b 10 30 lwz r0,4144(r11) 10000740: 7d 23 4b 78 mr r3,r9 10000744: 7c 04 03 78 mr r4,r0 10000748: 4c c6 31 82 crclr 4*cr1+eq 1000074c: 48 00 01 e5 bl 10000930 <printf@plt> 10000750: 48 00 00 48 b 10000798 <player_guess+0xd0> 10000754: 3c 00 10 01 lis r0,4097 10000758: 7c 0b 03 78 mr r11,r0 1000075c: 81 2b 10 30 lwz r9,4144(r11) 10000760: 3c 00 10 01 lis r0,4097 10000764: 7c 0b 03 78 mr r11,r0 10000768: 80 0b 10 2c lwz r0,4140(r11) 1000076c: 7f 89 00 00 cmpw cr7,r9,r0 10000770: 40 9c 00 28 bge- cr7,10000798 <player_guess+0xd0> 10000774: 3c 00 10 00 lis r0,4096 10000778: 31 20 0b 48 addic r9,r0,2888 1000077c: 3c 00 10 01 lis r0,4097 10000780: 7c 0b 03 78 mr r11,r0 10000784: 80 0b 10 30 lwz r0,4144(r11) 10000788: 7d 23 4b 78 mr r3,r9 1000078c: 7c 04 03 78 mr r4,r0 10000790: 4c c6 31 82 crclr 4*cr1+eq 10000794: 48 00 01 9d bl 10000930 <printf@plt> 10000798: 39 7f 00 20 addi r11,r31,32 1000079c: 80 0b 00 04 lwz r0,4(r11) 100007a0: 7c 08 03 a6 mtlr r0 100007a4: 83 eb ff fc lwz r31,-4(r11) 100007a8: 7d 61 5b 78 mr r1,r11 100007ac: 4e 80 00 20 blr . First of all we need to review our knowledge on the PPC ABI and a little bit of our assembly knowledge to make a sense out of this huge blob of code. Let’s start with the ABI.PPC ABI review: First of all we won’t be reviewing the whole ABI for that you might want to read the articles in the Prologue section of this tutorial.
![]() The information above comes from the http://refspecs.linuxfoundation.org/...64abi-1.7.html document. Here’s a little bit of an explanation on what the Link Register is and what the Stack Pointer is: Link Register(LR): The LR is actually a special purpose register which holds the address to return to when a function call completes. In other words this register stores an address where the program counter will jump to when the execution of the function is complete. It’s always moved from the LR and stored on r0 when a new stack is being built. Stack Pointer (stored on r1): Stores an address, that points to the top of the current stack. It’s also important to note that not every stack has the same size. The size of the stack depends on the local variables and the number of saved registers on the stack. But there is a minimum size which the stack must have where it can store the LR and the SP. When your decrementing the stack you are “allocating” stack space and when your incrementing it then you are actually “deleting” the stack. After this small review of the ABI let’s get back to our main() function and it’s PPU code: Setting up the Stack: Prologue and Epilogue In this section we are going to review how the stack is handled and how the ABI works. This will make our job easier to see what data is being stored on the stack. We are interested in Local Variables and looking at how the Stack is being setup, we can detect them. Prologue Phase: In the first few lines of our main function, we can see how the stack is being setup. This is an important part which we need to investigate. Code:
stwu r1,-32(r1) Code:
mflr r0 Code:
stw r0,36(r1) Code:
mr r31,r1 Code:
stw r3,12(r31) stw r4,8(r31) Program Code: Code:
bl 10000630 <initialize> bl 100006c8 <player_guess> So far we know that main() calls initialize and player_guess() right after initialize() was called. Code:
lis r0,4097 mr r11,r0 lwz r9,4144(r11) lis r0,4097 mr r11,r0 lwz r0,4140(r11) cmpw cr7,r9,r0 bne+ cr7,100005bc <main+0x20> 4097 (decimal) is equal to 0x1001 (hex-decimal, just grab a calculator ). After loaded into r0 it looks like this:0x10010000 in the next instruction we move this value into r11 from r0 and use this as a base to add 4144. Let’s see how we add the two addressed to create one: Code:
0x10010000 +0x00001030 -------------- 0x10011030 Just as I expected: our address is inside the .sbss. The .sbss is exactly 8 Bytes long. So there’s probably data inside. Let’s open up the .sbss using objdump -D -j .sbss PPU_guessing. At the address 10011030 we have <guess> .Bingo! We can see that r9 stores the value of guess. I think we can move on to the next instruction. Again, we have these three instructions: Code:
100005cc: 3c 00 10 01 lis r0,4097 100005d0: 7c 0b 03 78 mr r11,r0 100005d4: 80 0b 10 2c lwz r0,4140(r11) Code:
cmpw cr7,r9,r0 . But that’s not important what’s important is that we are comparing these two words, r9 with r0 and we are setting the bits on cr7 (Condition Register 7). After the bits are set we move on to our next instruction:Code:
bne+ cr7,100005bc <main+0x20> Code:
100005bc: 48 00 01 0d bl 100006c8 <player_guess> Code:
100005e0: 3c 00 10 00 lis r0,4096 100005e4: 30 60 0a 2c addic r3,r0,2604 100005e8: 48 00 03 79 bl 10000960 <puts@plt> Code:
readelf –S PPU_guessing Code:
.fini 100009e0 .rodata 10000a18 .eh_frame_hdr 10000b9c Code:
10000a28 00020001 4f682067 6f6f6479 2c20796f ....Oh goody, yo 10000a38 75206775 65737365 64207468 65206e75 u guessed the nu 10000a48 6d626572 21000000 74686520 6e756d62 mber!...the numb Code:
0x10000a2c -0x10000a28 --------------- 0x00000004 Code:
00020001 4f682067 6f6f6479 2c20796f Code:
4f 68 20 67 6f 6f 64 79 2c 20 79 6f ----------------------------------- O h g o o d y , y o Code:
10000a28 00020001 4f682067 6f6f6479 2c20796f ....Oh goody, yo 10000a38 75206775 65737365 64207468 65206e75 u guessed the nu 10000a48 6d626572 21000000 74686520 6e756d62 mber!...the numb Since there’s no data to display GCC thought that puts() is enough to do the job there’s no need for printf(). So how did I find out where’s the end of the string in .rodata? It’s simple if you’ve ever ran the program your reversing then it’s pretty easy to tell how strings are displayed. You can see them and write down the messages the program writes out. There’s another way to check this as you can see there’s a little bit of gap between each string. The gap consists of 0’s. Let’s move on to our next instruction series. This next one is also a function call. But this time we are calling printf(). This means we are going to display data as well as strings. Let’s see how this is preformed: Code:
100005ec: 3c 00 10 00 lis r0,4096 100005f0: 31 20 0a 50 addic r9,r0,2640 100005f4: 3c 00 10 01 lis r0,4097 100005f8: 7c 0b 03 78 mr r11,r0 100005fc: 80 0b 10 2c lwz r0,4140(r11) 10000600: 7d 23 4b 78 mr r3,r9 10000604: 7c 04 03 78 mr r4,r0 10000608: 4c c6 31 82 crclr 4*cr1+eq 1000060c: 48 00 03 25 bl 10000930 <printf@plt> Code:
crclr 4*cr1+eq Anyway, after that we Branch and Link into printf() with the two arguments to print out the string and the data together. Epilogue Phase: From now on we are moving into the Epilogue phase of the main() function. Let’s see how GCC handles that: Code:
#zeroing out some of the registers li r0,0 mr r3,r0 #Here we increment the Stack Pointer, thus getting rid of main()’s stack even its data addi r11,r31,32 #Here we get back our previous LR (the address) lwz r0,4(r11) #and we move it into the actual LR mtlr r0 #get the previous Frame Pointer (SP) from the main()’s stack, although we raised the stack #pointer already, the data wasn’t overridden. lwz r31,-4(r11) #and we store the SP in r1 mr r1,r11 #branch out of here, probably into the finalization and then into the program destructor blr Last edited by Disane; 08-19-2010 at 03:24 AM. |
|
|
|
|
|
Likes: (6) |
|
|
#2 |
|
Member
![]() Join Date: Jul 2008
Location: Austria
Posts: 121
Likes: 18
Liked 85 Times in 29 Posts
Mentioned: 0 Post(s)
Tagged: 0 Thread(s)
|
Reversing on the PS3 Linux using the GNU tool-chain (by Disane) part 2
Summary:
So let’s see what we know about main(): Stores argv and argc on the stack. Calls initialize() and player_guess(). Loads in the game_number and guess and compares them if they’re equal if not then the Program Counter jumps back and calls player_guess() again. Else it writes out the Congratulations message with the game_number and then the program ends. This is vital info in reverse engineering this small program (not that you would need to reverse engineer something as simple as this xD). So now that we know what main() does then let’s look at initialize() and player_guess(). I won’t break the code down like I previously did. I’m going to just comment it and when something special comes up, then I’ll make a more detailed explanation: Code:
#initialize() starts here
<initialize>:
#Prologue
#Create stack, about 32 Bytes
stwu r1,-32(r1)
#move LR in to r0
mflr r0
#store LR to previous code into the previous stack
stw r0,36(r1)
#store old Frame Pointer on SP+28
stw r31,28(r1)
#move new Frame Pointer into r31
mr r31,r1
#Program Code:
#10000a80 points to the following string: “Welcome to the Guessing game!”
#this gets displayed by puts()
lis r0,4096
addic r3,r0,2688
bl 10000960 <puts@plt>
#Call time() with 0 as the first argument
li r3,0
bl 10000940 <time@plt>
#Load the result into r0, this is one worthless instruction here
#deleting it would make the program execute faster
mr r0,r3
#move result back into r3 again, worthless :-/ the result is moved into r3 automatically and you don’t need to move around it between r0 and r3
mr r3,r0
#Call srand() with the result from time(), stored on r3
bl 10000900 <srand@plt>
#Call rand(), no arguments this time
bl 10000970 <rand@plt>
#move the result from r3 into r9
mr r9,r3
#this is what the MODULO operator generates
#no wonder why developers call it “expensive”, look at all these instructions:
lis r0,26214 #Load 0x6666 into r0
ori r0,r0,26215 #Or it with 0x6667 to generate 0x66666667
#Remember that r9 stores the random number
#mulhw stands for “Multiply High Word”,
#signed integers inside r9 and r0 form a 64 bit result
#the high word of this product is placed into r0, the result is a signed integer
mulhw r0,r9,r0
#I think I’ll leave the rest to you, it’s interesting to find out how the MODULO
#operator works in low level, but we are really not interested in it at the moment
#shift algebraic word by immediate
srawi r11,r0,2
srawi r0,r9,31
#subtract from, where we subtract from r11 by r0 and place the result into r0
#check out the 6xx_pem.pdf for more information
subf r0,r0,r11
rlwinm r0,r0,1,0,30
rlwinm r11,r0,2,0,29
add r0,r0,r11
subf r0,r0,r9
#The generated number resides in r9 we also add 1 to the random number to make
#it between 1 and 10, notice Add Immediate with Carry
addic r9,r0,1
Code:
/*modulo.c*/
#include <stdio.h>
int main()
{
int operand_1 = 50;
int operand_2 = 100;
double result = 0;
result = operand_1 % operand_2;
return 0;
}
![]() Notice one important thing. We can see what the operands are and how the MODULO code uses them. This can be helpful in recognizing operands in our PPU_guessing example. We can see the +1 immediate in the end and the result of rand() stored on r9. The only thing we can’t spot is the 10 immediate which we are “dividing” with (Notice rand() % 10 in the source code of PPU_guessing.) Code:
#Display information #1001102c points to game_number lis r0,4097 mr r11,r0 #But this time we are not loading the value but instead store r9 on the this address #which means the randomly generated number gets stored inside game_number stw r9,4140(r11) #0x10000aa0 points to the following string: “A random number was generated between 1 #to 10 for you.” lis r0,4096 addic r3,r0,2720 #we write out the message using the above address using puts() bl 10000960 <puts@plt> #Increment the Frame Pointer to get out of this stack addi r11,r31,32 #load back the old LR lwz r0,4(r11) #load r0 (old LR address) into the LR mtlr r0 #load back the old Frame Pointer lwz r31,-4(r11) #move it into r1 mr r1,r11 #branch and link out of this stack and back into main() blr Summary: So here’s what we know about initialize(): Writes a welcome message. Uses time(), srand(), rand() and the MODULO operator (% in C) to generate a random number between 1 to 10 Stores the random number as game_number (on game_number’s address) Writes out a message that the number was generated between 1 to 10 Next up, we have the player_guess() function. What we already know about player_guess() is that it probably asks the player to input values. Let’s see how this is done Code:
#player_guess() starts here:
<player_guess>:
#Prologue:
#Set up stack, about 32 Bytes
stwu r1,-32(r1)
#move LR into r0
mflr r0
#store LR on the old stack (main())
stw r0,36(r1)
#store old Frame Pointer on SP+28
stw r31,28(r1)
#FP = SP
mr r31,r1
#0x10000Ad8 points to “Please place your bet: ”
lis r0,4096
addic r0,r0,2776
mr r3,r0
#clear cr1’s EQ bits
crclr 4*cr1+eq
#Call printf() to write the string we mentioned: “Please place your bet: ”
bl 10000930 <printf@plt>
#0x10000AF0 points to “%d”, integer data
lis r0,4096
addic r0,r0,2800
mr r3,r0
#0x10011030 points to a data in .sbss, this one is actually a data known as guess
lis r0,4097
addic r4,r0,4144
#clear EQ bits in cr1
crclr 4*cr1+eq
#so here the program reads the data from the user, using scanf()
bl 10000950 <__isoc99_scanf@plt>
#Loads in the data at address 0x10011030 (guess) into r9
lis r0,4097
mr r11,r0
lwz r9,4144(r11)
# r0 gets the data at address 0x1001102c (game_number)
lis r0,4097
mr r11,r0
lwz r0,4140(r11)
#compare words, r9 and r0 and store the result inside cr7, in other words
#compares game_number and guess
cmpw cr7,r9,r0
#Predicted Branch into address 0x10000754(check if greater than) if r9(guess) is less
#than r0(game_number)
#or equal to, predicted not to be taken, notice the ‘bne-‘
ble- cr7,10000754 <player_guess+0x8c>
#if r9(guess) is not less than or equal to r0(game_number),which could mean that it is
#greater than game_number, then we proceed in this direction:
lis r0,4096
#r9 stores 0x10000AF4 which points to this string:” Nah, sorry you missed it!
#The number you are looking for is actually SMALLER than %d/n”
#Don’t be confused about the message, the program wants you to input a
#smaller number, cause the one you gave in is just too big.
addic r9,r0,2804
lis r0,4097
mr r11,r0
#r0 stores the address 0x10000030 which points to ‘guess’ data
lwz r0,4144(r11)
#move string(“Nah, sorry you missed it...”) into r3 (first argument) of printf()
mr r3,r9
#move data(guess) into r4 (second argument) of printf()
mr r4,r0
#clear EQ bits inside cr1, interesting the system seems to do this all the time
#when it calls printf(), probably uses this as a ‘No Operation’ ???
#we could actually check if printf() uses cr1’s EQ bits for something #interesting just dig into printf()
crclr 4*cr1+eq
bl 10000930 <printf@plt>
#branch unconditionally into the address at 0x10000798
#(which is the Epilogue)
#which means we destroy this stack and then recreate it once main() tells #the machine to branch back to this function (player_guess())
b 10000798 <player_guess+0xd0>
10000754:
lis r0,4097
mr r11,r0
#Load in 0x10011030 (guess) into r9
lwz r9,4144(r11)
lis r0,4097
mr r11,r0
#Load in 0x1001102c (game_number) into r0
lwz r0,4140(r11)
#Compare words r9 and r0 and store the result in cr7
cmpw cr7,r9,r0
#Predicted Branch if r9 (guess) is greater than or equal to r0
#(game_number), branch is predicted not to be taken, if taken then we
#branch to this address: 0x10000798 (Epilogue)
bge- cr7,10000798 <player_guess+0xd0>
#if r9(guess) is not greater than r0(game_number)
lis r0,4096
#Load address of string into r9:“Nah, sorry you missed it! The number your
#looking for is actually LARGER than %d”
addic r9,r0,2888
lis r0,4097
mr r11,r0
#Load address of data into r0: guess (data)
lwz r0,4144(r11)
#The string address goes into r3 (first argument)
mr r3,r9
#The data address goes into r4 (second argument)
mr r4,r0
#Again we clear the EQ bits inside cr1 cause we are calling printf()
crclr 4*cr1+eq
bl 10000930 <printf@plt>
0x10000798: #Epilogue:
#Increment the SP to delete the current Stack
addi r11,r31,32
#load in previous return address
lwz r0,4(r11)
#load previous return address into the LR
mtlr r0
#load previous SP into the Frame Pointer
lwz r31,-4(r11)
#pass SP to r1
mr r1,r11
#branch back to main()
blr
So what do we know about player_guess?: player_guess() asks the player to input an integer. It doesn’t check the size of the number! But it doesn’t matter cause, you can’t input anything larger than what an integer can hold. If you input a character or a string then the program goes into an infinite loop. So no character or string check as well. So the data is being read by scanf(%d). After that the program compares game_number and guess. If guess is less than or equal to game_number then the program checks if guess is actually larger than or equal to game_number if the two numbers are equal then we branch and link out of this function. The code to check if game_number and guess are equal is in the main() function. We can consider this as a big bottleneck to performance cause, the stack has to be rebuilt each time the player inputs the wrong number. It would be better to not to use a function for this rather copy the code into main(). We’ve managed to find out how the program works exactly by looking at its PPU assembly code. The information we’ve gathered is enough to rebuild this program or to view its flaws. We can also rewrite the whole program in C if we really want to. Yeah I know that the MODULO section was pretty tricky but if you view the assembly code your compiler generates then you’ll be able to spot things like these easily. Keep viewing PPU code and you’ll be able to reverse anything you want. Our next section will talk about reversing SPU code. Last edited by Disane; 08-19-2010 at 03:40 AM. |
|
|
|
|
Likes: (3) |
|
|
#3 | |
|
Member
![]() Join Date: Jul 2008
Location: Austria
Posts: 121
Likes: 18
Liked 85 Times in 29 Posts
Mentioned: 0 Post(s)
Tagged: 0 Thread(s)
|
Reversing on the PS3 Linux using the GNU tool-chain part 3
Reversing Cell SPU code Now that we know how to use spu-gdb, spu-objdump and spu-readelf, we should move on to reversing our sample program. There are not too many articles or tutorials about reversing actual SPU code (actually I wasn’t able to find any but I’m sure you guys will prove me wrong). So this part of my tutorial might be very interesting for newcomers to SPU assembly language and reversing. So as I mentioned before in the prologue section of this tutorial (nah, not the prologue section where we setup the stack :-D ). Open up the docs SPU ISA and SPU assembly language specification. Don’t worry you don’t have to actually read these documents from the beginning to the end. We are actually going to work with these documents when we are reversing our code. You can download these pdf’s or just use the online version from IBM. Alright I hope you are well rested cause, we are going deep into SPU code today. SPU code is a lot easier to reverse using objdump cause, you can view the data right away, and you don’t have to calculate the addresses. Also the addresses you need to load into your registers are smaller numbers and you don’t have to combine them. The LS can only store about 256 KBs of data. This means, if you want more memory, then you need to use MFC DMA to access the main memory. Using MFC DMA isn’t easy to use. You need to use 2^4 byte alignment and use some special function calls in order to send and receive data from the MFC DMA traffic. Our example program doesn’t handle this but you can try and write something that utilizes this fantastic feature of the CELL/BE or check out the tutorials I mentioned above. Let’s cut the chit-chat and look at the code we have here. Oh, I almost forgot, we haven’t used objdump to view the code, so here are the magic words you need to use: Code:
spu-objdump –D –j .text SPU_guessing _start __do_global_dtors_aux call__do_global_dtors_aux frame_dummy call_frame_dummy atexit exit printf: sent to the PPE (not called directly by the SPU) puts: sent to the PPE srand: sent to the PPE rand: sent to the PPE scanf: sent to the PPE __stack_reg_va reg_4 reg_3 save_regs_1 save_regs_2 time: calls gettimeofday() __register_exitproc __call_exitprocs _exit gettimeofday: sent to PPE __send_to_ppe:
__muldi3 __do_global_ctors_aux call __do_global_ctors_aux As you can see the C runtime functions are actually passed to the PPE. The SPU doesn’t call them directly. Interesting mechanism, isn’t it? The most important parts are as follows: Code:
00000168 <main>: 168: 24 00 40 80 stqd $0,16($1) 16c: 24 fe c0 81 stqd $1,-80($1) 170: 1c ec 00 81 ai $1,$1,-80 174: 34 00 80 82 lqd $2,32($1) # 20 178: 3e e0 00 86 cdd $6,0($1) 17c: b0 40 81 86 shufb $2,$3,$2,$6 180: 24 00 80 82 stqd $2,32($1) # 20 184: 34 00 c0 82 lqd $2,48($1) # 30 188: 3e e0 00 83 cdd $3,0($1) 18c: b0 40 82 03 shufb $2,$4,$2,$3 190: 24 00 c0 82 stqd $2,48($1) # 30 194: 34 01 00 82 lqd $2,64($1) # 40 198: 3e e0 00 83 cdd $3,0($1) 19c: b0 40 82 83 shufb $2,$5,$2,$3 1a0: 24 01 00 82 stqd $2,64($1) # 40 1a4: 34 00 80 82 lqd $2,32($1) # 20 1a8: 23 82 0b 02 stqr $2,1200 <host> # 1200 1ac: 33 00 08 80 brsl $0,1f0 <initialize> # 1f0 1b0: 33 00 25 00 brsl $0,2d8 <player_guess> # 2d8 1b4: 33 82 0b 82 lqr $2,1210 <guess> # 1210 1b8: 33 82 07 03 lqr $3,11f0 <game_number> # 11f0 1bc: 78 00 c1 02 ceq $2,$2,$3 1c0: 20 7f fe 02 brz $2,1b0 <main+0x48> # 1b0 1c4: 42 06 28 03 ila $3,3152 # c50 1c8: 33 00 45 00 brsl $0,3f0 <puts> # 3f0 1cc: 33 82 04 82 lqr $2,11f0 <game_number> # 11f0 1d0: 42 06 40 03 ila $3,3200 # c80 1d4: 04 00 01 04 ori $4,$2,0 1d8: 33 00 3d 00 brsl $0,3c0 <printf> # 3c0 1dc: 40 80 00 02 il $2,0 1e0: 04 00 01 03 ori $3,$2,0 1e4: 1c 14 00 81 ai $1,$1,80 # 50 1e8: 34 00 40 80 lqd $0,16($1) 1ec: 35 00 00 00 bi $0 000001f0 <initialize>: 1f0: 24 00 40 80 stqd $0,16($1) 1f4: 24 ff 80 81 stqd $1,-32($1) 1f8: 1c f8 00 81 ai $1,$1,-32 1fc: 42 06 58 03 ila $3,3248 # cb0 200: 33 00 3e 00 brsl $0,3f0 <puts> # 3f0 204: 33 81 ff 82 lqr $2,1200 <host> # 1200 208: 42 06 68 03 ila $3,3280 # cd0 20c: 04 00 01 04 ori $4,$2,0 210: 33 00 36 00 brsl $0,3c0 <printf> # 3c0 214: 40 80 00 03 il $3,0 218: 33 00 77 00 brsl $0,5d0 <time> # 5d0 21c: 04 00 01 82 ori $2,$3,0 220: 04 00 01 03 ori $3,$2,0 224: 33 00 41 80 brsl $0,430 <srand> # 430 228: 33 00 47 00 brsl $0,460 <rand> # 460 22c: 40 80 05 02 il $2,10 230: 35 90 00 00 hbrp 230 <initialize+0x40>,$0 234: 35 90 00 00 hbrp 234 <initialize+0x44>,$0 238: 7f 00 01 00 heqi $0,$2,0 23c: 12 00 08 9a hbrr 2a4 <initialize+0xb4>,280 <initialize+0x90> # 280 240: 0c 00 01 8a sfi $10,$3,0 244: 0c 00 01 0b sfi $11,$2,0 248: 4c ff c1 8c cgti $12,$3,-1 24c: 4c ff c1 0d cgti $13,$2,-1 250: 81 40 c5 0c selb $10,$10,$3,$12 254: 81 60 85 8d selb $11,$11,$2,$13 258: 54 a0 05 06 clz $6,$10 25c: 54 a0 05 89 clz $9,$11 260: 40 80 00 87 il $7,1 264: 32 80 00 04 fsmbi $4,0 268: 08 02 43 09 sf $9,$6,$9 26c: 3f e0 05 05 shlqbyi $5,$10,0 270: 48 23 46 0d xor $13,$12,$13 274: 0b 62 43 87 shl $7,$7,$9 278: 0b 62 45 86 shl $6,$11,$9 27c: 00 20 00 00 lnop 280: 08 21 c2 0e or $14,$4,$7 284: 3f 3f c3 87 rotqmbii $7,$7,-1 288: 58 01 43 08 clgt $8,$6,$5 28c: 00 20 00 00 lnop 290: 08 01 43 09 sf $9,$6,$5 294: 3f 3f c3 06 rotqmbii $6,$6,-1 298: 80 81 07 08 selb $4,$14,$4,$8 29c: 00 20 00 00 lnop 2a0: 80 a1 44 88 selb $5,$9,$5,$8 2a4: 21 7f fb 87 brnz $7,280 <initialize+0x90> # 280 2a8: 0c 00 02 8a sfi $10,$5,0 2ac: 0c 00 02 0b sfi $11,$4,0 2b0: 80 a1 45 0c selb $5,$10,$5,$12 2b4: 80 82 c2 0d selb $4,$4,$11,$13 2b8: 04 00 02 82 ori $2,$5,0 2bc: 1c 00 41 02 ai $2,$2,1 2c0: 23 81 e6 02 stqr $2,11f0 <game_number> # 11f0 2c4: 42 06 80 03 ila $3,3328 # d00 2c8: 33 00 25 00 brsl $0,3f0 <puts> # 3f0 2cc: 1c 08 00 81 ai $1,$1,32 # 20 2d0: 34 00 40 80 lqd $0,16($1) 2d4: 35 00 00 00 bi $0 000002d8 <player_guess>: 2d8: 24 00 40 80 stqd $0,16($1) 2dc: 24 ff 80 81 stqd $1,-32($1) 2e0: 1c f8 00 81 ai $1,$1,-32 2e4: 42 06 a0 03 ila $3,3392 # d40 2e8: 33 00 1b 00 brsl $0,3c0 <printf> # 3c0 2ec: 42 06 b0 03 ila $3,3424 # d60 2f0: 42 09 08 04 ila $4,4624 # 1210 2f4: 33 00 3f 80 brsl $0,4f0 <scanf> # 4f0 2f8: 33 81 e3 02 lqr $2,1210 <guess> # 1210 2fc: 33 81 de 83 lqr $3,11f0 <game_number> # 11f0 300: 48 00 c1 02 cgt $2,$2,$3 304: 20 00 03 02 brz $2,31c <player_guess+0x44> # 31c 308: 33 81 e1 02 lqr $2,1210 <guess> # 1210 30c: 42 06 b8 03 ila $3,3440 # d70 310: 04 00 01 04 ori $4,$2,0 314: 33 00 15 80 brsl $0,3c0 <printf> # 3c0 318: 32 00 04 80 br 33c <player_guess+0x64> # 33c 31c: 33 81 de 82 lqr $2,1210 <guess> # 1210 320: 33 81 da 03 lqr $3,11f0 <game_number> # 11f0 324: 48 00 81 82 cgt $2,$3,$2 328: 20 00 02 82 brz $2,33c <player_guess+0x64> # 33c 32c: 33 81 dc 82 lqr $2,1210 <guess> # 1210 330: 42 06 e8 03 ila $3,3536 # dd0 334: 04 00 01 04 ori $4,$2,0 338: 33 00 11 00 brsl $0,3c0 <printf> # 3c0 33c: 1c 08 00 81 ai $1,$1,32 # 20 340: 34 00 40 80 lqd $0,16($1) 344: 35 00 00 00 bi $0 The ABI is pretty much the same as with the PPU. We have $0 which stores the current LR (Link Register or Back Chain Pointer). $1 stores the SP (Stack Pointer). $2 stores the environment pointer, which is used by some programming languages. $3 to $74 are used as arguments. $3 is the register that stores the returned result. We can safely assume that we won’t be storing any arguments on the stack that’s for sure with this many registers. SPU stack organization: ![]() After the listing I guess we should take a look at the code more closely: Code:
<main>: Code:
Prologue: stqd $0,16($1) stqd $1,-80($1) ai $1,$1,-80 Code:
Program Code: lqd $2,32($1) # 20 cdd $6,0($1) shufb $2,$3,$2,$6 stqd $2,32($1) # 20 Code:
lqd $2,48($1) # 30 cdd $3,0($1) shufb $2,$4,$2,$3 stqd $2,48($1) # 30 Code:
lqd $2,64($1) # 40 cdd $3,0($1) shufb $2,$5,$2,$3 stqd $2,64($1) # 40 Code:
lqd $2,32($1) # 20 stqr $2,1200 <host> # 1200 Code:
brsl $0,1f0 <initialize> # 1f0 brsl $0,2d8 <player_guess> # 2d8 lqr $2,1210 <guess> # 1210 lqr $3,11f0 <game_number> # 11f0 ceq $2,$2,$3 brz $2,1b0 <main+0x48> # 1b0 ila $3,3152 # c50 brsl $0,3f0 <puts> # 3f0 lqr $2,11f0 <game_number> # 11f0 ila $3,3200 # c80 ori $4,$2,0 brsl $0,3c0 <printf> # 3c0 il $2,0 ori $3,$2,0 We load guess into $2 and game_number into $3. The third instruction does the comparison and writes the EQ bits into $2. Yeah you are not mistaken. Unlike the PPU, the SPU does not have conditional registers. It uses the “GPRs” for that. The third instruction branches if the result in $2 is zero. If so then we jump back and call player_guess. If $2 and $3 are not equal, then we branch back. Remember if EQ is set then the two registers are equal, but we are checking for zero, which means we are checking if the two are not equal. After this we load our address (0xc20) of a string into $3. Our string is the following: “Oh goody, you guessed the number!”. After outputting this string with puts(). We are loading in game_number’s address into $2 and a string address (c50) into $3. $4 gets a Zero. Then we can call printf(). Our message will be the following: “the number was indeed %d Congratulations !!!/n”. After that we clear out $2 and $3 and load a Zero inside them. If you are interested in where the symbols are for the host, game_number and guess. Check out the .bss (uninitialized data) section using the following command: Code:
spu-objdump –D –j .text SPU_guessing You should get the following results. As you can see we have a pointer to the effective address of the LS (Local Store) as well: Code:
000011d0 <completed.4604>: ... 000011e0 <__ea_local_store>: ... 000011f0 <game_number>: ... 00001200 <host>: ... 00001210 <guess>: ... Code:
Epilogue: ai $1,$1,32 # 20 lqd $0,16($1) bi $0 Summary Let’s summarize what we managed to discover about main(). In general main() is almost the same as we saw in the PPU code. Only this time we are storing SPE_ID inside host and we have 3 arguments which we store on the stack. After that the program calls initialize(). This happens only once. After that we enter a loop where the program keeps asking the player for a number between 1 and 10. The main() loop does the comparison of guess and game_number and branches according to the result. If the two numbers are not the same then the program calls player_guess. If the two values are the same then the program writes out some strings to congratulate the player and the game_number as well. After this the program ends. Other Functions: Now, we are going to go through the rest of the functions briefly (as we did with PPU_guess): Code:
#initialize() starts here: <initialize>: #PROLOGUE: #Store the LR on the previous stack(main()) stqd $0,16($1) #Store SP on the bottom of the new stack stqd $1,-32($1) #decrement the SP to create the stack ai $1,$1,-32 #PROGRAM CODE: #Load the address of the following string:”Welcome to the Guessing game!” into $3 #(first argument) ila $3,3248 # cb0 #Display string using puts() brsl $0,3f0 <puts> # 3f0 #load the address of host into $2, by the way ila stands for load immediate address lqr $2,1200 <host> # 1200 #load address of the following string: “Our host for today is SPE number %d!/n” into $3 #(first argument) ila $3,3280 # cd0 #writes a zero into $4 (second argument) with $2 ori $4,$2,0 #Print the value and the string out using printf() brsl $0,3c0 <printf> # 3c0 #Load zero into $3 il $3,0 #call time() using 0 as argument brsl $0,5d0 <time> # 5d0 # write return value of time() into $2 with a zero ori $2,$3,0 ori $3,$2,0 #call srand() with the return value of time(0) brsl $0,430 <srand> # 430 #call rand(), no arguments brsl $0,460 <rand> # 460 #load 10 into $2 which means we are using MODULO on rand() and dividing it by $2 (10) il $2,10 ![]() There are two numbers stored on the stack: operand_1 on SP+64 which has the value of 50 and operand_2 on SP+48 which has the value of 100. The two data are being loaded into registers 2 and 3. this is a big thing cause in the PPU version we weren't able to spot all the operands. If we look at our SPU_guessing, then we can see that we are loading in 10 (immediate) into $2 and the result of rand() is inside $3. On 0x294 address (code) we can see that 1 is being added to the result. Code:
# lots of hint branch calls, bit shifts, masking and subtraction, yes this is the MODULO # operator hbrp 230 <initialize+0x40>,$0 hbrp 234 <initialize+0x44>,$0 heqi $0,$2,0 hbrr 2a4 <initialize+0xb4>,280 <initialize+0x90> # 280 sfi $10,$3,0 sfi $11,$2,0 cgti $12,$3,-1 cgti $13,$2,-1 selb $10,$10,$3,$12 selb $11,$11,$2,$13 clz $6,$10 clz $9,$11 il $7,1 fsmbi $4,0 sf $9,$6,$9 shlqbyi $5,$10,0 xor $13,$12,$13 shl $7,$7,$9 shl $6,$11,$9 lnop or $14,$4,$7 rotqmbii $7,$7,-1 clgt $8,$6,$5 lnop sf $9,$6,$5 rotqmbii $6,$6,-1 selb $4,$14,$4,$8 lnop selb $5,$9,$5,$8 brnz $7,280 <initialize+0x90> # 280 sfi $10,$5,0 sfi $11,$4,0 selb $5,$10,$5,$12 selb $4,$4,$11,$13 ori $2,$5,0 #add 1 to the result ai $2,$2,1 #store the result from $2 into game_number’s address stqr $2,11f0 <game_number> # 11f0 #load address of the following string:” A random number was generated between 1 to 10 for #you” into $3 (first argument) ila $3,3328 # d00 #out put the string above by calling puts() brsl $0,3f0 <puts> # 3f0 #EPILOGUE: #increment the SP to destroy the current stack ai $1,$1,32 # 20 #load back LR from SP+16 lqd $0,16($1) #branch into LR (previous code, in main()) bi $0 First thing this function does is that it welcomes the player then writes out which SPE is being used using another message. This function initializes the game. It generates a number between 1 and 10. We can finally read what kind of operands we are dealing with here. After generating the number the program stores it as game_number. So the generated number is actually game_number. Code:
#player_guess() starts here:
<player_guess>:
#PROLOGUE
#store LR on the previous stack(main())
stqd $0,16($1)
#store SP on the bottom of the new stack
stqd $1,-32($1)
#decrement the SP to create the new stack
ai $1,$1,-32
#PROGRAM CODE:
#load the address of the following string:”Please place your bet: ” into $3 (first argument)
ila $3,3392 # d40
#we use printf() to write out the string above
brsl $0,3c0 <printf> # 3c0
#load the address of the following string:” %d” into $3 (first argument)
ila $3,3424 # d60
#interestingly here we don’t see where the data’s address gets loaded from
#don’t be fooled use the .bss to see what the data is, it’s actually the data named “guess”
ila $4,4624 # 1210
#call scanf()
brsl $0,4f0 <scanf> # 4f0
#load guess into $2
lqr $2,1210 <guess> # 1210
#load game_number into $3
lqr $3,11f0 <game_number> # 11f0
#compare if guess($2) is greater than game_number($3), write the GT bits of $2
#according to the result
cgt $2,$2,$3
#branch if zero, which means branch if the previous comparison ended with $2 “not greater
#then” $3, the address we are branching to is still inside this function (obviously)
#it’s the part where we load guess and game_number then check if game_number is greater than
#guess (0x31c)
brz $2,31c <player_guess+0x44> # 31c
#if guess was greater than game_number then we do the following:
#load guess into $2
lqr $2,1210 <guess> # 1210
#load the address of the following string:” Nah, sorry you missed it! The number your
#looking for is actually SMALLER than %d./n” into $3 (first argument)
ila $3,3440 # d70
#load guess with zero into $4 (second argument)
ori $4,$2,0
#write out the string and the data (guess) using printf()
brsl $0,3c0 <printf> # 3c0
#Branch to EPILOGUE and exit this function
br 33c <player_guess+0x64> # 33c
0x31c: #load guess into $2
lqr $2,1210 <guess> # 1210
#load game_number into $3
lqr $3,11f0 <game_number> # 11f0
#compare if game_number is greater than guess
cgt $2,$3,$2
#if game_number is not greater than guess, then the two values are probably equal and
#we branch to EPILOGUE to exit this function, remember that we are checking if the
#two values are not equal
brz $2,33c <player_guess+0x64> # 33c
#if the two are not equal and it turns out that game_number is greater than guess:
#we load guess into $2
lqr $2,1210 <guess> # 1210
#load the following string:” Nah, sorry you missed it! The number your looking for is
#actually LARGER than %d./n” into $3 (first argument)
ila $3,3536 # dd0
#write guess with zero into $4 (second argument)
ori $4,$2,0
#call printf() to write out the data(guess) with the message above
brsl $0,3c0 <printf> # 3c0
0x33c: #EPILOGUE
#increment the SP to destroy the current stack
ai $1,$1,32 # 20
#Load old LR
lqd $0,16($1)
#branch back to main()
bi $0
The player_guess() does some of the logical decisions of the program, but not all of them. It asks for a number (no limits) and then compares it with game_number. If guess is greater than game_number, then we write out that guess was too large and the number we are looking for is smaller. After that the program destroys the stack and goes back to main(). Where it jumps back to the player_guess and creates the stack again. (What a waste, this logic should be inside main(). The problem is the same as with the PPU_guessing program.) But if guess is not greater than game_number then we check if guess is smaller than game_number. If so then we write out that the number we are looking for is larger than guess. If not then we exit from the function directly. Meaning that the two values are probably equal and main() will check that separately. Right, we are done reversing. When we fail miserably ![]() I bumped into several problems during the time I wrote this tutorial. It appears that using objdump on PPC64 code which is compiled with the “-dynamic” flag (normally if you don’t ask for “–static” explicitly then you get dynamic code) you can’t tell which function from the dynamically linked object file is being called. The problem is that some function calls like the libc functions get resolved dynamically. Which means you can’t determine what kind of function call was made. You only get redirected to .glink resolver code which makes no sense to mere humans. The .got doesn’t store an absolute address to these calls which brings us back to the problem I mentioned in the last sentence. What can we do then? How can we use objdump to reverse PPC64 code? …Honestly? Use IDA. There’s really no other way. You can try and guess what the calls could be. Also the addresses to data are negative numbers which make no sense what so ever. The following commands could help you with PPC64 code (but I think PPC64 is just hopeless as it gets with objdump): objdump –R <File_Name> // shows relocations in the ELF file ldd <File_Name> // view dependencies in your ELF nm <File_Name> // allows you to view symbols in your ELF The above commands can help you finding out the name of the functions and methods being called. Also check out the following article on dynamic linking and ELF files: http://devpit.org/wiki/Debugging_PowerPC_ELF_Binaries This is the command I tried to use to disassemble the PPC64 bit code: Code:
objdump –D –j .text PPU_guessing Code:
ppu-objdump –D –j .text PPU_guessing Conclusion That’s it for now folks. If anyone has questions, then please post them on this thread so I can take a look. It was actually pretty hard and time consuming to write this tutorial. I hope it shed some light on a few mysteries about reverse engineering code. Remember we’ve only touched basic code with symbols turned on. In most cases you don’t have this comfort. You need to recognize C runtime functions your self and you cannot rely on symbols. Also C++ code is another thing to reverse. It can be really painful. Just keep looking at assembly code and you’ll get better. Also you might want to try and compile your code with the following parameters: Code:
GCC <Source_Code.c> -S <Destination_Assembly code.s> SPU-GCC <Source_Code.c> -S <Destination_Assembly_code.c> Written by Disane Special thanks to: ooPo and RichDevX for helping me out. |
|
|
|
|
|
Likes: (4) |
|
|
#4 |
|
Member
![]() Join Date: Jul 2008
Location: Austria
Posts: 121
Likes: 18
Liked 85 Times in 29 Posts
Mentioned: 0 Post(s)
Tagged: 0 Thread(s)
|
I already stared working on the sequel of this tutorial, It's going to be about reversing C++ code (Classes mostly).
|
|
|
|
|
Likes: (2) |
![]() |
| Bookmarks |
| Thread Tools | |
|
|