看雪2018ctf

出题人博客:https://blog.csdn.net/u011247544

  1. cygwin可运行linux程序如readlef来查看elf文件。
  2. 还配置了开源项目LIEF用py解析elf

出题人写的got表hook:https://blog.csdn.net/u011247544/article/details/78564564
有续

1
int __attribute__((constructor)) gothook()

经解析在init_array中被指向。
且type为E

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
SHT_NULL = 0: 该值表示该节是一个无效(非活动)的节头,它没有对应的节;该节头中的其它成员也都是无意义的;
SHT_PROGBITS = 1: 该值表示该节所包含的信息是由程序定义的,该节内容的格式和含义都是由程序来决定的;
SHT_SYMTAB = 2: Symbol table;SHT_SYMTAB和SHT_DYNSYM这两类节中都含有符号表;目前,目标文件中最多只能各包含一个这两种节,但以后会取消这种限制;一般来说,SHT_SYMTAB提供的符号用于在创建目标文件的时候编辑链接,在运行期间也有可能会用于动态链接;SHT_SYMTAB包含完整的符号表,它往往会包含很多在运行期间(动态链接)用不到的符号;所以,一个目标文件可以再有一个SHT_DYNSYM节,它含有一个较小的符号表,专门用于动态链接;
SHT_STRTAB = 3: String table;该值表示该节是一个字符串表;目标文件中可以包含多个字符串表节;
SHT_RELA = 4: Relocation entries with addends;该值表示该节是一个重定位节,包含有带明确加数(Addend)的重定位项,对于32位类型的目标文件来说,这个加数就是Elf32_Rela;一个目标文件可能含有多个重定位节;
SHT_HASH = 5: Symbol hash table;该值表示该节包含一张哈希表;所有参与动态链接的目标文件都必须包含一个符号哈希表;目前,一个目标文件中最多只能有一个哈希表,但这一限制以后可能取消;
SHT_DYNAMIC = 6: Dynamic linking information;该值表示该节包含的是动态链接信息;目前,一个目标文件中最多只能有一个SHT_DYNAMIC节,但这一限制以后可能取消;
SHT_NOTE = 7: Notes;该值表示该节中包含的信息用于以某种方式来标记文本文件;
SHT_NOBITS = 8: Program space with no data (bss);该值表示该节的内容是空的,节并不占用实际空间;这时字段sh_offset只代表一个逻辑上的位置概念,并不代表实际的内容;
SHT_REL = 9: Relocation entries,no addends;该值表示该节是一个重定位节,包含有带明确加数的重定位项,对于32位类型的目标文件来说,这个加数就是Elf32_Rel;一个目标文件中可能包含有多个重定位节;
SHT_SHLIB = 10: Reserved;
SHT_DYNSYM = 11: Dynamic linker symbol table;
SHT_INIT_ARRAY = 14: Array of constructors;
SHT_FINI_ARRAY = 15: Array of destructors;
SHT_PREINIT_ARRAY = 16: Array of pre-constructors;
SHT_GROUP = 17: Section group;
SHT_SYMTAB_SHNDX = 18: Extended section indeces;
SHT_NUM = 19: Number of defined types;
SHT_LOOS = 0x60000000: Start OS-specific;
SHT_GNU_ATTRIBUTES= 0x6ffffff5: Object attributes;
SHT_GNU_HASH = 0x6ffffff6: GNU-style hash table;
SHT_GNU_LIBLIST = 0x6ffffff7: Prelink library list;
SHT_CHECKSUM = 0x6ffffff8: Checksum for DSO content;
SHT_LOSUNW = 0x6ffffffa: Sun-specific low bound;
SHT_SUNW_move = 0x6ffffffa
SHT_SUNW_COMDAT = 0x6ffffffb
SHT_SUNW_syminfo = 0x6ffffffc
SHT_GNU_verdef = 0x6ffffffd: Version definition section;
SHT_GNU_verneed = 0x6ffffffe: Version needs section;
SHT_GNU_versym = 0x6fffffff: Version symbol table;
SHT_HISUNW = 0x6fffffff: Sun-specific high bound;
SHT_HIOS = 0x6fffffff: End OS-specific type;
SHT_LOPROC = 0x70000000: Start of processor-specific;
SHT_HIPROC = 0x7fffffff: End of processor-specific;
SHT_LOUSER = 0x80000000: Start of application-specific;
SHT_HIUSER = 0x8fffffff: End of application-specific;

