乐者为王

Do one thing, and do it well.

Ant系列之用JDepend生成包依赖性度量的问题

首先获取JDepend的最新jar文件jdepend-2.9.jar,把它添加到ANT_HOME/lib目录里。

在build.xml中添加:

1
2
3
4
5
6
7
<target name="jdepend" description="Generate dependency metrics for each package">
    <jdepend format="xml" outputfile="${jdepend.dir}/jdepend-report.xml">
        <classpath>
            <pathelement path="classes" />
        </classpath>
    </jdepend>
</target>

运行任务后出现如下的错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
BUILD FAILED
build.xml:59: Missing classespath required argument
    at org.apache.tools.ant.taskdefs.optional.jdepend.JDependTask.execute(JDependTask.java:397)
    at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:275)
    at org.apache.tools.ant.Task.perform(Task.java:364)
    at org.apache.tools.ant.Target.execute(Target.java:341)
    at org.apache.tools.ant.Target.performTasks(Target.java:369)
    at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1216)
    at org.apache.tools.ant.Project.executeTarget(Project.java:1185)
    at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:40)
    at org.eclipse.ant.internal.ui.antsupport.EclipseDefaultExecutor.executeTargets(EclipseDefaultExecutor.java:32)
    at org.apache.tools.ant.Project.executeTargets(Project.java:1068)
    at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.run(InternalAntRunner.java:423)
    at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.main(InternalAntRunner.java:137)

查看JDependTask.java文件的397行:

1
2
3
4
5
6
if (getSourcespath() == null && getClassespath() == null) {
    throw new BuildException("Missing classespath required argument");
} else if (getClassespath() == null) {
    String msg = "sourcespath is deprecated in JDepend >= 2.5 - please convert to classespath";
    log(msg);
}

发现是getClassespath() == null了,跳到221行:

1
2
3
public Path getClassespath() {
    return classesPath;
}

不是写了classpath吗?怎么classesPath的值会等于null呢?结果发现在247行有:

1
2
3
4
5
6
7
public void setClasspath(Path classpath) {
    if (compileClasspath == null) {
        compileClasspath = classpath;
    } else {
        compileClasspath.append(classpath);
    }
}

原来是在build.xml中把classespath错写成classpath了,那样当然有问题啦。改过来,再运行就可以了。

Ant系列之用Checkstyle检查代码规范的问题

首先将checkstyle-all-4.1.jar文件拷贝到项目的lib目录下,并建立CheckStyle配置文件。

然后在build.xml文件中声明CheckStyle任务:

1
<taskdef resource="checkstyletask.properties" classpath="${lib.dir}/checkstyle-all-4.1.jar" />

接着是建立CheckStyle任务:

1
2
3
4
5
6
7
8
9
10
<target name="checkstyle" description="Generates a report of code convention violations.">
    <checkstyle config="${docs.dir}/checkstyle_checks.xml" failOnViolation="false">
        <fileset dir="${src.dir}" includes="**/*.java" />
        <formatter type="xml" toFile="${checkstyle.dir}/checkstyle_report.xml" />
    </checkstyle>
    <xslt basedir="${checkstyle.dir}" destdir="${checkstyle.dir}/html" extension=".html"
            style="${docs.dir}/checkstyle-frames.xsl">
        <param name="output.dir" expression="${checkstyle.dir}/html" />
    </xslt>
</target>

其中output.dir是checkstyle-frames.xsl中的参数:

1
<xsl:param name="output.dir" />

在运行任务时可能会出现如下异常:

1
javax.xml.transform.TransformerException: java.lang.RuntimeException: Unrecognized XSLTC extension 'org.apache.xalan.xslt.extensions.Redirect:write'

可以将checkstyle-frames.xsl中的:

1
xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"

改成下面的内容:

1
xmlns:redirect="http://xml.apache.org/xalan/redirect"

如果出现这样的异常:

1
Got an exception - java.lang.RuntimeException: Unable to get class information for

可以修改对抛出异常的限制:允许抛出Unchecked异常和抛出另一个已声明异常的子类。

1
2
3
4
<module name="RedundantThrows">
    <property name="allowUnchecked" value="true" />
    <property name="allowSubclasses" value="true" />
</module>

WebWork中如何实现i18n

