4
Posted on 上午2:33:00 by Fan Zhang and filed under

  嗯...为了更好地学习与工作,我经过几天的酝酿,出手买了一台22宽的显示器:BenQ G2200W。

  本来想买 AOC 2217V 的,因为它性价比最高,也就是最便宜,但是实际看了几眼,感觉的确是一分价钱一分货。做工略显粗糙,漏光处理不好。

  最后买的这台 BenQ 其实也不怎么好,各项性能也一般,但是感觉稍微舒服一些。现在有 16:9 的型号,比如 BenQ T2200HD,分辨率可以达到 1920x1080,但是我感觉点距太小,平常编程或者文字处理会很不舒服。

  我的破旧的 9550 显卡还是能支持双显示器的。感觉不错,一边看电影,一边写文档,很自在。

4
Posted on 下午7:51:00 by Fan Zhang and filed under

  在 blogger 模板中有两个变量:data:newerPageTitle 和 data:olderPageTitle,一般显示在页面下方,是供翻页用的。

  其中 data:newerPageTitle 在中文环境下的内容为"较新的帖子",这是对的。但是 data:olderPageTitle 目前内容却为"帖子",应该翻译为"较旧的帖子"。

  我今天提交了这个问题,希望能尽快修正。

1
Posted on 下午1:37:00 by Fan Zhang and filed under , ,

  要编写一个类似于 Windows 任务管理器的软件,首先遇到的问题是如何实现枚举所有进程。暂且不考虑进入核心态去查隐藏进程一类的,下面提供几种方法。请注意每种方法的使用局限,比如使用这些 API 所需要的操作系统是什么(尤其是是否能在 Windows Mobile 下使用)。

  本文参考用户态枚举进程的几种方法,原文对于每一种方法都给出了完整的代码,被我照抄下来。还有一篇:如何用 Win32 APIs 枚举应用程序窗口和进程。基于我现学现卖的本质,对我演绎的部分请抱着批判的眼光来看,另外代码也没有充分验证。

使用 ToolHelp API

   ToolHelp API 的功能就是为了获取当前运行程序的信息,从而编写适合自己需要的工具(@MSDN)。它支持的平台比较广泛,可以在 Windows CE 下使用。在 Windows Mobile SDK 的 Samples 里面有一个 PViewCE 的样例程序,就是用这个来查看进程和线程信息的。

  使用方法就是先用 CreateToolhelp32Snapshot 将当前系统的进程、线程、DLL、堆的信息保存到一个缓冲区,这就是一个系统快照。如果你只是对进程信息感兴趣,那么只要包含 TH32CS_SNAPPROCESS 标志即可。

  然后调用一次 Process32First 函数,从快照中获取第一个进程,然后重复调用 Process32Next,直到函数返回 FALSE 为止。这样将遍历快照中进程列表。这两个函数都带两个参数,它们分别是快照句柄和一个 PROCESSENTRY32 结构。调用完 Process32First 或 Process32Next 之后,PROCESSENTRY32 中将包含系统中某个进程的关键信息。其中进程 ID 就存储在此结构的 th32ProcessID。此 ID 传给 OpenProcess API 可以获得该进程的句柄。对应的可执行文件名及其存放路径存放在 szExeFile 结构成员中。在该结构中还可以找到其它一些有用的信息。

  需要注意的是:在调用 Process32First() 之前,要将 PROCESSENTRY32 结构的 dwSize 成员设置成 sizeof(PROCESSENTRY32)。 然后再用 Process32First、Process32Next 来枚举进程。使用结束后要调用 CloseHandle 来释放保存的系统快照。

  以下为参考代码:

#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

void useToolHelp()
{
    HANDLE procSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if(procSnap == INVALID_HANDLE_VALUE)
    {
        printf("CreateToolhelp32Snapshot failed, %d ",GetLastError());
        return;
    }
    //
    PROCESSENTRY32 procEntry = { 0 };
    procEntry.dwSize = sizeof(PROCESSENTRY32);
    BOOL bRet = Process32First(procSnap,&procEntry);
    while(bRet)
    {
        wprintf(L"PID: %d (%s) ", procEntry.th32ProcessID, procEntry.szExeFile);                                                  
        bRet = Process32Next(procSnap, &procEntry);
    }
    CloseHandle(procSnap);
}

