304challenge-1

这道测试拖了很久了,之前也看过,一看是反汇编的知识,自己对于反汇编又不是很熟悉加上有很多杂事,没有集中的时间来做,就拖了又拖,现在可以来仔细看看题目了。

题目描述

题目给了三个任务:

  1. 对myvm进行分析,理解其指令格式,取址方式,了解指令用法;
  2. 基于对myvm的理解写出对应的反汇编器;
  3. 对hello程序进程分析,理解hello程序所做的所有事情。

我对题目的理解是:先分析myvm程序,得出需要的汇编语言指令集,有了指令集再编写反汇编器(反汇编器是把机器代码或二进制代码转换为汇编语言的程序),最后用这个反汇编器对hello程序进行反汇编,得到hello程序的汇编语言形式。

反汇编器最主要的是二进制指令集到汇编指令集的映射。

接下来先对myvm程序进行分析,得出反汇编器所需的指令集。

mymv程序分析

  1. myvm的主函数
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
__int64 __usercall main@<rax>(char **a1@<rsi>, char **a2@<rdx>, __int64 a3@<rax>, int a4@<edi>)
{
__int64 v4; // rbx
int v5; // eax

if ( a4 == 2 )
{
signal(2, handler);
v4 = sub_CE0(a1[1], handler);
unk_202014 = 1;
while ( 1 )
{
v5 = sub_E90(v4);
if ( v5 )
break;
if ( !unk_202014 )
goto LABEL_8;
}
if ( v5 == 2 )
puts("Illegal Instruction");
LABEL_8:
nullsub_1(v4);
}
else
{
fprintf(stderr, "Usage: %s <myvm program>\n", *a1, a3);
}
return 0LL;
}

从代码中可以得到主函数调用了signal、sub_CE0、sub_E90、nullsub_1函数,有while循环,有if条件语句。

signal()函数:
https://www.cnblogs.com/wuyepeng/p/9790396.html ,signal(2, handler)属于第三种情况,自定义一个信号处理函数handler,要求内核在处理该信号时切换到用户态执行这个处理函数。当程序是一个死循环(while(1))时,按Ctrl+c可以终止程序。

  1. 调用sub_CE0()函数
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
char *__fastcall sub_CE0(const char *a1)
{
FILE *v1; // rax
FILE *v2; // rbx
__int64 v3; // r15
void *v4; // r14
char *v5; // rbx

v1 = fopen(a1, "rb");
v2 = v1;
if ( !v1 )
return 0LL;
fseek(v1, 0LL, 2);
v3 = ftell(v2);
fseek(v2, 0LL, 0);
v4 = malloc(0x10000uLL);
memset(v4, 0, 0x10000uLL);
fread(v4, 1uLL, v3, v2);
fclose(v2);
_mm_storeu_si128(
(__m128i *)&unk_202018 + 4096,
_mm_unpacklo_epi64((__m128i)(unsigned __int64)&unk_202018, (__m128i)((unsigned __int64)&unk_202018 + 0x8000)));
memcpy(&unk_202018, v4, 0x10000uLL);
_mm_storeu_si128((__m128i *)&unk_202018 + 4097, (__m128i)0LL);
*(_QWORD *)((char *)&unk_202018 + 65566) = 0LL;
*((_WORD *)&unk_202018 + 32785) = 32764;
*((_QWORD *)&unk_202018 + 8197) = 0LL;
v5 = (char *)&unk_202018 + 0x10000;
free(v4);
return v5;
}

(1)程序中的FILE数据类型,FILE是C语言文件结构定义,打开文件和文件操作要用到这类结构。可以看成变量类型,用于变量声明。这个是一种数据结构类型,用来表示一个文件的相关信息,如果定义了一个文件指针,就用这个指针来指向某个文件,然后就能使用这个指针对文件来进行操作了。也就是说可以用v1和v2对文件进行操作。

(2)调用fopen()函数是打开一个文件的函数,其中有两个参数,第一个是要打开文件的文件名,第二个是对其文件进行哪种操作。
程序中是打开a1文件,对其进行rb操作,”rb”:打开一个二进制文件,文件必须存在,只允许读。

(3)调用fseek()函数,int fseek(FILE *stream, long offset, int fromwhere),函数设置文件指针stream的位置。如果执行成功,stream将指向以fromwhere(偏移起始位置:文件头0(SEEK_SET)当前位置1(SEEK_CUR)文件尾2(SEEK_END))为基准,偏移offset(指针偏移量)个字节的位置。如果执行失败(比如offset超过文件自身大小),则不改变stream指向的位置。
这里 fseek(v1, 0LL, 2),指针v1指向文件的文件尾,fseek(v2, 0LL, 0)指针v2指向文件的文件头。

(4)调用ftell()函数,用于得到文件位置指针当前位置相对于文件首的偏移字节数。v3 = ftell(v2),把文件位置指针v2当前位置相对于文件首的偏移字节数赋值给v3。

(5)调用malloc()函数,动态分配内存,其函数原型为void *malloc(unsigned int size);其作用是在内存的动态存储区中分配一个长度为size的连续空间。此函数的返回值是分配区域的起始地址,或者说,此函数是一个指针型函数,返回的指针指向该分配域的开头位置。如果分配成功则返回指向被分配内存的指针(此存储区中的初始值不确定),否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。
这里分配了一块长度为0x10000uLL的连续内存区域,且v4指向该区域的开头位置。

(6)调用memset()函数,对分配的以v4开头的长度为0x10000uLL的连续内存区域进行初始化,初始化为0.

(7)调用fread()函数,它从文件流中读数据,函数原型为:size_t fread ( void buffer, size_t size, size_t count, FILE stream) ;buffer用于接收数据的内存地址,从给定流 stream 读取数据,最多读取count个项,每个项size个字节,如果调用成功返回实际读取到的项个数(小于或等于count),如果不成功或读到文件末尾返回 0。
fread(v4, 1uLL, v3, v2)从v2文件流中读取数据,最多读取v3个项,每个项1ull个字节,读取的数据存在以v4开始的内存中。

这里以上的代码意思是:
打开a1文件,只读,用v1指向a1文件,把v1赋值给v2,v2也指向a1文件,如果返回指针v1为空,则不能打开该文件。接下来v1指向文件尾,v3暂存v2的位置(即a1文件所在位置),v2指向文件头,分配以v4开始的长度为0x10000uLL的内存空间,并初始化为0,读取v2指向的文件流。关闭文件v2。

(8)调用_mm_storeu_si128()函数,功能是可以存储128位的数据。将逗号后的__m128i 变量的值存储到&unk_202018 + 4096所指定的变量中去。

(9)调用 _mm_unpacklo_epi64()函数,返回一个_m128i的寄存器。

(10)调用memcpy()函数,void memcpy(void destin, void *source, unsigned n);从源source所指的内存地址的起始位置开始拷贝n个字节到目标destin所指的内存地址的起始位置中。
memcpy(&unk_202018, v4, 0x10000uLL)从v4所指的内存地址的起始位置开始拷贝0x10000uLL个字节到目标&unk_202018所指的内存地址的起始位置中。

(11)_QWORD是一种无符号整数类型。

文章目录
  1. 1. 题目描述
    1. 1.1. mymv程序分析