但是这里的elf被破坏了.
但是加载elf用的是progrem header,section无用,因此查看elf信息可以直接查看progrem header中的dynamic段,内部为一个一个项,以type区分用途,其中就包含init、got等
但是用cygwin+readelf查看ph:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Dynamic section at offset 0x34be0 contains 28 entries:
标记 类型 名称/值
0x00000003 (PLTGOT) 0x35f00
0x00000002 (PLTRELSZ) 488 (bytes)
0x00000017 (JMPREL) 0x5d00
0x00000014 (PLTREL) REL
0x00000011 (REL) 0x3a18
0x00000012 (RELSZ) 8936 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffa (RELCOUNT) 1109
0x00000006 (SYMTAB) 0x168
0x0000000b (SYMENT) 16 (bytes)
0x00000005 (STRTAB) 0x13c8
0x0000000a (STRSZ) 7571 (bytes)
0x00000004 (HASH) 0x315c
0x00000001 (NEEDED) 共享库:[liblog.so]
0x00000001 (NEEDED) 共享库:[libstdc++.so]
0x00000001 (NEEDED) 共享库:[libm.so]
0x00000001 (NEEDED) 共享库:[libc.so]
0x00000001 (NEEDED) 共享库:[libdl.so]
0x0000000e (SONAME) Library soname: [libexecute_table.so]
0x0000000c (INIT) 0x8c59
0x0000001a (FINI_ARRAY) 0x34cc8
0x0000001c (FINI_ARRAYSZ) 8 (bytes)
0x00000019 (INIT_ARRAY) 0x34cd0
0x0000001b (INIT_ARRAYSZ) 60 (bytes)
0x00000010 (SYMBOLIC) 0x0
0x0000001e (FLAGS) SYMBOLIC BIND_NOW
0x6ffffffb (FLAGS_1) 标志: NOW
0x00000000 (NULL) 0x0

找到0x34cd0处ida打开:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LOAD:00034CD0 ; ELF Initialization Function Table
LOAD:00034CD0 DCD sub_625C+1
LOAD:00034CD4 DCD sub_635C+1
LOAD:00034CD8 DCD sub_654C+1
LOAD:00034CDC DCD sub_67BC+1
LOAD:00034CE0 DCD sub_6CDC+1
LOAD:00034CE4 DCD sub_7048+1
LOAD:00034CE8 DCD sub_738C+1
LOAD:00034CEC DCD sub_76C4+1
LOAD:00034CF0 DCD sub_7868+1
LOAD:00034CF4 DCD sub_7884+1
LOAD:00034CF8 DCD sub_78C4+1
LOAD:00034CFC DCD sub_78D4+1
LOAD:00034D00 DCD sub_79B0+1
LOAD:00034D04 DCD sub_7A8C+1
LOAD:00034D08 ALIGN 0x10
LOAD:00034D08 ; LOAD ends

