ThomasKing 发表于 2014-11-10 21:43

CrackMe源码

本帖最后由 ThomasKing 于 2014-11-10 21:45 编辑

源码已放,大家尽情玩耍吧。
----------------------------------------------------------------------------------------------------------------
本题的重点不是在算法本身,壳子和校验算法都比较简单。主要看攻击组是否被fake到吧,下面简单分析下原理。libverify.so作为简单的壳子,仅自加密了On_load函数。 校验算法分为两个,一个是real.c,一个是fake.c。fake.c生成libmodule.so文件,而real.c生成的libreal.so被隐藏在了文件末尾。APK运行时,linker调用自解密函数,解密On_load。之后调用On_load函数将libreal.so在内存中加载起来,即需要实现linker加载的一些机制,主要是重定位。可以看到libverify.so其实是作为简单的SO加载器的,其中包含了读取进程状态的反调试。攻击组已经调试看到,校验函数并不存在libverify.so的地址范围,是因为libreal.so是通过匿名内存释放出来的。

//-------------------------
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <elf.h>
#include <sys/mman.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

typedef struct _funcInfo{
Elf32_Addr st_value;
Elf32_Word st_size;
}funcInfo;

void init_verify() __attribute__((constructor));
static void print_debug(const char *msg){
#ifdef DEBUG
    __android_log_print(ANDROID_LOG_INFO, "JNITag", "%s", msg);
#endif
}

static Elf32_Sym *g_dSymtab;
static char *g_dStrtab;
static unsigned g_dStrsz;
static unsigned g_nbucket, g_nchain, *g_bucket, *g_chain;
static unsigned g_base, g_filesz;
static Elf32_Rel *g_reldyn, *g_relplt;
static unsigned g_reldynsz, g_relpltsz;
static int g_fd;
void (*__eabi_Unwind_cpp_ptr384)(JNIEnv*, jobject, jstring, jstring);
static const char g_szStackChkGuard[] = "__stack_chk_guard";
static unsigned g_guard;
static const char g_szMemcpy[] = "memcpy";
static unsigned g_memcpy;

static unsigned elfhash(const char *_name)
{
    const unsigned char *name = (const unsigned char *) _name;
    unsigned h = 0, g;

    while(*name) {
      h = (h << 4) + *name++;
      g = h & 0xf0000000;
      h ^= g;
      h ^= g >> 24;
    }
    return h;
}

static unsigned int getLibAddr(char *name){
unsigned int ret = 0;
char buf, *temp;
FILE *fp;
sprintf(buf, "/proc/self/maps");
fp = fopen(buf, "r");
if(fp == NULL)
{
      print_debug("open failed");
    goto _error;
}
while(fgets(buf, sizeof(buf), fp)){
    if(strstr(buf, name)){
      temp = strtok(buf, "-");
      ret = strtoul(temp, NULL, 16);
      break;
    }
}
_error:
fclose(fp);
return ret;
}

