GKLBB

当你经历了暴风雨,你也就成为了暴风雨

导航

应用安全 --- app加固 之 盐 + frida检测

我有一个app会将输入的加盐比较字符串是否正确,同时在比较前会在so文件中检测frida是不是在监听服务是就结束程序。

app位置 https://github.com/r0ysue/FridaAndrioidNativeBeginnersBook/tree/main/Chap08/3

解决方法:

加盐只能用python遍历所有可能字符串。使用工具是jadx

frida检测有两个方法,一个是修改程序破坏检测流程,一个是hook掉strcmp函数始终返回1。工具是ida + kiro + frida

详细分析:

/*
 * 反编译函数: Java_com_kanxue_pediy1_MainActivity_stringFromJNI
 * 地址: 0x0000000000000DCC
 * 功能: JNI函数,从Java字符串中解析整数并返回加1后的值

在这个JNI函数中的作用:
Java层传入一个字符串(比如用户输入的 "100")
通过 GetStringUTFChars 转换成C字符串
用 sscanf 从字符串中提取数字 100
给这个数字加1,变成 101
返回给Java层

 */

#include <jni.h>
#include <stdio.h>

extern const char* _ZN7_JNIEnv17GetStringUTFCharsEP8_jstringPh(JNIEnv* env, jstring str, jboolean* isCopy);
extern int sscanf(const char* str, const char* format, ...);
extern void __stack_chk_fail(void);

jint Java_com_kanxue_pediy1_MainActivity_stringFromJNI(JNIEnv* env, jobject thiz, jstring input) {
    // 栈保护和局部变量
    const char* utf_chars;
    int parsed_value;
    int result;
    
    // 获取字符串的UTF-8表示
    // BL ._ZN7_JNIEnv17GetStringUTFCharsEP8_jstringPh
    utf_chars = _ZN7_JNIEnv17GetStringUTFCharsEP8_jstringPh(env, input, NULL);
    
    // 使用sscanf解析字符串中的整数
    // ADRL X1, unk_1444 ; format (可能是"%d"格式字符串)
    // BL .sscanf
    sscanf(utf_chars, "%d", &parsed_value);
    
    // 将解析的值加1
    // ADD W10, W10, #1
    result = parsed_value + 1;
    
    // 栈保护检查
    // 如果栈被破坏,调用__stack_chk_fail
    // SUBS X9, X9, X11
    // B.NE loc_E58
    // 在loc_E58处: BL .__stack_chk_fail
    
    return result;
}
分析发现这是干扰函数


/*
 * 反编译函数: JNI_OnLoad
 * 地址: 0x00000000000012CC
 * 功能: JNI库加载时的初始化函数
 */

#include <jni.h>

extern jint _ZN7_JavaVM6GetEnvEPPvi(JavaVM* vm, void** penv, jint version);
extern jclass _ZN7_JNIEnv9FindClassEPKc(JNIEnv* env, const char* name);
extern jint _ZN7_JNIEnv15RegisterNativesEP7_jclassPK15JNINativeMethodi(
    JNIEnv* env, jclass clazz, const JNINativeMethod* methods, jint nMethods);
extern void init(void* arg1, void* arg2);
extern jint Java_com_kanxue_pediy1_MainActivity_stringFromJNI(JNIEnv* env, jobject thiz, jstring input);
extern void __stack_chk_fail(void);

jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env;
    jclass clazz;
   
    // JNI本地方法表
    static JNINativeMethod methods[] = {
        {"init", "()V", (void*)init},
        // 可能还有其他方法
    };
   
    // 获取JNI环境
    // LDR X0, [SP,#0x60+var_30] ; this (JavaVM)
    // SUB X1, X29, #-var_28 ; void ** (JNIEnv**)
    // MOV W2, #0x10006 ; int (JNI_VERSION_1_6)
    // BL ._ZN7_JavaVM6GetEnvEPPvi
    _ZN7_JavaVM6GetEnvEPPvi(vm, (void**)&env, JNI_VERSION_1_6);
   
    // 查找MainActivity类
    // ADRL X1, aComKanxuePediy ; "com/kanxue/pediy1/MainActivity"
    // BL ._ZN7_JNIEnv9FindClassEPKc
    clazz = _ZN7_JNIEnv9FindClassEPKc(env, "com/kanxue/pediy1/MainActivity");
   
    // 注册本地方法
    // SUB X2, X29, #-var_20  ; methods
    // MOV W3, #1             ; nMethods
    // BL ._ZN7_JNIEnv15RegisterNativesEP7_jclassPK15JNINativeMethodi
    _ZN7_JNIEnv15RegisterNativesEP7_jclassPK15JNINativeMethodi(env, clazz, methods, 1);
   
    // 返回JNI版本
    // MOV W0, #0x10006
    return JNI_VERSION_1_6;
}

/*
 * 反编译函数: init
 * 地址: 0x0000000000001264
 * 功能: 初始化函数,创建Frida检测线程
 */

#include <pthread.h>

extern int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                         void *(*start_routine)(void *), void *arg);
extern void detect_frida_loop(void *arg);
extern void __stack_chk_fail(void);

void init(void *arg1, void *arg2) {
    pthread_t newthread;
   
    // 创建新线程运行Frida检测循环
    // ADRP X2, #_Z17detect_frida_loopPv_ptr@PAGE
    // LDR X2, [X2,#_Z17detect_frida_loopPv_ptr@PAGEOFF] ; start_routine
    // SUB X0, X29, #-newthread ; newthread
    // MOV X1, XZR ; attr
    // MOV X3, XZR ; arg
    // BL .pthread_create
   
    pthread_create(&newthread, NULL, (void*(*)(void*))detect_frida_loop, NULL);
   
    // 栈保护检查
    // 如果栈被破坏,调用__stack_chk_fail
}
 
 
/*
 * 反编译函数: _Z17detect_frida_loopPv (detect_frida_loop)
 * 地址: 0x0000000000000E9C
 * 功能: Frida检测循环,通过网络连接检测Frida服务器
 */

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <android/log.h>

extern int socket(int domain, int type, int protocol);
extern int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
extern ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
extern ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
extern int close(int fd);
extern int usleep(useconds_t usec);
extern pid_t getpid(void);
extern int kill(pid_t pid, int sig);
extern int strcmp(const char *s1, const char *s2);
extern int __android_log_print(int prio, const char *tag, const char *fmt, ...);
extern int inet_aton(const char *cp, struct in_addr *inp);
extern void *memset(void *s, int c, size_t n);
extern void *__memset_chk(void *dest, int c, size_t len, size_t destlen);

void __attribute__((noreturn)) detect_frida_loop(void *arg) {
    struct sockaddr_in addr;
    char buffer[8];
    int fd;
    int port;
   
    // 初始化地址结构
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;  // MOV W8, #2; STRH W8, [SP,#addr]
    inet_aton("0.0.0.0", &addr.sin_addr);  // ADRL X0, a0000 ; "0.0.0.0"
   
    // 无限循环检测不同端口
    while (1) {
        // 端口范围循环 (1 到 65533)
        for (port = 1; port <= 65533; port++) {
            // 创建socket
            fd = socket(AF_INET, SOCK_STREAM, 0);  // MOV W0, #2; MOV W1, #1; MOV W2, WZR
           
            // 设置端口 (网络字节序)
            addr.sin_port = htons(port);  // REV W8, W8; LSR W8, W8, #0x10
           
            // 尝试连接
            if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) != -1) {
                // 连接成功,清空缓冲区
                memset(buffer, 0, 7);
               
                // 发送空数据包
                sendto(fd, NULL, 1, 0, NULL, 0);
               
                // 发送AUTH命令
                sendto(fd, "AUTH\r\n", 6, 0, NULL, 0);
               
                // 等待500微秒
                usleep(500);  // MOV W8, #0x1F4
               
                // 接收响应
                if (recvfrom(fd, buffer, 7, 0x40, NULL, NULL) != -1) {
                    // 检查响应是否为"REJECT"
                    if (strcmp(buffer, "REJECT") == 0) {
                        // 检测到Frida服务器,终止进程
                        kill(getpid(), SIGKILL);  // MOV W1, #9
                    } else {
                        // 记录未找到Frida服务器
                        __android_log_print(4, "pediy", "not FOUND FRIDA SERVER");
                    }
                }
            }
           
            // 关闭socket
            close(fd);
        }
       
        // 重新开始端口扫描
        continue;
    }
}
 

 

