refa: signature check

This commit is contained in:
xtaodada 2024-11-10 21:22:08 +08:00
parent 49d7de346e
commit ebc0a45b13
Signed by: xtaodada
GPG Key ID: 4CBB3F4FA8C85659
27 changed files with 657 additions and 3395 deletions

View File

@ -665,7 +665,7 @@ target_link_libraries(${NATIVE_LIB}
android
OpenSLES
cpufeatures
genuine
integrity
breakpad)

View File

@ -1,13 +1,10 @@
add_library(genuine STATIC
integrity/am-proxy.c
integrity/apk-sign-v2.c
integrity/common.c
integrity/genuine.c
integrity/inline.c
add_library(integrity STATIC
integrity/integrity.cpp
integrity/meth.cpp
integrity/openat.c
integrity/path.c
integrity/plt.c)
target_compile_options(genuine PUBLIC
integrity/read_cert.cpp
integrity/SHA1.cpp)
target_compile_options(integrity PUBLIC
-Oz -Wall -fvisibility=hidden)
target_include_directories(genuine PUBLIC
target_include_directories(integrity PUBLIC
integrity)

View File

@ -0,0 +1,341 @@
// 100% Public Domain.
//
// Original C Code
// -- Steve Reid <steve@edmweb.com>
// Small changes to fit into bglibs
// -- Bruce Guenter <bruce@untroubled.org>
// Translation to simpler C++ Code
// -- Volker Grabsch <vog@notjusthosting.com>
// Safety fixes
// -- Eugene Hopkinson <slowriot at voxelstorm dot com>
// Adapt for project
// Dmitriy Khaustov <khaustov.dm@gmail.com>
//
// File created on: 2017.02.25
// SHA1.cpp
#include "SHA1.h"
#include <sstream>
#include <iomanip>
#include <fstream>
namespace toolkit {
static const size_t BLOCK_INTS = 16; /* number of 32bit integers per SHA1 block */
static const size_t BLOCK_BYTES = BLOCK_INTS * 4;
static void reset(uint32_t digest[], std::string &buffer, uint64_t &transforms)
{
/* SHA1 initialization constants */
digest[0] = 0x67452301;
digest[1] = 0xefcdab89;
digest[2] = 0x98badcfe;
digest[3] = 0x10325476;
digest[4] = 0xc3d2e1f0;
/* Reset counters */
buffer = "";
transforms = 0;
}
static uint32_t rol(const uint32_t value, const size_t bits)
{
return (value << bits) | (value >> (32 - bits));
}
static uint32_t blk(const uint32_t block[BLOCK_INTS], const size_t i)
{
return rol(block[(i+13)&15] ^ block[(i+8)&15] ^ block[(i+2)&15] ^ block[i], 1);
}
/*
* (R0+R1), R2, R3, R4 are the different operations used in SHA1
*/
static void R0(const uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i)
{
z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5);
w = rol(w, 30);
}
static void R1(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i)
{
block[i] = blk(block, i);
z += ((w&(x^y))^y) + block[i] + 0x5a827999 + rol(v, 5);
w = rol(w, 30);
}
static void R2(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i)
{
block[i] = blk(block, i);
z += (w^x^y) + block[i] + 0x6ed9eba1 + rol(v, 5);
w = rol(w, 30);
}
static void R3(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i)
{
block[i] = blk(block, i);
z += (((w|x)&y)|(w&x)) + block[i] + 0x8f1bbcdc + rol(v, 5);
w = rol(w, 30);
}
static void R4(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, const uint32_t x, const uint32_t y, uint32_t &z, const size_t i)
{
block[i] = blk(block, i);
z += (w^x^y) + block[i] + 0xca62c1d6 + rol(v, 5);
w = rol(w, 30);
}
/*
* Hash a single 512-bit block. This is the core of the algorithm.
*/
static void transform(uint32_t digest[], uint32_t block[BLOCK_INTS], uint64_t &transforms)
{
/* Copy digest[] to working vars */
uint32_t a = digest[0];
uint32_t b = digest[1];
uint32_t c = digest[2];
uint32_t d = digest[3];
uint32_t e = digest[4];
/* 4 rounds of 20 operations each. Loop unrolled. */
R0(block, a, b, c, d, e, 0);
R0(block, e, a, b, c, d, 1);
R0(block, d, e, a, b, c, 2);
R0(block, c, d, e, a, b, 3);
R0(block, b, c, d, e, a, 4);
R0(block, a, b, c, d, e, 5);
R0(block, e, a, b, c, d, 6);
R0(block, d, e, a, b, c, 7);
R0(block, c, d, e, a, b, 8);
R0(block, b, c, d, e, a, 9);
R0(block, a, b, c, d, e, 10);
R0(block, e, a, b, c, d, 11);
R0(block, d, e, a, b, c, 12);
R0(block, c, d, e, a, b, 13);
R0(block, b, c, d, e, a, 14);
R0(block, a, b, c, d, e, 15);
R1(block, e, a, b, c, d, 0);
R1(block, d, e, a, b, c, 1);
R1(block, c, d, e, a, b, 2);
R1(block, b, c, d, e, a, 3);
R2(block, a, b, c, d, e, 4);
R2(block, e, a, b, c, d, 5);
R2(block, d, e, a, b, c, 6);
R2(block, c, d, e, a, b, 7);
R2(block, b, c, d, e, a, 8);
R2(block, a, b, c, d, e, 9);
R2(block, e, a, b, c, d, 10);
R2(block, d, e, a, b, c, 11);
R2(block, c, d, e, a, b, 12);
R2(block, b, c, d, e, a, 13);
R2(block, a, b, c, d, e, 14);
R2(block, e, a, b, c, d, 15);
R2(block, d, e, a, b, c, 0);
R2(block, c, d, e, a, b, 1);
R2(block, b, c, d, e, a, 2);
R2(block, a, b, c, d, e, 3);
R2(block, e, a, b, c, d, 4);
R2(block, d, e, a, b, c, 5);
R2(block, c, d, e, a, b, 6);
R2(block, b, c, d, e, a, 7);
R3(block, a, b, c, d, e, 8);
R3(block, e, a, b, c, d, 9);
R3(block, d, e, a, b, c, 10);
R3(block, c, d, e, a, b, 11);
R3(block, b, c, d, e, a, 12);
R3(block, a, b, c, d, e, 13);
R3(block, e, a, b, c, d, 14);
R3(block, d, e, a, b, c, 15);
R3(block, c, d, e, a, b, 0);
R3(block, b, c, d, e, a, 1);
R3(block, a, b, c, d, e, 2);
R3(block, e, a, b, c, d, 3);
R3(block, d, e, a, b, c, 4);
R3(block, c, d, e, a, b, 5);
R3(block, b, c, d, e, a, 6);
R3(block, a, b, c, d, e, 7);
R3(block, e, a, b, c, d, 8);
R3(block, d, e, a, b, c, 9);
R3(block, c, d, e, a, b, 10);
R3(block, b, c, d, e, a, 11);
R4(block, a, b, c, d, e, 12);
R4(block, e, a, b, c, d, 13);
R4(block, d, e, a, b, c, 14);
R4(block, c, d, e, a, b, 15);
R4(block, b, c, d, e, a, 0);
R4(block, a, b, c, d, e, 1);
R4(block, e, a, b, c, d, 2);
R4(block, d, e, a, b, c, 3);
R4(block, c, d, e, a, b, 4);
R4(block, b, c, d, e, a, 5);
R4(block, a, b, c, d, e, 6);
R4(block, e, a, b, c, d, 7);
R4(block, d, e, a, b, c, 8);
R4(block, c, d, e, a, b, 9);
R4(block, b, c, d, e, a, 10);
R4(block, a, b, c, d, e, 11);
R4(block, e, a, b, c, d, 12);
R4(block, d, e, a, b, c, 13);
R4(block, c, d, e, a, b, 14);
R4(block, b, c, d, e, a, 15);
/* Add the working vars back into digest[] */
digest[0] += a;
digest[1] += b;
digest[2] += c;
digest[3] += d;
digest[4] += e;
/* Count the number of transformations */
transforms++;
}
static void buffer_to_block(const std::string &buffer, uint32_t block[BLOCK_INTS])
{
/* Convert the std::string (byte buffer) to a uint32_t array (MSB) */
for (size_t i = 0; i < BLOCK_INTS; i++)
{
block[i] =
(buffer[4*i+3] & 0xFF)
| (buffer[4*i+2] & 0xFF) << 8
| (buffer[4*i+1] & 0xff) << 16
| (buffer[4*i+0] & 0xff) << 24;
}
}
SHA1::SHA1()
{
reset(digest, buffer, transforms);
}
void SHA1::update(const std::string &s)
{
std::istringstream is(s);
update(is);
}
void SHA1::update(std::istream &is)
{
while (true)
{
char sbuf[BLOCK_BYTES];
is.read(sbuf, BLOCK_BYTES - buffer.size());
buffer.append(sbuf, is.gcount());
if (buffer.size() != BLOCK_BYTES)
{
return;
}
uint32_t block[BLOCK_INTS];
buffer_to_block(buffer, block);
transform(digest, block, transforms);
buffer.clear();
}
}
/*
* Add padding and return the message digest.
*/
std::string SHA1::final()
{
auto str = final_bin();
std::ostringstream result;
for (size_t i = 0; i < str.size(); i++)
{
char b[3];
sprintf(b, "%02x", static_cast<unsigned char>(str[i]));
result << b;
}
return result.str();
}
std::string SHA1::final_bin()
{
/* Total number of hashed bits */
uint64_t total_bits = (transforms*BLOCK_BYTES + buffer.size()) * 8;
/* Padding */
buffer += 0x80;
size_t orig_size = buffer.size();
while (buffer.size() < BLOCK_BYTES)
{
buffer += (char)0x00;
}
uint32_t block[BLOCK_INTS];
buffer_to_block(buffer, block);
if (orig_size > BLOCK_BYTES - 8)
{
transform(digest, block, transforms);
for (size_t i = 0; i < BLOCK_INTS - 2; i++)
{
block[i] = 0;
}
}
/* Append total_bits, split this uint64_t into two uint32_t */
block[BLOCK_INTS - 1] = total_bits;
block[BLOCK_INTS - 2] = (total_bits >> 32);
transform(digest, block, transforms);
/* Hex std::string */
std::string result;
for (size_t i = 0; i < sizeof(digest) / sizeof(digest[0]); i++)
{
for (size_t b = 0; b < sizeof(digest[0])/sizeof(uint8_t); b++)
{
result.push_back((digest[i] >> (8 * (sizeof(digest[0]) / sizeof(uint8_t) - 1 - b))) & 0xFF);
}
}
/* Reset for next run */
reset(digest, buffer, transforms);
return result;
}
std::string SHA1::from_file(const std::string &filename)
{
std::ifstream stream(filename.c_str(), std::ios::binary);
SHA1 checksum;
checksum.update(stream);
return checksum.final();
}
std::string SHA1::encode(const std::string &s)
{
SHA1 sha1;
sha1.update(s);
return sha1.final();
}
std::string SHA1::encode_bin(const std::string &s)
{
SHA1 sha1;
sha1.update(s);
return sha1.final_bin();
}
} //namespace toolkit

View File

@ -0,0 +1,47 @@
// 100% Public Domain.
//
// Original C Code
// -- Steve Reid <steve@edmweb.com>
// Small changes to fit into bglibs
// -- Bruce Guenter <bruce@untroubled.org>
// Translation to simpler C++ Code
// -- Volker Grabsch <vog@notjusthosting.com>
// Safety fixes
// -- Eugene Hopkinson <slowriot at voxelstorm dot com>
// Adapt for project
// Dmitriy Khaustov <khaustov.dm@gmail.com>
//
// File created on: 2017.02.25
// SHA1.h
#pragma once
#include <cstdint>
#include <iostream>
#include <string>
namespace toolkit {
class SHA1 final
{
public:
SHA1();
void update(const std::string &s);
void update(std::istream &is);
std::string final();
std::string final_bin();
static std::string from_file(const std::string &filename);
static std::string encode(const std::string &s);
static std::string encode_bin(const std::string &s);
private:
uint32_t digest[5];
std::string buffer;
uint64_t transforms;
};
}//namespace toolkit

View File

@ -1,824 +0,0 @@
//
// Created by Thom on 2019/3/15.
//
#include "common.h"
#include "am-proxy.h"
static inline void fill_android_app_ActivityManager(char v[]) {
// android/app/ActivityManager
static unsigned int m = 0;
if (m == 0) {
m = 23;
} else if (m == 29) {
m = 31;
}
v[0x0] = 'e';
v[0x1] = 'k';
v[0x2] = 'b';
v[0x3] = 'u';
v[0x4] = 'g';
v[0x5] = '`';
v[0x6] = 'n';
v[0x7] = '$';
v[0x8] = 'm';
v[0x9] = '}';
v[0xa] = '~';
v[0xb] = ' ';
v[0xc] = 'Q';
v[0xd] = 'r';
v[0xe] = 'f';
v[0xf] = 'z';
v[0x10] = 'b';
v[0x11] = '|';
v[0x12] = 'b';
v[0x13] = 'y';
v[0x14] = 'L';
v[0x15] = 'c';
v[0x16] = 'm';
v[0x17] = 'e';
v[0x18] = 'b';
v[0x19] = 'c';
v[0x1a] = 'u';
for (unsigned int i = 0; i < 0x1b; ++i) {
v[i] ^= ((i + 0x1b) % m);
}
v[0x1b] = '\0';
}
static inline void fill_getService(char v[]) {
// getService
static unsigned int m = 0;
if (m == 0) {
m = 7;
} else if (m == 11) {
m = 13;
}
v[0x0] = 'd';
v[0x1] = 'a';
v[0x2] = 'q';
v[0x3] = 'U';
v[0x4] = 'e';
v[0x5] = 's';
v[0x6] = 't';
v[0x7] = 'j';
v[0x8] = 'g';
v[0x9] = '`';
for (unsigned int i = 0; i < 0xa; ++i) {
v[i] ^= ((i + 0xa) % m);
}
v[0xa] = '\0';
}
static inline void fill_getService_signature(char v[]) {
// ()Landroid/app/IActivityManager;
static unsigned int m = 0;
if (m == 0) {
m = 31;
} else if (m == 37) {
m = 41;
}
v[0x0] = ')';
v[0x1] = '+';
v[0x2] = 'O';
v[0x3] = 'e';
v[0x4] = 'k';
v[0x5] = 'b';
v[0x6] = 'u';
v[0x7] = 'g';
v[0x8] = '`';
v[0x9] = 'n';
v[0xa] = '$';
v[0xb] = 'm';
v[0xc] = '}';
v[0xd] = '~';
v[0xe] = ' ';
v[0xf] = 'Y';
v[0x10] = 'P';
v[0x11] = 'q';
v[0x12] = 'g';
v[0x13] = '}';
v[0x14] = 'c';
v[0x15] = '\x7f';
v[0x16] = 'c';
v[0x17] = 'a';
v[0x18] = 'T';
v[0x19] = '{';
v[0x1a] = 'u';
v[0x1b] = '}';
v[0x1c] = 'z';
v[0x1d] = '{';
v[0x1e] = 'r';
v[0x1f] = ':';
for (unsigned int i = 0; i < 0x20; ++i) {
v[i] ^= ((i + 0x20) % m);
}
v[0x20] = '\0';
}
static inline void fill_android_app_ActivityManagerNative(char v[]) {
// android/app/ActivityManagerNative
static unsigned int m = 0;
if (m == 0) {
m = 31;
} else if (m == 37) {
m = 41;
}
v[0x0] = 'c';
v[0x1] = 'm';
v[0x2] = '`';
v[0x3] = 'w';
v[0x4] = 'i';
v[0x5] = 'n';
v[0x6] = 'l';
v[0x7] = '&';
v[0x8] = 'k';
v[0x9] = '{';
v[0xa] = '|';
v[0xb] = '"';
v[0xc] = 'O';
v[0xd] = 'l';
v[0xe] = 'd';
v[0xf] = 'x';
v[0x10] = 'd';
v[0x11] = 'z';
v[0x12] = '`';
v[0x13] = 'l';
v[0x14] = '[';
v[0x15] = 'v';
v[0x16] = 'v';
v[0x17] = 'x';
v[0x18] = '}';
v[0x19] = '~';
v[0x1a] = 'n';
v[0x1b] = 'S';
v[0x1c] = '\x7f';
v[0x1d] = 't';
v[0x1e] = 'h';
v[0x1f] = 't';
v[0x20] = 'f';
for (unsigned int i = 0; i < 0x21; ++i) {
v[i] ^= ((i + 0x21) % m);
}
v[0x21] = '\0';
}
static inline void fill_getDefault(char v[]) {
// getDefault
static unsigned int m = 0;
if (m == 0) {
m = 7;
} else if (m == 11) {
m = 13;
}
v[0x0] = 'd';
v[0x1] = 'a';
v[0x2] = 'q';
v[0x3] = 'B';
v[0x4] = 'e';
v[0x5] = 'g';
v[0x6] = 'c';
v[0x7] = 'v';
v[0x8] = 'h';
v[0x9] = 'q';
for (unsigned int i = 0; i < 0xa; ++i) {
v[i] ^= ((i + 0xa) % m);
}
v[0xa] = '\0';
}
static inline void fill_getDefault_signature(char v[]) {
// ()Landroid/app/IActivityManager;
static unsigned int m = 0;
if (m == 0) {
m = 31;
} else if (m == 37) {
m = 41;
}
v[0x0] = ')';
v[0x1] = '+';
v[0x2] = 'O';
v[0x3] = 'e';
v[0x4] = 'k';
v[0x5] = 'b';
v[0x6] = 'u';
v[0x7] = 'g';
v[0x8] = '`';
v[0x9] = 'n';
v[0xa] = '$';
v[0xb] = 'm';
v[0xc] = '}';
v[0xd] = '~';
v[0xe] = ' ';
v[0xf] = 'Y';
v[0x10] = 'P';
v[0x11] = 'q';
v[0x12] = 'g';
v[0x13] = '}';
v[0x14] = 'c';
v[0x15] = '\x7f';
v[0x16] = 'c';
v[0x17] = 'a';
v[0x18] = 'T';
v[0x19] = '{';
v[0x1a] = 'u';
v[0x1b] = '}';
v[0x1c] = 'z';
v[0x1d] = '{';
v[0x1e] = 'r';
v[0x1f] = ':';
for (unsigned int i = 0; i < 0x20; ++i) {
v[i] ^= ((i + 0x20) % m);
}
v[0x20] = '\0';
}
static inline void fill_android_os_BinderProxy(char v[]) {
// android/os/BinderProxy
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = 'b';
v[0x1] = 'j';
v[0x2] = 'a';
v[0x3] = 't';
v[0x4] = 'h';
v[0x5] = 'a';
v[0x6] = 'm';
v[0x7] = '%';
v[0x8] = 'd';
v[0x9] = '\x7f';
v[0xa] = '"';
v[0xb] = 'L';
v[0xc] = 'f';
v[0xd] = '~';
v[0xe] = 'u';
v[0xf] = 'w';
v[0x10] = 'r';
v[0x11] = 'Q';
v[0x12] = 'p';
v[0x13] = 'l';
v[0x14] = '|';
v[0x15] = '|';
for (unsigned int i = 0; i < 0x16; ++i) {
v[i] ^= ((i + 0x16) % m);
}
v[0x16] = '\0';
}
static inline void fill_android_os_IInterface(char v[]) {
// android/os/IInterface
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = 'c';
v[0x1] = 'm';
v[0x2] = '`';
v[0x3] = 'w';
v[0x4] = 'i';
v[0x5] = 'n';
v[0x6] = 'l';
v[0x7] = '&';
v[0x8] = 'e';
v[0x9] = 'x';
v[0xa] = '#';
v[0xb] = 'D';
v[0xc] = 'G';
v[0xd] = 'a';
v[0xe] = 'd';
v[0xf] = 't';
v[0x10] = '`';
v[0x11] = 'f';
v[0x12] = '`';
v[0x13] = 'a';
v[0x14] = 'f';
for (unsigned int i = 0; i < 0x15; ++i) {
v[i] ^= ((i + 0x15) % m);
}
v[0x15] = '\0';
}
static inline void fill_asBinder(char v[]) {
// asBinder
static unsigned int m = 0;
if (m == 0) {
m = 7;
} else if (m == 11) {
m = 13;
}
v[0x0] = '`';
v[0x1] = 'q';
v[0x2] = 'A';
v[0x3] = 'm';
v[0x4] = 'k';
v[0x5] = 'b';
v[0x6] = 'e';
v[0x7] = 's';
for (unsigned int i = 0; i < 0x8; ++i) {
v[i] ^= ((i + 0x8) % m);
}
v[0x8] = '\0';
}
static inline void fill_asBinder_signature(char v[]) {
// ()Landroid/os/IBinder;
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = '+';
v[0x1] = '-';
v[0x2] = 'I';
v[0x3] = 'g';
v[0x4] = 'i';
v[0x5] = 'l';
v[0x6] = '{';
v[0x7] = 'e';
v[0x8] = 'b';
v[0x9] = 'h';
v[0xa] = '"';
v[0xb] = 'a';
v[0xc] = '|';
v[0xd] = '?';
v[0xe] = 'X';
v[0xf] = 'P';
v[0x10] = 'i';
v[0x11] = 'o';
v[0x12] = 'f';
v[0x13] = 'f';
v[0x14] = 'v';
v[0x15] = '>';
for (unsigned int i = 0; i < 0x16; ++i) {
v[i] ^= ((i + 0x16) % m);
}
v[0x16] = '\0';
}
static inline void fill_android_os_ServiceManager(char v[]) {
// android/os/ServiceManager
static unsigned int m = 0;
if (m == 0) {
m = 23;
} else if (m == 29) {
m = 31;
}
v[0x0] = 'c';
v[0x1] = 'm';
v[0x2] = '`';
v[0x3] = 'w';
v[0x4] = 'i';
v[0x5] = 'n';
v[0x6] = 'l';
v[0x7] = '&';
v[0x8] = 'e';
v[0x9] = 'x';
v[0xa] = '#';
v[0xb] = '^';
v[0xc] = 'k';
v[0xd] = '}';
v[0xe] = 'f';
v[0xf] = 'x';
v[0x10] = 'q';
v[0x11] = 'v';
v[0x12] = 'Y';
v[0x13] = 't';
v[0x14] = 'x';
v[0x15] = 'a';
v[0x16] = 'f';
v[0x17] = 'g';
v[0x18] = 'q';
for (unsigned int i = 0; i < 0x19; ++i) {
v[i] ^= ((i + 0x19) % m);
}
v[0x19] = '\0';
}
static inline void fill_getService_signature_IBinder(char v[]) {
// (Ljava/lang/String;)Landroid/os/IBinder;
static unsigned int m = 0;
if (m == 0) {
m = 37;
} else if (m == 41) {
m = 43;
}
v[0x0] = '+';
v[0x1] = 'H';
v[0x2] = 'o';
v[0x3] = 'g';
v[0x4] = 'q';
v[0x5] = 'i';
v[0x6] = '&';
v[0x7] = 'f';
v[0x8] = 'j';
v[0x9] = 'b';
v[0xa] = 'j';
v[0xb] = '!';
v[0xc] = '\\';
v[0xd] = 'd';
v[0xe] = 'c';
v[0xf] = '{';
v[0x10] = '}';
v[0x11] = 's';
v[0x12] = '.';
v[0x13] = '?';
v[0x14] = '[';
v[0x15] = 'y';
v[0x16] = 'w';
v[0x17] = '~';
v[0x18] = 'i';
v[0x19] = 's';
v[0x1a] = 't';
v[0x1b] = 'z';
v[0x1c] = '0';
v[0x1d] = 'O';
v[0x1e] = 'R';
v[0x1f] = '\r';
v[0x20] = 'j';
v[0x21] = 'f';
v[0x22] = 'i';
v[0x23] = 'o';
v[0x24] = 'f';
v[0x25] = 'f';
v[0x26] = 'v';
v[0x27] = '>';
for (unsigned int i = 0; i < 0x28; ++i) {
v[i] ^= ((i + 0x28) % m);
}
v[0x28] = '\0';
}
static inline void fill_activity(char v[]) {
// activity
static unsigned int m = 0;
if (m == 0) {
m = 7;
} else if (m == 11) {
m = 13;
}
v[0x0] = '`';
v[0x1] = 'a';
v[0x2] = 'w';
v[0x3] = 'm';
v[0x4] = 's';
v[0x5] = 'o';
v[0x6] = 't';
v[0x7] = 'x';
for (unsigned int i = 0; i < 0x8; ++i) {
v[i] ^= ((i + 0x8) % m);
}
v[0x8] = '\0';
}
static inline void fill_getName(char v[]) {
// getName
static unsigned int m = 0;
if (m == 0) {
m = 5;
} else if (m == 7) {
m = 11;
}
v[0x0] = 'e';
v[0x1] = 'f';
v[0x2] = 'p';
v[0x3] = 'N';
v[0x4] = '`';
v[0x5] = 'o';
v[0x6] = 'f';
for (unsigned int i = 0; i < 0x7; ++i) {
v[i] ^= ((i + 0x7) % m);
}
v[0x7] = '\0';
}
static inline void fill_getName_signature(char v[]) {
// ()Ljava/lang/String;
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = ')';
v[0x1] = '+';
v[0x2] = 'O';
v[0x3] = 'n';
v[0x4] = 'd';
v[0x5] = 'p';
v[0x6] = 'f';
v[0x7] = '\'';
v[0x8] = 'e';
v[0x9] = 'k';
v[0xa] = 'e';
v[0xb] = 'k';
v[0xc] = '"';
v[0xd] = ']';
v[0xe] = '{';
v[0xf] = 'b';
v[0x10] = 'x';
v[0x11] = '|';
v[0x12] = 'g';
v[0x13] = ':';
for (unsigned int i = 0; i < 0x14; ++i) {
v[i] ^= ((i + 0x14) % m);
}
v[0x14] = '\0';
}
static inline void fill_activity_manager_service_is_s(char v[]) {
// activity manager service is %s
static unsigned int m = 0;
if (m == 0) {
m = 29;
} else if (m == 31) {
m = 37;
}
v[0x0] = '`';
v[0x1] = 'a';
v[0x2] = 'w';
v[0x3] = 'm';
v[0x4] = 's';
v[0x5] = 'o';
v[0x6] = 's';
v[0x7] = 'q';
v[0x8] = ')';
v[0x9] = 'g';
v[0xa] = 'j';
v[0xb] = 'b';
v[0xc] = 'l';
v[0xd] = 'i';
v[0xe] = 'j';
v[0xf] = 'b';
v[0x10] = '1';
v[0x11] = 'a';
v[0x12] = 'v';
v[0x13] = 'f';
v[0x14] = 'c';
v[0x15] = '\x7f';
v[0x16] = 't';
v[0x17] = '}';
v[0x18] = '9';
v[0x19] = 's';
v[0x1a] = 'h';
v[0x1b] = '<';
v[0x1c] = '%';
v[0x1d] = 'r';
for (unsigned int i = 0; i < 0x1e; ++i) {
v[i] ^= ((i + 0x1e) % m);
}
v[0x1e] = '\0';
}
static inline void showError(JNIEnv *env, jclass object) {
char v1[0x8], v2[0x1f];
if (object == NULL) {
fill_activity_manager_service_is_s(v2); // 0x1f
LOGE(v2, NULL);
} else {
fill_getName(v1); // 0x8
fill_getName_signature(v2); // 0x15
jclass clazz = (*env)->GetObjectClass(env, object);
jmethodID method = (*env)->GetMethodID(env, clazz, v1, v2);
jstring string = (*env)->CallObjectMethod(env, object, method);
const char *name = (*env)->GetStringUTFChars(env, string, NULL);
fill_activity_manager_service_is_s(v2); // 0x1f
LOGE(v2, name);
(*env)->ReleaseStringUTFChars(env, string, name);
(*env)->DeleteLocalRef(env, string);
(*env)->DeleteLocalRef(env, clazz);
}
}
bool isAmProxy(JNIEnv *env, int sdk) {
char v1[0xb], v2[0x29];
bool proxy = false;
jmethodID method;
jclass classActivityManager;
if (sdk >= 26) {
fill_android_app_ActivityManager(v2); // 0x1b + 1
classActivityManager = (*env)->FindClass(env, v2);
debug(env, "ActivityManager: %s", classActivityManager);
fill_getService(v1); // 0xa + 1
fill_getService_signature(v2); // 0x20 + 1
method = (*env)->GetStaticMethodID(env, classActivityManager, v1, v2);
#ifdef DEBUG
LOGI("ActivityManager.getService: %p", method);
#endif
if (method == NULL) {
#ifdef DEBUG
LOGW("cannot find ActivityManager.getService");
#endif
(*env)->ExceptionClear(env);
goto clean3;
}
} else {
fill_android_app_ActivityManagerNative(v2); // 0x21 + 1
classActivityManager = (*env)->FindClass(env, v2);
debug(env, "ActivityManagerNative: %s", classActivityManager);
fill_getDefault(v1); // 0xa + 1
fill_getDefault_signature(v2); // 0x20 + 1
method = (*env)->GetStaticMethodID(env, classActivityManager, v1, v2);
#ifdef DEBUG
LOGI("ActivityManagerNative.getDefault: %p", method);
#endif
}
jobject activityManager = (*env)->CallStaticObjectMethod(env, classActivityManager, method);
if (activityManager == NULL) {
#ifdef DEBUG
LOGW("activity manager is null");
#endif
if ((*env)->ExceptionCheck(env)) {
#ifdef DEBUG
(*env)->ExceptionDescribe(env);
#endif
(*env)->ExceptionClear(env);
}
goto clean3;
}
debug(env, "activity manager: %s", activityManager);
jclass binderClass = NULL;
jobject binder = NULL;
fill_android_os_BinderProxy(v2); // 0x16 + 1
jclass classBinderProxy = (*env)->FindClass(env, v2);
if (classBinderProxy == NULL) {
#ifdef DEBUG
LOGW("cannot find BinderProxy");
#endif
(*env)->ExceptionClear(env);
} else {
debug(env, "BinderProxy: %s", classBinderProxy);
}
fill_android_os_IInterface(v2); // 0x15 + 1
jclass classIInterface = (*env)->FindClass(env, v2);
debug(env, "IInterface: %s", classIInterface);
if (!(*env)->IsInstanceOf(env, activityManager, classIInterface)) {
#ifdef DEBUG
LOGW("activity manager is not IInterface");
#endif
goto clean2;
}
fill_asBinder(v1); // 0x8 + 1
fill_asBinder_signature(v2); // 0x16 + 1
method = (*env)->GetMethodID(env, classIInterface, v1, v2);
#ifdef DEBUG
LOGI("IInterface.asBinder: %p", method);
#endif
binder = (*env)->CallObjectMethod(env, activityManager, method);
if (binder == NULL) {
if ((*env)->ExceptionCheck(env)) {
#ifdef DEBUG
(*env)->ExceptionDescribe(env);
#endif
(*env)->ExceptionClear(env);
}
showError(env, NULL);
proxy = true;
goto clean2;
}
debug(env, "binder: %s", binder);
binderClass = (*env)->GetObjectClass(env, binder);
debug(env, "binder class: %s", binderClass);
if (classBinderProxy != NULL && !(*env)->IsSameObject(env, binderClass, classBinderProxy)) {
showError(env, binderClass);
proxy = true;
goto clean2;
}
fill_android_os_ServiceManager(v2); // 0x19 + 1
jclass classServiceManager = (*env)->FindClass(env, v2);
if (classServiceManager == NULL) {
#ifdef DEBUG
LOGW("cannot find ServiceManager");
#endif
(*env)->ExceptionClear(env);
goto clean2;
}
debug(env, "ServiceManager: %s", classServiceManager);
fill_getService(v1); // 0xa + 1
fill_getService_signature_IBinder(v2); // 0x28 + 1
method = (*env)->GetStaticMethodID(env, classServiceManager, v1, v2);
#ifdef DEBUG
LOGI("ServiceManager.getService: %p", method);
#endif
if (method == NULL) {
(*env)->ExceptionClear(env);
goto clean1;
}
fill_activity(v2); // 0x8 + 1
jstring stringActivity = (*env)->NewStringUTF(env, v2);
jobject service = (*env)->CallStaticObjectMethod(env, classServiceManager, method,
stringActivity);
jclass serviceClass = NULL;
if (service == NULL) {
if ((*env)->ExceptionCheck(env)) {
#ifdef DEBUG
(*env)->ExceptionDescribe(env);
#endif
(*env)->ExceptionClear(env);
}
showError(env, NULL);
proxy = true;
goto clean;
}
debug(env, "service: %s", service);
serviceClass = (*env)->GetObjectClass(env, service);
debug(env, "service class: %s", serviceClass);
if (classBinderProxy != NULL && !(*env)->IsSameObject(env, serviceClass, classBinderProxy)) {
showError(env, serviceClass);
proxy = true;
goto clean;
}
bool equals = (*env)->IsSameObject(env, binder, service);
#ifdef DEBUG
LOGI("binder: %p, service: %p, equals: %d", binder, service, equals);
#endif
proxy = !equals;
clean:
if (serviceClass != NULL) {
(*env)->DeleteLocalRef(env, serviceClass);
}
if (service != NULL) {
(*env)->DeleteLocalRef(env, service);
}
(*env)->DeleteLocalRef(env, stringActivity);
clean1:
(*env)->DeleteLocalRef(env, classServiceManager);
clean2:
if (binderClass != NULL) {
(*env)->DeleteLocalRef(env, binderClass);
}
if (binder != NULL) {
(*env)->DeleteLocalRef(env, binder);
}
if (classBinderProxy != NULL) {
(*env)->DeleteLocalRef(env, classBinderProxy);
}
(*env)->DeleteLocalRef(env, classIInterface);
(*env)->DeleteLocalRef(env, activityManager);
clean3:
(*env)->DeleteLocalRef(env, classActivityManager);
return proxy;
}

View File

@ -1,21 +0,0 @@
//
// Created by Thom on 2019/3/15.
//
#ifndef BREVENT_AM_PROXY_H
#define BREVENT_AM_PROXY_H
#include <jni.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
bool isAmProxy(JNIEnv *env, int sdk);
#ifdef __cplusplus
}
#endif
#endif //BREVENT_AM_PROXY_H

