编程题
I live2 in an ordinary family, but my parents3 give me all their love4, especially my mom. She takes care of me all the time and sometimes33 I think she2 is a little overprotective. I have already grown up and want to make my own decision, so I always want to tell44 my mom11 that she can let me make my own decision. I can be44 responsible for what I do.
第一题分解质数比较简单,C++的练习题也都有,属于送分题。
第二题:
利用下面的函数将文件中的数字,字符及其他(空格或者特殊字符)统计出来。
isalpha()
isdigit()
others()
要求:代码运行时显示文件中的内容,并显示各个内容的个数。
解题
string=file.read()
shuzi=""
zifu=""
others=""
for i in range(len(string)):
if string[i].isalpha():
zifu+=string[i]
elif string[i].isdigit():
shuzi+=string[i]
else:
others+=string[i]
print shuzi
print zifu
print others
编程题A.txt
A:请利用python编写一个二进制转换器,要求以函数(def)形式编写,可以将某个单词或者flag转换为相应二进制形式输出,用时也能反转换。
例子:输入:hello
输出:0b1101000 0b1100101 0b1101100 0b1101100 0b1101111
输入:0b1101000 0b1100101 0b1101100 0b1101100 0b1101111
输出hello
a=raw_input()
for i in range(len(a)):
print bin(ord(a[i])),
bin是转化为二进制符号,其实是用str来表示的。
分解.txt
将一个正整数分解质因数。
例如:
输入30,显示结果为2*3*5;
反之,输出“该整数无质因数”。(输入的数字为用户输入)
a=input()
flag=[]
for i in range(2,a):
if a%i==0:
a=a/i
flag.append(i)
i=2
print flag
逆向题
app10.apk
看main函数里面有个check函数。进去之后就是关键代码。
public class encode
{
private static byte[] b = { 23, 22, 26, 26, 25, 25, 25, 26, 27, 28, 30, 30, 29, 30, 32, 32 };
public static boolean check(String paramString)
{
byte[] arrayOfByte1 = paramString.getBytes();
byte[] arrayOfByte2 = new byte[16];
for (int i = 0; i < 16; i++) {
arrayOfByte2[i] = ((byte)(byte)((arrayOfByte1[i] + b[i]) % 61));
}
for (i = 0; i < 16; i++) {
arrayOfByte2[i] = ((byte)(byte)(arrayOfByte2[i] * 2 - i));
}
return new String(arrayOfByte2).equals(paramString);
}
}
这里是输入字符串进行了一些操作之后和它自己相等。
b = [ 23, 22, 26, 26, 25, 25, 25, 26, 27, 28, 30, 30, 29, 30, 32, 32 ]
flag=[]
for i in range(16):
for j in range(128):
if ((j+b[i])%61)*2-i==j:
print chr(j),
输出结果为
LOHILMNMLKHILKHI
IgniteMe.exe
这是main函数的一部分
sub_402B30(&unk_446360, "Give me your flag:");
sub_4013F0(sub_403670);
sub_401440(v6, 127);
if ( strlen(v6) < 0x1E && strlen(v6) > 4 )
{
strcpy(v5, "EIS{");
for ( i = 0; i < strlen(v5); ++i )
{
if ( v6[i] != v5[i] )
{
sub_402B30(&unk_446360, "Sorry, keep trying! ");
sub_4013F0(sub_403670);
return 0;
}
}
if ( v7 == 125 )
{
if ( sub_4011C0(v6) )
sub_402B30(&unk_446360, "Congratulations! ");
else
sub_402B30(&unk_446360, "Sorry, keep trying! ");
sub_4013F0(sub_403670);
result = 0;
}
else
{
sub_402B30(&unk_446360, "Sorry, keep trying! ");
sub_4013F0(sub_403670);
result = 0;
}
}
这里可以看出来flag的格式EIS{}。sub_4011C0(v6)这个函数就是关键了。
{
size_t v2; // eax
signed int v3; // [esp+50h] [ebp-B0h]
char v4[32]; // [esp+54h] [ebp-ACh]
int v5; // [esp+74h] [ebp-8Ch]
int v6; // [esp+78h] [ebp-88h]
size_t i; // [esp+7Ch] [ebp-84h]
char v8[128]; // [esp+80h] [ebp-80h]
if ( strlen(a1) <= 4 )
return 0;
i = 4;
v6 = 0;
while ( i < strlen(a1) - 1 )
v8[v6++] = a1[i++];
v8[v6] = 0;
v5 = 0;
v3 = 0;
memset(v4, 0, 0x20u);
for ( i = 0; ; ++i )
{
v2 = strlen(v8);
if ( i >= v2 )
break;
if ( v8[i] >= 97 && v8[i] <= 122 )
{
v8[i] -= 32;
v3 = 1;
}
if ( !v3 && v8[i] >= 65 && v8[i] <= 90 )
v8[i] += 32;
v4[i] = byte_4420B0[i] ^ sub_4013C0(v8[i]);
v3 = 0;
}
return strcmp("GONDPHyGjPEKruv{{pj]X@rF", v4) == 0;
}
这里先进行了大小写转化+(-)32。再经过sub_4013C0(v8[i]这个函数操作之后,然后跟一个数组异或,之后的值要等于“GONDPHyGjPEKruv{{pj]X@rF“。
接下来就是看一下sub_4013C0(v8[i]这个函数进行了什么操作吧。
int __cdecl sub_4013C0(int a1)
{
return (a1 ^ 0x55) + 72;
}
先异或再加。
现在整理一下思路
(serial[i]^0x55+72)^byte_4420B0=GONDPHyGjPEKruv{{pj]X@rF
解出来为
(byte_4420B0^GONDPHyGjPEKruv{{pj]X@rF-72)^0x55
a=[0x0D, 0x13, 0x17, 0x11, 0x02, 0x01, 0x20, 0x1D, 0x0C, 0x02,0x19, 0x2F, 0x17, 0x2B, 0x24, 0x1F, 0x1E, 0x16, 0x09, 0x0F, 0x15, 0x27, 0x13, 0x26, 0x0A, 0x2F, 0x1E, 0x1A, 0x2D, 0x0C, 0x22, 0x04]
print len(a)
b="GONDPHyGjPEKruv{{pj]X@rF"
b=list(b)
print len(b)
for i in range(len(b)):
print chr(((a[i]^ord(b[i]))-72)^0x55),
#wadx_tdgk_aihc_ihkn_pjlm
#EIS{wadx_tdgk_aihc_ihkn_pjlm}
python里面有一点需要注意:是先加减再异或也就是注意要加括号。
ReverseMe.exe
查找字符串找到了它congratulations, your input is the flag ^_^。
进入这里
sub_401AD0();
v5 = 1177698609;
v6 = 1127429177;
v0 = 0;
v7 = 1127760948;
v8 = 1161183797;
v9 = 960705345;
v10 = 1145127746;
v11 = 17719;
v12 = 0;
do
{
v13[v0] = 0;
++v0;
}
while ( v0 < 8 );
puts("input your key:");
scanf("%s", v13);
v1 = strlen((const char *)v13);
if ( v1 <= 19 )
{
printf("too short!");
result = -1;
}
else if ( v1 > 30 )
{
printf("too long!");
result = -1;
}
else
{
if ( sub_4014A0((int)v13, (int)&v5, v1) )
printf("congratulations, your input is the flag ^_^");
else
printf("try agian");
v2 = (FILE *)((char *)iob[1] - 1);
iob[1] = v2;
if ( (signed int)v2 < 0 )
{
filbuf(iob[0]);
v2 = iob[1];
从上面这段代码里面我们能得到的信息如下:
- 输入的字符串存到了v13
- sub_4014A0(v13, &v5, v1)这个函数是验证函数。
- flag长度在20到29之内
下面看一下这个函数的内容吧
signed int __cdecl sub_4014A0(int a1, int a2, int a3)
{
unsigned int v3; // ebx
int v5; // eax
int v6; // ebx
char v7; // dl
int i; // eax
char v9; // [esp+Ah] [ebp-4Ah]
char v10; // [esp+Bh] [ebp-49h]
char v11; // [esp+Ch] [ebp-48h]
char v12; // [esp+Dh] [ebp-47h]
char v13; // [esp+Eh] [ebp-46h]
char v14; // [esp+Fh] [ebp-45h]
char v15; // [esp+10h] [ebp-44h]
char v16; // [esp+11h] [ebp-43h]
char v17; // [esp+12h] [ebp-42h]
char v18; // [esp+13h] [ebp-41h]
char v19; // [esp+14h] [ebp-40h]
char v20; // [esp+15h] [ebp-3Fh]
char v21; // [esp+16h] [ebp-3Eh]
char v22; // [esp+17h] [ebp-3Dh]
char v23; // [esp+18h] [ebp-3Ch]
char v24; // [esp+19h] [ebp-3Bh]
char v25; // [esp+1Ah] [ebp-3Ah]
char v26; // [esp+1Bh] [ebp-39h]
char v27; // [esp+1Ch] [ebp-38h]
char v28; // [esp+1Dh] [ebp-37h]
char v29; // [esp+1Eh] [ebp-36h]
char v30; // [esp+1Fh] [ebp-35h]
char v31; // [esp+20h] [ebp-34h]
char v32; // [esp+21h] [ebp-33h]
char v33; // [esp+22h] [ebp-32h]
int v34; // [esp+24h] [ebp-30h]
char v35[44]; // [esp+28h] [ebp-2Ch]
v3 = 0;
v34 = 0;
do
{
*(_DWORD *)(&v11 + v3) = 0;
v3 += 4;
}
while ( v3 < ((&v9 - &v11 + 30) & 0xFFFFFFFC) );
v9 = 15;
v10 = -121;
v11 = 98;
v12 = 20;
v13 = 1;
v14 = -58;
v15 = -16;
v16 = 33;
v17 = 48;
v18 = 17;
v19 = 80;
v20 = -48;
v21 = -126;
v22 = 35;
v23 = -82;
v24 = 35;
v25 = -18;
v26 = -87;
v27 = -76;
v28 = 82;
v29 = 120;
v30 = 87;
v31 = 12;
v32 = -122;
v33 = -117;
if ( a3 == 25 )
{
v5 = 0;
do
{
v35[v5] = __ROL1__(*(_BYTE *)(a1 + v5), 2);
++v5;
}
while ( v5 != 25 );
v6 = 0;
do
{
v35[v6] ^= sub_401460(a2, v6);
++v6;
}
while ( v6 != 25 );
v7 = 15;
for ( i = 0; v35[i] == v7; v7 = *(&v9 + i) )
{
if ( ++i == 25 )
return 1;
}
}
return 0;
}
先看一下她的三个参数,第一个是输入的字符串,第二个是v5这个数组。第三个参数是输入字符串的长度。
进行了一个v35[v5] = ROL1((_BYTE )(a1 + v5), 2);运算,这个本来不知道干嘛,不过后来用od跟一下就知道了,这个叫循环左移,跟左移是有区别的。
sub_401460(a2, v6)这个函数也是一个关键,不过注意一下就会发现,这个函数没有对serial进行操作,也就是说在OD里面完全可以得到它得出的结果,当然,完全可以还原函数,就是照抄一下。函数的内容也不多。
int __cdecl sub_401460(int a1, int a2)
{
char v2; // al
char v3; // cl
int v4; // eax
int v5; // edx
v2 = *(_BYTE *)(a1 + a2);
v3 = *(_BYTE *)(a1 + a2 + 1);
if ( (unsigned __int8)(v2 - 48) > 9u )
v2 -= 55;
v4 = v2 & 0xF;
v5 = (v3 - 55) & 0xF;
if ( (unsigned __int8)(v3 - 48) <= 9u )
v5 = v3 & 0xF;
return v5 | 16 * v4;
}
可以用python等价
a1=[0x31,0x41,0x32,0x46,0x39,0x34,0x33,0x43,0x34,0x44,0x38,0x43,0x35,0x42,0x36,0x45,0x41,0x33,0x43,0x39,0x42,0x43,0x41,0x44,0x04,0x37,0x45,0x00,0x00]
def func(a1,a2):
v2=a1[a2]
v3=a1[a2+1]
if (v2-0x30)&0xff>9:
v2=(v2-0x37)&0xff
v4 = v2 & 0xF
v5 = (v3 - 0x37) & 0xF
if (v3-0x30)&0xff<=9:
v5=v3&0xf
return v5 | 16*v4
```![ ](https://img-blog.csdn.net/20180413180230859?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MjA0NDgx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
这里call的返回值,跟几次就会发现是
<div class="se-preview-section-delimiter"></div>
key=[0x1A,0xA2,0x2F,0xF9,0x94,0x43,0x3C,0xC4,0x4D,0xD8,0x8C,0xC5,0x5B,0xB6,0x6E,0xEA,0xA3,0x3C,0xC9,0x9B,0xBC,0xCA,0xAD,0xD7,0x7E]
“`
key=[0x1A,0xA2,0x2F,0xF9,0x94,0x43,0x3C,0xC4,0x4D,0xD8,0x8C,0xC5,0x5B,0xB6,0x6E,0xEA,0xA3,0x3C,0xC9,0x9B,0xBC,0xCA,0xAD,0xD7,0x7E]
然后异或异或后的结果要等于v9这个数组。
整理一下思路:(serial rol 2)^key==v9
接下来直接爆破就好。
不过需要注意的是找了一圈好像没有找到python里面的循环左移,只有字节写了
def rol(serial):
a=serial<<2
b=serial&0xc0
b=b>>6
serial=a|b
return serial&0xff
解题脚本
key=[0x1A,0xA2,0x2F,0xF9,0x94,0x43,0x3C,0xC4,0x4D,0xD8,0x8C,0xC5,0x5B,0xB6,0x6E,0xEA,0xA3,0x3C,0xC9,0x9B,0xBC,0xCA,0xAD,0xD7,0x7E]
flag=[0x0F,0x87,0x62 ,0x14,0x01,0xC6,0xF0,0x21,0x30,0x11,0x50,0xD0,0x82,0x23,0xAE,0x23,0xEE,0xA9,0xB4,0x52,0x78,0x57,0x0C,0x86,0x8B]
for i in range(25):
for a in range(300):
if key[i]^(rol(a))==flag[i]:
print chr(a),
99.exe
先看main函数
v31 = 0;
v9 = 1;
v10 = 4;
v11 = 14;
v12 = 10;
v13 = 5;
v14 = 36;
v15 = 23;
v16 = 42;
v17 = 13;
v18 = 19;
v19 = 28;
v20 = 13;
v21 = 27;
v22 = 39;
v23 = 48;
v24 = 41;
v25 = 42;
v26 = 26;
v27 = 20;
v28 = 59;
v29 = 4;
v30 = 0;
printf("please enter flag:");
while ( 1 )
{
v6 = getch();
v32[v31] = v6;
if ( !(_BYTE)v6 || v32[v31] == 13 )
break;
if ( v32[v31] == 8 )
{
printf("\b\b");
--v31;
}
else
{
printf("%c", v32[v31++]);
}
}
v8 = 0;
for ( i = 0; i < 17; ++i )
{
if ( v32[i] != byte_415768[*(&v9 + i)] )
v8 = 1;
}
if ( v33 != 49 || v34 != 48 || v35 != 50 || v36 != 52 || v37 != 125 )
v8 = 1;
v32[v31] = 0;
printf("\r\n");
if ( v8 )
{
printf("wrong\n");
main(v3, v4, v5);
}
else
{
printf("success\n");
}
打眼一看,不知道谁是输入的字符,可以逆向推一下,决定success输出的是v8的值,决定v8的值得就是下面的代码了。
if ( v33 != '1' || v34 != '0' || v35 != '2' || v36 != '4' || v37 != '}' )
这里可以推断出最后几个字符1024}
v8 = 0;
for ( i = 0; i < 17; ++i )
{
if ( v32[i] != byte_415768[*(&v9 + i)] )
v8 = 1;
}
上面这段可以判断出就是决定程序的关键了。
byte_415768[*(&v9 + i)]这里是把v9数组作为下标来取byte_415768中的值。
贴上脚本吧
a=[0x73, 0x4B, 0x66, 0x78, 0x45, 0x65, 0x66, 0x74, 0x7D, 0x66,
0x7B, 0x67, 0x79, 0x72, 0x59, 0x67, 0x74, 0x68, 0x74, 0x79,
0x68, 0x69, 0x66, 0x73, 0x6A, 0x65, 0x69, 0x35, 0x33, 0x55,
0x55, 0x72, 0x72, 0x72, 0x5F, 0x74, 0x32, 0x63, 0x64, 0x73,
0x65, 0x66, 0x36, 0x36, 0x32, 0x34, 0x36, 0x30, 0x38, 0x37,0x31, 0x33, 0x38, 0x5C, 0x30, 0x30, 0x38, 0x37, 0x31, 0x33, 0x38]
b=[1,4,14,10,5,36,23,42,13,19,28,13,27,39,48,41,42,26,20,59,4,0]
for i in range(17):
print chr(a[b[i]]),
#KET{e2s6ry3r5s8f61024}
总结,pwn还是没做出来,扎心。发现了一些被忽略的小错误,总是卡在那些错误上。还需要更多的实践吧。