std::unique_ptr DexFile::OpenOneDexFileFromZip(const ZipArchive& zip_archive,
const char* entry_name,
const std::string& location,
bool verify_checksum,
std::string* error_msg,
ZipOpenErrorCode* error_code) {
ScopedTrace trace("Dex file open from Zip Archive " + std::string(location));
CHECK(!location.empty());
std::unique_ptr zip_entry(zip_archive.Find(entry_name, error_msg));
if (zip_entry == nullptr) {
*error_code = ZipOpenErrorCode::kEntryNotFound;
return nullptr;
}
if (zip_entry->GetUncompressedLength() == 0) {
*error_msg = StringPrintf("Dex file '%s' has zero length", location.c_str());
*error_code = ZipOpenErrorCode::kDexFileError;
return nullptr;
}
std::unique_ptr map;
if (zip_entry->IsUncompressed()) {
if (!zip_entry->IsAlignedTo(alignof(Header))) {
// Do not mmap unaligned ZIP entries because
// doing so would fail dex verification which requires 4 byte alignment.
LOG(WARNING) <
<
<
} else {
// Map uncompressed files within zip as file-backed to avoid a dirty copy.
map.reset(zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg));
if (map == nullptr) {
LOG(WARNING) <
<
// Try again with Extraction which still has a chance of recovery.
}
}
}
if (map == nullptr) {
// Default path for compressed ZIP entries,
// and fallback for stored ZIP entries.
map.reset(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg));
}
if (map == nullptr) {
*error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(),
error_msg->c_str());
*error_code = ZipOpenErrorCode::kExtractToMemoryError;
return nullptr;
}
VerifyResult verify_result;
std::unique_ptr dex_file = OpenCommon(map->Begin(),
map->Size(),
location,
zip_entry->GetCrc32(),
kNoOatDexFile,
/* verify */ true,
verify_checksum,
error_msg,
&verify_result);
if (dex_file == nullptr) {
if (verify_result == VerifyResult::kVerifyNotAttempted) {
*error_code = ZipOpenErrorCode::kDexFileError;
} else {
*error_code = ZipOpenErrorCode::kVerifyError;
}
return nullptr;
}
dex_file->mem_map_ = std::move(map);
if (!dex_file->DisableWrite()) {
*error_msg = StringPrintf("Failed to make dex file '%s' read only", location.c_str());
*error_code = ZipOpenErrorCode::kMakeReadOnlyError;
return nullptr;
}
CHECK(dex_file->IsReadOnly()) <
if (verify_result != VerifyResult::kVerifySucceeded) {
*error_code = ZipOpenErrorCode::kVerifyError;
return nullptr;
}
*error_code = ZipOpenErrorCode::kNoError;
return dex_file;
}
3.最后我们认真分析OpenCommon这个函数,无论是通过oat_file获得的
oat_dex_file
获得
dex_file
也好,是直接打开zip或者dex文件获得
dex_file
也好,最终都得用到这个函数,所以它作为常用脱壳点的意义就很清楚了,它的前2个参数分别是dex的起始地址和大小,直接hook就可以dump出dex了。
其实只要有base地址,通过dex数据结构就可以定位size,加个偏移就行parseInt(base,16) + 0x20
std::unique_ptr DexFile::OpenCommon(const uint8_t* base,//这里是dex的开始
size_t size,//这里是dex的大小
const std::string& location,//这里是地址
uint32_t location_checksum,
const OatDexFile* oat_dex_file,//如果直接打开文件而不是通过oat文件获得dex,这个参数是kNoOatDexFile,hook打印这个参数就可以判断一些壳是否放弃了oat文件强制以dex解释运行
bool verify,
bool verify_checksum,
std::string* error_msg,
VerifyResult* verify_result) {
if (verify_result != nullptr) {
*verify_result = VerifyResult::kVerifyNotAttempted;
}
std::unique_ptr dex_file(new DexFile(base,
size,
location,
location_checksum,
oat_dex_file));//这里new了一个dex_file实例,至此dex_file加载结束
if (dex_file == nullptr) {
*error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
error_msg->c_str());
return nullptr;
}
if (!dex_file->Init(error_msg)) {//init初始化
dex_file.reset();
return nullptr;
}
if (verify && !DexFileVerifier::Verify(dex_file.get(),
dex_file->Begin(),
dex_file->Size(),
location.c_str(),
verify_checksum,
error_msg)) {//Verify验证
if (verify_result != nullptr) {
*verify_result = VerifyResult::kVerifyFailed;
}
return nullptr;
}
if (verify_result != nullptr) {
*verify_result = VerifyResult::kVerifySucceeded;
}
return dex_file;
}
最后,仍然是画个及其丑陋的图,辅助自己理解。
4.下面贴一下frida hook脱壳脚本,很简单,每一步我都备注了,上次问我的同学仔细看一下,看看还有什么问题