View File

@ -1,169 +0,0 @@
//
// Created by Thom on 2019/3/8.
//
#include <fcntl.h>
#include <unistd.h>
#include <stdbool.h>
#ifdef MAIN
#include <stdio.h>
#else
#include "common.h"
#include "openat.h"
#endif
static bool isApkSigBlock42(const char *buffer) {
// APK Sig Block 42
return *buffer == 'A'
&& *++buffer == 'P'
&& *++buffer == 'K'
&& *++buffer == ' '
&& *++buffer == 'S'
&& *++buffer == 'i'
&& *++buffer == 'g'
&& *++buffer == ' '
&& *++buffer == 'B'
&& *++buffer == 'l'
&& *++buffer == 'o'
&& *++buffer == 'c'
&& *++buffer == 'k'
&& *++buffer == ' '
&& *++buffer == '4'
&& *++buffer == '2';
}
int checkSignature(const char *path) {
unsigned char buffer[0x11] = {0};
uint32_t size4;
uint64_t size8, size_of_block;
#ifdef DEBUG
LOGI("check signature for %s", path);
#endif
int sign = -1;
int fd = (int) openAt(AT_FDCWD, path, O_RDONLY);
#ifdef DEBUG_OPENAT
LOGI("openat %s returns %d", path, fd);
#endif
if (fd < 0) {
return sign;
}
sign = 1;
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
for (int i = 0;; ++i) {
unsigned short n;
lseek(fd, -i - 2, SEEK_END);
read(fd, &n, 2);
if (n == i) {
lseek(fd, -22, SEEK_CUR);
read(fd, &size4, 4);
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
#ifdef MAIN
if (i > 0) {
printf("warning: comment length is %d\n", i);
}
#endif
break;
}
}
if (i == 0xffff) {
#ifdef MAIN
printf("error: cannot find eocd\n");
#endif
goto clean;
}
}
lseek(fd, 12, SEEK_CUR);
// offset
read(fd, &size4, 0x4);
lseek(fd, (off_t) (size4 - 0x18), SEEK_SET);
read(fd, &size8, 0x8);
read(fd, buffer, 0x10);
if (!isApkSigBlock42((char *) buffer)) {
goto clean;
}
lseek(fd, (off_t) (size4 - (size8 + 0x8)), SEEK_SET);
read(fd, &size_of_block, 0x8);
if (size_of_block != size8) {
goto clean;
}
for (;;) {
uint32_t id;
uint32_t offset;
read(fd, &size8, 0x8); // sequence length
if (size8 == size_of_block) {
break;
}
read(fd, &id, 0x4); // id
offset = 4;
#ifdef MAIN
printf("id: 0x%08x\n", id);
#endif
if ((id ^ 0xdeadbeefu) == 0xafa439f5u || (id ^ 0xdeadbeefu) == 0x2efed62f) {
read(fd, &size4, 0x4); // signer-sequence length
read(fd, &size4, 0x4); // signer length
read(fd, &size4, 0x4); // signed data length
offset += 0x4 * 3;
read(fd, &size4, 0x4); // digests-sequence length
lseek(fd, (off_t) (size4), SEEK_CUR);// skip digests
offset += 0x4 + size4;
read(fd, &size4, 0x4); // certificates length
read(fd, &size4, 0x4); // certificate length
offset += 0x4 * 2;
#ifdef MAIN
int hash = 1;
signed char c;
for (unsigned i = 0; i < size4; ++i) {
read(fd, &c, 0x1);
hash = 31 * hash + c;
}
offset += size4;
printf(" size: 0x%04x, hash: 0x%08x\n", size4, ((unsigned) hash) ^ 0x14131211u);
#else
#if defined(GENUINE_SIZE) && defined(GENUINE_HASH)
if (size4 == GENUINE_SIZE) {
int hash = 1;
signed char c;
for (unsigned i = 0; i < size4; ++i) {
read(fd, &c, 0x1);
hash = 31 * hash + c;
}
offset += size4;
if ((((unsigned) hash) ^ 0x14131211u) == GENUINE_HASH) {
sign = 0;
break;
}
}
#else
sign = 0;
break;
#endif
#endif
}
lseek(fd, (off_t) (size8 - offset), SEEK_CUR);
}
clean:
close(fd);
return sign;
}
#ifdef MAIN
int main(int argc, char **argv) {
if (argc > 1) {
checkSignature(argv[1]);
}
}
#endif

