看雪·深信服 2021 KCTF 春季赛 | 第五题设计思路及解析
 
                    
武林大会在即,看雪er们跟随武林少才的脚步齐聚华山,开启第五题《华山论剑》,一共有 4 支战队完成目标。

xtgo战队当仁不让,率先拿下本题,仅用时52092秒。 hzqmwne战队和雨落星沉战队紧追不舍,很快也成功攻破此题。

目前赛程已经过半,我们来看看目前场上的状况吧!攻击方排名前10如下:

大家在赛场上你追我赶,好不热闹!接下来我们一起来看看本题的“通关宝典”吧!
出题团队简介
本题出题战队为 ArmVMP :

专家点评
赛题设计思路
算法简单,可玩性高,有兴趣可以随时交流。
规则2的demo为:KCTF-2.sign.apk
其中规则2的两组序列号如下:
serial:7C9815255BFE832D3F93140B
serial:17726331DA0fE737149c8202
设计思路
2. hash值做明文和Java_com_example_hellojni_HelloJni_stringFromJNI地址做密钥K参与rc4运算(稍作修改),其中hash前12字节与密钥流按位做异或并与hash后12字节按位相加得到新的12字节串值。
3. 将新得到的十六进制12字节串转换为ASCII码形式即为24字节的serial值。
保护方法
解题思路
赛题解析
本赛题解析由看雪论坛 mb_mgodlfyn 给出:

先尝试用ida在真机上调试,体验很不好(最主要的问题是,0x5000处的汇编指令ida识别为"BL LR, #0xBA ",但是单步调试时无法进入这条指令内部,不知道原因)
#include <stdio.h>#include <string.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include <sys/mman.h>#include <assert.h>struct str {const char *s;int len;};//struct str global_name = {.s = "ed8b9244350d3644", .len = 16};//struct str global_serial = {.s = "7C9815255BFE832D3F93140B", .len = 24};struct str global_name = {.s = "KCTF", .len = 4};struct str global_serial = {.s = "17726331DA0FE737149C8202", .len = 24};//struct str global_serial = {.s = "17726331da0fe737149c8202", .len = 24};struct JNINativeInterface_ {unsigned int f[0x1000/4];};typedef struct JNIEnv_ {struct JNINativeInterface_ *functions;} JNIEnv;void JNICALL_FindClass(JNIEnv *env, const char *name) {printf("JNICALL_FindClass %s\n", name);}void JNICALL_NewStringUTF(JNIEnv *env, const char *utf) {printf("%s %s\n", __func__, utf);}int JNICALL_GetMethodID(JNIEnv *env, void *clazz, const char *name, const char *sig) {printf("%s %p %s %s\n", __func__, clazz, name, sig);return 0x55555501;}void *JNICALL_CallObjectMethod(JNIEnv *env, void *obj, int methodID) {assert(methodID == 0x55555501);printf("%s %p %x\n", __func__, obj, methodID);return obj;}int JNICALL_GetArrayLength(JNIEnv *env, struct str *array) {printf("%s %p\n", __func__, array);return array->len;}unsigned char *JNICALL_GetByteArrayElements(JNIEnv *env, struct str *array, int isCopy) {printf("%s %p %d\n", __func__, array, isCopy);return array->s;}void JNICALL_ReleaseByteArrayElements(JNIEnv *env, void *array, void *elems, int mode) {printf("%s %p %p %d\n", __func__, array, elems, mode);}void *got_malloc(int size) {void *r = malloc(size);printf("%s %d %p\n", __func__, size, r);return r;}void got_free(void *p) {printf("%s\n", __func__);free(p);}void got_memset(char *p, int n, int count) {printf("%s %p %d %d\n", __func__, p, n, count);memset(p, n, count);}void bp(void) {;}void stack_chk_guard(void) {printf("%s\n", __func__);}void imp___gnu_Unwind_Find_exidx(void) {printf("%s\n", __func__);}void cxa_call_unexpected(void) {printf("%s\n", __func__);}int main(void) {int fd = open("libhello-jni.so", O_RDONLY);unsigned char *fmem = mmap(NULL, 0x7000, PROT_READ, MAP_PRIVATE, fd, 0);unsigned char *mem = mmap(0xdead0000, 0x8000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);memcpy(mem, fmem, 0x2b13);memcpy(mem+0x3e8c, fmem+0x2e8c, 0x1a8);memcpy(mem+0x5000, fmem+0x4000, 0x26bc);munmap(fmem, 0x7000);close(fd);*(unsigned int *)(mem+0x3f98) = mem+0x1034+1; // xxxxxxxxxx2+1*(unsigned int *)(mem+0x3f9c) = stack_chk_guard;*(unsigned int *)(mem+0x3fb8) = imp___gnu_Unwind_Find_exidx;*(unsigned int *)(mem+0x3fc4) = cxa_call_unexpected;*(unsigned int *)(mem+0x3fc8) = mem+0x3f98; // _GLOBAL_OFFSET_TABLE_@got*(unsigned int *)(mem+0x3fa0) = mem+0x10e4+1; // Java_com_example_hellojni_HelloJni_stringFromJNI_ptr+1*(unsigned int *)(mem+0x3fa4) = mem+0x4004; // f_data_key_dllink*(unsigned int *)(mem+0x3fa8) = mem+0x401c; // f_sucess*(unsigned int *)(mem+0x3fdc) = got_malloc; // malloc@got*(unsigned int *)(mem+0x3fe0) = got_memset; // memset@got*(unsigned int *)(mem+0x3fe4) = got_free; // free@gotfor (int i = 0x3f98; i < 0x4000; i+=4) {if (*(unsigned int *)(mem+i) == 0) {//*(unsigned int *)(0x77770000+i) = i;}}struct JNINativeInterface_ jnii = {.f = {0}};for(int i = 0; i < 0x100; i++) {jnii.f[i] = 0x11110000+i*4;}jnii.f[0x18/4] = JNICALL_FindClass;jnii.f[0x29c/4] = JNICALL_NewStringUTF;jnii.f[0x84/4] = JNICALL_GetMethodID;jnii.f[0x88/4] = JNICALL_CallObjectMethod;jnii.f[0x2ac/4] = JNICALL_GetArrayLength;jnii.f[0x2e0/4] = JNICALL_GetByteArrayElements;jnii.f[0x300/4] = JNICALL_ReleaseByteArrayElements;JNIEnv env;env.functions = &jnii;printf("&global_name: %p, &global_serial: %p\n", &global_name, &global_serial);bp();((void (*)(int, int, int, int, int))(mem+0x10e4+1))(&env, 0xaaaa, &global_name, &global_serial, 0xdddd);return 0;}
调试:
从thumb指令开始(0x5d04),通过一个B跳转跳过1或2个dword(0x5d06和0x5d08)到后面的arm指令(0x5d10),先是 BX PC ,然后是 STR PC, [SP,#var_FC] 把PC放入栈,最后B跳转到一个外部函数(0x7270),外部函数返回到下一条指令。
外部函数大部分以push所有寄存器开始,以pop所有寄存器结束,通过栈上保存的PC向前找参数(0x5d06和0x5d08),返回到下一条指令(0x5d1c)。
LOAD:00005D04 ; ---------------------------------------------------------------------------LOAD:00005D04 CODE16LOAD:00005D04 B sub_5D10 ; BranchLOAD:00005D04 ; ---------------------------------------------------------------------------LOAD:00005D06 CODE32LOAD:00005D06 DCW 0xBF00LOAD:00005D08 DCD 8, 0x30D00LOAD:00005D10 CODE16LOAD:00005D10LOAD:00005D10 ; =============== S U B R O U T I N E =======================================LOAD:00005D10LOAD:00005D10 ; Attributes: thunkLOAD:00005D10LOAD:00005D10 sub_5D10 ; CODE XREF: LOAD:00005D04↑jLOAD:00005D10 BX PC ; Branch to/from Thumb modeLOAD:00005D10 ; ---------------------------------------------------------------------------LOAD:00005D12 DCB 1LOAD:00005D13 DCB 0LOAD:00005D13 ; End of function sub_5D10LOAD:00005D13LOAD:00005D14 CODE32LOAD:00005D14LOAD:00005D14 ; =============== S U B R O U T I N E =======================================LOAD:00005D14LOAD:00005D14LOAD:00005D14 sub_5D14 ; CODE XREF: sub_5D10↑jLOAD:00005D14LOAD:00005D14 var_FC = -0xFCLOAD:00005D14LOAD:00005D14 STR PC, [SP,#var_FC] ; Store to MemoryLOAD:00005D18 B sub_7270 ; BranchLOAD:00005D18 ; End of function sub_5D14LOAD:00005D1C ; ---------------------------------------------------------------------------LOAD:00005D1C CODE16LOAD:00005D1C B sub_5D28 ; BranchLOAD:00005D1C ; ---------------------------------------------------------------------------LOAD:00005D1E CODE32
几个关键的位置:
前期调试分析过程很漫长,但最终找出serial很简单:
serial:17726331DA0FE737149C8202
往期解析
1. 看雪·深信服 2021 KCTF 春季赛 | 第二题设计思路及解析
主办方


第六题正在火热进行中,
- 
                                        看雪·众安 KCTF赛况直播 | 战况升级!比分逐渐拉大看雪·众安 2021 KCTF秋季赛 已于11月15日中午12点正式开赛!第9题《万事俱备》已经持续2天。现在跟随我一起看看赛况如何吧!截至发文,目前该题围观人数多达927人,已有4支战队攻破此题!分 
- 
                                        看雪·众安 KCTF赛况直播 | 白热化!第八题《群狼环伺》谁先拿下?看雪·众安 2021 KCTF秋季赛 已于11月15日中午12点正式开赛!今天中午12点,第8题《群狼环伺》已正式开战!截至发文,该题共计围观人数:325,攻破战队数:0截至发文防守方总排名如下:攻击 
- 
                                        看雪·众安 2021 KCTF 秋季赛 | 第六题设计思路及解析看雪·众安 2021 KCTF秋季赛的第六题《窥伺者谁》已于今天中午12点截止答题,经统计,本题围观人数多达1080人,共计6支战队成功破解。恭喜v3r1t4s501用时19720秒拿下“一血”,接下 
[广告]赞助链接:
                        关注数据与安全,洞悉企业级服务市场:https://www.ijiandao.com/
                        让资讯触达的更精准有趣:https://www.0xu.cn/
                    

 关注KnowSafe微信公众号
            关注KnowSafe微信公众号 
                 
             
             
            
 
        
 
        