void main()
{
    useToolHelp();
    getchar();
}

使用 Processing Status API

  在 Windows SDK 中可以找到 PSAPI,通过 PSAPI 可以获取进程列表和设备驱动列表。通过 EnumProcesses、EnumProcessModules、GetModuleFileNameEx 和 GetModuleBaseName 来实现。

  首先使用 EnumProcesses 来枚举所有进程,它有三个参数:DWORD 类型的数组指针 lpidProcess;该数组的大小尺寸 cb;以及一个指向 DWORD 的指针 cbNeeded,它接收返回数据的长度。DWORD 数组用于保存当前运行的进程 IDs。cbNeeded 返回数组所用的内存大小。下面算式可以得出返回了多少进程:nReturned = cbNeeded / sizeof(DWORD)。
  注意:虽然文档将返回的 DWORD 命名为“cbNeeded”,实际上是没有办法知道到底要传多大的数组的。EnumProcesses 根本不会在 cbNeeded 中返回一个大于 cb 参数传递的数组值。所以,唯一确保 EnumProcesses 函数成功的方法是分配一个 DWORD 数组,并且,如果返回的 cbNeeded 等于 cb,分配一个较大的数组,并不停地尝试直到 cbNeeded 小于 cb 。

  下面是参考代码:

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include "psapi.h"
#pragma comment(lib,"psapi.lib")


void PrintProcessNameAndID(DWORD processID)
{
    TCHAR szProcessName[MAX_PATH] = _T("<unknown>");
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS/* | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ*/,FALSE,processID);    
    //Process name.
    if(NULL!=hProcess)
    {
        HMODULE hMod;
        DWORD cbNeeded;
        if(EnumProcessModules(hProcess,&hMod,sizeof(hMod), &cbNeeded))        
        {
            GetModuleBaseName(hProcess,hMod,szProcessName,sizeof(szProcessName)/sizeof(TCHAR));                       
        }
    }
    wprintf(_T("PID: %d (%s) "),processID,szProcessName);
    CloseHandle(hProcess);
}

void main( )
{
    DWORD aProcesses[1024], cbNeeded, cProcesses;
    unsigned int i;
    if(!EnumProcesses(aProcesses,sizeof(aProcesses),&cbNeeded))
        return;
    cProcesses = cbNeeded/sizeof(DWORD);
    for(i=0;i<cProcesses;i++)
        PrintProcessNameAndID(aProcesses[i]);
    getchar();
}

  注意到,此方法由于需要进行 OpenProcess 操作,所以需要一定的权限,当权限不够时,有些进程将不能被打开。下面给出提升权限的相关代码:
void RaisePrivilege()
{
    HANDLE hToken;
    TOKEN_PRIVILEGES tp;
    tp.PrivilegeCount = 1;
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    if(OpenProcessToken(GetCurrentProcess(),TOKEN_ALL_ACCESS,&hToken))
    {
        if(LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tp.Privileges[0].Luid))
        {
            AdjustTokenPrivileges(hToken,FALSE,&tp,NULL,NULL,0);
        }
    }
    if(hToken)
        CloseHandle(hToken);
}



使用 Native API

  在 使用Native API 探测本机系统信息 中我介绍了 Native API 中的 NtQuerySystemInformation(ZwQuerySystemInformation)。当设置查询的信息类型为  SystemProcessesAndThreadsInformation 时(第5号功能),可以用来枚举所有进程和线程。

  提醒:这个函数属于 Undocumented API,并且不建议使用,因为不同系统的结构和常量有所不同。下面列出 Windows XP 下可以用的相关结构和常量:

typedef DWORD (WINAPI *ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);

typedef struct _SYSTEM_PROCESS_INFORMATION
{
    DWORD NextEntryDelta;
    DWORD ThreadCount;
    DWORD Reserved1[6];
    FILETIME ftCreateTime;
    FILETIME ftUserTime;
    FILETIME ftKernelTime;
    UNICODE_STRING ProcessName;
    DWORD BasePriority;
    DWORD ProcessId;
    DWORD InheritedFromProcessId;
    DWORD HandleCount;
    DWORD Reserved2[2];
    DWORD VmCounters;
    DWORD dCommitCharge;
    PVOID ThreadInfos[1];
}SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