View File

@ -1,18 +0,0 @@
//
// Created by Thom on 2019/3/8.
//
#ifndef BREVENT_APK_SIGN_V2_H
#define BREVENT_APK_SIGN_V2_H
#ifdef __cplusplus
extern "C" {
#endif
int checkSignature(const char *path);
#ifdef __cplusplus
}
#endif
#endif //BREVENT_APK_SIGN_V2_H

View File

@ -1,233 +0,0 @@
//
// Created by Thom on 2019/3/20.
//
#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include <sys/system_properties.h>
#include "common.h"
#include "plt.h"
static volatile int mGenuine;
static bool onCheckTrue(JNIEnv *env __unused) {
#ifdef DEBUG_NATIVE
has_native_libs();
#endif
#ifdef DEBUG_GENUINE_MOCK
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckFalse(JNIEnv *env __unused) {
#if defined(GENUINE_FALSE_CRASH)
return false;
#elif defined(GENUINE_FALSE_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckFake(JNIEnv *env __unused) {
#if defined(GENUINE_FAKE_CRASH)
return false;
#elif defined(GENUINE_FAKE_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckOverlay(JNIEnv *env __unused) {
#if defined(GENUINE_OVERLAY_CRASH)
return false;
#elif defined(GENUINE_OVERLAY_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckOdex(JNIEnv *env __unused) {
#if defined(GENUINE_ODEX_CRASH)
return false;
#elif defined(GENUINE_ODEX_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckDex(JNIEnv *env __unused) {
#if defined(GENUINE_DEX_CRASH)
return false;
#elif defined(GENUINE_DEX_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckProxy(JNIEnv *env __unused) {
#if defined(GENUINE_PROXY_CRASH)
return false;
#elif defined(GENUINE_PROXY_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckError(JNIEnv *env __unused) {
#if defined(GENUINE_ERROR_CRASH)
return false;
#elif defined(GENUINE_ERROR_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckFatal(JNIEnv *env __unused) {
#if defined(GENUINE_FATAL_CRASH)
return false;
#elif defined(GENUINE_FATAL_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
static bool onCheckNoapk(JNIEnv *env __unused) {
#if defined(GENUINE_NOAPK_CRASH)
return false;
#elif defined(GENUINE_NOAPK_NATIVE)
start_native_activity_async(env);
#endif
return true;
}
bool setGenuine(JNIEnv *env, int genuine) {
mGenuine = genuine;
switch (genuine) {
case CHECK_TRUE:
return onCheckTrue(env);
case CHECK_FALSE:
return onCheckFalse(env);
case CHECK_FAKE:
return onCheckFake(env);
case CHECK_OVERLAY:
return onCheckOverlay(env);
case CHECK_ODEX:
return onCheckOdex(env);
case CHECK_DEX:
return onCheckDex(env);
case CHECK_PROXY:
return onCheckProxy(env);
case CHECK_ERROR:
return onCheckError(env);
case CHECK_FATAL:
return onCheckFatal(env);
case CHECK_NOAPK:
return onCheckNoapk(env);
default:
return true;
}
}
int getGenuine() {
return mGenuine;
}
char *getGenuinePackageName() {
#ifdef GET_GENUINE_PACKAGE_NAME
return GET_GENUINE_PACKAGE_NAME();
#elif defined(GENUINE_NAME)
static unsigned int m = 0;
if (m == 0) {
m = 20;
} else if (m == 23) {
m = 29;
}
char name[] = GENUINE_NAME;
unsigned int length = sizeof(name) - 1;
for (unsigned int i = 0; i < length; ++i) {
name[i] ^= ((i + length) % m);
}
name[length] = '\0';
return strdup(name);
#else
return NULL;
#endif
}
__attribute__((__format__ (__printf__, 2, 0)))
void genuine_log_print(int prio, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
__android_log_vprint(prio, TAG, fmt, ap);
va_end(ap);
}
static inline void fill_ro_build_version_sdk(char v[]) {
// ro.build.version.sdk
static unsigned int m = 0;
if (m == 0) {
m = 19;
} else if (m == 23) {
m = 29;
}
v[0x0] = 's';
v[0x1] = 'm';
v[0x2] = '-';
v[0x3] = 'f';
v[0x4] = 'p';
v[0x5] = 'o';
v[0x6] = 'k';
v[0x7] = 'l';
v[0x8] = '\'';
v[0x9] = '|';
v[0xa] = 'n';
v[0xb] = '~';
v[0xc] = '~';
v[0xd] = 'g';
v[0xe] = '`';
v[0xf] = '~';
v[0x10] = '?';
v[0x11] = 'a';
v[0x12] = 'd';
v[0x13] = 'j';
for (unsigned int i = 0; i < 0x14; ++i) {
v[i] ^= ((i + 0x14) % m);
}
v[0x14] = '\0';
}
int getSdk() {
static int sdk = 0;
if (sdk == 0) {
char v1[0x20];
char prop[PROP_VALUE_MAX] = {0};
fill_ro_build_version_sdk(v1);
__system_property_get(v1, prop);
sdk = (int) strtol(prop, NULL, 10);
}
return sdk;
}
#ifdef DEBUG
void debug(JNIEnv *env, const char *format, jobject object) {
if (object == NULL) {
LOGI(format, NULL);
} else {
jclass objectClass = (*env)->FindClass(env, "java/lang/Object");
jmethodID toString = (*env)->GetMethodID(env, objectClass, "toString",
"()Ljava/lang/String;");
jstring string = (jstring) (*env)->CallObjectMethod(env, object, toString);
const char *value = (*env)->GetStringUTFChars(env, string, NULL);
LOGI(format, value);
(*env)->ReleaseStringUTFChars(env, string, value);
(*env)->DeleteLocalRef(env, string);
(*env)->DeleteLocalRef(env, objectClass);
}
}
#endif

View File

@ -1,73 +0,0 @@
//
// Created by Thom on 2019/3/20.
//
#ifndef BREVENT_COMMON_H
#define BREVENT_COMMON_H
#include <jni.h>
#include <stdbool.h>
#include <android/log.h>
#if __has_include("genuine.h")
#include "genuine.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif
enum {
CHECK_TRUE,
CHECK_FALSE,
CHECK_FAKE,
CHECK_OVERLAY,
CHECK_ODEX,
CHECK_DEX,
CHECK_PROXY,
CHECK_ERROR,
CHECK_FATAL,
CHECK_NOAPK,
};
#ifndef TAG
#define TAG "Genuine"
#endif
#ifndef LOGI
#define LOGI(...) (genuine_log_print(ANDROID_LOG_INFO, __VA_ARGS__))
#endif
#ifndef LOGW
#define LOGW(...) (genuine_log_print(ANDROID_LOG_WARN, __VA_ARGS__))
#endif
#ifndef LOGE
#define LOGE(...) (genuine_log_print(ANDROID_LOG_ERROR, __VA_ARGS__))
#endif
void genuine_log_print(int prio, const char *fmt, ...);
char *getGenuinePackageName();
bool setGenuine(JNIEnv *env, int genuine);
int getGenuine();
int getSdk();
bool has_native_libs();
#ifdef DEBUG
void debug(JNIEnv *env, const char *format, jobject object);
#else
#define debug(...) do {} while(0);
#endif
#ifdef __cplusplus
}
#endif
#endif //BREVENT_COMMON_H

File diff suppressed because it is too large Load Diff

View File

@ -1,43 +0,0 @@
#include <stdbool.h>
#define GENUINE_NAME {0x61, 0x6c, 0x69, 0x2b, 0x7e, 0x73, 0x69, 0x66, 0x66, 0x6a, 0x6e, 0x7e, 0x20, 0x7f, 0x71, 0x76, 0x77, 0x61, 0x67, 0x73, 0x63, 0x6e, 0x0}
#define GENUINE_SIZE 0x0313
#define GENUINE_HASH 0xe21978bf
/* genuine false handler */
#define GENUINE_FALSE_CRASH
// #define GENUINE_FALSE_NATIVE
/* genuine fake handler */
#define GENUINE_FAKE_CRASH
// #define GENUINE_FAKE_NATIVE
/* genuine overlay handler */
// #define GENUINE_OVERLAY_CRASH
// #define GENUINE_OVERLAY_NATIVE
/* genuine odex handler */
// #define GENUINE_ODEX_CRASH
// #define GENUINE_ODEX_NATIVE
/* genuine dex handler */
#define GENUINE_DEX_CRASH
// #define GENUINE_DEX_NATIVE
/* genuine proxy handler */
#define GENUINE_PROXY_CRASH
// #define GENUINE_PROXY_NATIVE
/* genuine error handler */
#define GENUINE_ERROR_CRASH
// #define GENUINE_ERROR_NATIVE
/* genuine fatal handler */
#define GENUINE_FATAL_CRASH
// #define GENUINE_FATAL_NATIVE
/* genuine noapk handler */
#define GENUINE_NOAPK_CRASH
// #define GENUINE_NOAPK_NATIVE
bool checkGenuine(JNIEnv *env);

View File

@ -1,333 +0,0 @@
//
// Created by Thom on 2019/3/18.
//
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "inline.h"
#include "common.h"
#ifdef DEBUG_HOOK_SELF
#include <syscall.h>
#include <unistd.h>
#include <sys/stat.h>
#include "whale/whale.h"
static long faccessat_replace(int dirfd, const char *pathname, int mode, int flags) {
return syscall(__NR_faccessat, dirfd, pathname, mode, flags);
}
static long fchownat_replace(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags) {
return syscall(__NR_fchownat, dirfd, pathname, owner, group, flags);
}
static int futimens_replace(int fd, const struct timespec times[2]) {
return utimensat(fd, NULL, times, 0);
}
#if defined(__arm__) || defined(__aarch64__)
void check_inline_hook_hookzz() {
void *backup;
zz_disable_arm_arm64_b_branch();
ZzReplace(&fchownat, &fchownat_replace, &backup);
LOGI("hookzz fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace,
isInlineHooked(&fchownat));
ZzReplace(&faccessat, &faccessat_replace, &backup);
LOGI("hookzz faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace,
isInlineHooked(&faccessat));
ZzReplace(&futimens, &futimens_replace, &backup);
LOGI("hookzz futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace,
isInlineHooked(&futimens));
}
void check_inline_hook_hookzz_b() {
void *backup;
zz_enable_arm_arm64_b_branch();
ZzReplace(&fchownat, &fchownat_replace, &backup);
LOGI("hookzz-b fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace,
isInlineHooked(&fchownat));
ZzReplace(&faccessat, &faccessat_replace, &backup);
LOGI("hookzz-b faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace,
isInlineHooked(&faccessat));
ZzReplace(&futimens, &futimens_replace, &backup);
LOGI("hookzz-b futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace,
isInlineHooked(&futimens));
}
#endif
void check_inline_hook_whale() {
WInlineHookFunction(&fchownat, &fchownat_replace, NULL);
LOGI("whale fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace,
isInlineHooked(&fchownat));
WInlineHookFunction(&faccessat, &faccessat_replace, NULL);
LOGI("whale faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace,
isInlineHooked(&faccessat));
WInlineHookFunction(&futimens, &futimens_replace, NULL);
LOGI("whale futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace,
isInlineHooked(&futimens));
}
#if defined(__arm__) || defined(DEBUG_HOOK_IDE)
#include "substrate/CydiaSubstrate.h"
void check_inline_hook_substrate() {
MSHookFunction(&fchownat, &fchownat_replace, NULL);
LOGI("substrate fchownat: %p, replace: %p, hooked: %d", &fchownat, &fchownat_replace,
isInlineHooked(&fchownat));
MSHookFunction(&faccessat, &faccessat_replace, NULL);
LOGI("substrate faccessat: %p, repalce: %p, hooked: %d", &faccessat, &faccessat_replace,
isInlineHooked(&faccessat));
MSHookFunction(&futimens, &futimens_replace, NULL);
LOGI("substrate futimens: %p, replace: %p, hooked: %d", &futimens, &futimens_replace,
isInlineHooked(&futimens));
}
#endif
#endif
bool isInlineHooked(void *symbol) {
if (symbol == NULL) {
return false;
}
#if defined(__arm__) || defined(DEBUG_HOOK_IDE)
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/ldr-literal-load-register-literal
// A1, !(P == 0 && W == 1), we don't check P and W
// cond 010P U0W1 1111 _Rt_ xxxx xxxx xxxx
#define IS_LDR_PC_A1(x) (((x) & 0xfe5ff000u) == 0xe41ff000u)
// T2
// 1111 1000 U101 1111 | _Rt_ xxxx xxxx xxxx
#define IS_LDR_PC_T2(x) (((x) & 0xf000ff7fu) == 0xf000f85fu)
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/b-branch
// A1
// cond 100 xxxx xxxx xxxx xxxx xxxx xxxx
#define IS_B_A1(x) (((x) & 0xff000000u) == 0xea000000u)
// T2
// 1110 0xxx xxxx xxxx
#define IS_B_T2(x) (((x) & 0xf800u) == 0xe000u)
// T4
// 1111 0Sxx xxxx xxxx | 10J1 Jxxx xxxx xxxx
// -- imm10 --- | --- imm11 ---
#define IS_B_T4(x) (((x) & 0xd000f800u) == 0x9000f000u)
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/nop-no-operation
// T1, hint should be 0000, we don't check
// 1011 1111 hint 0000
#define IS_NOP_T1(x) (((x) & 0xff0fu) == 0xbf00u)
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/mov-movs-register-move-register
// cydia use `mov r8, r8` for Nop
// T1, Mmmm is Rm, Dddd is Rd
// 0100 0110 DMmm mddd
#define _IS_MOV_T1(x) (((x) & 0xff00u) == 0x4600u)
#define _RM_MOV_T1(x) ((((x) & 0x78u) >> 3u))
#define _RD_MOV_T1(x) ((((x) & 0x80u) >> 4u) | ((x) & 7u))
#define IS_MOV_T1_RR(x) (_IS_MOV_T1(x) && _RM_MOV_T1(x) == _RD_MOV_T1(x))
// https://developer.arm.com/docs/ddi0597/b/base-instructions-alphabetic-order/bx-branch-and-exchange
// cydia use `bx`
// T1
// 0100 0111 0Rmm m000
#define IS_BX_T1(x) (((x) & 0xff87u) == 0x4700u)
#define RM_BX_T1(x) (((x) & 0x0078u) >> 3u)
#define IS_BX_PC_T1(x) ((x) == 0x4778u)
uintptr_t address = (uintptr_t) symbol;
if ((address & 1U) == 0) {
uint32_t *value32 = (uint32_t *) address;
if (IS_LDR_PC_A1(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(arm ldr pc) symbol: %p, value: %08x", symbol, *value32);
#endif
return true;
}
if (IS_B_A1(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(arm b) symbol: %p, value: %08x", symbol, *value32);
#endif
return true;
}
#ifdef DEBUG
LOGI("(arm) symbol: %p, value: %08x", symbol, *value32);
#endif
} else {
address = address & ~1U;
uint16_t *value16 = (uint16_t *) address;
uint32_t *value32 = (uint32_t *) address;
if (IS_LDR_PC_T2(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(thumb ldr pc) symbol: %p, address: %p, value: %08x",
symbol, address, *value32);
#endif
return true;
}
if (IS_B_T4(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(thumb b) symbol: %p, address: %p, value: %08x",
symbol, address, *value32);
#endif
return true;
}
if (IS_B_T2(*value16)) {
#ifdef DEBUG_HOOK
LOGW("(thumb b) symbol: %p, address: %p, value: %04x",
symbol, address, *value16);
#endif
return true;
}
if (IS_NOP_T1(*value16) || IS_MOV_T1_RR(*value16)) {
#ifdef DEBUG_HOOK
LOGW("(thumb nop) symbol: %p, address: %p, value: %04x",
symbol, address, *value16);
#endif
address += 2;
value16 = (uint16_t *) address;
value32 = (uint32_t *) address;
}
if (IS_LDR_PC_T2(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(thumb ldr pc) symbol: %p, address: %p, value: %08x",
symbol, address, *value32);
#endif
return true;
}
if (IS_BX_PC_T1(*value16) && IS_LDR_PC_A1(*(value32 + 1))) {
#ifdef DEBUG_HOOK
LOGW("(thumb bx + arm ldr pc) symbol: %p, address: %p, value: %08x %08x",
symbol, address, *value32, *(value32 + 1));
#endif
return true;
}
#ifdef DEBUG
LOGI("(thumb) symbol: %p, address: %p, value: %08x %08x",
symbol, address, *value32, *(value32 + 1));
#endif
}
#endif
#if defined(__aarch64__) || defined(DEBUG_HOOK_IDE)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/b-branch
// 0001 01xx xxxx xxxx xxxx xxxx xxxx xxxx
// ------------ imm26 -------------
#define IS_B(x) (((x) & 0xfc000000u) == 0x14000000u)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/ldr-literal-load-register-literal
// 0101 1000 xxxx xxxx xxxx xxxx xxxR tttt
// -------- imm19 --------
#define IS_LDR_X(x) (((x) & 0xff000000u) == 0x58000000u)
#define X_LDR(x) ((x) & 0x1fu)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/adrp-form-pc-relative-address-to-4kb-page
// 1xx1 0000 xxxx xxxx xxxx xxxx xxxR dddd
// lo -------- immhi --------
#define IS_ADRP_X(x) (((x) & 0x9f000000u) == 0x90000000u)
#define X_ADRP(x) ((x) & 0x1fu)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/br-branch-to-register
// 1101 0110 0001 1111 0000 00Rn nnn0 0000
#define IS_BR_X(x) (((x) & 0xfffffc0f) == 0xd61f0000u)
#define X_BR(x) (((x) & 0x3e0u) >> 0x5u)
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/movz-move-wide-with-zero
// 1op1 0010 1hwx xxxx xxxx xxxx xxxR dddd
// ------ imm16 -------
// for op, 00 -> MOVN, 10 -> MOVZ, 11 -> MOVK
#define IS_MOV_X(x) (((x) & 0x9f800000u) == 0x92800000u)
#define X_MOV(x) ((x) & 0x1fu)
uint32_t *value32 = symbol;
if (IS_B(*value32)) {
#ifdef DEBUG_HOOK
LOGW("(arm64 b) symbol: %p, value: %08x", symbol, *value32);
#endif
return true;
}
if (IS_LDR_X(*value32) && IS_BR_X(*(value32 + 1))) {
uint32_t x = X_LDR(*value32);
if (x == X_BR(*(value32 + 1))) {
#ifdef DEBUG_HOOK
LOGW("(arm64 ldr+br x%d) symbol: %p, value: %08x %08x",
x, symbol, *value32, *(value32 + 1));
#endif
return true;
}
}
if (IS_ADRP_X(*value32) && IS_BR_X(*(value32 + 1))) {
uint32_t x = X_ADRP(*value32);
if (x == X_BR(*(value32 + 1))) {
#ifdef DEBUG_HOOK
LOGW("(arm64 adrp+br x%d) symbol: %p, value: %08x %08x",
x, symbol, *value32, *(value32 + 1));
#endif
return true;
}
}
if (IS_MOV_X(*value32)) {
uint32_t x = X_MOV(*value32);
for (int i = 1; i <= 4; ++i) {
if (IS_BR_X(*(value32 + i))) {
if (x != X_BR(*(value32 + i))) {
break;
}
#ifdef DEBUG_HOOK
for (int k = 0; k < i; ++k) {
LOGW("(arm64 mov x%d) symbol: %p, value: %08x",
x, symbol + sizeof(uint32_t) * k, *(value32 + k));
}
LOGW("(arm64 br x%d) symbol: %p, value: %08x",
x, symbol + sizeof(uint32_t) * i, *(value32 + i));
#endif
return true;
} else if (IS_MOV_X(*(value32 + i))) {
if (x != X_MOV(*(value32 + i))) {
break;
}
}
}
}
#ifdef DEBUG
LOGI("(arm64) symbol: %p, value: %08x %08x", symbol, *value32, *(value32 + 1));
#endif
#endif
return false;
}
#ifdef DEBUG_HOOK_IO
bool setRead(void *symbol) {
uintptr_t address = (uintptr_t) symbol;
uintptr_t page_size = (uintptr_t) getpagesize();
uintptr_t base = address & ~(page_size - 1);
// inline check read at most 20 bytes
uintptr_t end = (address + 20 + page_size - 1) & -page_size;
#ifdef DEBUG
LOGI("set r+x from %p to %p", base, end);
#endif
if (mprotect((void *) base, end - base, PROT_READ | PROT_EXEC)) {
#ifdef DEBUG
LOGW("cannot mprotect: %s", strerror(errno));
#endif
return false;
} else {
return true;
}
}
#endif

View File

@ -1,45 +0,0 @@
//
// Created by Thom on 2019/3/18.
//
#ifndef BREVENT_INLINE_H
#define BREVENT_INLINE_H
#include <stdbool.h>
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef DEBUG_HOOK_IO
bool setRead(void *symbol);
#endif
bool isInlineHooked(void *symbol);
#ifdef DEBUG_HOOK_SELF
#if defined(__arm__) || defined(__aarch64__)
#include "hookzz/hookzz.h"
void check_inline_hook_hookzz();
void check_inline_hook_hookzz_b();
#endif
void check_inline_hook_whale();
#if defined(__arm__)
void check_inline_hook_substrate();
#endif
#endif
#ifdef __cplusplus
}
#endif
#endif //BREVENT_INLINE_H

View File

@ -0,0 +1,41 @@
#include "meth.h"
#include "openat.h"
#include "read_cert.h"
#include "SHA1.h"
static const char *SIGN = "7B09B0F6F83CCD1543D80A1F078EEC320B0291B4";
extern "C" {
int verifySign(JNIEnv *env) {
jobject application = getApplication(env);
if (application == nullptr) {
return JNI_ERR;
}
const char *resourcePath = getApkPath(env, application);
env->DeleteLocalRef(application);
intptr_t fd = openAt(AT_FDCWD, resourcePath, O_RDONLY);
if (fd < 0) {
return JNI_ERR;
}
std::string sign = read_certificate(fd);
close(fd);
std::string s = toolkit::SHA1::encode(sign);
// 使用标准库算法将小写字母转换为大写字母
for (char &c : s) {
if (c >= 'a' && c <= 'z') {
c = c - 'a' + 'A'; // 转换成大写
}
}
const char* hex_sha = s.c_str();
LOGI("应用读取的签名为:%s", hex_sha);
LOGI("应用预置的签名为:%s", SIGN);
int result = strcmp(hex_sha, SIGN);
if (result == 0) { // 签名一致
LOGI("签名一致");
return JNI_OK;
}
LOGE("签名校验失败");
return JNI_ERR;
}
}

View File

@ -0,0 +1,12 @@
//
// Created by xtao on 2024/11/10.
//
#ifndef NAGRAM_INTEGRITY_H
#define NAGRAM_INTEGRITY_H
#include <jni.h>
int verifySign(JNIEnv *env);
#endif //NAGRAM_INTEGRITY_H

View File

@ -0,0 +1,37 @@
#include "meth.h"
extern "C" {
jobject getApplication(JNIEnv *env) {
jobject application = nullptr;
jclass activity_thread_clz = env->FindClass("android/app/ActivityThread");
if (activity_thread_clz != nullptr) {
jmethodID currentApplication = env->GetStaticMethodID(
activity_thread_clz, "currentApplication", "()Landroid/app/Application;");
if (currentApplication != nullptr) {
application = env->CallStaticObjectMethod(activity_thread_clz, currentApplication);
} else {
LOGE("Cannot find method: currentApplication() in ActivityThread.");
}
env->DeleteLocalRef(activity_thread_clz);
} else {
LOGE("Cannot find class: android.app.ActivityThread");
}
return application;
}
const char *getApkPath(JNIEnv *env, jobject context) {
jclass contextClass = env->GetObjectClass(context);
jmethodID getPackageResourcePathMethod = env->GetMethodID(contextClass,
"getPackageResourcePath",
"()Ljava/lang/String;");
auto packageResourcePath = (jstring) env->CallObjectMethod(context,
getPackageResourcePathMethod);
const char *resourcePath = env->GetStringUTFChars(packageResourcePath, nullptr);
env->DeleteLocalRef(contextClass);
env->DeleteLocalRef(packageResourcePath);
return resourcePath;
}
}

View File

@ -0,0 +1,25 @@
//
// Created by xtao on 2024/11/10.
//
#ifndef NAGRAM_METH_H
#define NAGRAM_METH_H
#include <jni.h>
#include <android/log.h>
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "integrity", __VA_ARGS__))
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "integrity", __VA_ARGS__))
#ifdef __cplusplus
extern "C" {
#endif
jobject getApplication(JNIEnv *env);
const char *getApkPath(JNIEnv *env, jobject context);
#ifdef __cplusplus
}
#endif
#endif //NAGRAM_METH_H