ognl-2.6.5.jar + oscore-2.2.4.jar + webwork-2.1.7.jar + xwork-1.0.5.jar

web.xml配置文件的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app PUBLIC
    "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <display-name>i18n</display-name>

    <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>com.opensymphony.webwork.dispatcher.ServletDispatcher</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

    <taglib>
        <taglib-uri>webwork</taglib-uri>
        <taglib-location>/WEB-INF/webwork.tld</taglib-location>
    </taglib>
</web-app>

webwork.properties配置文件:

1
2
3
4
5
6
7
### This can be used to set your locale and encoding scheme
#webwork.locale=en_US    <- 这行一定要注释掉,因为WebWork首先判断webwork.locale有没有被设置,
                            如有则始终以该locale为准,忽略浏览器的locale
webwork.i18n.encoding=utf-8

### Load custom default resource bundles
webwork.custom.i18n.resources=ApplicationResources

xwork.xml配置文件的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xwork PUBLIC
    "-//OpenSymphony Group//XWork 1.0//EN"
    "http://www.opensymphony.com/xwork/xwork-1.0.dtd">

<xwork>
    <include file="webwork-default.xml" />

    <package name="default" extends="webwork-default">
        <action name="hello" class="com.codemany.i18n.action.HelloAction">
            <result name="success" type="dispatcher">/i18n.jsp</result>
        </action>
    </package>
</xwork>

HelloAction.java实现代码:

1
2
3
4
5
6
7
8
import com.opensymphony.xwork.ActionSupport;

public class HelloAction extends ActionSupport {

    public String execute() throws Exception {
        return SUCCESS;
    }
}

i18n.jsp的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ page pageEncoding="utf-8" contentType="text/html; charset=utf-8" %>

<%@ taglib uri="webwork" prefix="ww" %>

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>

<body>
    <ww:property value="getText('i18n.value')" />
</body>
</html>

ApplicationResources_zh_CN.properties的内容:

1
i18n.value=国际化

ApplicationResources_en_US.properties的内容:

1
i18n.value=internationalization

练习破解'Crackme2 - by CoSH'

Crackme2程序

  1. 用PEiD查看,程序没有加壳;
  2. 首先找到注册错误提示信息“One of the Details you entered was wrong”;
  3. 用W32Dasm反汇编,利用String Data References找到上述字符串,双击它,看到以下程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
* Reference To: MFC42.Ordinal:0F24, Ord:0F24h
|
:004014EB E85A030000              call 0040184A
:004014F0 83F805                  cmp eax, 00000005                 -> 比较Name的长度是否不大于5
:004014F3 7E41                    jle 00401536                      -> 如果是就跳到出错信息处
:004014F5 8D86E0000000            lea eax, dword ptr [esi+000000E0] -> Name字符串的地址
:004014FB 8BCF                    mov ecx, edi
:004014FD 50                      push eax

* Reference To: MFC42.Ordinal:0F22, Ord:0F22h
|
:004014FE E841030000              call 00401844
:00401503 8DBEE4000000            lea edi, dword ptr [esi+000000E4] -> Serial字符串的地址
:00401509 8BCD                    mov ecx, ebp
:0040150B 57                      push edi

从004014F0到004014F3可以知道Name必须大于5个字符,且和Serial无关。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
* Reference To: MFC42.Ordinal:0F22, Ord:0F22h
|
:0040150C E833030000              call 00401844
:00401511 8B07                    mov eax, dword ptr [edi]
:00401513 803836                  cmp byte ptr [eax], 36
:00401516 751E                    jne 00401536                      -> 跳到出错信息处
:00401518 80780132                cmp byte ptr [eax+01], 32
:0040151C 7518                    jne 00401536                      -> 跳到出错信息处
:0040151E 80780238                cmp byte ptr [eax+02], 38
:00401522 7512                    jne 00401536                      -> 跳到出错信息处
:00401524 80780337                cmp byte ptr [eax+03], 37
:00401528 750C                    jne 00401536                      -> 跳到出错信息处
:0040152A 8078042D                cmp byte ptr [eax+04], 2D
:0040152E 7506                    jne 00401536                      -> 跳到出错信息处
:00401530 80780541                cmp byte ptr [eax+05], 41
:00401534 7417                    je 0040154D                       -> 跳到正确信息处

由上面的比较代码可以得到:

1
2
3
4
5
6
36(hex) = 6
32(hex) = 2
38(hex) = 8
37(hex) = 7
2D(hex) = -
41(hex) = A

所以Serial是:6287-A

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
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004014E4(C), :004014F3(C), :00401516(C), :0040151C(C), :00401522(C)
|:00401528(C), :0040152E(C)
|
:00401536 6A00                    push 00000000

* Possible StringData Ref from Data Obj ->"ERROR"
|
:00401538 6864304000              push 00403064

* Possible StringData Ref from Data Obj ->"One of the Details you entered was wrong"
|
:0040153D 6838304000              push 00403038
:00401542 8BCE                    mov ecx, esi

* Reference To: MFC42.Ordinal:1080, Ord:1080h
|
:00401544 E8F5020000              Call 0040183E
:00401549 6A00                    push 00000000
:0040154B FFD3                    call ebx

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401534(C)
|
:0040154D 8D8EE0000000            lea ecx, dword ptr [esi+000000E0]
:00401553 8D542414                lea edx, dword ptr [esp+14]
:00401557 51                      push ecx

* Possible StringData Ref from Data Obj ->"Well done,"
|
:00401558 682C304000              push 0040302C
:0040155D 52                      push edx

* Reference To: MFC42.Ordinal:039E, Ord:039Eh
|
:0040155E E8D5020000              Call 00401838
:00401563 683C314000              push 0040313C
:00401568 50                      push eax
:00401569 8D442418                lea eax, dword ptr [esp+18]
:0040156D C744242800000000        mov [esp+28], 00000000
:00401575 50                      push eax

* Reference To: MFC42.Ordinal:039C, Ord:039Ch
|
:00401576 E8B7020000              Call 00401832
:0040157B 8B00                    mov eax, dword ptr [eax]
:0040157D 6A00                    push 00000000

* Possible StringData Ref from Data Obj ->"YOU DID IT"
|
:0040157F 6820304000              push 00403020
:00401584 50                      push eax
:00401585 8BCE                    mov ecx, esi
:00401587 C644242C01              mov [esp+2C], 01

整理后得到:Name的长度必须大于5个字符,且和Serial无关。

小技巧:如何截取W32Dasm中的汇编代码呢?很简单,在W32Dasm中点击其最左边,会有一红点,再按Shift键,点击另一处,选中所需范围,按Ctrl+C复制到剪贴版,剩下的事就是粘贴了。

去除退出中国游戏中心时弹出的IE窗口

这次破解的版本是0.8.011.4。

中国游戏中心退出时弹出的IE窗口很烦人,决定做个补丁把它去除掉。

省略无数次尝试……确定退出中国游戏中心后打开的网页地址是 http://www.chinagames.net/PlazaJump/open 。用W32Dasm反汇编iGame.exe,通过String Data References找到网址字符串,双击该字符串,看到以下程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
:0043397A 90                nop
:0043397B 90                nop
:0043397C 90                nop
:0043397D 90                nop
:0043397E 90                nop
:0043397F 90                nop
:00433980 A1A2034800        mov eax, dword ptr [004803A2]
:00433985 56                push esi
:00433986 85C0              test eax, eax
:00433988 8BF1              mov esi, ecx
:0043398A 7411              je 0043399D               -> 判断是否要打开IE窗口
:0043398C 6A00              push 00000000

* Possible StringData Ref from Data Obj ->"http://www.chinagames.net/PlazaJump/open/"
|
:0043398E 6860D84700        push 0047D860
:00433993 B9D0FC4700        mov ecx, 0047FCD0
:00433998 E8B3D2FFFF        call 00430C50             -> 打开IE窗口

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0043398A(C)
|
:0043399D 8BCE              mov ecx, esi

从上面可以知道,只要将0043398A处的je指令改成jmp指令就可以避免退出时弹出的IE窗口了。

现在使用CodeFusion来制作一个文件补丁。它有三种制作补丁的方案,这里使用Find&Replace方法。将0043397A到00433998段的数据作为查找匹配数据,并将0043398A处的74(je指令)改成eb(jmp指令),然后按照步骤生成补丁文件就可以了。

2008/3/19更新

应ls的要求制作了一个针对最新版本(0.8.011.10)的补丁,和原来的补丁放在一起提供下载。

补丁文件