static char getTargetFuncInfo(unsigned long base, const char *funcName, funcInfo *info){
    char flag = -1, *dynstr;
    int i;
    Elf32_Ehdr *ehdr;
    Elf32_Phdr *phdr;
    Elf32_Off dyn_vaddr;
    Elf32_Word dyn_size, dyn_strsz;
    Elf32_Dyn *dyn;
    Elf32_Addr dyn_symtab, dyn_strtab, dyn_hash;
    Elf32_Sym *funSym;
    unsigned funHash, nbucket;
    unsigned *bucket, *chain;

    ehdr = (Elf32_Ehdr *)base;
    phdr = (Elf32_Phdr *)(base + ehdr->e_phoff);
    for (i = 0; i < ehdr->e_phnum; ++i) {
      if(phdr->p_type ==PT_DYNAMIC){
            flag = 0;
            break;
      }
      phdr ++;
    }
    if(flag)
      goto _error;
    dyn_vaddr = phdr->p_vaddr + base;
    dyn_size = phdr->p_filesz;
    flag = 0;
    for (i = 0; i < dyn_size / sizeof(Elf32_Dyn); ++i) {
      dyn = (Elf32_Dyn *)(dyn_vaddr + i * sizeof(Elf32_Dyn));
      if(dyn->d_tag == DT_SYMTAB){
            dyn_symtab = (dyn->d_un).d_ptr;
            flag += 1;
      }
      if(dyn->d_tag == DT_HASH){
            dyn_hash = (dyn->d_un).d_ptr;
            flag += 2;
      }
      if(dyn->d_tag == DT_STRTAB){
            dyn_strtab = (dyn->d_un).d_ptr;
            flag += 4;
      }
      if(dyn->d_tag == DT_STRSZ){
            dyn_strsz = (dyn->d_un).d_val;
            flag += 8;
      }
    }
    if((flag & 0x0f) != 0x0f){
      goto _error;
    }
    dyn_symtab += base;
    dyn_hash += base;
    dyn_strtab += base;
    dyn_strsz += base;

    funHash = elfhash(funcName);
    funSym = (Elf32_Sym *) dyn_symtab;
    dynstr = (char*) dyn_strtab;
    nbucket = *((int *) dyn_hash);
    bucket = (int *)(dyn_hash + 8);
    chain = (unsigned int *)(dyn_hash + 4 * (2 + nbucket));

    flag = -1;
    for(i = bucket; i != 0; i = chain){
      if(strcmp(dynstr + (funSym + i)->st_name, funcName) == 0){
            flag = 0;
            break;
      }
    }
    if(flag) goto _error;
    info->st_value = (funSym + i)->st_value;
    info->st_size = (funSym + i)->st_size;
    return 0;
_error:
    return -1;
}

static void decrypt_verify(unsigned int base, const char target_fun[]){
    funcInfo info;
    int i;
    unsigned int npage;

    if(getTargetFuncInfo(base, target_fun, &info) == -1){
      return ;
    }
    npage = info.st_size / PAGE_SIZE + ((info.st_size % PAGE_SIZE == 0) ? 0 : 1);
    if(mprotect((void *) ((base + info.st_value) / PAGE_SIZE * PAGE_SIZE), npage, PROT_READ | PROT_EXEC | PROT_WRITE) != 0){
    }
    for(i=0;i< info.st_size - 1; i++){
      char *addr = (char*)(base + info.st_value + i);
      (*addr)--;
      *addr = ~(*addr);
    }
    if(mprotect((void *) ((base + info.st_value) / PAGE_SIZE * PAGE_SIZE), npage, PROT_READ | PROT_EXEC) != 0){
    }
    clearcache((char*)(base + info.st_value- 1), (char*)(base + info.st_value -1 + info.st_size -1));
}

static char is_traced(){
    FILE *fp = NULL;
    char flag = 1;
    char buf, *ptr;
    int ppid;
    memset(buf, 0, sizeof(buf));
    sprintf(buf, "/proc/%d/status", getpid());
    fp = fopen(buf, "r");
    if(fp == NULL)
      return flag;
    while(fgets(buf, sizeof(buf)-1, fp)){
      if(ptr = strstr(buf, "TracerPid:")){
            sscanf(ptr + strlen("TracerPid:"), "%d", &ppid);
            if(!ppid)
                flag = 0;
            break;
      }
    }
    fclose(fp);
    return flag;
}

static void release(unsigned base, char* addr){
    int i, soLen = 0;
    char *so_base = NULL;
    Elf32_Ehdr *ehdr = NULL;
    Elf32_Phdr *phdr = NULL;

    ehdr = (Elf32_Ehdr*)base;
    phdr = (Elf32_Phdr *)(base + ehdr->e_phoff);

    for(i=0;i<ehdr->e_phnum;i++){
      if(phdr->p_type == PT_LOAD){
            break;
      }
      phdr ++;
    }
    if(i == ehdr->e_phnum){
      return;
    }
    phdr ++;
    soLen = *((int*)(base + phdr->p_vaddr + phdr->p_filesz - 4));
    so_base = (char*)(base + phdr->p_vaddr + phdr->p_filesz - 4 - soLen);

    memcpy(addr, so_base, sizeof(Elf32_Ehdr) + 10 * sizeof(Elf_Phdr));
    for (i = 0; i < sizeof(Elf32_Ehdr) + 10 * sizeof(Elf_Phdr); ++i) {
      addr = addr - 3;
      addr = ~addr;
    }
    ehdr = (Elf32_Ehdr*)addr;
    phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);
    for(i=0;i<ehdr->e_phnum;i++){
      if(phdr->p_type == PT_LOAD){
            break;
      }
      phdr ++;
    }
    memcpy(addr, so_base, phdr->p_filesz);
    for (i = 0; i < phdr->p_filesz; ++i) {
      addr = addr - 3;
      addr = ~addr;
    }
    phdr ++;
    memcpy(addr + phdr->p_vaddr, so_base + phdr->p_offset, phdr->p_filesz);
    for (i = 0; i < phdr->p_filesz; ++i) {
      (addr + phdr->p_vaddr) = (addr + phdr->p_vaddr) - 3;
      (addr + phdr->p_vaddr) = ~(addr + phdr->p_vaddr);
    }
}