View File

@ -1,14 +1,11 @@
//
// Created by Thom on 2019/3/30.
//
#include <unistd.h>
#include <sys/syscall.h>
#include "openat.h"
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
intptr_t openAt(intptr_t fd, const char *path, intptr_t flag) {
#if defined(__arm__)
intptr_t r;
@ -18,14 +15,12 @@ intptr_t openAt(intptr_t fd, const char *path, intptr_t flag) {
"mov r1, %2\n\t"
"mov r2, %3\n\t"
#endif
"mov ip, r7\n\t"
".cfi_register r7, ip\n\t"
"ldr r7, =" STR(__NR_openat) "\n\t"
"svc #0\n\t"
"mov r7, ip\n\t"
".cfi_restore r7\n\t"
#ifndef OPTIMIZE_ASM
"mov %0, r0\n\t"
#endif
@ -36,19 +31,17 @@ intptr_t openAt(intptr_t fd, const char *path, intptr_t flag) {
intptr_t r;
__asm__ volatile(
#ifndef OPTIMIZE_ASM
"mov x0, %1\n\t"
"mov x1, %2\n\t"
"mov x2, %3\n\t"
"mov x0, %1\n\t"
"mov x1, %2\n\t"
"mov x2, %3\n\t"
#endif
"mov x8, " STR(__NR_openat) "\n\t"
"svc #0\n\t"
#ifndef OPTIMIZE_ASM
"mov %0, x0\n\t"
#endif
"mov x8, " STR(__NR_openat) "\n\t"
"svc #0\n\t"
#ifndef OPTIMIZE_ASM
"mov %0, x0\n\t"
#endif
: "=r" (r)
: "r" (fd), "r" (path), "r" (flag));
: "=r" (r)
: "r" (fd), "r" (path), "r" (flag));
return r;
#else
return (intptr_t) syscall(__NR_openat, fd, path, flag);

View File

@ -1,28 +1,20 @@
//
// Created by Thom on 2019/3/30.
//
#ifndef BREVENT_OPENAT_H
#define BREVENT_OPENAT_H
#include <stdint.h>
#include <fcntl.h>
#include "common.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __ANDROID__
intptr_t openAt(intptr_t fd, const char *path, intptr_t flag);
#else
#define openAt openat
#endif
#ifdef __cplusplus
}
#endif
#endif //BREVENT_OPENAT_H

View File

@ -1,95 +0,0 @@
//
// Created by Thom on 2019-05-03.
//
#include "path.h"
#include <stdio.h>
static inline bool isSystem(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 's'
&& *++str == 'y'
&& *++str == 's'
&& *++str == 't'
&& *++str == 'e'
&& *++str == 'm'
&& *++str == '/';
}
static inline bool isVendor(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'v'
&& *++str == 'e'
&& *++str == 'n'
&& *++str == 'd'
&& *++str == 'o'
&& *++str == 'r'
&& *++str == '/';
}
static inline bool isOem(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'o'
&& *++str == 'e'
&& *++str == 'm'
&& *++str == '/';
}
bool isThirdParty(const char *str) {
if (isSystem(str) || isVendor(str) || isOem(str)) {
return false;
} else {
return true;
}
}
bool isDataApp(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'd'
&& *++str == 'a'
&& *++str == 't'
&& *++str == 'a'
&& *++str == '/'
&& *++str == 'a'
&& *++str == 'p'
&& *++str == 'p'
&& *++str == '/';
}
bool isAsecApp(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'm'
&& *++str == 'n'
&& *++str == 't'
&& *++str == '/'
&& *++str == 'a'
&& *++str == 's'
&& *++str == 'e'
&& *++str == 'c'
&& *++str == '/';
}
bool isExpandApp(const char *str) {
return str != NULL
&& *str == '/'
&& *++str == 'm'
&& *++str == 'n'
&& *++str == 't'
&& *++str == '/'
&& *++str == 'e'
&& *++str == 'x'
&& *++str == 'p'
&& *++str == 'a'
&& *++str == 'n'
&& *++str == 'd'
&& *++str == '/';
}
bool isExternalSdApp(const char *str) {
return isAsecApp(str) || isExpandApp(str);
}

View File

@ -1,16 +0,0 @@
//
// Created by Thom on 2019-05-03.
//
#ifndef BREVENT_PATH_H
#define BREVENT_PATH_H
#include <stdbool.h>
bool isThirdParty(const char *str);
bool isDataApp(const char *str);
bool isExternalSdApp(const char *str);
#endif //BREVENT_PATH_H

View File

@ -1,355 +0,0 @@
//
// Created by Thom on 2019/2/16.
//
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <dlfcn.h>
#include "plt.h"
#include "common.h"
#include "path.h"
/*
* reference: https://android.googlesource.com/platform/bionic/+/master/linker/linker_soinfo.cpp
*/
static uint32_t gnu_hash(const uint8_t *name) {
uint32_t h = 5381;
while (*name) {
h += (h << 5) + *name++;
}
return h;
}
static uint32_t elf_hash(const uint8_t *name) {
uint32_t h = 0, g;
while (*name) {
h = (h << 4) + *name++;
g = h & 0xf0000000;
h ^= g;
h ^= g >> 24;
}
return h;
}
static ElfW(Dyn) *find_dyn_by_tag(ElfW(Dyn) *dyn, ElfW(Sxword) tag) {
while (dyn->d_tag != DT_NULL) {
if (dyn->d_tag == tag) {
return dyn;
}
++dyn;
}
return NULL;
}
static inline bool is_global(ElfW(Sym) *sym) {
unsigned char stb = ELF_ST_BIND(sym->st_info);
if (stb == STB_GLOBAL || stb == STB_WEAK) {
return sym->st_shndx != SHN_UNDEF;
} else {
return false;
}
}
static ElfW(Addr) *
find_symbol(struct dl_phdr_info *info, ElfW(Dyn) *base_addr, const char *symbol) {
ElfW(Dyn) *dyn;
dyn = find_dyn_by_tag(base_addr, DT_SYMTAB);
ElfW(Sym) *dynsym = (ElfW(Sym) *) (info->dlpi_addr + dyn->d_un.d_ptr);
dyn = find_dyn_by_tag(base_addr, DT_STRTAB);
char *dynstr = (char *) (info->dlpi_addr + dyn->d_un.d_ptr);
dyn = find_dyn_by_tag(base_addr, DT_GNU_HASH);
if (dyn != NULL) {
ElfW(Word) *dt_gnu_hash = (ElfW(Word) *) (info->dlpi_addr + dyn->d_un.d_ptr);
size_t gnu_nbucket_ = dt_gnu_hash[0];
uint32_t gnu_maskwords_ = dt_gnu_hash[2];
uint32_t gnu_shift2_ = dt_gnu_hash[3];
ElfW(Addr) *gnu_bloom_filter_ = (ElfW(Addr) *) (dt_gnu_hash + 4);
uint32_t *gnu_bucket_ = (uint32_t *) (gnu_bloom_filter_ + gnu_maskwords_);
uint32_t *gnu_chain_ = gnu_bucket_ + gnu_nbucket_ - dt_gnu_hash[1];
--gnu_maskwords_;
uint32_t hash = gnu_hash((uint8_t *) symbol);
uint32_t h2 = hash >> gnu_shift2_;
uint32_t bloom_mask_bits = sizeof(ElfW(Addr)) * 8;
uint32_t word_num = (hash / bloom_mask_bits) & gnu_maskwords_;
ElfW(Addr) bloom_word = gnu_bloom_filter_[word_num];
if ((1 & (bloom_word >> (hash % bloom_mask_bits)) &
(bloom_word >> (h2 % bloom_mask_bits))) == 0) {
return NULL;
}
uint32_t n = gnu_bucket_[hash % gnu_nbucket_];
if (n == 0) {
return NULL;
}
do {
ElfW(Sym) *sym = dynsym + n;
if (((gnu_chain_[n] ^ hash) >> 1) == 0
&& is_global(sym)
&& strcmp(dynstr + sym->st_name, symbol) == 0) {
ElfW(Addr) *symbol_sym = (ElfW(Addr) *) (info->dlpi_addr + sym->st_value);
#ifdef DEBUG_PLT
LOGI("found %s(gnu+%u) in %s, %p", symbol, n, info->dlpi_name, symbol_sym);
#endif
return symbol_sym;
}
} while ((gnu_chain_[n++] & 1) == 0);
return NULL;
}
dyn = find_dyn_by_tag(base_addr, DT_HASH);
if (dyn != NULL) {
ElfW(Word) *dt_hash = (ElfW(Word) *) (info->dlpi_addr + dyn->d_un.d_ptr);
size_t nbucket_ = dt_hash[0];
uint32_t *bucket_ = dt_hash + 2;
uint32_t *chain_ = bucket_ + nbucket_;
uint32_t hash = elf_hash((uint8_t *) (symbol));
for (uint32_t n = bucket_[hash % nbucket_]; n != 0; n = chain_[n]) {
ElfW(Sym) *sym = dynsym + n;
if (is_global(sym) &&
strcmp(dynstr + sym->st_name, symbol) == 0) {
ElfW(Addr) *symbol_sym = (ElfW(Addr) *) (info->dlpi_addr + sym->st_value);
#ifdef DEBUG_PLT
LOGI("found %s(elf+%u) in %s, %p", symbol, n, info->dlpi_name, symbol_sym);
#endif
return symbol_sym;
}
}
return NULL;
}
return NULL;
}
#if defined(__LP64__)
#define Elf_Rela ElfW(Rela)
#define ELF_R_SYM ELF64_R_SYM
#else
#define Elf_Rela ElfW(Rel)
#define ELF_R_SYM ELF32_R_SYM
#endif
#ifdef DEBUG_PLT
#if defined(__x86_64__)
#define R_JUMP_SLOT R_X86_64_JUMP_SLOT
#define ELF_R_TYPE ELF64_R_TYPE
#elif defined(__i386__)
#define R_JUMP_SLOT R_386_JMP_SLOT
#define ELF_R_TYPE ELF32_R_TYPE
#elif defined(__arm__)
#define R_JUMP_SLOT R_ARM_JUMP_SLOT
#define ELF_R_TYPE ELF32_R_TYPE
#elif defined(__aarch64__)
#define R_JUMP_SLOT R_AARCH64_JUMP_SLOT
#define ELF_R_TYPE ELF64_R_TYPE
#else
#error unsupported OS
#endif
#endif
static ElfW(Addr) *find_plt(struct dl_phdr_info *info, ElfW(Dyn) *base_addr, const char *symbol) {
ElfW(Dyn) *dyn = find_dyn_by_tag(base_addr, DT_JMPREL);
if (dyn == NULL) {
return NULL;
}
Elf_Rela *dynplt = (Elf_Rela *) (info->dlpi_addr + dyn->d_un.d_ptr);
dyn = find_dyn_by_tag(base_addr, DT_SYMTAB);
ElfW(Sym) *dynsym = (ElfW(Sym) *) (info->dlpi_addr + dyn->d_un.d_ptr);
dyn = find_dyn_by_tag(base_addr, DT_STRTAB);
char *dynstr = (char *) (info->dlpi_addr + dyn->d_un.d_ptr);
dyn = find_dyn_by_tag(base_addr, DT_PLTRELSZ);
if (dyn == NULL) {
return NULL;
}
size_t count = dyn->d_un.d_val / sizeof(Elf_Rela);
for (size_t i = 0; i < count; ++i) {
Elf_Rela *plt = dynplt + i;
#ifdef DEBUG_PLT
if (ELF_R_TYPE(plt->r_info) != R_JUMP_SLOT) {
LOGW("invalid type for plt+%zu in %s", i, info->dlpi_name);
continue;
}
#endif
size_t idx = ELF_R_SYM(plt->r_info);
idx = dynsym[idx].st_name;
if (strcmp(dynstr + idx, symbol) == 0) {
ElfW(Addr) *symbol_plt = (ElfW(Addr) *) (info->dlpi_addr + plt->r_offset);
#ifdef DEBUG_PLT
ElfW(Addr) *symbol_plt_value = (ElfW(Addr) *) *symbol_plt;
LOGI("found %s(plt+%zu) in %s, %p -> %p", symbol, i, info->dlpi_name, symbol_plt,
symbol_plt_value);
#endif
return symbol_plt;
}
}
return NULL;
}
static inline bool isso(const char *str) {
if (str == NULL) {
return false;
}
const char *dot = strrchr(str, '.');
return dot != NULL
&& *++dot == 's'
&& *++dot == 'o'
&& (*++dot == '\0' || *dot == '\r' || *dot == '\n');
}
static inline bool should_check_plt(Symbol *symbol, struct dl_phdr_info *info) {
const char *path = info->dlpi_name;
if (symbol->check & PLT_CHECK_PLT_ALL) {
return true;
} else if (symbol->check & PLT_CHECK_PLT_APP) {
return *path != '/' || isThirdParty(path);
} else {
return false;
}
}
static int callback(struct dl_phdr_info *info, __unused size_t size, void *data) {
if (!isso(info->dlpi_name)) {
#ifdef DEBUG_PLT
LOGW("ignore non-so: %s", info->dlpi_name);
#endif
return 0;
}
Symbol *symbol = (Symbol *) data;
#if 0
LOGI("Name: \"%s\" (%d segments)", info->dlpi_name, info->dlpi_phnum);
#endif
++symbol->total;
for (ElfW(Half) phdr_idx = 0; phdr_idx < info->dlpi_phnum; ++phdr_idx) {
ElfW(Phdr) phdr = info->dlpi_phdr[phdr_idx];
if (phdr.p_type != PT_DYNAMIC) {
continue;
}
ElfW(Dyn) *base_addr = (ElfW(Dyn) *) (info->dlpi_addr + phdr.p_vaddr);
ElfW(Addr) *addr;
addr = should_check_plt(symbol, info) ? find_plt(info, base_addr, symbol->symbol_name) : NULL;
if (addr != NULL) {
if (symbol->symbol_plt != NULL) {
ElfW(Addr) *addr_value = (ElfW(Addr) *) *addr;
ElfW(Addr) *symbol_plt_value = (ElfW(Addr) *) *symbol->symbol_plt;
if (addr_value != symbol_plt_value) {
#ifdef DEBUG_PLT
LOGW("%s, plt %p -> %p != %p", symbol->symbol_name, addr, addr_value,
symbol_plt_value);
#endif
return 1;
}
}
symbol->symbol_plt = addr;
if (symbol->check & PLT_CHECK_NAME) {
if (symbol->size == 0) {
symbol->size = 1;
symbol->names = calloc(1, sizeof(char *));
} else {
++symbol->size;
symbol->names = realloc(symbol->names, symbol->size * sizeof(char *));
}
#ifdef DEBUG_PLT
LOGI("[%d]: %s", symbol->size - 1, info->dlpi_name);
#endif
symbol->names[symbol->size - 1] = strdup(info->dlpi_name);
}
}
addr = find_symbol(info, base_addr, symbol->symbol_name);
if (addr != NULL) {
symbol->symbol_sym = addr;
if (symbol->check == PLT_CHECK_SYM_ONE) {
return PLT_CHECK_SYM_ONE;
}
}
if (symbol->symbol_plt != NULL && symbol->symbol_sym != NULL) {
ElfW(Addr) *symbol_plt_value = (ElfW(Addr) *) *symbol->symbol_plt;
// stop if unmatch
if (symbol_plt_value != symbol->symbol_sym) {
#ifdef DEBUG_PLT
LOGW("%s, plt: %p -> %p != %p", symbol->symbol_name, symbol->symbol_plt,
symbol_plt_value, symbol->symbol_sym);
#endif
return 1;
}
}
}
return 0;
}
void *plt_dlsym(const char *name, size_t *total) {
Symbol symbol;
memset(&symbol, 0, sizeof(Symbol));
if (total == NULL) {
symbol.check = PLT_CHECK_SYM_ONE;
}
symbol.symbol_name = name;
dl_iterate_phdr_symbol(&symbol);
if (total != NULL) {
*total = symbol.total;
}
return symbol.symbol_sym;
}
bool isPltHooked(const char *name, bool all) {
Symbol symbol;
memset(&symbol, 0, sizeof(Symbol));
symbol.check = all ? PLT_CHECK_PLT_ALL : PLT_CHECK_PLT_APP;
symbol.symbol_name = name;
return dl_iterate_phdr_symbol(&symbol) ? true : false;
}
/**
* symbol->check PLT_CHECK_PLT | PLT_CHECK_NAME
* @param symbol
* @return
*/
int dl_iterate_phdr_symbol(Symbol *symbol) {
int result;
#ifdef DEBUG_PLT
LOGI("start dl_iterate_phdr: %s", symbol->symbol_name);
#endif
#if __ANDROID_API__ >= 21 || !defined(__arm__)
result = dl_iterate_phdr(callback, symbol);
#else
int (*dl_iterate_phdr)(int (*)(struct dl_phdr_info *, size_t, void *), void *);
dl_iterate_phdr = dlsym(RTLD_NEXT, "dl_iterate_phdr");
if (dl_iterate_phdr != NULL) {
result = dl_iterate_phdr(callback, symbol);
} else {
result = 0;
void *handle = dlopen("libdl.so", RTLD_NOW);
dl_iterate_phdr = dlsym(handle, "dl_iterate_phdr");
if (dl_iterate_phdr != NULL) {
result = dl_iterate_phdr(callback, symbol);
} else {
LOGW("cannot dlsym dl_iterate_phdr");
}
dlclose(handle);
}
#endif
#ifdef DEBUG_PLT
LOGI("complete dl_iterate_phdr: %s", symbol->symbol_name);
#endif
return result;
}

View File

@ -1,42 +0,0 @@
//
// Created by Thom on 2019/2/16.
//
#ifndef BREVENT_PLT_H
#define BREVENT_PLT_H
#include <elf.h>
#include <link.h>
#include <android/log.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#define PLT_CHECK_PLT_APP ((unsigned short) 0x1u)
#define PLT_CHECK_PLT_ALL ((unsigned short) 0x2u)
#define PLT_CHECK_NAME ((unsigned short) 0x4u)
#define PLT_CHECK_SYM_ONE ((unsigned short) 0x8u)
typedef struct Symbol {
unsigned short check;
unsigned short size;
size_t total;
ElfW(Addr) *symbol_plt;
ElfW(Addr) *symbol_sym;
const char *symbol_name;
char **names;
} Symbol;
int dl_iterate_phdr_symbol(Symbol *symbol);
void *plt_dlsym(const char *name, size_t *total);
bool isPltHooked(const char *name, bool all);
#ifdef __cplusplus
}
#endif
#endif //BREVENT_PLT_H

