Reverse
WxyVM2
大概是要读这个程序里面的一段汇编代码,这段汇编代码非常长,连ida都无法反编译,但是非常有规律,可以通过写脚本来还原 这是初始化代码,估计是对加密的字符串进行还原的:
.text:00000000004005FE mov cs:dword_694140, 9C06AA99h
.text:0000000000400608 mov cs:dword_694144, 0B4B2B03Fh
.text:0000000000400612 mov cs:dword_694148, 0C51F73CFh
.text:000000000040061C mov cs:dword_69414C, 223520F8h
.text:0000000000400626 mov cs:dword_694150, 0C0C53B9h
.text:0000000000400630 mov cs:dword_694154, 0B59C78EAh
.text:000000000040063A mov cs:dword_694158, 0F7DE2D34h
.text:0000000000400644 mov cs:dword_69415C, 0B27EEE2Ch
类似于下面这样的
.text:000000000048BAF1 mov edx, cs:dword_694174
.text:000000000048BAF7 mov eax, cs:dword_694178
.text:000000000048BAFD xor eax, edx
.text:000000000048BAFF mov cs:dword_694178, eax
.text:000000000048B93B movzx eax, cs:byte_694116
.text:000000000048B93C add eax, 7Dh
.text:000000000048B93F mov cs:byte_694116, al
.text:000000000048B95C movzx eax, cs:byte_694100
.text:000000000048B95C xor eax, 6Ah
.text:000000000048B95F mov cs:byte_694100, al
.text:000000000041DD09 movzx eax, cs:byte_69410F
.text:000000000041DD09 sub eax, 6Eh
.text:000000000041DD0C mov cs:byte_69410F, al
还有一种 我 mov 我 自 己
.text:000000000040093E movzx eax, cs:byte_694114
.text:0000000000400945 mov cs:byte_694114, al
ida可以修改配置文件,这样就可以反编译了 byte_694100是字符串读入的起始地址 读取18h字节
.text:00000000004005CD call _puts
.text:00000000004005D2 mov esi, offset byte_694100
.text:00000000004005D7 mov edi, offset format ; "%s"
.text:00000000004005DC mov eax, 0
.text:00000000004005E1 call _ssanf
694100~694117 查看汇编代码后发现很多操作都超出了这个内存的范围,而且最后也没有对这个内存进行任何修改,那么就应该直接舍去 先将所有汇编代码dump下来,用脚本筛掉垃圾代码
def get_string(line):
l = []
s = ""
for i in range(len(line)):
if(line[i] == ' ' or line[i] == ',' or i == len(line) - 1):#如果读取到了末尾
if(len(s) != 0):
l.append(s)
s = ""
else:
s = s + line[i]
return l
i = 0
f = open("code.dump", "r")
fout = open("decode.txt", "w")
#fout.write("Below is decoded code.\n(format:) addr [opt] num\n")
while(1):
line = f.readline()
i += 1
if(line == ""):
print("正常退出")
fout.close()
break
tab = get_string(line)#获取到一个汇编指令
if(tab[1] == "mov"):#如果是垃圾指令,就直接跳过
line = f.readline()
i += 1
line = f.readline()
i += 1
line = f.readline()
i += 1
elif(tab[1] == "movzx"):
addr = tab[3]
addr = "0x" + addr[8::]#获取地址信息
addr_num = int(addr, 16)
addr_num -= 6897920 #减去起始地址 得到偏移量
addr = str(addr_num)
line = f.readline()
i += 1
tab = get_string(line)
if(tab[1] != "mov"):#只有当这个指令不是mov的时候才进行判断,因为有一种垃圾指令是自己mov自己
if(tab[1] == "add"):
opt = "+="
elif(tab[1] == "xor"):
opt = "^="
elif(tab[1] == "sub"):
opt = "-="
else:
print("something goes wrong! " + tab[1] + " at :" + str(i))
break
s = tab[3]
# s = s[:-1:]
# s = "0x" + s
if(s[-1] == "h"):
s = s[:-1]
s = "0x" + s
s = str(int(s, 16) % 128)
line = f.readline()
i += 1
fout.write(addr + " " + opt + " " + s + "\n")
else:
print("something goes wrong! " + tab[1] + " at :" + str(i))
break
f.close()
fout.close()
得到类似于如下的数据
23 += 11
3 += 31
5 += 78
23 += 109
2 -= 25
1 -= 48
11 += 123
13 ^= 38
23 ^= 89
12 += 3
11 -= 5
13 += 75
4 ^= 72
5 -= 88
4 -= 107
0 ^= 60
23 ^= 27
再写python进行逆向计算:
def get_string(line):
l = []
s = ""
for i in range(len(line)):
if(line[i] == ' ' ):#如果读取到了末尾
if(len(s) != 0):
l.append(s)
s = ""
else:
s = s + line[i]
if(len(s) != 0):
l.append(s)
return l
enc =[0xC0,0x85,0xF9,0x6C,0xE2,0x14,0xBB,0xe4,0xd,0x59,0x1c,0x23,0x88,0x6e,0x9b,0xca,0xba,0x5c,0x37,0xfff,0x48,0xd8,0x1f,0xab,0xa5]
f = open("decode.txt", "r")
all = []
i = -1
while(1):
line = f.readline()
if(line == ""):
break
i += 1
all.append(line)
while(i >= 0):
line = all[i]
tab = get_string(line)
i -= 1
if(tab[1] == "+="):
enc[int(tab[0])] -= int(tab[2])
elif(tab[1] == "-="):
enc[int(tab[0])] += int(tab[2])
else:
enc[int(tab[0])] ^= int(tab[2])
enc[int(tab[0])] %= 128
ans = ""
for c in enc:
ans = ans + chr(c)
print(ans)
HumuraVM
太丢脸了,这么简单一个vm还是做了三个多小时
概况
这道题是WxyVM的一个加强版,即使把vm的指令翻译出来了,也会发现得到的是一串鸟语,俗称类brainf**k语言
程序的大概思路是这样的,读入一串数据,然后把这段数据载入内存,随后执行保存在程序里面的一段虚拟机代码,虚拟机的各种函数已经给出,最终生成的加密结果也能找到,逆向程序。
对vm的代码进行读取
自己写的IDA中的注释:
while ( 1 )
{
result = *(unsigned __int8 *)(COUNTER + a1);
if ( !(_BYTE)result ) // 没有指令了 就退出
return result;
v2 = *(char *)(COUNTER + a1);
switch ( (unsigned int)off_1048 )
{
case 0x43u: // *j -= 2 * (*i & *a) C
*(_DWORD *)global_j -= 2 * (*(_DWORD *)global_i & *(_DWORD *)array_input);
++COUNTER;
break;
case 0x47u: // (*j)-- G
--*(_DWORD *)global_j;
++COUNTER;
break;
case 0x4Du: // *j = *i + a[0] M
*(_DWORD *)global_j = *(_DWORD *)global_i + *(_DWORD *)array_input;
++COUNTER;
break;
case 0x54u: // (*j)++ T
++*(_DWORD *)global_j;
++COUNTER;
break;
case 0x5Bu: // 如果a[0]!=0 就循环,否则直接跳出 [
if ( *(_DWORD *)array_input )
{
++COUNTER;
}
else
{
do
v3 = COUNTER++;
while ( *(_BYTE *)(v3 + a1) != 93 );
}
break;
case 0x5Du: // 如果a[0]!=0 就返回[继续循环 ]
if ( *(_DWORD *)array_input )
{
do
--COUNTER;
while ( *(_BYTE *)(COUNTER + a1) != 91 );
++COUNTER;
}
else
{
++COUNTER;
}
break;
case 0x61u: // (*i)-- a
--*(_DWORD *)global_i;
++COUNTER;
break;
case 0x68u: // a[0] += 4 h
array_input = (char *)array_input + 4;
++COUNTER;
break;
case 0x6Du: // a[0]自加 m
++*(_DWORD *)array_input;
++COUNTER;
break;
case 0x6Fu: // a[0] -= 4 o
array_input = (char *)array_input - 4;
++COUNTER;
break;
case 0x72u: // (*i)++ r
++*(_DWORD *)global_i;
++COUNTER;
break;
case 0x75u: // a[0]-- u
--*(_DWORD *)array_input;
++COUNTER;
break;
case 0x76u: // j = i v
*(_DWORD *)global_j = *(_DWORD *)global_i;
++COUNTER;
break;
case 0x7Bu:
if ( *(_DWORD *)global_j ) // *j !=0 就进入循环,否则直接跳出
{
++COUNTER;
}
else
{
do
v4 = COUNTER++;
while ( *(_BYTE *)(v4 + a1) != 125 );
}
break;
case 0x7Du:
if ( *(_DWORD *)global_j ) // 如果*j不为0就返回{处
{
do
--COUNTER;
while ( *(_BYTE *)(COUNTER + a1) != 123 );
++COUNTER;
}
else
{
++COUNTER;
}
break;
default:
continue;
}
}
清除无用虚拟机指令
首先我尝试着把无效命令先清除掉:
str = """h[ur]ovMCh{mG}hv{aG}[ur]ovaaaMCh{mG}hv{aG}[ur]
ovrrMCh{mG}hv{aG}[ur]ovrararaMCh{mG}hv{aG}[ur]ovrarar
rrMCh{mG}hv{aG}[ur]ovararaaMCh{mG}hv{aG}[ur]ovrararra
raMCh{mG}hv{aG}[ur]ovrrrarrrMCh{mG}hv{aG}[ur]ovaarrar
rMCh{mG}hv{aG}[ur]ovaaarrarMCh{mG}hv{aG}[ur]ovrrrarrM
Ch{mG}hv{aG}[ur]ovaarrraaMCh{mG}hv{aG}[ur]ovarraarMCh
{mG}hv{aG}[ur]ovrrraaarrMCh{mG}hv{aG}[ur]ovaaarrrrarr
MCh{mG}hv{aG}[ur]ovrrrraarrarrMCh{mG}hv{aG}[ur]ovrrar
raMCh{mG}hv{aG}[ur]ovaaraarMCh{mG}hv{aG}[ur]ovrrarraM
Ch{mG}hv{aG}[ur]ovaarrrarMCh{mG}hv{aG}[ur]ovrraarraMC
h{mG}hv{aG}[ur]ovrrarMCh{mG}hv{aG}[ur]ovaarrarMCh{mG}
hv{aG}[ur]ovrrraarMCh{mG}hv{aG}[ur]ovrrrraaMCh{mG}hv{
aG}[ur]ovrrarraMCh{mG}hv{aG}[ur]ovrrrrrrMCh{mG}hv{aG}
[ur]ovaaaarMCh{mG}hv{aG}[ur]ovrraaaMCh{mG}hv{aG}[ur]o
vaarraMCh{mG}hv{aG}[ur]ovrrarMCh{mG}hv{aG}[ur]ovaarra
aMCh{mG}hv{aG}[ur]ovaarraraMCh{mG}hv{aG}[ur]ovaarrara
rMCh{mG}"""
str = str.replace("\n", "")
old = ""
while(old != str):
old = str
str = str.replace("ra", "")
str = str.replace("ar", "")
str = str.replace("oh", "")
str = str.replace("ho", "")
str = str.replace("um", "")
str = str.replace("mu", "")
print("\n\n\n")
h_cnt = 0
o_cnt = 0
for c in str:
if(c == 'h'):
h_cnt += 1
if(h_cnt == 3):
h_cnt = 1
o_cnt = 0
print(" ")
elif(c == 'o'):
o_cnt += 1
print(c, end="")
可得以下字符串(VScode显示有点问题,打印到文件中就行)
h[ur]ovMCh{mG}
hv{aG}[ur]ovaaaMCh{mG}
hv{aG}[ur]ovrrMCh{mG}
hv{aG}[ur]ovMCh{mG}
hv{aG}[ur]ovrrrMCh{mG}
hv{aG}[ur]ovaaMCh{mG}
hv{aG}[ur]ovrMCh{mG}
hv{aG}[ur]ovrrrrrMCh{mG}
hv{aG}[ur]ovrMCh{mG}
hv{aG}[ur]ovaMCh{mG}
hv{aG}[ur]ovrrrrMCh{mG}
hv{aG}[ur]ovaMCh{mG}
hv{aG}[ur]ovMCh{mG}
hv{aG}[ur]ovrrMCh{mG}
hv{aG}[ur]ovrrMCh{mG}
hv{aG}[ur]ovrrrrrMCh{mG}
hv{aG}[ur]ovrrMCh{mG}
hv{aG}[ur]ovaaMCh{mG}
hv{aG}[ur]ovrrMCh{mG}
hv{aG}[ur]ovrMCh{mG}
hv{aG}[ur]ovrMCh{mG}
hv{aG}[ur]ovrrMCh{mG}
hv{aG}[ur]ovMCh{mG}
hv{aG}[ur]ovrrMCh{mG}
hv{aG}[ur]ovrrMCh{mG}
hv{aG}[ur]ovrrMCh{mG}
hv{aG}[ur]ovrrrrrrMCh{mG}
hv{aG}[ur]ovaaaMCh{mG}
hv{aG}[ur]ovaMCh{mG}
hv{aG}[ur]ovaMCh{mG}
hv{aG}[ur]ovrrMCh{mG}
hv{aG}[ur]ovaaMCh{mG}
hv{aG}[ur]ovaMCh{mG}
hv{aG}[ur]ovMCh{mG}
得到正向算法(递推公式)
//MCh{mG}hv{aG}[ur]ov
*j = *i + *a - 2 * (*i & *a)
//{mG}: *a += j j清空
while(*j):
*a++
*j--
//{aG}: i -= j j清空
while(*j):
*i--
*j--
//[ur]: i = *a a清空
while(*a)
*a--
*i++
//r:
*i++
//a:
*i--
//v:
j = i
经过研究
“v{ag}” 没有起到任何作用,
“v” 将i的值取到了j中
然后i和j一同被清空了
“ov”中的“v”也没有起到作用,因为在后面j的值会被刷新
再次精简:(去掉v{aG} )
h[ur]oMCh{mG}
h[ur]oaaaMCh{mG}
h[ur]orrMCh{mG}
h[ur]oMCh{mG}
h[ur]orrrMCh{mG}
h[ur]oaaMCh{mG}
h[ur]orMCh{mG}
h[ur]orrrrrMCh{mG}
h[ur]orMCh{mG}
h[ur]oaMCh{mG}
h[ur]orrrrMCh{mG}
h[ur]oaMCh{mG}
h[ur]oMCh{mG}
h[ur]orrMCh{mG}
h[ur]orrMCh{mG}
h[ur]orrrrrMCh{mG}
h[ur]orrMCh{mG}
h[ur]oaaMCh{mG}
h[ur]orrMCh{mG}
h[ur]orMCh{mG}
h[ur]orMCh{mG}
h[ur]orrMCh{mG}
h[ur]oMCh{mG}
h[ur]orrMCh{mG}
h[ur]orrMCh{mG}
h[ur]orrMCh{mG}
h[ur]orrrrrrMCh{mG}
h[ur]oaaaMCh{mG}
h[ur]oaMCh{mG}
h[ur]oaMCh{mG}
h[ur]orrMCh{mG}
h[ur]oaaMCh{mG}
h[ur]oaMCh{mG}
h[ur]oMCh{mG}即
h 指针加一
[ur] 将*(a+1)全部转移到 i 中
o 指针减一
ar index改变 i = *(a+1) + index
M j = (a+1) + index + a
C j -= (((a+1) + index) & a) * 2
MC: j = (a+1) + index + a - (( (a+1) + index) & a) * 2
h 指针加一
{mG} 把j的内容放到*(a+1)
递推公式:new((a+1)) = (a+1) + index + a - (( ( a+1) + index) & a) 2
(index为 一大堆rraa 最后得到的值)
解密
知道了(a+1) 后面的新值,知道了 (a+1) ,要求原来的 *(a+1)的值,只能通过枚举
enc = [27, 114, 17, 118, 8, 74, 126, 5, 55, 124, 31, 88, 104, 7,
112, 7, 49, 108, 4, 47, 4, 105, 54, 77, 127, 8, 80, 12, 109, 28, 127, 80, 29, 96]
index = []
f = open("index.txt", "r")
for i in range(34):
cnt = 0
s = f.readline()
for c in s:
if(c == 'r'):
cnt += 1
if(c == 'a'):
cnt -= 1
index.append(cnt)
ans = ""
for i in range(33, 0, -1):
for c in range(128):
if(enc[i] == c + index[i] + enc[i-1] - 2 * ((c + index[i]) & enc[i-1])):
ans = chr(c) + ans
break
print(ans)
输出:lag{D3v1L_H0mur4_f**k_y0uR_bra1N}
由于如下代码(IDA dump出来的)
__isoc99_scanf("%s", s);
for ( i = 0; ; ++i )
{
v3 = i;
if ( v3 >= strlen(s) )
break;
*((_DWORD *)array_input + i + 1LL) = s[i];
}
*(_DWORD *)array_input = s[strlen(s) - 1]; // 读取方式有点奇怪,最后一个字符放在开头的
输入的字符串并没有按照顺序存储,而是将输入的最后一个字符放到了内存开头,说明之后的操作全部都是从第二位开始的
通过最后一位和第一位的值同样可以逆推出原值,直接根据格式猜也行,flag嘛缺个"f"hhhh
flag{D3v1L_H0mur4_f**k_y0uR_bra1N}
本题用到了程序语言的化简,还有寻找规律的相关方法,遇到这样的题最好是先自己模拟一个循环节,找到规律再尝试写脚本解。因为并不是所有的虚拟机都是每步操作都可逆的23333
simple_machine
general
这道题说起来是vm,实际上和vm的挂钩并不大,就是用高级语言去实现了汇编,把变量都当做了寄存器来使用,其中还有各种压栈出栈过程,不过跟本题关系不大,也就不用过于细究了
fun1
fun1主要是通过输入的字符串,与预置的feed进行异或,得到一串加密的字符串,存在内存中
int __cdecl fun1(int str, unsigned int a2)
{
unsigned int v2; // eax
int v3; // eax
push(base_addr_804B15C);
base_addr_804B15C = esp1;
push(temp);
push(temp2);
esp1 -= 48;
*(_DWORD *)(base_addr_804B15C - 29) = 'deef'; // 0x804B13F
*(_DWORD *)(base_addr_804B15C - 25) = 'daed';
*(_DWORD *)(base_addr_804B15C - 21) = 'feeb';
*(_DWORD *)(base_addr_804B15C - 17) = 'efac';
*(_BYTE *)(base_addr_804B15C - 13) = 0;
for ( *(_DWORD *)(base_addr_804B15C - 12) = 0; ; ++*(_DWORD *)(base_addr_804B15C - 12) )// i = base_addr_804B15C - 12
{
eaxx = *(_DWORD *)(base_addr_804B15C - 12); // i
if ( eaxx >= a2 )
break; // 判断是否退出循环
//
//
//
ebxx = *(_DWORD *)(base_addr_804B15C - 12); // i
eaxx = str; // str
temp = ebxx + str; // str + i
ebxx = *(_DWORD *)(base_addr_804B15C - 12); // i
eaxx = ebxx + str; // str + i
eaxx = *(unsigned __int8 *)(ebxx + str); // 取当前字符串
temp1 = eaxx;
*(_BYTE *)(base_addr_804B15C - 41) = eaxx; // 把当前字符串保存到一个地址里面
//
//
//
temp2 = *(_DWORD *)(base_addr_804B15C - 12);
esp1 -= 12;
eaxx = base_addr_804B15C - 29; // 取feed
push(base_addr_804B15C - 29); // push(feed)
v2 = strlen(*(const char **)esp1); // v2 是 feed的长度
esp1 += 16;
len = v2;
eaxx = temp2;
ebxx = 0;
sub_80485AB(v2); // j = i % v2
// i = i / v2
eaxx = ebxx;
eaxx = *(unsigned __int8 *)(ebxx - 29 + base_addr_804B15C);// 算feed中的偏移
temp1 = eaxx;
temp1 = *(_BYTE *)(base_addr_804B15C - 41) ^ eaxx;// c ^ feed[]
*(_BYTE *)temp = temp1;
v3 = eaxx;
LOBYTE(v3) = 0;
eaxx = v3 + (unsigned __int8)temp1;
}
sub_80485A5();
esp1 = base_addr_804B15C - 8;
pop(&temp2);
pop(&temp);
return pop(&base_addr_804B15C);
}
fun2
fun2中有两个循环,看似很复杂实则简单
看到两个寄存器在之间移来移去,发现很多操作都是无效的
void __cdecl fun2(int a1, int a2, int a3)
{
push(base_addr_804B15C);
base_addr_804B15C = esp1;
esp1 -= 16;
for ( *(_DWORD *)(base_addr_804B15C - 4) = 0; // 循环三次
*(_DWORD *)(base_addr_804B15C - 4) <= 2u;
++*(_DWORD *)(base_addr_804B15C - 4) )
{
for ( *(_DWORD *)(base_addr_804B15C - 8) = 0; ; ++*(_DWORD *)(base_addr_804B15C - 8) )
{
len = a3;
ebxx = 1431655766;
eaxx = a3;
sub_80485DB(1431655766); // ebxx = (eaxx * 1431655766) >> 32
ebxx -= (unsigned int)len >> 31;
eaxx = ebxx;
if ( *(_DWORD *)(base_addr_804B15C - 8) >= (unsigned int)ebxx )
break; // 前面一段是关于判断跳出条件的
len = a3;
ebxx = 0x55555556;
eaxx = a3;
sub_80485DB(0x55555556); // ebxx = (unsigned __int64)(a1 * (signed __int64)eaxx) >> 32;
//
ebxx -= (unsigned int)len >> 31;
eaxx = ebxx; // repeated
eaxx = ebxx * *(_DWORD *)(base_addr_804B15C - 4);
ebxx = eaxx;
eaxx = *(_DWORD *)(base_addr_804B15C - 8);
eaxx += ebxx;
ebxx = eaxx;
eaxx = a2;
len = ebxx + a2; // 算偏移地址 addr = 0x804B100 + (i * 18) + j
ebxx = *(_DWORD *)(base_addr_804B15C - 8);
eaxx = 2 * ebxx; // j * 2
ebxx *= 3; // j * 3
eaxx = *(_DWORD *)(base_addr_804B15C - 4);// i
eaxx += ebxx; // i + j * 3
ebxx = eaxx; // i + j * 3
eaxx += a1;
eaxx = *(unsigned __int8 *)(a1 + ebxx); // *(input + i + j * 3)
temp1 = eaxx;
*(_BYTE *)len = eaxx; // 修改上方的那个偏移地址
}
}
sub_80485A5();
}
翻译过来是这样:
for(int i = 0; i <= 2; i++)
for(int j = 0;;j++){
addr[(i * 18) + j] = input + i + j * 3
}
验证
比较fun1和fun2中生成的字符串是否相等
经高人指点,不是比较fun1和fun2的字符串,而是先经过fun1进行处理,然后再经过fun2处理,然后直接和内存中的一串字符串进行比较
solve
feed = "feeddeadbeefcafe"
enc = [0x00,0x03,0x09,0x3A,0x05,0x0E,0x02,0x16,0x0F,0x1F,0x12,0x56,0x3B,0x0B,0x51,0x50,0x39,0x00,0x09,0x1F,0x50,0x04,0x14,0x57,0x3B,0x12,0x07,0x3C,0x1C,0x3A,0x15,0x05,0x0B,0x08,0x06,0x01,0x04,0x12,0x16,0x39,0x05,0x0B,0x50,0x57,0x09,0x12,0x0A,0x27,0x13,0x17,0x0E,0x02,0x55,0x18]
dec = [0x00,0x03,0x09,0x3A,0x05,0x0E,0x02,0x16,0x0F,0x1F,0x12,0x56,0x3B,0x0B,0x51,0x50,0x39,0x00,0x09,0x1F,0x50,0x04,0x14,0x57,0x3B,0x12,0x07,0x3C,0x1C,0x3A,0x15,0x05,0x0B,0x08,0x06,0x01,0x04,0x12,0x16,0x39,0x05,0x0B,0x50,0x57,0x09,0x12,0x0A,0x27,0x13,0x17,0x0E,0x02,0x55,0x18]
for i in range(3):
for j in range(18):
dec[i + j * 3] = enc[i * 18 + j]
ans = ""
for i in range(54):
dec[i] ^= ord(feed[i % 16])
ans = ans + chr(dec[i])
print(ans)
single
思路
经观察 发现a1是一个矩阵
sub_400833
unsigned __int64 __fastcall sub_400833(__int64 a1)
{
signed int i; // [rsp+18h] [rbp-28h]
signed int j; // [rsp+1Ch] [rbp-24h]
signed int k; // [rsp+1Ch] [rbp-24h]
char s[24]; // [rsp+20h] [rbp-20h]
unsigned __int64 v6; // [rsp+38h] [rbp-8h]
v6 = __readfsqword(0x28u);
for ( i = 0; i <= 8; ++i )
{
memset(s, 0, 0xAuLL);
for ( j = 0; j <= 8; ++j )
++s[*(unsigned __int8 *)(9 * i + j + a1)];
for ( k = 1; k <= 9; ++k )
{
if ( s[k] != 1 )
sub_4006F6();
}
}
return __readfsqword(0x28u) ^ v6;
}
将a1矩阵的每一行所指向的地址加1 最后s[1] - s[9] 全部等于1
sub_4008FE
unsigned __int64 __fastcall sub_4008FE(__int64 a1)
{
signed int i; // [rsp+18h] [rbp-28h]
signed int j; // [rsp+1Ch] [rbp-24h]
signed int k; // [rsp+1Ch] [rbp-24h]
char s[24]; // [rsp+20h] [rbp-20h]
unsigned __int64 v6; // [rsp+38h] [rbp-8h]
v6 = __readfsqword(0x28u);
for ( i = 0; i <= 8; ++i )
{
memset(s, 0, 0xAuLL);
for ( j = 0; j <= 8; ++j )
++s[*(unsigned __int8 *)(9 * j + i + a1)];
for ( k = 1; k <= 9; ++k )
{
if ( s[k] != 1 )
sub_4006F6();
}
}
return __readfsqword(0x28u) ^ v6;
}
将a1矩阵的每一列所指向的地址加1 最后s[1] - s[9] 全部等于1
其实做完这一步的时候我就反应出来这道题可能是一个数独了,并且验证最后一个函数的功能,发现确实是一个数独
sub_4009C9
unsigned __int64 __fastcall sub_4009C9(__int64 a1)
{
signed int i; // [rsp+1Ch] [rbp-34h]
int j; // [rsp+20h] [rbp-30h]
signed int l; // [rsp+20h] [rbp-30h]
int k; // [rsp+24h] [rbp-2Ch]
signed int v6; // [rsp+28h] [rbp-28h]
signed int v7; // [rsp+2Ch] [rbp-24h]
char s[24]; // [rsp+30h] [rbp-20h]
unsigned __int64 v9; // [rsp+48h] [rbp-8h]
v9 = __readfsqword(0x28u);
v6 = 3;
v7 = 3;
for ( i = 0; i <= 8; ++i )
{
memset(s, 0, 0xAuLL);
for ( j = v6 - 3; j < v6; ++j )
{
for ( k = v7 - 3; k < v7; ++k )
++s[*(unsigned __int8 *)(9 * j + k + a1)];
}
for ( l = 1; l <= 9; ++l )
{
if ( s[l] != 1 )
sub_4006F6();
}
if ( v7 == 9 )
{
v7 = 3;
v6 += 3;
}
else
{
v7 += 3;
}
}
return __readfsqword(0x28u) ^ v9;
}
将a1矩阵分为九个正方形的小块 最后s[1] - s[9] 全部等于1
计算
程序先读入一个81位长的字符串
并且通过c -= '0' 来转化为数字
如果数独的某个位置上面已经有数的话,就必须输入0
dump
addr = 0x602080
for i in range(81):
print Byte(addr+i),",",
if((i+1) % 9 == 0):
print ""
map
0 , 3 , 0 , 6 , 0 , 0 , 0 , 0 , 0 ,
6 , 0 , 0 , 0 , 3 , 2 , 4 , 9 , 0 ,
0 , 9 , 0 , 1 , 0 , 7 , 0 , 6 , 0 ,
7 , 4 , 6 , 0 , 0 , 0 , 0 , 0 , 0 ,
0 , 1 , 8 , 0 , 0 , 0 , 6 , 3 , 0 ,
0 , 0 , 0 , 0 , 0 , 0 , 1 , 4 , 7 ,
0 , 8 , 0 , 9 , 0 , 4 , 0 , 7 , 0 ,
0 , 7 , 4 , 2 , 1 , 0 , 0 , 0 , 6 ,
0 , 0 , 0 , 0 , 0 , 3 , 0 , 1 , 0 ,
solve
http://www.llang.net/sudoku/calsudoku.html 求解即可
401095728057800001802040305000321589500479002923586000105060203300008950269750804
加个flag
flag{401095728057800001802040305000321589500479002923586000105060203300008950269750804}
Comments | NOTHING