参考 expend20.github.io/2018/05/24/… www.freebuf.com/column/1746…
打开bin看字符串,字符串没被加密
ida通过Imports通过fopen交叉引用到main
程序流程
fseek,ftell读到文件大小,将文件内容写入malloc的空间里,然后进入虚拟机
RECOMPILE
将ida伪代码转化为c,有几点要注意的:
- v1类型byte *,*(_DWORD *)&v1[v2];先v1[v2]取第二个byte 然后&取地址 然后*(_DWORD *)取出的一个int
- v2为下一位索引(v0+1),v0为当前位索引(eip),v1是data
- ida宏定义,__int64这里定义为unsigned int,因为参与运算的变量最大为int,没必要long long
- 把main简单定义为int main()
- ++,--注意回显的位置
- 有分支的opcde要标识出来,这些opcode又分为循环opcode和条件opcode
- printf按照 opcode eip变化 最终值 = 中间过程 = 值 来写
- 最终值 = 中间过程 = 值 如c = v1[%d] = %d ;c = ~(dword_6010A4 & c) = ~(%d & %d) = %d
按照以上要求写的话程序就比较清晰了
#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>
#include "conio.h"
#define _BYTE unsigned char
#define _DWORD unsigned int
#define __int64 unsigned int
__int64 sub_400896(_BYTE *ptr,int filesize)
{
__int64 v0; // rax
_BYTE *v1; // rbp
int v2; // ebx
int v4; // rdx
int v5; // rax
__int64 v6; // rax
__int64 v7; // rax
__int64 v8; // rax
__int64 v9; // rax
int v10; // eax
__int64 v11; // rax
char v12; // dl
int v13; // eax
int v14; // eax
_BYTE *v15; // rax
__int64 v16; // rax
__int64 v17; // rax
__int64 v18; // rax
_DWORD dword_6010A4;
_DWORD c;
v0 = 0LL;
v1 = ptr;
while ( 1 )
{
v2 = v0 + 1; //v2下一位的索引
switch ( v1[v0] ) //v0当前位的索引(eip)
{ // 读取到的opcode
case 0uLL:
printf("0x00 返回,返回值为%d\n",*(unsigned int *)&v1[v2]);
return *(unsigned int *)&v1[v2];
case 1uLL:
printf("0x01 eip = %d\n",*(_DWORD *)&v1[v2]);
goto LABEL_35;
case 2uLL:
printf("0x02 eip += 9 v1[%d] = v1[%d] = %d\n",*(int *)&v1[v2],v0+5,*(_DWORD *)&v1[(int)v0 + 5]);
v4 = v2;
v2 = v0 + 9;
v1[*(int *)&v1[v4]] = *(_DWORD *)&v1[(int)v0 + 5];//v1[下一位的值] = v1[当前值的索引+5]
break;
case 3uLL:
printf("0x03 eip += 5 c = v1[%d] = %d\n",*(int *)&v1[v2],v1[*(int *)&v1[v2]]);
v5 = v2;
v2 += 4;//v2为当前开始第五位的索引
v6 = *(int *)&v1[v5];//下一位的值
goto LABEL_27;
case 4uLL:
printf("0x04 eip += 5 v1[%d] = c = %d\n",*(int *)&v1[v2],c);
v7 = v2;
v2 += 4;
v8 = *(int *)&v1[v7];
goto LABEL_31;
case 5uLL:
printf("0x05 eip += 5 dword_6010A4 = v1[%d] = %d\n",*(int *)&v1[v2],(char)v1[*(int *)&v1[v2]]);
v9 = v2;
v2 += 4;
v10 = (char)v1[*(int *)&v1[v9]];
goto LABEL_21;
case 6uLL:
printf("0x06 eip += 5 v1[%d] = %d\n",*(int *)&v1[v2],dword_6010A4);
v11 = v2;
v12 = dword_6010A4;
v2 += 4;
v8 = *(int *)&v1[v11];
goto LABEL_9;
case 7uLL:
printf("0x07 eip++ c += %d = %d\n",dword_6010A4,c+dword_6010A4);
v13 = dword_6010A4;
goto LABEL_23;
case 8uLL:
printf("0x08 eip++ c = ~(dword_6010A4 & c) = ~(%d & %d) = %d\n",dword_6010A4,c,~(dword_6010A4 & c));
v14 = ~(dword_6010A4 & c);
goto LABEL_12;
case 0xAuLL:
printf("输入开始\n");
v14 = _getch();
printf("0x0A eip++ c = 输入字符的askii %d\n",v14);
goto LABEL_12;
case 0xBuLL:
printf("0x0B eip++ 输出 c = ");
putchar(c);
printf("\n");
break;
case 0xCuLL:
printf("0xC分支开始\n");
v15 = &v1[*(int *)&v1[v2]];
if ( *v15 )
{
v2 = *(_DWORD *)&v1[v2 + 4];
printf("0x0C 分支一v1[%d]不等于0 eip = %d v1[%d]-- = %d\n",*(int *)&v1[v2],*(_DWORD *)&v1[v2 + 4],*(int *)&v1[v2],--*v15);
}
else
{
printf("0x0C 分支二v1[%d]等于0 eip += 9\n",*(int *)&v1[v2]);
v2 += 8;
}
break;
case 0xDuLL:
++c;
printf("0x0D eip++ c++ = %d\n",c);
break;
case 0xEuLL:
printf("0x0E eip++ dword_6010A4++ = %d\n",++dword_6010A4);
break;
case 0xFuLL:
printf("0x0F eip++ c = dword_6010A4 = %d\n",dword_6010A4);
v14 = dword_6010A4;
goto LABEL_12;
case 0x10uLL:
printf("0x10 eip++ dword_6010A4 = c = %d\n",c);
v10 = c;
goto LABEL_21;
case 0x11uLL:
printf("0x11 eip += 5 c += v1[%d] = %d\n",v2,c+*(_DWORD *)&v1[v2]);
v16 = v2;
v2 += 4;
v13 = *(_DWORD *)&v1[v16];
LABEL_23:
c += v13;
break;
case 0x12uLL:
printf("0x12 eip++ c = v1[%d] = %d\n",dword_6010A4,(char)v1[dword_6010A4]);
v6 = dword_6010A4;
goto LABEL_27;
case 0x13uLL:
printf("0x13 eip++ c = v1[%d] = %d\n",c,(char)v1[c]);
v6 = c;
LABEL_27:
v14 = (char)v1[v6];//v1[下一位的值]
goto LABEL_12;
case 0x14uLL:
printf("0x14 eip += 5 c = v1[%d] = %d\n",v2,*(_DWORD *)&v1[v2]);
v17 = v2;
v2 += 4;
v14 = *(_DWORD *)&v1[v17];
goto LABEL_12;
case 0x15uLL:
printf("0x15 eip += 5 dword_6010A4 = v1[%d] = %d\n",v2,*(_DWORD *)&v1[v2]);
v18 = v2;
v2 += 4;
v10 = *(_DWORD *)&v1[v18];
LABEL_21:
dword_6010A4 = v10;
break;
case 0x16uLL:
printf("0x16 eip++ v1[%d] = c = %d\n",dword_6010A4,c);
v8 = dword_6010A4;
LABEL_31:
v12 = c;
LABEL_9:
v1[v8] = v12;
break;
case 0x17uLL:
printf("0x17 eip++ c = c - dword_6010A4 = %d\n",c - dword_6010A4);
v14 = c - dword_6010A4;
LABEL_12:
c = v14;
break;
case 0x18uLL:
printf("0x18分支开始 c为%d\n",c);
c = 0;
if ( c ){
LABEL_35:
printf("0x18 分支一c不为零 eip = v1[%d] = %d\n",v2,*(_DWORD *)&v1[v2]);
v2 = *(_DWORD *)&v1[v2];
}
else{
printf("0x18 分支二c为0 eip += 5\n");
v2 = v0 + 5;
}
break;
default:
printf("DEFAULT eip++\n");
break;
}
if ( v2 >= filesize )
return 0LL;
v0 = v2;
}
}
int main()
{
FILE *v3; // rax
const char *v4; // rdi
FILE *v5; // rbx
errno_t err;
err = fopen_s(&v3, "C:\\Users\\lenovo\\Desktop\\fuxian\\simplevm\\p.bin", "rb");
if(err == 0) {
printf("The file was opened\n");
}
else {
printf("The file was not opened\n");
}
v4 = "err 0";
if ( !v3 )
goto LABEL_4;
v5 = v3;
fseek(v3, 0LL, 2); // 文件结尾
int filesize = ftell(v5); // 得到文件大小
fseek(v5, 0LL, 0); // 文件开头
if ( filesize <= 0 )
{
v4 = "err 1";
LABEL_4:
puts(v4);
return 0xFFFFFFFF;
}
_BYTE *ptr = (_BYTE *)malloc(filesize);
v4 = "err 3";
if ( !ptr )
goto LABEL_4;
v4 = "err 4";
if ( filesize != fread(ptr, 1uLL, filesize, v5) )
goto LABEL_4;
fclose(v5);
v4 = "err 5";
if ( (unsigned int)sub_400896(ptr,filesize) ) // 函数必返回0
goto LABEL_4;
free(ptr);
return 0LL;
}
回显信息
The file was opened
0x01 eip = 48
0x18 分支一c不为零 eip = v1[1] = 48
0x15 eip += 5 dword_6010A4 = v1[49] = 256
0x0E eip++ dword_6010A4++ = 257
0x12 eip++ c = v1[257] = 73
0x0B eip++ 输出 c = I
0xC分支开始
0x0C 分支一v1[202052110]不等于0 eip = 256 v1[202052110]-- = 9
0x0E eip++ dword_6010A4++ = 258
0x12 eip++ c = v1[258] = 110
0x0B eip++ 输出 c = n
0xC分支开始
0x0C 分支一v1[202052110]不等于0 eip = 256 v1[202052110]-- = 8
0x0E eip++ dword_6010A4++ = 259
0x12 eip++ c = v1[259] = 112
0x0B eip++ 输出 c = p
0xC分支开始
0x0C 分支一v1[202052110]不等于0 eip = 256 v1[202052110]-- = 7
0x0E eip++ dword_6010A4++ = 260
0x12 eip++ c = v1[260] = 117
0x0B eip++ 输出 c = u
0xC分支开始
0x0C 分支一v1[202052110]不等于0 eip = 256 v1[202052110]-- = 6
0x0E eip++ dword_6010A4++ = 261
0x12 eip++ c = v1[261] = 116
0x0B eip++ 输出 c = t
由这可推出,想要回显信息,流程为
index = 256
index++
c = v1[index]
输出c
for i in range(10-0)
i--
index++
c = v1[index]
输出c
保存输入
//输入abc
输入开始
0x0A eip++ c = 输入字符的askii 97
DEFAULT eip++
0x16 eip++ v1[273] = c = 97
0xC分支开始
0x0C 分支一v1[375785998]不等于0 eip = 69644 v1[375785998]-- = 30
0x0E eip++ dword_6010A4++ = 274
输入开始
0x0A eip++ c = 输入字符的askii 98
DEFAULT eip++
0x16 eip++ v1[274] = c = 98
0xC分支开始
0x0C 分支一v1[375785998]不等于0 eip = 69644 v1[375785998]-- = 29
0x0E eip++ dword_6010A4++ = 275
输入开始
0x0A eip++ c = 输入字符的askii 99
DEFAULT eip++
0x16 eip++ v1[275] = c = 99
0xC分支开始
0x0C 分支一v1[375785998]不等于0 eip = 69644 v1[375785998]-- = 28
0x0E eip++ dword_6010A4++ = 276
由v1[375785998]推出输入长度为32,将输入存到了v1[273]开始的空间 大致流程为
index = 272
index++
for i in range(31-0)
v1[index] = 输入
i--
index++
之后流程
循环做了些事,不好分析,去看末尾回显那有没有关键点
来到末尾回显wrong这
DEFAULT eip++
0x03 eip += 5 c = v1[320] = 63
0x11 eip += 5 c += v1[150] = 304
0x10 eip++ dword_6010A4 = c = 304
0x03 eip += 5 c = v1[324] = 90
0x16 eip++ v1[304] = c = 90
0x05 eip += 5 dword_6010A4 = v1[320] = 63
0x0E eip++ dword_6010A4++ = 64
0x06 eip += 5 v1[320] = 64
0xC分支开始
0x0C 分支二v1[325]等于0 eip += 9
DEFAULT eip++
0x03 eip += 5 c = v1[326] = 31
0x11 eip += 5 c += v1[188] = 36
0x13 eip++ c = v1[36] = 9
0x10 eip++ dword_6010A4 = c = 9
0x03 eip += 5 c = v1[326] = 31
0x11 eip += 5 c += v1[200] = 304
0x13 eip++ c = v1[304] = 90
0x17 eip++ c = c - dword_6010A4 = 81
0x18分支开始
0x18 分支一c不为零 eip = v1[207] = 352
0x15 eip += 5 dword_6010A4 = v1[353] = 336
0x0E eip++ dword_6010A4++ = 337
0x12 eip++ c = v1[337] = 87
0x0B eip++ 输出 c = W
0xC分支开始
0x0C 分支一v1[202052110]不等于0 eip = 336 v1[202052110]-- = 4
0x0E eip++ dword_6010A4++ = 338
0x12 eip++ c = v1[338] = 114
0x0B eip++ 输出 c = r
0xC分支开始
0x0C 分支一v1[202052110]不等于0 eip = 336 v1[202052110]-- = 3
0x0E eip++ dword_6010A4++ = 339
0x12 eip++ c = v1[339] = 111
0x0B eip++ 输出 c = o
0xC分支开始
0x0C 分支一v1[202052110]不等于0 eip = 336 v1[202052110]-- = 2
0x0E eip++ dword_6010A4++ = 340
0x12 eip++ c = v1[340] = 110
0x0B eip++ 输出 c = n
0xC分支开始
0x0C 分支一v1[202052110]不等于0 eip = 336 v1[202052110]-- = 1
0x0E eip++ dword_6010A4++ = 341
0x12 eip++ c = v1[341] = 103
0x0B eip++ 输出 c = g
0xC分支开始
0x0C 分支一v1[202052110]不等于0 eip = 336 v1[202052110]-- = 0
0x0E eip++ dword_6010A4++ = 342
0x12 eip++ c = v1[342] = 10
0x0B eip++ 输出 c =
0xC分支开始
0x0C 分支二v1[336]等于0 eip += 9
0x00 返回,返回值为0
注意到0x18分支,0xC是loop opcode,0x18是conditional opcode(意味着只有这里能check)
猜测是不是c=0输出,源码改这里c=0,会发现显示Right
But our goal is to find that input flag. So we need to understand how ‘c’ variable depends on input flag.
源码在0x18分支处下断,发现每次输入都有一次0x18,且要为0,回溯c,记住输入的地方为273开始到304
输入a-z1-6共32位
0xC分支开始
0x0C 分支二v1[325]等于0 eip += 9
DEFAULT eip++
0x03 eip += 5 c = v1[326] = 31
0x11 eip += 5 c += v1[188] = 36
0x13 eip++ c = v1[36] = 9
0x10 eip++ dword_6010A4 = c = 9
0x03 eip += 5 c = v1[326] = 31
0x11 eip += 5 c += v1[200] = 304
0x13 eip++ c = v1[304] = 9这次是最后一位输入
0x17 eip++ c = c - dword_6010A4 = 0
0x18分支开始 c为0
0xC分支开始 0x0C 分支一v1[83459]不等于0 eip = 332032 v1[83459]-- = 30 0x03 eip += 5 c = v1[326] = 30 0x11 eip += 5 c += v1[188] = 35 0x13 eip++ c = v1[35] = 88 0x10 eip++ dword_6010A4 = c = 88 0x03 eip += 5 c = v1[326] = 30 0x11 eip += 5 c += v1[200] = 303 0x13 eip++ c = v1[303] = 11 0x17 eip++ c = c - dword_6010A4 = -77 0x18分支开始 c为-77
多次调试后可知最后要比对的值v1[5]到v1[36] [0x10,0x18,0x43,0x14,0x15,0x47,0x40,0x17,0x10,0x1d,0x4b,0x12,0x1f,0x49,0x48,0x18,0x53,0x54,0x1,0x57,0x51,0x53,0x5,0x56,0x5a,0x8,0x58,0x5f,0xa,0xc,0x58,0x9]
追踪v1[304,找与输入有关的
分析后大致进行了如下过程
def f(i,j):
return ~(i & j)
for i in range(len(s)):#0-31
res = f(f(f(i+32,ord(s[i])),i+32),f(f(i+32,ord(s[i])),ord(s[i])))
解题脚本
def f(i,j):
return ~(i & j)
res = [0x10,0x18,0x43,0x14,0x15,0x47,0x40,0x17,0x10,0x1d,0x4b,0x12,0x1f,0x49,0x48,\
0x18,0x53,0x54,0x1,0x57,0x51,0x53,0x5,0x56,0x5a,0x8,0x58,0x5f,0xa,0xc,0x58,0x9]
import z3
s = z3.Solver()
x = [z3.BitVec("x%d"%i,8) for i in range(32)]
for i in range(len(x)):
s.add(31<x[i])
s.add(x[i]<127)
for i in range(len(res)):#0-31
s.add( f(f(f(i+32,x[i]),i+32),f(f(i+32,x[i]),x[i])) == res[i] )
flag = ''
if s.check() == z3.sat:
m = s.model()
for i in range(len(x)):
flag += chr(m[x[i]].as_long())
print(flag) #09a71bf084a93df7ce3def3ab1bd61f6
还有另一种解法是逐位爆破
import subprocess, re
def checkIdx(s):
p1 = subprocess.Popen(["ConsoleApplication2.exe"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p1.stdin.write(s)
p1.stdin.close()
res = p1.stdout.read()
print(res)
r = re.search('\[(\d+)\]', res)
return int(r.group(1))
tail = ''
head = '0000000000000000000000000000000'
idx = 1
while idx < 33:
for i in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789':
s = head + i + tail
print('try %s...' % s)
r = checkIdx(s)
if idx != r:
print("found: %s (idx = %d, r = %d)" % (s, idx, r))
idx += 1
head = head[:-1]
tail = i + tail
break
# Input Flag:09a71bf084a93df7ce3def3ab1bd61f6
# Right