使用ida找打

 inet_aton("0.0.0.0", &addr.sin_addr);  // ADRL X0, a0000 ; "0.0.0.0" 对应的字符串0.0.0.0,修改二进制为2.2.2.2,破坏了检测地址
 
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
高级暴力破解脚本 - 支持多线程和进度显示
"""

import hashlib
import itertools
import string
import threading
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
import queue

class BruteForceEngine:
    def __init__(self):
        self.target_hash = "971b82e071392d8293e57b39fc5056c731517d4e"
        self.salt = [95, 35, 83, 73, 75, 35, 95]  # [_, #, S, I, K, #, _]
        self.found = False
        self.result = None
        self.tested_count = 0
        self.start_time = None
        self.lock = threading.Lock()
    
    def test_candidate(self, candidate):
        """测试单个候选字符串"""
        if self.found or len(candidate) != 5:
            return False
        
        try:
            # 构造字符串
            sb = chr(self.salt[0]) + chr(self.salt[1])  # _#
            for i in range(len(candidate)):
                sb += candidate[i] + chr(self.salt[i + 2])
            sb += chr(self.salt[6])  # _
            
            # 计算SHA-1哈希
            calculated_hash = hashlib.sha1(sb.encode('iso-8859-1')).hexdigest()
            
            with self.lock:
                self.tested_count += 1
            
            if calculated_hash == self.target_hash:
                with self.lock:
                    if not self.found:
                        self.found = True
                        self.result = candidate
                        print(f"\n🎉 找到答案: {candidate}")
                        print(f"构造字符串: {repr(sb)}")
                        print(f"计算哈希: {calculated_hash}")
                return True
            
            return False
        except Exception:
            return False
    
    def get_progress_info(self):
        """获取进度信息"""
        with self.lock:
            elapsed = time.time() - self.start_time if self.start_time else 0
            speed = self.tested_count / elapsed if elapsed > 0 else 0
            return self.tested_count, speed, elapsed

class WeakPasswordTester(BruteForceEngine):
    """弱口令测试器"""
    
    def get_weak_passwords(self):
        """获取弱口令列表"""
        return [
            # 数字组合
            "12345", "54321", "11111", "22222", "33333", "44444", "55555",
            "66666", "77777", "88888", "99999", "00000", "12321", "11223",
            "12312", "23456", "34567", "45678", "56789", "67890", "98765",
            "87654", "76543", "65432", "13579", "24680", "02468", "13571",
            "10000", "20000", "30000", "40000", "50000", "60000", "70000",
            "80000", "90000", "10001", "20002", "30003", "40004", "50005",
            
            # 常见单词
            "admin", "guest", "login", "user1", "test1", "pass1", "hello",
            "world", "china", "pediy", "kanxue", "crack", "debug", "patch",
            "keygen", "serial", "license", "android", "native", "frida",
            "bypass", "hook", "trace", "break", "shell", "root", "super",
            
            # 字母组合
            "aaaaa", "bbbbb", "ccccc", "ddddd", "eeeee", "fffff", "ggggg",
            "abcde", "edcba", "qwert", "asdfg", "zxcvb", "qazws", "plmok",
            "mnbvc", "lkjhg", "poiuy", "trewq", "yxcvb", "hjklm", "vbnma",
            
            # 混合组合
            "a1234", "1234a", "admin1", "test12", "user01", "pass01",
            "abc12", "12abc", "a1b2c", "1a2b3", "aa111", "11aaa",
            "pass0", "user0", "test0", "admin0", "root0", "super0",
            "hack1", "crack1", "patch1", "key01", "code1", "flag1",
        ]
    
    def run(self):
        """运行弱口令测试"""
        print("=== 第1阶段:测试弱口令 ===")
        self.start_time = time.time()
        
        weak_passwords = self.get_weak_passwords()
        print(f"测试 {len(weak_passwords)} 个弱口令...")
        
        for i, candidate in enumerate(weak_passwords, 1):
            if len(candidate) == 5:
                print(f"[{i:3d}] 测试: {candidate}")
                if self.test_candidate(candidate):
                    return self.result
        
        print(f"弱口令测试完成,未找到答案")
        return None

class NumberTester(BruteForceEngine):
    """数字测试器"""
    
    def run(self, max_workers=4):
        """运行数字测试"""
        print("\n=== 第2阶段:测试纯数字 ===")
        self.start_time = time.time()
        
        total = 100000
        print(f"测试 {total:,} 个数字组合...")
        
        def test_range(start, end):
            for i in range(start, end):
                if self.found:
                    break
                candidate = f"{i:05d}"
                if self.test_candidate(candidate):
                    return self.result
            return None
        
        # 分块处理
        chunk_size = total // max_workers
        futures = []
        
        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            for i in range(max_workers):
                start = i * chunk_size
                end = start + chunk_size if i < max_workers - 1 else total
                future = executor.submit(test_range, start, end)
                futures.append(future)
            
            # 监控进度
            while not self.found and any(not f.done() for f in futures):
                time.sleep(1)
                count, speed, elapsed = self.get_progress_info()
                if count > 0:
                    progress = (count / total) * 100
                    eta = (total - count) / speed if speed > 0 else 0
                    print(f"\r进度: {progress:5.1f}% ({count:6d}/{total:6d}) "
                          f"速度: {speed:6.0f}/s ETA: {eta:4.0f}s", end="")
            
            # 获取结果
            for future in as_completed(futures):
                result = future.result()
                if result:
                    return result
        
        print(f"\n数字测试完成,未找到答案")
        return None

class LetterTester(BruteForceEngine):
    """字母测试器"""
    
    def run(self, max_workers=4):
        """运行字母测试"""
        print("\n=== 第3阶段:测试纯字母 ===")
        self.start_time = time.time()
        
        # 先测试小写字母
        result = self._test_letters(string.ascii_lowercase, "小写字母", max_workers)
        if result:
            return result
        
        # 再测试大写字母
        result = self._test_letters(string.ascii_uppercase, "大写字母", max_workers)
        if result:
            return result
        
        print("字母测试完成,未找到答案")
        return None
    
    def _test_letters(self, charset, name, max_workers):
        """测试指定字符集的字母组合"""
        print(f"测试{name}组合...")
        total = len(charset) ** 5
        print(f"总计 {total:,} 个组合")
        
        def test_chunk(combinations):
            for combination in combinations:
                if self.found:
                    break
                candidate = ''.join(combination)
                if self.test_candidate(candidate):
                    return self.result
            return None
        
        # 生成所有组合并分块
        all_combinations = list(itertools.product(charset, repeat=5))
        chunk_size = len(all_combinations) // max_workers
        
        futures = []
        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            for i in range(max_workers):
                start = i * chunk_size
                end = start + chunk_size if i < max_workers - 1 else len(all_combinations)
                chunk = all_combinations[start:end]
                future = executor.submit(test_chunk, chunk)
                futures.append(future)
            
            # 监控进度
            last_count = 0
            while not self.found and any(not f.done() for f in futures):
                time.sleep(2)
                count, speed, elapsed = self.get_progress_info()
                if count > last_count:
                    progress = (count / total) * 100
                    eta = (total - count) / speed if speed > 0 else 0
                    print(f"\r{name}进度: {progress:5.1f}% ({count:8d}/{total:8d}) "
                          f"速度: {speed:6.0f}/s ETA: {eta:6.0f}s", end="")
                    last_count = count
            
            # 获取结果
            for future in as_completed(futures):
                result = future.result()
                if result:
                    return result
        
        print(f"\n{name}测试完成")
        return None

class MixedTester(BruteForceEngine):
    """混合字符测试器"""
    
    def run(self, max_workers=4):
        """运行混合字符测试"""
        print("\n=== 第4阶段:测试字母+数字组合 ===")
        self.start_time = time.time()
        
        letters = string.ascii_lowercase
        digits = string.digits
        
        patterns = [
            (4, 1, "4字母+1数字"),
            (3, 2, "3字母+2数字"),
            (2, 3, "2字母+3数字"),
            (1, 4, "1字母+4数字"),
        ]
        
        for letter_count, digit_count, desc in patterns:
            print(f"测试模式: {desc}")
            result = self._test_pattern(letters, digits, letter_count, digit_count, max_workers)
            if result:
                return result
        
        print("混合字符测试完成,未找到答案")
        return None
    
    def _test_pattern(self, letters, digits, letter_count, digit_count, max_workers):
        """测试特定模式"""
        def test_combinations():
            for letter_combo in itertools.product(letters, repeat=letter_count):
                if self.found:
                    break
                for digit_combo in itertools.product(digits, repeat=digit_count):
                    if self.found:
                        break
                    
                    # 数字在末尾
                    candidate = ''.join(letter_combo) + ''.join(digit_combo)
                    if self.test_candidate(candidate):
                        return self.result
                    
                    # 数字在开头
                    candidate = ''.join(digit_combo) + ''.join(letter_combo)
                    if self.test_candidate(candidate):
                        return self.result
            return None
        
        with ThreadPoolExecutor(max_workers=1) as executor:
            future = executor.submit(test_combinations)
            
            # 监控进度
            while not future.done():
                time.sleep(1)
                count, speed, elapsed = self.get_progress_info()
                if count > 0:
                    print(f"\r已测试: {count:8d} 速度: {speed:6.0f}/s", end="")
            
            result = future.result()
            if result:
                return result
        
        print()
        return None

def main():
    """主函数"""
    print("=" * 70)
    print("com.kanxue.pediy1 高级暴力破解脚本")
    print("=" * 70)
    
    print("\n选择破解模式:")
    print("1. 快速模式(仅弱口令)")
    print("2. 标准模式(弱口令 + 数字)")
    print("3. 完整模式(弱口令 + 数字 + 字母)")
    print("4. 全面模式(弱口令 + 数字 + 字母 + 混合)")
    
    choice = input("\n请选择模式 (1-4): ").strip()
    
    # 第1阶段:弱口令
    tester = WeakPasswordTester()
    result = tester.run()
    if result:
        print(f"\n🎉 在弱口令阶段找到答案: {result}")
        return
    
    if choice == '1':
        print("\n❌ 快速模式未找到答案")
        return
    
    # 第2阶段:数字
    tester = NumberTester()
    result = tester.run(max_workers=4)
    if result:
        print(f"\n🎉 在数字阶段找到答案: {result}")
        return
    
    if choice == '2':
        print("\n❌ 标准模式未找到答案")
        return
    
    # 第3阶段:字母
    tester = LetterTester()
    result = tester.run(max_workers=4)
    if result:
        print(f"\n🎉 在字母阶段找到答案: {result}")
        return
    
    if choice == '3':
        print("\n❌ 完整模式未找到答案")
        return
    
    # 第4阶段:混合
    tester = MixedTester()
    result = tester.run(max_workers=4)
    if result:
        print(f"\n🎉 在混合阶段找到答案: {result}")
        return
    
    print("\n❌ 全面模式未找到答案")
    print("建议检查加密逻辑或扩大搜索范围")

if __name__ == "__main__":
    main()

 

 

 

posted on 2025-08-11 05:03  GKLBB  阅读(22)  评论(0)    收藏  举报