#define SystemProcessesAndThreadsInformation 5

  然后动态加载 ntdll.dll,获得函数的地址。便可以进行进程的枚举相关代码如下:
#include <windows.h>
#include <ntsecapi.h>
#include <stdio.h>

typedef DWORD (WINAPI *ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);

typedef struct _SYSTEM_PROCESS_INFORMATION
{
    DWORD NextEntryDelta;
    DWORD ThreadCount;
    DWORD Reserved1[6];
    FILETIME ftCreateTime;
    FILETIME ftUserTime;
    FILETIME ftKernelTime;
    UNICODE_STRING ProcessName;
    DWORD BasePriority;
    DWORD ProcessId;
    DWORD InheritedFromProcessId;
    DWORD HandleCount;
    DWORD Reserved2[2];
    DWORD VmCounters;
    DWORD dCommitCharge;
    PVOID ThreadInfos[1];
}SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

#define SystemProcessesAndThreadsInformation 5

void main()
{
    HMODULE hNtDll = GetModuleHandle(L"ntdll.dll");
    if(!hNtDll)
        return;
    ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll,"ZwQuerySystemInformation");
    ULONG cbBuffer = 0x10000;
    LPVOID pBuffer = NULL;
    pBuffer = malloc(cbBuffer);
    if(pBuffer == NULL)
        return;
    ZwQuerySystemInformation(SystemProcessesAndThreadsInformation,pBuffer,cbBuffer,NULL);
    PSYSTEM_PROCESS_INFORMATION pInfo = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
    for(;;)
    {
        wprintf(L"PID: %d (%ls) ",pInfo->ProcessId,pInfo->ProcessName.Buffer);
        if(pInfo->NextEntryDelta == 0)
            break;
        pInfo = (PSYSTEM_PROCESS_INFORMATION)(((PUCHAR)pInfo) + pInfo->NextEntryDelta);
    }
    free(pBuffer);
    getchar();
}
  
  对这个方法有问题的,可以参考我之前的那篇介绍 Native API 的文章。

  同样使用 ZwQuerySystemInformation 函数,查询类型如果设置为 SystemHandleInformation(第16号功能)也可以达到目的。它能获取系统中所有句柄,再加上进程 ID 的判断就可以枚举所有进程了。
#include <windows.h>
#include <ntsecapi.h>
#include <ntstatus.h>
#include <stdio.h>

typedef NTSTATUS (WINAPI *ZWQUERYSYSTEMINFORMATION)(DWORD, PVOID, DWORD, PDWORD);

typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG ProcessId;
    UCHAR ObjectTypeNumber;
    UCHAR Flags;
    USHORT Handle;
    PVOID Object;
    ACCESS_MASK GrantedAccess;
}SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

typedef struct _SYSTEM_HANDLE_INFORMATION_EX
{
    ULONG NumberOfHandles;
    SYSTEM_HANDLE_INFORMATION Information[1];
}SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;

#define SystemHandleInformation 0x10   //16

void main()
{
    HMODULE hNtDll = LoadLibrary(L"ntdll.dll");
    if(!hNtDll)
        return;
    ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtDll,"ZwQuerySystemInformation");
    ULONG cbBuffer = 0x4000;
    LPVOID pBuffer = NULL;
    NTSTATUS s;
    do
    {
        pBuffer = malloc(cbBuffer);
        if(pBuffer == NULL)
            return;
        memset(pBuffer,0,cbBuffer);
        s = ZwQuerySystemInformation(SystemHandleInformation,pBuffer,cbBuffer,NULL);
        if(s == STATUS_INFO_LENGTH_MISMATCH)
        {
            free(pBuffer);
            cbBuffer = cbBuffer * 2;
        }
    }while(s == STATUS_INFO_LENGTH_MISMATCH);

    PSYSTEM_HANDLE_INFORMATION_EX pInfo = (PSYSTEM_HANDLE_INFORMATION_EX)pBuffer;
    ULONG OldPID = 0;
    for(DWORD i = 0;i<pInfo->NumberOfHandles;i++)
    {
        if(OldPID != pInfo->Information[i].ProcessId)
        {
            OldPID = pInfo->Information[i].ProcessId;
            wprintf(L"PID: %d ",OldPID);
        }
    }
    free(pBuffer);
    FreeLibrary(hNtDll);
    getchar();
}

  原文中提到,在进行进程“隐藏”工作的时候,此处的句柄是一件容易被忽略的地方,因此需要注意隐藏由程序打开的相关句柄。由于系统中句柄数量经常变换,所以没有什么必要修改其中的 NumberOfHandles 域,因为如果修改此处的值,则需要不停对句柄的变化进行维护,开销比较大。在用户态下的进程枚举已经变得不可靠,因为一个内核级的 Rootkit 很容易就能够更改这些函数的返回结果。所以进程的可靠枚举应在内核态中实现,可以通过编写驱动来实现。

