致梦的开始

前言

这篇文章是在第一学期快结束补充的,我不断地思索,哪个文章可以作为逆向启程呢,是ida的熟悉流程?是遇到最简单题目的题解?是c语言的小知识?亦或是其他,但我总认为,这些不足以为我的逆向开一个好头,思来想去最后决定以main函数来作为逆向的启蒙

start

开始,也意味着程序的开始,一个用c语言写好了main的程序并不是直接从main开始的,而是start,start紧接着去调用很多函数,他最终的目的也是位main铺路

以一个最简单的程序为例:

1
2
3
4
5
#include<stdio.h>
int main() {
printf("hallo re");
return 0;
}
将这个程序生成的文件拖入ida

hello re

1
2
3
4
5
// attributes: thunk
__int64 start()
{
return start_0();
}
反编译出的第一条是他,start,什么都没有,不过别急,点进去,连着点进去两个函数出现了第一个岔路
1
2
3
4
5
__int64 sub_140011E30()
{
road1();
return road2();
}
这两个函数原本不叫这个,是我将其随意命名的,他在你的ida中可能会叫sub_地址,是ida给这个函数起的名字,选择road2点进去
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
__int64 sub_140011E50()
{
__int64 v1; // rcx
char v2; // [rsp+20h] [rbp-48h]
unsigned __int8 v3; // [rsp+21h] [rbp-47h]
unsigned int Code; // [rsp+28h] [rbp-40h]
void (__fastcall **v5)(_QWORD, __int64, _QWORD); // [rsp+30h] [rbp-38h]
_tls_callback_type *v6; // [rsp+38h] [rbp-30h]

if ( !(unsigned __int8)sub_140011393(1i64) )
sub_140011311(7i64);
v2 = 0;
v3 = sub_140011325();
if ( dword_14001D1F8 == 1 )
{
sub_140011311(7i64);
}
else if ( dword_14001D1F8 )
{
v2 = 1;
}
else
{
dword_14001D1F8 = 1;
if ( j__initterm_e((_PIFV *)&First, (_PIFV *)&Last) )
return 255i64;
j__initterm((_PVFV *)&qword_14001A000, (_PVFV *)&qword_14001A220);
dword_14001D1F8 = 2;
}
sub_1400112FD(v3);
v5 = (void (__fastcall **)(_QWORD, __int64, _QWORD))sub_140011299();
if ( *v5 && (unsigned __int8)sub_14001114F(v5) )
(*v5)(0i64, 2i64, 0i64);
v6 = (_tls_callback_type *)sub_1400112CB();
if ( *v6 && (unsigned __int8)sub_14001114F(v6) )
j__register_thread_local_exe_atexit_callback(*v6);
Code = sub_1400120A0();
if ( !(unsigned __int8)sub_140011276() )
j_exit(Code);
if ( !v2 )
j__cexit();
LOBYTE(v1) = 1;
sub_140011140(v1, 0i64);
return Code;
}

看到最后return Code了吗,记得一个main函数的基本框架有return 0吗,当时说0是返回值,返回程序是否正确运行,那么如果想要返回值0,返回到start那里,这一步返回的Code里面的值就应该是return的0,我们要去看Code在这个程序里那里被定义了——sub_1400120A0,在37行,所以说这个函数往里延伸的就是main函数,点进去

1
2
3
4
5
6
7
8
9
10
11
__int64 sub_1400120A0()
{
unsigned int v1; // [rsp+20h] [rbp-28h]
char **initial_narrow_environment; // [rsp+28h] [rbp-20h]
char **v3; // [rsp+30h] [rbp-18h]

initial_narrow_environment = j__get_initial_narrow_environment();
v3 = *j___p___argv();
v1 = *j___p___argc();
return sub_14001126C(v1, v3, initial_narrow_environment);
}

还是刚刚的原理,return返回的要是main里面返回的0,所以点开return后跟着的函数,一路到底

1
2
3
4
5
6
int __cdecl main(int argc, const char **argv, const char **envp)
{
sub_14001136B(&unk_140022008);
printf("hallo re");
return 0;
}
获得了主函数,如果到这里不是很明白的话,从最后一段代码的return 0,回到最初的start,观察一下,0是不是一直作为返回值被返回

逆向就是这样,总会有很多东西混淆你,谨记自己的初心,静下来,慢慢观察,最终你也可以从空白的的start抵达目标main