C语言的扬声器发声程序

计算机的主板通常装有8253/8254定时与计数器芯片和8255可编程并行接口芯片,由它们组成的硬件电路可用来产生扬声器的声音。目前的计算机由于采用了超大规模集成电路,因而看不到这些芯片,它们均集成在外围电路芯片中。以下是扬声器的电路图:

使用程序对这些电路编程可以控制声音的长短和音调的高低。在扬声器电路中,定时器的频率决定了扬声器发音的频率,所以可通过设定定时器电路的频率来使扬声器发出不同的声音。对定时器电路进行频率设定时,首先对其命令寄存器端口地址0x43写命令来选择定时器的通道,接着向计数寄存器端口地址0x42发送频率计数值,先送低8位,后送高8位。通过这两步使定时器电路产生一系列的方波信号,此信号能否推动扬声器发音,还要看由8255产生的送数信号和门控信号是否为1,而它们也是可以编程的,端口地址为0x61。以下是完整的实现代码:

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
#include <dos.h>
#include <conio.h>

#define FREQ 1193180.0

unsigned long getticks()
{
    union REGS regs;

    regs.h.ah = 0x00;
    int86(0x1a, ®s, ®s);
    return ((unsigned long)regs.x.cx << 16) + regs.x.dx;
}

void beep(int tone)
{
    unsigned long ticks;
    unsigned char bits;
    unsigned short count;

    count = FREQ / tone;
    outportb(0x43, 0xb6);    /* 选择定时器的某个通道 */
    outportb(0x42, (unsigned char)(count & 0xff));    /* 发声频率计数值的低8位 */
    outportb(0x42, (unsigned char)(count >> 8));    /* 发声频率计数值的高8位 */

    ticks = getticks();
    while (ticks == getticks())
        ;

    /*
     * 当8255的PB端口的第0位和第1位为1时,表示允许发声;
     * 为0时,表示禁止发声。
     */
    bits = inportb(0x61);
    outportb(0x61, bits | 3);    /* 允许发声 */
    ticks += 2;
    while (getticks() < ticks)
        ;
    outportb(0x61, bits & 0xfc);    /* 禁止发声 */
}

void main()
{
    do
    {
        beep(1046.50);
    } while (!kbhit());
}

一步一步整合WebWork和Spring

把webwork-2.1.7.jar、spring-1.2.1.jar以及webwork2-srping.jar复制到WEB-INF/lib目录下。

添加以下内容到web.xml文件中:

1
2
3
4
5
6
7
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<listener>
    <listener-class>com.atlassian.xwork.ext.ResolverSetupServletContextListener</listener-class>
</listener>

applicationContext.xml中的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<beans>
    <bean id="dataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
        <property name="driverClassName" value="${driver}" />
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
    </bean>

    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation" value="/WEB-INF/sql-map-config.xml" />
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="userManager" class="com.codemany.netlink.service.impl.UserManagerImpl" singleton="true">
        <property name="sqlMapClient" ref="sqlMapClient" />
    </bean>
</beans>

配置文件sql-map-config.xml中的内容如下:

1
2
3
<sqlMapConfig>
    <sqlMap resource="com/codemany/netlink/dao/impl/User.xml" />
</sqlMapConfig>

在xwork.xml中添加以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<package name="default" extends="webwork-default"
        externalReferenceResolver="com.atlassian.xwork.ext.SpringServletContextReferenceResolver">

    <interceptors>
        <interceptor name="reference-resolver"
                class="com.opensymphony.xwork.interceptor.ExternalReferencesInterceptor" />

        <interceptor-stack name="interceptors">
            <interceptor-ref name="params" />
            <interceptor-ref name="reference-resolver" />
        </interceptor-stack>
    </interceptors>

    <default-interceptor-ref name="default-interceptor" />

    <action name="login" class="com.codemany.netlink.action.LoginAction">
        <external-ref name="userManager">userManager</external-ref>
        <result name="success" type="dispatcher">/success.jsp</result>
        <result name="error" type="dispatcher">/error.jsp</result>
    </action>
</package>

在LoginAction.java中添加代码:

1
2
3
4
private UserManager userManager = null;

