题目链接:https://pan.baidu.com/s/1UdIycPyVpSjSnt88cSqnyA 提取码:5o9v
南邮的一道16位汇编的题,也算是好好复习了一遍汇编,在64位上运行需要DosBox。于是在xp里面运行
,虽然是16位的程序,但还是可以用32位ida打开,不过不能F5只能看汇编。
不推荐纯看代码,太累了,还是推荐用动态调试,od不能用但还可以用windows最基本的debug调试。
基本命令:
-t是单句跟踪相当于F7
-p是不进入函数相当于F8
-d看内存的数据
-r看寄存器的值
我们可以发现有很多的int 21h,int 21h是汇编里面非常重要的一个中断,根据ah的值的不同进行不同的作用。具体可以参考:https://www.cnblogs.com/ynwlgh/archive/2011/12/12/2285017.html
我们一步一步往下运行可以发现
seg001:000C
seg001:000C
seg001:000C sub_100AC proc near ; CODE XREF: start+5↑p
seg001:000C mov di, 3
seg001:000F call sub_10115
seg001:0012 mov di, 24h ; '$'
seg001:0015 call sub_10123
seg001:0018 mov bx, 24h ; '$'
seg001:001B add bx, 1
seg001:001E cmp byte ptr [bx], 23h ; '#'
seg001:0021 jnz short loc_100DD
seg001:0023 mov di, 26h ; '&'
seg001:0026 call sub_100F9
seg001:0029 mov si, 76h ; 'v'
seg001:002C mov dx, 23h ; '#'
seg001:002F call sub_100E4
seg001:0032 test ax, ax
seg001:0034 jnz short loc_100DD
seg001:0036 mov di, 0Fh
seg001:0039 call sub_10115
seg001:003C retn
这段是主函数
seg001:0075 sub_10115 proc near ; CODE XREF: sub_100AC+3↑p
seg001:0075 ; sub_100AC+2D↑p ...
seg001:0075 mov dx, di
seg001:0077 mov ah, 9
seg001:0079 int 21h ; DOS - PRINT STRING
seg001:0079 ; DS:DX -> string terminated by "$"
seg001:007B mov dx, 0
seg001:007E mov ah, 9
seg001:0080 int 21h ; DOS - PRINT STRING
seg001:0080 ; DS:DX -> string terminated by "$"
seg001:0082 retn
seg001:0082 sub_10115 endp
这段是显示字符串的。
seg001:0083 sub_10123 proc near ; CODE XREF: sub_100AC+9↑p
seg001:0083 mov dx, di
seg001:0085 mov ah, 0Ah
seg001:0087 int 21h ; DOS - BUFFERED KEYBOARD INPUT
seg001:0087 ; DS:DX -> buffer
seg001:0089 mov bx, di
seg001:008B add bx, 1
seg001:008E xor cx, cx
seg001:0090 mov cl, [bx]
seg001:0092 add bx, cx
seg001:0094 add bx, 1
seg001:0097 mov byte ptr [bx], 24h ; '$'
seg001:009A mov dx, 0
seg001:009D mov ah, 9
seg001:009F int 21h ; DOS - PRINT STRING
seg001:009F ; DS:DX -> string terminated by "$"
seg001:00A1 retn
这段是要求我们输入字符串的。比较坑的就是当你输入完字符串后,当前字符串第一个字符的前一个地址存的是输入字符串的长度,之前一直不知道,所以在这上面掉坑里了。
这是debug下的一段指令,内存0B3F:0025就是输入字符串的长度
-p
ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789
AX=0A0D BX=0000 CX=0142 DX=0024 SP=FFFC BP=0000 SI=0000 DI=0024
DS=0B3F ES=0B2F SS=0B3F CS=0B49 IP=0089 NV UP EI PL NZ NA PO NC
0B49:0089 8BDF MOV BX,DI
-D 0B3F:0025
0B3F:0020 23 41 42-43 44 45 46 47 48 49 4A #ABCDEFGHIJ
0B3F:0030 4B 4C 4D 4E 4F 50 51 52-53 54 55 56 57 58 59 5A KLMNOPQRSTUVWXYZ
0B3F:0040 31 32 33 34 35 36 37 38-39 0D 00 00 00 00 00 00 123456789.......
0B3F:0050 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0B3F:0060 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0B3F:0070 00 00 00 00 00 00 C9 68-8A C8 6F 07 06 0F 07 C6 .......h..o.....
0B3F:0080 EB 86 6E 6E 66 AD 4C 8D-AC EB 26 6E EB CC AE CD ..nnf.L...&n....
0B3F:0090 8C 86 AD 66 CD 8E 86 8D-AF 00 00 00 00 00 00 00 ...f............
0B3F:00A0 B8 3F 0B 8E D8 .?...
走完输入的函数之后会有一个判断
seg001:0015 call sub_10123
seg001:0018 mov bx, 24h ; '$'
seg001:001B add bx, 1
seg001:001E cmp byte ptr [bx], 23h ; '#'
seg001:0021 jnz short loc_100DD
其实也就是判断字符串的长度是否为0x23
seg001:0059 sub_100F9 proc near ; CODE XREF: sub_100AC+1A↑p
seg001:0059 mov bx, di
seg001:005B mov si, 0
seg001:005E
seg001:005E loc_100FE: ; CODE XREF: sub_100F9+19↓j
seg001:005E mov al, [bx+si]
seg001:0060 mov dl, al
seg001:0062 mov cl, 3
seg001:0064 shr al, cl
seg001:0066 mov cl, 5
seg001:0068 shl dl, cl
seg001:006A xor al, dl
seg001:006C mov [bx+si], al
seg001:006E inc si
seg001:006F cmp si, 23h ; '#'
seg001:0072 jnz short loc_100FE
seg001:0074 retn
seg001:0074 sub_100F9 endp
这个函数是把你输入的字符串单个的取出来,然后进行移位操作,右移3位和左移5位。然后把两个数异或之后再存到原来的地址里面。这里要考虑一个问题,就是因为用的是al,只有8位所以将一个数左移5位可能会超过8位,那就会只截取后面8位。所以后面再写脚本的时候需要在移位之后&0xFF就可以了。最后就剩一个比较函数了
seg001:0029 mov si, 76h ; 'v'
seg001:002C mov dx, 23h ; '#'
seg001:0044 sub_100E4 proc near ; CODE XREF: sub_100AC+23↑p
seg001:0044 xor bx, bx
seg001:0046
seg001:0046 loc_100E6: ; CODE XREF: sub_100E4+B↓j
seg001:0046 mov al, [bx+si]
seg001:0048 cmp al, [bx+di]
seg001:004A jnz short loc_100F5
seg001:004C inc bx
seg001:004D cmp bx, dx
seg001:004F jnz short loc_100E6
seg001:0051 mov ax, 0
seg001:0054 retn
这里di代表的偏移位置是经过异或之后的字符串,si偏移的位置的数据可以在ida这里找
000:0050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ……………. 000:0060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ……………. 000:0070 00 00 00 00 00 00 C9 68 8A C8 6F 07 06 0F 07 C6 ……蒱 娙 o… 000:0080 EB 86 6E 6E 66 AD 4C 8D AC EB 26 6E EB CC AE CD 雴 nnf璍 崿 ..n胩
000:0090 8C 86 AD 66 CD 8E 86 8D AF 00 00 00 00 00 00 00 寙 璮 蛶 啀 ….
也可以在debug里面找
AX=0928 BX=0000 CX=0005 DX=0023 SP=FFFC BP=0000 SI=0076 DI=0026
DS=0B3F ES=0B2F SS=0B3F CS=0B49 IP=0046 NV UP EI PL ZR NA PE NC
0B49:0046 8A00 MOV AL,[BX+SI] DS:0076=C9
-d 0B3F:0076
0B3F:0070 C9 68-8A C8 6F 07 06 0F 07 C6 .h..o.....
0B3F:0080 EB 86 6E 6E 66 AD 4C 8D-AC EB 26 6E EB CC AE CD ..nnf.L...&n....
0B3F:0090 8C 86 AD 66 CD 8E 86 8D-AF 00 00 00 00 00 00 00 ...f............
0B3F:00A0 B8 3F 0B 8E D8 E8 04 00-B4 4C CD 21 BF 03 00 E8 .?.......L.!....
0B3F:00B0 63 00 BF 24 00 E8 6B 00-BB 24 00 83 C3 01 80 3F c..$..k..$.....?
0B3F:00C0 23 75 1A BF 26 00 E8 30-00 BE 76 00 BA 23 00 E8 #u..&..0..v..#..
0B3F:00D0 12 00 85 C0 75 07 BF 0F-00 E8 39 00 C3 BF 18 00 ....u.....9.....
0B3F:00E0 E8 32 00 C3 33 DB 8A 00-3A 01 75 09 43 3B DA 75 .2..3...:.u.C;.u
0B3F:00F0 F5 B8 00 00 C3 B8 ......
最后就可以写脚本了,数据不大,可以直接爆破。
脚本:
l1='C9 68 8A C8 6F 07 06 0F 07 C6 EB 86 6E 6E 66 AD 4C 8D AC EB 26 6E EB CC AE CD 8C 86 AD 66 CD 8E 86 8D AF'
l2=l1.split(' ')
#print(l2)
flag=[]
for i in range(35):
for j in range(33,256):
a=j>>3&0xFF
b=j<<5&0xFF
c=a^b
if c==int(l2[i],16): \#把16进制转成10进制
flag.append(chr(j))
break
print(''.join(flag))
再给一个这题c代码的原型
#include <stdio.h>
#include <string.h>
int main()
{
puts("Input Flag:");
char flag[35];
int flag2[35];
scanf("%s", flag);
int exa[] = {201,104,138,200,111,7,6,15,7,198,235,134,110,110,102,173,76,141,172,235,38,110,235,204,174,205,140,134,173,102,205,142,134,141,175};
if (strlen(flag) != 0x23)
puts("Wrong Flag!");
for (int i = 0;i < strlen(flag);i++)
{
int a = flag[i] << 5 & 0xFF,b = flag[i] >> 3 & 0xFF;
flag2[i] = a ^ b;
}
for (int i = 0;i < 35;i++)
{
if (flag2[i] != exa[i])
{
puts("Wrong Flag!");
return 0;
}
}
puts("Correct!");
}