static void init(){
    Elf32_Ehdr *ehdr = NULL;
    Elf32_Phdr *phdr = NULL;
    Elf32_Dyn *dyn;
    unsigned *dHash;
    int i;
    unsigned e_phnum = 0;

    ehdr = (Elf32_Ehdr*) g_base;
    e_phnum = ehdr->e_phnum;
    phdr = (Elf32_Phdr*)(g_base + ehdr->e_phoff);
    for (i = 0; i < e_phnum; ++i) {
      if(phdr->p_type ==PT_DYNAMIC){
            break;
      }
      phdr ++;
    }
    for (i = 0; i < phdr->p_filesz / sizeof(Elf32_Dyn); ++i) {
      dyn = (Elf32_Dyn *)(phdr->p_vaddr + g_base + i * sizeof(Elf32_Dyn));
      if(dyn->d_tag == DT_SYMTAB){
            g_dSymtab = (void*)(g_base + (dyn->d_un).d_ptr);
      }
      if(dyn->d_tag == DT_HASH){
            dHash = (void*)(g_base + (dyn->d_un).d_ptr);
      }
      if(dyn->d_tag == DT_STRTAB){
            g_dStrtab = (void*)(g_base + (dyn->d_un).d_ptr);
      }
      if(dyn->d_tag == DT_STRSZ){
            g_dStrsz = (dyn->d_un).d_val;
      }
      if(dyn->d_tag == DT_REL){
            g_reldyn = (void*)(g_base + (dyn->d_un).d_ptr);
      }
      if(dyn->d_tag == DT_RELSZ){
            g_reldynsz = (dyn->d_un).d_val;
      }
      if(dyn->d_tag == DT_JMPREL){
            g_relplt = (void*)(g_base + (dyn->d_un).d_ptr);
      }
      if(dyn->d_tag == DT_PLTRELSZ){
            g_relpltsz = (dyn->d_un).d_val;
      }
    }
    g_nbucket = *dHash;
    g_nchain = *(dHash + 1);
    g_bucket = dHash + 2;
    g_chain = g_bucket + g_nbucket;
}

static unsigned findExportedSym(const char name[]){
    int i;
    unsigned hashval = elfhash(name);
    for(i = g_bucket; i != 0; i = g_chain){
      if(strcmp(g_dStrtab + (g_dSymtab + i)->st_name, name) == 0){
            return i;
      }
    }
    return 0;
}

static unsigned findAddrInGOT(Elf32_Rel *rel, unsigned relsz, unsigned index){
    unsigned i, nRel;
    nRel = relsz / sizeof(Elf32_Rel);
    for(i=0;i<nRel;i++){
      if(ELF32_R_SYM((rel + i)->r_info) == index)
      {
            return (rel + i)->r_offset;
      }
    }
    return 0;
}

void init_verify(){
    unsigned base;
    unsigned offset;
    if(is_traced())
      return;
    base = getLibAddr("libverify.so");
    decrypt_verify(base, "JNI_OnLoad");
    g_base = base;
    init();
    offset = findExportedSym(g_szStackChkGuard);
    offset = findAddrInGOT(g_reldyn, g_reldynsz, offset);
    g_guard = *((unsigned*)(base + offset));
    offset = findExportedSym(g_szMemcpy);
    offset = findAddrInGOT(g_relplt, g_relpltsz, offset);
    g_memcpy = *((unsigned*)(base + offset));
}

void verify(JNIEnv* env, jobject obj, jstring username, jstring regcode){
    if(is_traced())
      return;
    __eabi_Unwind_cpp_ptr384(env, obj, username, regcode);
}

#define JNIREG_CLASS "com/example/crackme52/MainActivity"