public void setUserManager(UserManager userManager) {
    this.userManager = userManager;

建立UserManager.java接口文件:

1
2
3
public interface UserManager {
    public User login(String username, String password) throws UserLoginException;
}

UserManagerImpl.java实现代码:

1
2
3
4
5
6
public class UserManagerImpl extends SqlMapClientDaoSupport implements UserManager {

    public User login(String username, String password) throws UserLoginException {
        // do something
    }
}

系统I/O地址表

PC机中仅使用A[0]-A[9]地址位来表示I/O地址,即可有1024个地址。前512个供系统电路使用,后512个供扩充插槽使用。当A[9]=0时表示为系统板上的I/O地址;A[9]=1时表示为扩充插槽接口卡上的地址。

系统I/O地址使用情况:

I/O地址范围 用途 I/O地址范围 用途
0000-001F 8237A DMA控制器1 00E0-00EF
0020-003F 8259A中断控制器1 00F0 重置协处理器总线
0040-005F 8253/8254定时/计数器(PIT) 00F1 设置协处理器总线
0060-006F 8042键盘控制器(AT) 00F2-00F7
0070-007F CMOS RAM与NMI屏蔽寄存器(AT) 00F8-00FF 协处理器
0080-009F DMA页寄存器 0100-01EF
00A0-00BF 8259A中断控制器2 01F0-01F7 硬盘
00C0-00DF 8237A DMA控制器2 01F8-01FF

扩充插槽I/O地址使用情况:

I/O地址范围 用途 I/O地址范围 用途
0200-0207 游戏卡I/O 0360-036F 保留
0208-020F 0370-0377
0210-0217 扩展部件(仅XT用) 0378-037F 并行口打印机1
0218-021F 0380-038F SDLC 通信及同步通信1
0220-024F 保留 0390-039F
0250-0277 03A0-03AF 同步通信2
0278-027F 并行口打印机2 03B0-03BF MDA 单色显示器
0280-02EF 03C0-03CF 保留
02F0-02F7 保留 03D0-03DF 彩色图形适配器
02F8-02FF 串行口2 03E0-03EF
0300-031F 试验卡 03F0-03F7 软盘适配器
0320-032F 硬盘适配器 03F8-03FF 串行口1
0330-035F

POSIX Conventions for Command Line Arguments

  1. An option is a hyphen followed by a single alphanumeric character, like this: -o.
  2. An option may require an argument (which must appear immediately after the option); for example, -oargument or -o argument.
  3. Options that do not require arguments can be grouped after a hyphen, so, for example, -lst is equivalent to -t -l -s.
  4. Options can appear in any order; thus -lst is equivalent to -tls.
  5. Options can appear multiple times.
  6. Options precede other nonoption arguments: -lst nonoption.
  7. The -- argument terminates options.
  8. The - option is typically used to represent one of the standard input streams.

如何编写完美的equals方法

Java语言规范要求equals方法具有下面的特点:

  1. 自反性(reflexive):对于任意非空引用x,x.equals(x)应返回true;
  2. 对称性(symmetric):对于任意引用x、y,当且仅当y.equals(x)返回true时,x.equals(y)返回true;
  3. 传递性(transitive):对于任意引用x、y、z,如果x.equals(y)返回true,且y.equals(z)也返回true,那么x.equals(z)应返回true;
  4. 一致性(consistent):如果x和y引用的对象没有改变,那么对x.equals(y)的重复调用应返回同一结果;
  5. 对任意非空引用x,x.equals(null)应返回false。

下面是编写完美equals方法的建议:

1
2
3
4
5
6
7
8
9
10
11
// 测试this和other是否是同一个对象
if (this == other) return true;
// 测试other是否为null,如果是就返回false
if (other == null) return false;
// 测试this和other是否属于同一个类
if (getClass() != other.getClass()) {
    return false;
}
// 将other对象的类型转换为你的类所属的类型
ClassName obj = (ClassName)other;
// 最后测试需要比较的字段。使用==比较基本类型字段,使用equals比较对象类型字段

如果遵循以上的规则定义了一个类的equals方法,定义其子类的equals方法时,首先调用父类的equals方法,如果这项测试能通过,那么再比较子类的字段。

1
if (!super.equals(other)) return false;

这里有个问题是如果this和other两个对象不是同一个类,equals方法该如何工作?有些程序员可能会使用instanceof代替getClass()来做判断,但这种方法是错误的,因为它违反了规则2所要求的对称性。