refa: signature check
This commit is contained in:
parent
49d7de346e
commit
ebc0a45b13
@ -665,7 +665,7 @@ target_link_libraries(${NATIVE_LIB}
|
||||
android
|
||||
OpenSLES
|
||||
cpufeatures
|
||||
genuine
|
||||
integrity
|
||||
breakpad)
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
341
TMessagesProj/jni/integrity/SHA1.cpp
Normal file
341
TMessagesProj/jni/integrity/SHA1.cpp
Normal 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
|
47
TMessagesProj/jni/integrity/SHA1.h
Normal file
47
TMessagesProj/jni/integrity/SHA1.h
Normal 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
|
@ -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;
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
@ -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);
|
@ -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
|
@ -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
|
41
TMessagesProj/jni/integrity/integrity.cpp
Normal file
41
TMessagesProj/jni/integrity/integrity.cpp
Normal 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;
|
||||
}
|
||||
}
|
12
TMessagesProj/jni/integrity/integrity.h
Normal file
12
TMessagesProj/jni/integrity/integrity.h
Normal 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
|
37
TMessagesProj/jni/integrity/meth.cpp
Normal file
37
TMessagesProj/jni/integrity/meth.cpp
Normal 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;
|
||||
}
|
||||
}
|
25
TMessagesProj/jni/integrity/meth.h
Normal file
25
TMessagesProj/jni/integrity/meth.h
Normal 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
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
@ -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
|
95
TMessagesProj/jni/integrity/read_cert.cpp
Normal file
95
TMessagesProj/jni/integrity/read_cert.cpp
Normal 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, ¢ral_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 {};
|
||||
}
|
39
TMessagesProj/jni/integrity/read_cert.h
Normal file
39
TMessagesProj/jni/integrity/read_cert.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user