static JNINativeMethod gMethods[] = {
    {"verify", "(Ljava/lang/String;Ljava/lang/String;)V", (void*)verify}
};

static int registerNativeMethods(JNIEnv* env, const char* className,
      JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;
    clazz = (*env)->FindClass(env, className);
    if (clazz == NULL) {
      return JNI_FALSE;
    }
    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
      return JNI_FALSE;
    }
    return JNI_TRUE;
}

static int registerNatives(JNIEnv* env)
{
    if (!registerNativeMethods(env, JNIREG_CLASS, gMethods,
                                 sizeof(gMethods) / sizeof(gMethods)))
      return JNI_FALSE;

    return JNI_TRUE;
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;
    unsigned idx;
    unsigned base;
    struct stat file_stat;
    void* addr;
    if(is_traced())
      return result;
    base = getLibAddr("libverify.so");
    stat("/data/data/com.example.crackme52/lib/libmodule.so", &file_stat);
    g_filesz = 4096 * (file_stat.st_size / 4096 + (file_stat.st_size % 4096 ? 1 : 0));
    g_filesz += 0x1000;

    g_fd = open("/data/data/com.example.crackme52/lib/libmodule.so", O_RDONLY); //障眼法。。。
    if(g_fd == -1){
      __android_log_print(ANDROID_LOG_INFO, "JNITag", "Open libmodule.so error\n");
      return result;
    }
    addr = mmap(NULL, g_filesz, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);//匿名映射,关键点
    if(addr == (void*)-1){
      __android_log_print(ANDROID_LOG_INFO, "JNITag", "Map load error\n");
      return result;
    }
    if(is_traced())
      return result;
    release(base, addr);
    g_base = (unsigned)addr;
    init();
    idx = findExportedSym("begin_verify");
    __eabi_Unwind_cpp_ptr384 = (void (*)(JNIEnv*, jobject, jstring, jstring))(g_base + (g_dSymtab + idx)->st_value);

    idx = findExportedSym(g_szStackChkGuard);
    idx = findAddrInGOT(g_reldyn, g_reldynsz, idx);
    *((unsigned*)(g_base + idx)) = g_guard;

    idx = findExportedSym(g_szMemcpy);
    idx = findAddrInGOT(g_relplt, g_relpltsz, idx);
    *((unsigned*)(g_base + idx)) = g_memcpy;

    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
      return -1;
    }
    if(env == NULL)
      return -1;
    if (!registerNatives(env)) {
      return -1;
    }
    result = JNI_VERSION_1_4;
    return result;
}

JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved){
    JNIEnv* env = NULL;
    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
      return;
    }
    (*env) -> UnregisterNatives(env, JNIREG_CLASS);
    close(g_fd);
    munmap((void*)g_base, g_filesz);
}

//real.c 算法校验源码:
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <elf.h>
#include <sys/mman.h>

static is_legal(jint length){
    if(length >= 8 && length <= 20)
      return 1;
    else
      return 0;
}

void begin_verify(JNIEnv* env, jobject thiz, jstring username, jstring regcode)
{
    jclass c_toast;
    jmethodID m_make, m_show;
    jobject iToast;
    jint length, i;
    const char dic[] = "0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"; //length = 62
    const char code[] = ",.?!";
    char msg1;
    const char *temp;
    int sum = 0;

    length = (*env) -> GetStringUTFLength(env, regcode);
    if(length != 12)
      return;
    if(!is_legal((*env) -> GetStringUTFLength(env, username))){
      return;
    }

    length = (*env) -> GetStringUTFLength(env, username);
    temp = (*env) -> GetStringUTFChars(env, username, JNI_FALSE);
    if(length > 12){
      for (i = 0; i < 12; ++i) {
            msg1 = temp;
      }
    }
    else{
      for (i = 0; i < length; ++i) {
            msg1 = temp;
      }
      for(i = length; i < 12; ++i){
            msg1 = code;
      }
    }
    temp = (*env) -> GetStringUTFChars(env, regcode, JNI_FALSE);
    for (i = 11; i>=0; --i) {
      sum += msg1;
      if(dic != temp)
      {
            return ;
      }
    }
    c_toast = (*env) -> FindClass(env, "android/widget/Toast");
    if(c_toast == NULL){
      goto _error;
    }
    m_make = (*env) -> GetStaticMethodID(env, c_toast, "makeText", "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;");
    if(m_make == NULL){
      goto _error;
    }
    iToast = (*env) -> CallStaticObjectMethod(env, c_toast, m_make, thiz, (*env)->NewStringUTF(env, "Congratulation! You crack it!"), 0);
    if(iToast == NULL){
      goto _error;
    }
    m_show = (*env) -> GetMethodID(env, c_toast, "show", "()V");
    if(m_make == NULL){
      goto _error;
    }
    (*env) -> CallVoidMethod(env, iToast, m_show);
_error:
    return ;
}