有关16位程序


  根据参考的第二篇文章:在 Windows 95,Windows 98 和 Windows ME 中,ToolHelp32 对待16位程序一视同仁,它们与 Win32 程序一样有自己的进程 IDs。但是在 Windows NT,Windows 2000 或 Windows XP 中情况并不是这样。在这些操作系统中,16位程序运行在所谓的 VDM 当中(也就是DOS机)。
  为了在 Windows NT,Windows 2000 和 Windows XP 中枚举16位程序,必须使用一个名为 VDMEnumTaskWOWEx 的函数。它的声明包含在 Windows  SDK 中的 VDMDBG.h 中,并且需要在项目中链接 VDMDBG.lib 文件。
  微软的网上帮助里面有一篇介绍的文章:如何在 Windows NT、 Windows 2000 和 Windows XP 上使用 VDMDBG 函数
0
Posted on 下午5:15:00 by Fan Zhang and filed under ,

  锁定屏幕的快捷方式是:Win + L,但是某些键盘没有 Windows 键,那如何方便的锁屏幕呢。

  一种方法是,打开开始菜单,选择注销,再选择切换用户。

  另外一种是,可以在运行中输入下面的命令,也可以建立一个批处理:

rundll32.exe user32.dll,LockWorkStation

0
Posted on 下午2:03:00 by Fan Zhang and filed under

  使用 GetText 来实现国际化是要依赖相应的语言文件的。其中 po(或者 pot)文件是原始的翻译文档。而 mo 文件是编译生成的二进制文件。在发布软件的时候,只要将 mo 文件存放在正确的位置,软件根据不同的 locale 设定寻找不同语言的 mo 文件,这样就能正确显示语言了。

  对于普通的软件使用者,如果对软件的翻译不满意,也可以手动修改翻译。这就需要反编译 mo 文件成 po 文件,修改 po 文件,再编译成 mo 文件。

  首先下载 GetText for Windows(还需要下载一个 libiconv,GetText 依赖 libiconv )。反编译 mo 文件成 po 文件:

msgunfmt.exe d:\test.mo -o d:\test.po

编译 po 文件为 mo 文件:

msgfmt.exe -o d:\test.mo d:\test.po

  po 文件用一般的文件编辑器就可以编辑,但是因为编码多为 unicode,所以记事本可能不行。还可以使用开源跨平台的 po 编辑器:Poedit

0
Posted on 下午1:47:00 by Fan Zhang and filed under

  安装完 SVN 后如果在命令行运行时出现乱码,则是因为中文编码没有正确配置。在 Windows 系统中,可以设置环境变量:

LANG = zh_CN.UTF8
APR_ICONV_PATH = svn安装目录\iconv
SVN_EDITOR = notepad.exe

  如果喜欢英文,也可以把 LANG 设置成 EN_US。至于 iconv 目录存放的是各种编码文件。

  还有一个问题,在使用 TortoiseSVN 的时候,可能会遇到这样的错误信息:"璁よ瘉澶辫触",这个明显是编码错误,转换一下就知道是"认证失败"。我 TSVN 的语言包看了下,这里翻译没有错误,看来是 TSVN 程序的一个 bug。

0
Posted on 下午8:07:00 by Fan Zhang and filed under

  今天,我在笔记本上的 Visual Studio 2008 TS 上编译一个正确的解决方案,发现在生成以后会自动关闭,但是项目已经正确生成。或者虽然不自动关闭,但是手动关闭时会报错。

  为了解决这个问题,我甚至还重装了一遍系统。最后终于确定,原来是和安全卫士360冲突...

  有些奇怪的是,我使用的另外几台机器安装的程序都一样,也是 VS2008 + 360,但都没有出现这个问题。

  顺便说下,Visual Studio 在第一次运行的时候可以看到它在进行一下初始化的配置。如何恢复默认设置呢?只要在 Visual Studio 的安装目录下找到 devenv.exe 文件,然后在命令行中输入:

devenv /ResetSettings

就可以恢复 IDE 的默认设置了。

0
Posted on 上午12:23:00 by Fan Zhang and filed under ,

  一般我们实现系统功能都是通过调用微软提供的公用 Win32 API 函数来实现。但今天我们采用的方法是通过本机系统服务(Native API)来探测本机系统信息。

  Native API 是 Windows 用户模式中为上层 Win32 API 提供接口的本机系统服务。微软没有为我们提供关于本机系统服务的文档 (Undocumented),也就是不会为对它的使用提供任何的保证,所以不提倡使用 Native API 来开发软件。

  不过由于通过 Native API 可以实现很多功能,所以相关的参考也可以找到很多。 比如 Gary Nebbett, Windows NT/2000 Native API Reference. New Riders Publishing, 2000. 是一本详细的参考书(@Amazon ),内容可能不是最新的,但非常有价值。 而 The Undocumented Functions 是一个内容充足的在线参考手册。

  为了探测系统信息,我们要使用一个函数:NtQuerySystemInformation(与ZwQuerySystemInformation相同,只是入口不同),这是一个属于 ntdll.dll 的函数。

  在 MSDN 上对这个函数定义如下:

NTSTATUS WINAPI NtQuerySystemInformation(
  __in       SYSTEM_INFORMATION_CLASS SystemInformationClass,
  __inout    PVOID SystemInformation,
  __in       ULONG SystemInformationLength,
  __out_opt  PULONG ReturnLength
);

  难点在于各个数据结构是如何定义的。事实上,在不同版本的 Windows 中,这些数据结构的定义是有区别的,这也是微软不建议使用这些 API 的一个原因。

  通过我上面给出的资料倒是可以查到这些数据是如何定义的。比如 SYSTEM_INFORMATION_CLASS 是一个集合,如果我想要查看有关系统进程的信息,则要取 5 即 SystemProcessInformation。这个在 winternl.h 也有定义,但是这个头文件中的定义比较保守。更多的信息还需要自己 Google 才行。(比如上文提到的 The Undocumented Functions 中的 SYSTEM_PROCESS_INFORMATION

  对这个函数更详细的说明可以参见:NtQuerySystemInformation(ZwQuerySystemInformation)函数说明,这篇文章也有很全的 SYSTEM_INFORMATION_CLASS 是如何定义的。以及我最早参考的NtQuerySystemInformation,也讲了大部分数据结构如何定义。

  至于如果调用这个函数在分析完数据结构后就比较简单了。下面是一个简单的示例,里面还有很多没有说明的类型,不影响理解,进一步查看相关资料就能清楚了。

// 定义类型 
typedef NTSTATUS (WINAPI *PROCNTQSI)(UINT,PVOID,ULONG,PULONG); 
PROCNTQSI NtQuerySystemInformation;

// 调用 
NtQuerySystemInformation = (PROCNTQSI)GetProcAddress( 
GetModuleHandle("ntdll"), 
"NtQuerySystemInformation" 
);

  网上有很多例子可以参考,第一个是获取每个进程和线程:Processes and Threads Sample,讲解详细(网页上有一个小笔误),提供代码下载,用到了 Windows NT DDK,但是如果用上面我说的方法调用的话就不需要了 DDK 了。研究明白了这个,要点就全掌握了。

  还有一个是 Windows NT/2000系统中如何获取系统的启动时间,也有代码下载。

  上面提到的 GetProcAddress,GetModuleHandle,要涉及到动态链接库的使用。针对性的,可以参考:3个脱壳相关的重要函数介绍。简单地说就是:

  • 如果文件已经被映射进了调用进程的地址空间,则函数 GetModuleHandle 返回指定模块的句柄。注意,GetModuleHandle 是在不增加引用次数的情况下返回已映射模块的句柄。
  • LoadLibrary 把模块映射进调用进程的地址空间内。映射进地址空间后,如果必要,则增加模块的引用次数。最后用 FreeLibrary 把它从进程地址空间释放掉。
  • 使用 GetProcAddress 函数返回指定的输出动态链接库内的函数地址。
0
Posted on 下午3:58:00 by Fan Zhang and filed under

  最近遇到一个 Office PowerPoint 2007 的 bug,因为某些原因,一般是安装、删除输入法,导致在 PowerPoint 中无法输入中文,但是英文输入没有问题,也可以复制粘贴中文。

  网上给出的解决方案是开启高级文字服务:控制面板-区域与语言-语言-详细-高级-去掉"关闭高级文字服务"的勾。

  但是我用了也没有效果。最后重新安装了 Office 中的微软拼音输入法后问题解决。

1
Posted on 下午3:38:00 by Fan Zhang and filed under

  前面讲了用 Python 模拟浏览器进行登陆、抓取页面然后分析等操作。有的时候,我们还需要把筛选出的页面显示出来,最简单的方法自然是调用系统默认的浏览器。

  直接使用标准库中的 webbrowser 模块,看下面的例子:

import webbrowser 
webbrowser.open_new_tab('www.163.com')   
2
Posted on 下午7:01:00 by Fan Zhang and filed under

  之前说过使用 Python 登陆网站,我们已经可以获取任意一个网址的数据了。但是,在实际应用中,代理服务器往往是少不了的。

  在 urllib2 模块中,每一个 opener 可以用多个 handler 来增强功能,在前一篇,我们使用的是 Cookie 的支持,我们只要在这里再加上 proxy 支持就可以了。

#!/usr/bin/env python
# -*- coding: GB2312 -*-
# 

from urllib import urlencode
import cookielib, urllib2

# 准备cookie
cj = cookielib.LWPCookieJar()
cookie_support = urllib2.HTTPCookieProcessor(cj)

# 设置代理服务器
proxy_info = {  
        'host' : '127.0.0.1' ,
        'port' : 8118
}
proxy_support = urllib2 . ProxyHandler ( { 'http' : \
        'http://%(host)s:%(port)d' % proxy_info } ) 

# 构造opener
opener = urllib2.build_opener(cookie_support, proxy_support)
urllib2.install_opener(opener)

# 打开网页
page = urllib2.urlopen("http://www.163.com")
print page.read(1000)
page.close()
3
Posted on 下午7:17:00 by Fan Zhang and filed under

  据说,古今中外,不少伟大的雕塑和建筑,在问世之初都饱受非议,比如埃菲尔铁塔最初被认为是极其丑陋的庞然大物,现在已是法国的象征和巴黎第一地标。

  所以,对于我这个不懂艺术的人来说,看到一个狗屁不是的雕塑时,也不敢轻易发表意见。万一哪天这坨垃圾变成国宝了,我岂不是显得很不识货?

  因此,我绝对不说下面这两个立在北京大学光华管理学院新楼门前的两个玩意儿是垃圾。

guanghua_laozi.jpg

作者:田世信,名称:《刚柔之道--老子像》

guanghua_zhanli.jpg

作者:申红飙,名称:《蒙古人--站》

  不看作者的解释(见北大校园裸体雕塑与老子雕像相对),我是真不知道原来那个吐舌头的俏皮老头(或者说像个吊死鬼),是"源自孔子向老子请教何为刚柔之道,老子吐舌露牙、以唇齿比拟刚柔",还有那个光屁股遛鸟男是在展示"知识就是力量"。

  我不敢妄谈艺术,作者对这两个雕塑给出了合理的解释,为什么还有这么多人觉得不能接受呢?我想,这莫非是作者的功夫不到,不能表达出自己的思想?毕竟创作意图谁都会扯,但作品能否完美表现出来,就是艺术家该做的事情了。

17
Posted on 下午3:10:00 by Fan Zhang and filed under ,

  前几天,我的 Firefox 突然无法保存 Cookie 了,问题是:每次退出 Firefox 再进入,以前论坛的登陆信息都不存在了,需要重新登陆。

  我检查了各项设置(选项-隐私-Cookie),发现没有问题。我就没有理会,直到今天才得到解决方法:

  关闭 Firefox,把 Profiles 目录下的 cookies.sqlite 删除即可。

  分析原因,可能是某次非正常关闭,导致保存 Cookies 的数据库出错了。删除这个数据库,再次开启 Firefox 会重新建立一个,就正常了。