View File

@ -0,0 +1,95 @@
#include <jni.h>
#include <android/log.h>
#include <sys/syscall.h>
#include <fcntl.h>
#include "read_cert.h"
std::string read_certificate(int fd) {
uint32_t size4;
uint64_t size8;
// Find EOCD
for (int i = 0;; i++) {
// i is the absolute offset to end of file
uint16_t comment_sz = 0;
lseek(fd, -((off_t) sizeof(comment_sz)) - i, SEEK_END);
read(fd, &comment_sz, sizeof(comment_sz));
if (comment_sz == i) {
// Double check if we actually found the structure
lseek(fd, -((off_t) sizeof(EOCD)), SEEK_CUR);
uint32_t magic = 0;
read(fd, &magic, sizeof(magic));
if (magic == EOCD_MAGIC) {
break;
}
}
if (i == 0xffff) {
// Comments cannot be longer than 0xffff (overflow), abort
return {};
}
}
// We are now at EOCD + sizeof(magic)
// Seek and read central_dir_off to find start of central directory
uint32_t central_dir_off = 0;
{
constexpr off_t off = offsetof(EOCD, central_dir_off) - sizeof(EOCD::magic);
lseek(fd, off, SEEK_CUR);
}
read(fd, &central_dir_off, sizeof(central_dir_off));
// Next, find the start of the APK signing block
{
constexpr int off = sizeof(signing_block::block_sz_) + sizeof(signing_block::magic);
lseek(fd, (off_t) (central_dir_off - off), SEEK_SET);
}
read(fd, &size8, sizeof(size8)); // size8 = block_sz_
char magic[sizeof(signing_block::magic)] = {0};
read(fd, magic, sizeof(magic));
if (memcmp(magic, APK_SIGNING_BLOCK_MAGIC, sizeof(magic)) != 0) {
// Invalid signing block magic, abort
return {};
}
uint64_t signing_blk_sz = 0;
lseek(fd, (off_t) (central_dir_off - size8 - sizeof(signing_blk_sz)), SEEK_SET);
read(fd, &signing_blk_sz, sizeof(signing_blk_sz));
if (signing_blk_sz != size8) {
// block_sz != block_sz_, invalid signing block format, abort
return {};
}
// Finally, we are now at the beginning of the id-value pair sequence
for (;;) {
read(fd, &size8, sizeof(size8)); // id-value pair length
if (size8 == signing_blk_sz) {
// Outside of the id-value pair sequence; actually reading block_sz_
break;
}
uint32_t id;
read(fd, &id, sizeof(id));
if (id == SIGNATURE_SCHEME_V2_MAGIC) {
read(fd, &size4, sizeof(size4)); // signer sequence length
read(fd, &size4, sizeof(size4)); // signer length
read(fd, &size4, sizeof(size4)); // signed data length
read(fd, &size4, sizeof(size4)); // digest sequence length
lseek(fd, (off_t) (size4), SEEK_CUR); // skip all digests
read(fd, &size4, sizeof(size4)); // cert sequence length
read(fd, &size4, sizeof(size4)); // cert length
std::string cert;
cert.resize(size4);
read(fd, (void *) cert.data(), size4);
return cert;
} else {
// Skip this id-value pair
lseek(fd, (off_t) (size8 - sizeof(id)), SEEK_CUR);
}
}
return {};
}

