0x01 监视文件相关操作
运行程序,打开任务管理器,查看进程名称如下:
Process Monitor添加条件如下:
只监视文件相关操作,可以看到程序要读取的文件名称如下:
0x02 使用IDA Pro分析
程序先使用CreateFile()
函数来打开KeyFile文件,故通过该函数定位:
0x02.1 三处ReadFile()
成功打开文件后下面有三处ReadFile()
操作:
第一处ReadFile()
操作读取1个字节到缓冲区中,笔者将其命名为StringLength
。接下来判断该值是否为0,若为0,直接跳转到CloseHandle()
函数处;不为0,继续下面的操作。
第二处ReadFile()
操作读取StringLength
个字节到缓冲区中,然后调用sub_401000()
。跟进sub_401000()
查看:
403288h
处存放的是刚刚ReadFile()
读取内容,故该函数功能是将读取内容逐字节相加,取其低8位保存于String_Char_Add
(由笔者命名)中。
第三处ReadFile()
读取18个字节,然后调用sub_4010C9
,跟进查看。
0x02.2 核心验证函数
先跟进sub_40101D
函数查看其功能:
该函数功能是将上述第三处ReadFile()
读取出来的18字节(笔者名之为String_Char_Xor
)逐个与String_Char_Add
作异或运算。
将下面的指令粗略转成C语言形式(分析时随手写成,仅为表达程序逻辑,无法运行):
注意上图红色方框中的两个向上箭头明显是Do...While
的标志。
int tmp1=0; //[ebp-2]
int tmp2; //[ebp-1]
int ecx=0;
char String_Char_Xor[18];
char aC_0_After_C[]="C*......*...****.*.****...*....*.*..**********.*..*....*...*...**.****.*.*...****.*....*.*******..*.***..*.....*.*..***.**.***.*...****....*X..*****************";
int ret;
do
{
tmp2=8;
do
{
tmp2-=2;
ecx=tmp1;
ch=String_Char_Xor[ecx];
ch>>=ecx;
ch&=3;
sub_401033();
if()
;
}while(tmp2!=0)
tmp1+=1;
}while(tmp1!=18)
由于函数sub_401033()
功能暂不清楚,故只写下if
框架,等待分析完函数sub_401033()
功能后再补充。
注:笔者起初没有使用IDA Pro的F5,分析并写完后方才以F5验证是否正确,但发现其不靠谱...
跟进函数sub_401033()
查看,首先是一处if...else if...else
代码块:
粗略转成C语言形式:
char* loc;
char tmp1;
if(ch!=0)
{
if(ch==1)
loc=aC_0_After_C+1;
else if(ch==2)
loc=aC_0_After_C+16;
else
loc=aC_0_After_C-1;
}
else
loc=aC_0_After_C-16;
tmp1=*loc;
下面是一处if...else
代码块:
粗略转成C语言形式:
if(tmp1!='*')
{
if(tmp1=='X')
printf("Success");
else
{
*loc='C';
*aC_0_After_C=' ';
return 1;
}
}
else
return 0;
将之前Do...While
中的if
补齐如下:
do
{
tmp2=8;
do
{
tmp2-=2;
ecx=tmp1;
ch=String_Char_Xor[ecx];
ch>>=ecx;
ch&=3;
ret=sub_401033(ch);
if(!ret)
return ret;
}while(tmp2!=0)
tmp1+=1;
}while(tmp1!=18)
0x02.3 验证思路
综合上述分析,可以总结出该程序的验证思路。
-
读取KeyFile文件中第一个字节,该字节存储的是Len(Name)
-
根据长度读取Name
,将Name
逐字节相加,取其低8位,存储于L8
中
-
于KeyFile文件中继续读取18个字节内容,逐个与L8
作异或运算
关键在于第3步中读取的18个字节是何种意义。
将奇怪字符串以16(16是由aC_0_After_C
起始位置得来)为单位分开,可以发现端倪:
****************
C*......*...****
.*.****...*....*
.*..**********.*
..*....*...*...*
*.****.*.*...***
*.*....*.*******
..*.***..*.....*
.*..***.**.***.*
...****....*X..*
****************
.
是路径,*
是墙壁,C
是起点,X
是终点。由C
沿.
走到X
,验证通过;其间若碰到*
,验证失败。
那么,sub_401033()
函数一开始的if...else if...else
代码块的功能:
而下面的if...else
代码块则是判断是否碰壁或者到达终点。
0x02.4 路径计算
既然sub_401033()
函数功能是移动一步并判断,那么第3步中读取的18个字节存储的应是与路径相关。那么就来走一下这个迷宫(只写出部分,后面请自行补充):
2 2 2 1 2 2 2 3 2 2 1 1...
外层的Do...While
循环是18次,那么每次处理1字节。内层的Do...While
循环是4次,1Byte=8bits,故2 2 2 1
——>>10 10 10 01
——>>A9
。
A9
是KeyFile文件于第3步中被读取出的字节与第2步L8
异或所得,那么将路径按上述过程全部转换之后再与L8
异或再加上Len(Name)
及Name
就能得到KeyFile文件。