Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: ida script & improve README #95

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,35 @@ pip3 install pyelftools requests
```

## Usage
Extract "lib" directory from apk file
```
python3 blutter.py path/to/app/lib/arm64-v8a out_dir
Blutter can analyze Flutter applications in several ways.

### APK File
If you have an `.apk` file. Simply provide the path to the APK file and the output directory as arguments:
```shell
python3 blutter.py path/to/app.apk out_dir
```
The blutter.py will automatically detect the Dart version from the flutter engine and call executable of blutter to get the information from libapp.so.

If the blutter executable for required Dart version does not exists, the script will automatically checkout Dart source code and compiling it.
### `.so` File(s)
Blutter can also analyze `.so` files directly. This can be done in two ways:

1. **Analyzing `.so` files extracted from an APK:**

If you have extracted the lib directory from an APK file, you can analyze it using Blutter. Provide the path to the lib directory and the output directory as arguments:
```shell
python3 blutter.py path/to/app/lib/arm64-v8a out_dir
```
> The `blutter.py` will automatically detect the Dart version from the Flutter engine and use the appropriate executable to extract information from `libapp.so`.

2. **Analyzing `libapp.so` with a known Dart version:**

If you only have `libapp.so` and know its Dart version, you can specify it to Blutter. Provide the Dart version with `--dart-version` option, the path to `libapp.so`, and the output directory as arguments:
```shell
python3 blutter.py --dart-version X.X.X_android_arm64 libapp.so out_dir
```
> Replace `X.X.X` with your lib dart version such as "3.4.2_android_arm64".


If the Blutter executable for the required Dart version does not exist, the script will automatically checkout the Dart source code and compile it.

## Update
You can use ```git pull``` to update and run blutter.py with ```--rebuild``` option to force rebuild the executable
Expand Down Expand Up @@ -83,4 +105,4 @@ python blutter.py path\to\lib\arm64-v8a build\vs --vs-sln
- Object modification
- Obfuscated app (still missing many functions)
- Reading iOS binary
- Input as apk or ipa
- Input as ipa
24 changes: 23 additions & 1 deletion blutter/src/DartDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ static std::string getFunctionName4Ida(const DartFunction& dartFn, const std::st
return "_anon_closure";
}

if (fnName.starts_with("#")) {
fnName.replace(0, 1, "@");
}

for (size_t pos = 0; ; pos += 1) {
pos = fnName.find("|_", pos);
if (pos == std::string::npos) break;
fnName.replace(pos, 2, "_");
}

auto periodPos = fnName.find('.');
std::string prefix;
if (dartFn.IsStatic() && dartFn.Kind() == DartFunction::NORMAL && periodPos != std::string::npos) {
Expand All @@ -44,6 +54,18 @@ static std::string getFunctionName4Ida(const DartFunction& dartFn, const std::st
fnName = fnName.substr(periodPos + 1);
}

// fnNames: #0#4internal, #0#1internal gives invalid name in IDA due to '#'
// lib file: https://github.com/worawit/blutter/issues/93#issuecomment-2283490634
for (size_t pos = 0; pos < fnName.size(); ++pos) {
if (fnName[pos] == '@' && pos + 1 < fnName.size() && fnName[pos + 1] == '#') {
fnName.replace(pos, 2, "_");
} else if (fnName[pos] == '0' && pos + 1 < fnName.size() && fnName[pos + 1] == '#') {
fnName.replace(pos, 2, "0");
} else if (fnName[pos] == '#') {
fnName[pos] = '_';
}
}

if (OP_MAP.contains(fnName)) {
return prefix + "op_" + OP_MAP[fnName];
}
Expand Down Expand Up @@ -842,4 +864,4 @@ void DartDumper::DumpObjects(const char* filename)
of << dumpInstance(obj, simpleForm, nestedObj, 0);
of << "\n\n";
}
}
}