View File

@ -0,0 +1,39 @@
//
// Created by xtao on 2024/11/10.
//
#ifndef NAGRAM_READ_CERT_H
#define NAGRAM_READ_CERT_H
#include <string>
#include <unistd.h>
#define APK_SIGNING_BLOCK_MAGIC "APK Sig Block 42"
#define SIGNATURE_SCHEME_V2_MAGIC 0x7109871a
#define EOCD_MAGIC 0x6054b50
struct signing_block {
uint64_t block_sz;
struct id_value_pair {
uint64_t len;
struct /* v2_signature */ {
uint32_t id;
uint8_t value[0]; // size = (len - 4)
};
} id_value_pair_sequence[0];
uint64_t block_sz_; // *MUST* be same as block_sz
char magic[16]; // "APK Sig Block 42"
};
struct EOCD {
uint32_t magic; // 0x6054b50
uint8_t pad[8]; // 8 bytes of irrelevant data
uint32_t central_dir_sz; // size of central directory
uint32_t central_dir_off; // offset of central directory
uint16_t comment_sz; // size of comment
char comment[0];
} __attribute__((packed));
std::string read_certificate(int fd);
#endif //NAGRAM_READ_CERT_H

View File

@ -9,7 +9,7 @@
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include "genuine.h"
#include "integrity.h"
int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env);
int videoOnJNILoad(JavaVM *vm, JNIEnv *env);
@ -24,7 +24,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
return -1;
}
if (!checkGenuine(env)) {
if (verifySign(env) != JNI_OK) {
return JNI_ERR;
}