此时含有大量垃圾指令,需要动态调试。断在init的方法:
http://www.blogfshare.com/linker-load-so.html
pull出system/bin/linker 在ida中分析出”[ Calling %s (size %zd) @ %p for ‘%s’ ]”处:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
int __fastcall _dl__ZL10call_arrayIPFviPPcS1_EEvPKcPT_jbS5_(int result, int a2, int a3, int a4)
{
int v4; // r7
int v5; // r5
int v6; // r6
int v7; // r8
int i; // r4
v4 = a2;
v5 = a4;
v6 = a3;
v7 = result;
if ( a2 )
{
result = _dl_g_ld_debug_verbosity;
if ( _dl_g_ld_debug_verbosity < 2 )
{
i = 0;
goto LABEL_6;
}
((void (*)(signed int, const char *, const char *, ...))_dl_async_safe_format_log)(
4,
"linker",
"[ Calling %s (size %zd) @ %p for '%s' ]",
v7,
a3,
a2,
a4);
for ( i = 0; ; ++i )
{
result = _dl_g_ld_debug_verbosity;
LABEL_6:
if ( i == v6 )
break;
if ( result >= 2 )
((void (*)(signed int, const char *, const char *, ...))_dl_async_safe_format_log)(
4,
"linker",
"[ %s[%d] == %p ]",
v7,
i,
*(_DWORD *)(v4 + 4 * i));
_dl__ZL13call_functionPKcPFviPPcS2_ES0_((int)"function", *(char **)(v4 + 4 * i), v5);
}
if ( result > 1 )
result = ((int (*)(signed int, const char *, const char *, ...))_dl_async_safe_format_log)(
4,
"linker",
"[ Done calling %s for '%s' ]",
v7,
v5);
}
return result;
}

_dl_ZL13call_functionPKcPFviPPcS2ES0((int)”function”, (char **)(v4 + 4 i), v5):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int __fastcall _dl__ZL13call_functionPKcPFviPPcS2_ES0_(int a1, char *a2, int a3)
{
char *v3; // r6
int v4; // r4
int result; // r0
int v6; // r5
v3 = a2;
v4 = a1;
result = (int)(a2 + 1);
v6 = a3;
if ( (unsigned int)(a2 + 1) >= 2 )
{
if ( _dl_g_ld_debug_verbosity >= 2 )
((void (*)(signed int, const char *, const char *, ...))_dl_async_safe_format_log)(
4,
"linker",
"[ Calling c-tor %s @ %p for '%s' ]",
v4,
a2,
a3);
((void (__fastcall *)(int, int, int))v3)(_dl_g_argc, _dl_g_argv, _dl_g_envp);
result = _dl_g_ld_debug_verbosity;
if ( _dl_g_ld_debug_verbosity >= 2 )
result = ((int (*)(signed int, const char *, const char *, ...))_dl_async_safe_format_log)(
4,
"linker",
"[ Done calling c-tor %s @ %p for '%s' ]",
v4,
v3,
v6);
}
return result;
}

((void (__fastcall *)(int, int, int))v3)(_dl_g_argc, _dl_g_argv, _dl_g_envp);就是调用init_array init_proc
的地方。

其中的函数开启线程检测/proc/self/status反调试。hook jni_load函数。
mprotect调用了两次,第一次修改JNI_OnLoad的偏移,由0x8205修改为0xA260 ,jni_onload中FindClass与RegisterNatives注册native函数0xAC98。
第二次mprotect调用就是解密0xAC98处的验证函数。分析验证即可。

不要随意在jni_load直接下断点,可能会检测是否更改这俩处。而且hook 符号表了后不会走jni_onload符号处的函数?不是一次性读取完符号表的么?还是init之后读?。

2018.6.28:
由于无法断在jni_load处,(没找到libdvm.so,经询问得知5.0后全是art执行….)无法得知真正的jni_onload内容。
5.0后在libart.so中调用,这里变动较大,需要分析8.0源码

只能动调init那几个函数分析关键调用了,因为hook与解密,反调试等操作肯定会用到几个系统调用:pthread_create,mprotect与_NR_openat。在这里作者存了几个关键函数(各种非正常调用系统调用的方式?):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//proc_4
v8 = (struc_table *)operator new(0x20u);
sub_C8A0(v8);
//sub_C8A0
struc_table *__fastcall sub_C8A0(struc_table *result)
{
result->dlopen = (int)off_35D08;
result->sscanf = (int)off_35D0C;
result->__NR_read = (int)off_35D10[0];
result->__NR_close = (int)off_35D14[0];
result->__NR_mprotect = (int)off_35D18;
result->__NR_openat = (int)sub_C874;
result->sleep = (int)off_35D1C;
result->pthread_create = (int)off_35D20;
return result;
}

发现自己所欠缺:密码学,ida脚本dump,android系统源码知识(源码中8.0下apk执行流程)、linux系统知识。反调试与混淆法不熟悉