//fake.c源码,就是libmodule.so中看到的。。
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <elf.h>
#include <sys/mman.h>

static is_legal(jint length){
    if(length >= 8 && length <= 20)
      return 1;
    else
      return 0;
}

void begin_verify(JNIEnv* env, jobject thiz, jstring username, jstring regcode)
{
    jclass c_toast;
    jmethodID m_make, m_show;
    jobject iToast;
    jint length, i;
    const char dic[] = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm0123456789"; //length = 62
    const char code[] = ",.?!";
    char msg1;
    const char *temp;
    int sum = 0;

    length = (*env) -> GetStringUTFLength(env, regcode);
    if(length != 10)
      return ;
    if(!is_legal((*env) -> GetStringUTFLength(env, username))){
      return ;
    }

    length = (*env) -> GetStringUTFLength(env, username);
    temp = (*env) -> GetStringUTFChars(env, username, JNI_FALSE);
    if(length > 12){
      for (i = length - 12; i < length; ++i) {
            msg1 = temp;
      }
    }
    else{
      for (i = 0; i < length; ++i) {
            msg1 = temp;
      }
      for(i = length; i < 12; ++i){
            msg1 = code;
      }
    }
    temp = (*env) -> GetStringUTFChars(env, regcode, JNI_FALSE);
    for (i = 0; i < 12; ++i) {
      sum += msg1;
      if(dic != temp)
      {
            return ;
      }
    }
    c_toast = (*env) -> FindClass(env, "android/widget/Toast");
    if(c_toast == NULL){
      goto _error;
    }
    m_make = (*env) -> GetStaticMethodID(env, c_toast, "makeText", "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;");
    if(m_make == NULL){
      goto _error;
    }
    iToast = (*env) -> CallStaticObjectMethod(env, c_toast, m_make, thiz, (*env)->NewStringUTF(env, "You got it!"), 0);
    if(iToast == NULL){
      goto _error;
    }
    m_show = (*env) -> GetMethodID(env, c_toast, "show", "()V");
    if(m_make == NULL){
      goto _error;
    }
    if(username)
      goto _error;
    (*env) -> CallVoidMethod(env, iToast, m_show);
_error:
    return ;
}

可以看到,自加密算法和校验算法都很easy吧。 小心慢慢调试就没问题的。
当然,如果把section加密,再加点反调试,同时加密反调试代码,ELF来点DIY,搞个section移动什么的,校验算法再搞复杂点,可能调试起来就有点恶心了。
-----------------------------------------------------------------------------------------------------------------
如果对elf格式及其机制不太熟悉,可以参看我在看雪的几篇文章,主要看这两篇吧
http://bbs.pediy.com/showthread.php?t=191649
http://bbs.pediy.com/showthread.php?t=193279

源码审核@Hmily
Hmily

Falcon_2015 发表于 2014-11-10 22:06

太高度了 看不懂留给有需要的大神吧 O(∩_∩)O~

懒惰的上帝 发表于 2014-11-10 22:46

看不懂了 还是留给有用的人吧

manbajie 发表于 2014-11-11 20:44

这个可以有哦

wolaileo 发表于 2014-11-19 08:38

apk的????

天哪移动程序啊

雨强 发表于 2014-11-25 18:36

看来该学学c了……

joshdexipi 发表于 2016-3-19 16:23

该多看看了

子月 发表于 2016-3-27 22:38

什么源码?不懂?

qlss123006 发表于 2016-3-29 14:59

做一个热心并受欢迎的人!

lhpmain 发表于 2016-4-11 19:46

好多看不懂 ,要是有人解释下就好了
页: [1] 2 3
查看完整版本: CrackMe源码