~thestr4ng3r/rz-amsdump

cb8ff8c41bcc7fd580f66061382ab5b56d518f94 — Florian Märkl 3 years ago d166a75 master
Port to Rizin
9 files changed, 50 insertions(+), 66 deletions(-)

D .gitmodules
M README.md
M cabal.project
D r2-amsdump.cabal
D r2pipe
A rz-amsdump.cabal
R src/{DumpR2.hs => DumpRz.hs}
M src/Main.hs
M src/Stacktrace.hs
D .gitmodules => .gitmodules +0 -3
@@ 1,3 0,0 @@
[submodule "r2pipe"]
	path = r2pipe
	url = git@github.com:radareorg/radare2-r2pipe.git

M README.md => README.md +17 -17
@@ 1,36 1,36 @@

# r2-amsdump
# rz-amsdump

Tool for analyzing [Atmosphère](https://github.com/Atmosphere-NX/Atmosphere) (Nintendo Switch CFW) crash dumps in radare2.
Tool for analyzing [Atmosphère](https://github.com/Atmosphere-NX/Atmosphere) (Nintendo Switch CFW) crash dumps in Rizin.
It lets you load binary dumps that Atmosphère puts into `atmosphere/crash_reports/dumps` on the SD card as flags and
mapped stack and tls contents.

## Installation

r2-amsdump works as a single executable that you can run from inside radare2.
rz-amsdump works as a single executable that you can run from inside Rizin.
To build it, you need to have GHC and Cabal installed.
The easiest way to install these is by using [ghcup](https://www.haskell.org/ghcup/) but if you prefer, you may want to use
your distribution's packages instead.

Then, clone this repo including its submodules and install r2-amsdump like this
Then, clone this repo and install rz-amsdump like this
(replace `$HOME/bin` by the directory you want the final executable to be installed to):
```
git clone --recursive https://github.com/thestr4ng3r/r2-amsdump.git
cd r2-amsdump
git clone https://git.sr.ht/~thestr4ng3r/rz-amsdump
cd rz-amsdump
cabal v2-install --installdir=$HOME/bin
```

## Usage

```
Usage: r2-amsdump COMMAND
  Tool for analyzing Atmosphère crash dumps in radare2
Usage: rz-amsdump COMMAND
  Tool for analyzing Atmosphère crash dumps in Rizin

Available options:
  -h,--help                Show this help text

Available commands:
  load                     Load dump from file as flags into r2
  load                     Load dump from file as flags into Rizin
  stacktrace               Print stacktrace of a thread (by default the crashed
                           one)
```


@@ 38,7 38,7 @@ Available commands:
### Loading

In this example, we will analyze a crash of the `hid_mitm` module.
First, make sure to load the modules into r2 at the correct base addresses from the dump.
First, make sure to load the modules into Rizin at the correct base addresses from the dump.
This info is not available in the binary dump, so you have to manually read it from the text .log file:

```


@@ 48,21 48,21 @@ Module Info:
    Module 00:
        Address:                 0000000060400000-0000000060457000
        Name:                    hid_mitm
$ r2 -B 0x60400000 hid_mitm.elf
$ rizin -B 0x60400000 hid_mitm.elf
[0x604001c0]>
```

Then, load the dump on top of it from inside r2 (make sure r2-amsdump is in your PATH or provide the full path to it):
Then, load the dump on top of it from inside rizin (make sure rz-amsdump is in your PATH or provide the full path to it):

```
[0x604001c0]> #!pipe r2-amsdump load .../crash_reports/dumps/01582056428_0100000000000faf_thread_info.bin
# running inside r2, applying commands through r2pipe.
[0x604001c0]> #!pipe rz-amsdump load .../crash_reports/dumps/01582056428_0100000000000faf_thread_info.bin
# running inside rizin, applying commands through rz-pipe.
# done.
```

Or, the entire loading as a one-liner:
```
r2 -c '#!pipe r2-amsdump load .../crash_reports/dumps/01582056428_0100000000000faf_thread_info.bin' -B 0x60400000 hid_mitm.elf
rizin -c '#!pipe rz-amsdump load .../crash_reports/dumps/01582056428_0100000000000faf_thread_info.bin' -B 0x60400000 hid_mitm.elf
```

### Displaying Info


@@ 162,7 162,7 @@ Register contents for the crashed thread:

Stacktrace of thread with tid 395:
```
[0x604001c0]> #!pipe r2-amsdump stacktrace 395
[0x604001c0]> #!pipe rz-amsdump stacktrace 395
┌────────────┬────────────────────┬───────────────────────────────────────────────────────────────────────┐
│  Address   │        Flag        │                              Description                              │
╞════════════╪════════════════════╪═══════════════════════════════════════════════════════════════════════╡


@@ 176,7 176,7 @@ Stacktrace of thread with tid 395:

Stacktrace of the crashed thread:
```
[0x604001c0]> #!pipe r2-amsdump stacktrace
[0x604001c0]> #!pipe rz-amsdump stacktrace
┌────────────┬──────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│  Address   │           Flag           │                                                                                       Description                                                                                       │
╞════════════╪══════════════════════════╪═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╡

M cabal.project => cabal.project +1 -1
@@ 1,1 1,1 @@
packages: . r2pipe/haskell
packages: .

D r2-amsdump.cabal => r2-amsdump.cabal +0 -28
@@ 1,28 0,0 @@
cabal-version:       >=1.10
-- Initial package description 'r2-amsdump.cabal' generated by 'cabal
-- init'.  For further documentation, see
-- http://haskell.org/cabal/users-guide/

name:                r2-amsdump
version:             0.1.0.0
-- synopsis:
-- description:
-- bug-reports:
-- license:
license-file:        COPYING
author:              Florian Märkl
maintainer:          r2-amsdump@florianmaerkl.de
-- copyright:
-- category:
build-type:          Simple
-- extra-source-files:  CHANGELOG.md

executable r2-amsdump
  main-is:             Main.hs
  other-modules:       AMSDump DumpR2 Stacktrace
  -- other-extensions:
  build-depends:       base >=4.12 && <5,
                       r2pipe, bytestring, aeson, binary, utf8-string,
                       optparse-applicative, regex-pcre-builtin, transformers, table-layout
  hs-source-dirs:      src
  default-language:    Haskell2010

D r2pipe => r2pipe +0 -1
@@ 1,1 0,0 @@
Subproject commit b1a28543874dd7b608f0ef3456ae4969e59976c0

A rz-amsdump.cabal => rz-amsdump.cabal +17 -0
@@ 0,0 1,17 @@
cabal-version:       >=1.10

name:                rz-amsdump
version:             0.1.0.0
license-file:        COPYING
author:              Florian Märkl
maintainer:          rz-amsdump@florianmaerkl.de
build-type:          Simple

executable rz-amsdump
  main-is:             Main.hs
  other-modules:       AMSDump DumpRz Stacktrace
  build-depends:       base >=4.12 && <5,
                       rz-pipe, bytestring, aeson, binary, utf8-string,
                       optparse-applicative, regex-pcre-builtin, transformers, table-layout
  hs-source-dirs:      src
  default-language:    Haskell2010

R src/DumpR2.hs => src/DumpRz.hs +1 -1
@@ 1,6 1,6 @@
{-# LANGUAGE OverloadedStrings #-}

module DumpR2 (loadDump) where
module DumpRz (loadDump) where

import Control.Arrow
import Data.Word

M src/Main.hs => src/Main.hs +11 -11
@@ 3,7 3,7 @@
module Main where

import System.Environment
import R2Pipe
import RzPipe
import qualified Data.ByteString.Lazy as L
import qualified Data.Aeson as JSON
import Control.Exception


@@ 12,21 12,21 @@ import Options.Applicative
import Data.Semigroup ((<>))

import qualified AMSDump as Dump
import qualified DumpR2 as DumpR2
import qualified DumpRz as DumpRz
import Stacktrace

openR2 :: IO R2Context
openR2 = open Nothing
openRz :: IO RzContext
openRz = open Nothing

applyDump :: Dump.Dump -> IO ()
applyDump dump = do
    let cmds = DumpR2.loadDump dump
    r <- try openR2 <&> (\res -> case res :: Either SomeException R2Context of
    let cmds = DumpRz.loadDump dump
    r <- try openRz <&> (\res -> case res :: Either SomeException RzContext of
                                    Left e -> Nothing
                                    Right r -> Just r)
    case r of
        Just _ -> putStrLn "# running inside r2, applying commands through r2pipe."
        Nothing -> putStrLn "# not running inside r2, just printing commands."
        Just _ -> putStrLn "# running inside rizin, applying commands through rizinpipe."
        Nothing -> putStrLn "# not running inside rizin, just printing commands."
    let runCmd c = case r of
                        Just r -> () <$ cmd r c
                        Nothing -> putStrLn c


@@ 43,7 43,7 @@ runCommand (LoadDump filename) = do
    case dump of
        Just d -> applyDump d
        Nothing -> putStrLn "# load failed"
runCommand (Stacktrace tid) = stacktrace tid =<< openR2
runCommand (Stacktrace tid) = stacktrace tid =<< openRz

main :: IO ()
main = runCommand =<< customExecParser p optsInfo


@@ 51,10 51,10 @@ main = runCommand =<< customExecParser p optsInfo
          opts = subparser
                    (  command "load" (info
                        (  (LoadDump <$> argument str (metavar "FILE")) <**> helper)
                        (  progDesc "Load dump from file as flags into r2" ))
                        (  progDesc "Load dump from file as flags into Rizin" ))
                    <> command "stacktrace" (info
                        (  (Stacktrace <$> (optional $ argument auto (metavar "[THREAD-ID]"))) <**> helper)
                        (  progDesc "Print stacktrace of a thread (by default the crashed one)" )))
          optsInfo = info (opts <**> helper) (progDesc "Tool for analyzing Atmosphère crash dumps in radare2")
          optsInfo = info (opts <**> helper) (progDesc "Tool for analyzing Atmosphère crash dumps in Rizin")
          p = prefs showHelpOnEmpty


M src/Stacktrace.hs => src/Stacktrace.hs +3 -4
@@ 12,9 12,8 @@ import Data.Maybe
import Data.Char (isSpace)
import Text.Layout.Table
import Data.List (sortOn)
import qualified Data.ByteString.Lazy.UTF8 as BU

import R2Pipe
import RzPipe

trim :: String -> String
trim = f . f where f = reverse . dropWhile isSpace


@@ 25,7 24,7 @@ data Flag = Flag
    , offset :: Word64 }
    deriving (Show, Generic, JSON.FromJSON)

stacktrace :: Maybe Int -> R2Context -> IO ()
stacktrace :: Maybe Int -> RzContext -> IO ()
stacktrace th r = do
    let regex = case th of
                    Just tid -> printf "th%d(?:.crash)?" tid


@@ 41,7 40,7 @@ stacktrace th r = do
        putStrLn "No Stacktrace flags found for this thread."
    else do
            flagcols <- mapM (\(flag, num) -> do
                                          desc <- BU.toString <$> cmd r (printf "fd @ %#x @F:symbols" $ offset flag)
                                          desc <- cmd r (printf "fd @ %#x @F:symbols" $ offset flag)
                                          return $ trim <$> [printf "%#010x" $ offset flag, name flag, desc]) sorted
            let table = tableString [def, def, def] unicodeS (titlesH ["Address", "Flag", "Description"]) $ rowG <$> flagcols
            putStrLn table