CRC原理

矛与盾的较量(2)——CRC原理篇

 

下载本节例子程序 (4.29 KB)


(特别感谢汇编高手 dREAMtHEATER 对我的代码作出了相当好的优化!请参观他的主页

上一节我们介绍了花指令,不过花指令毕竟是一种很简单的东西,基本上入了门的Cracker都可以对付得了。所以,我们很有必要给自己的软件加上更好的保护。CRC校验就是其中的一种不错的方法。

CRC是什么东西呢?其实我们大家都不应该会对它陌生,回忆一下?你用过RAR和ZIP等压缩软件吗?它们是不是常常会给你一个恼人的“CRC校验错误”信息呢?我想你应该明白了吧,CRC就是块数据的计算值,它的全称是“Cyclic Redundancy Check”,中文名是“循环冗余码”,“CRC校验”就是“循环冗余校验”。(哇,真拗口,希望大家不要当我是唐僧,呵呵。^_^)

CRC有什么用呢?它的应用范围很广泛,最常见的就是在网络传输中进行信息的校对。其实我们大可以把它应用到软件保护中去,因为它的计算是非常非常非常严格的。严格到什么程度呢?你的程序只要被改动了一个字节(甚至只是大小写的改动),它的值就会跟原来的不同。Hoho,是不是很厉害呢?所以只要给你的“原”程序计算好CRC值,储存在某个地方,然后在程序中随机地再对文件进行CRC校验,接着跟第一次生成并保存好的CRC值进行比较,如果相等的话就说明你的程序没有被修改/破解过,如果不等的话,那么很可能你的程序遭到了病毒的感染,或者被Cracker用16进制工具暴力破解过了。

废话说完了,我们先来看看CRC的原理。
(由于CRC实现起来有一定的难度,所以具体怎样用它来保护文件,留待下一节再讲。)

首先看两个式子:
式一:9 / 3 = 3          (余数 = 0)
式二:(9 + 2 ) / 3 = 3   (余数 = 2)

在小学里我们就知道,除法运算就是将被减数重复地减去除数X次,然后留下余数。
所以上面的两个式子可以用二进制计算为:(什么?你不会二进制计算?我倒~~~)

式一:
1001        --> 9
0011    -   --> 3
---------
0110        --> 6
0011    -   --> 3
---------
0011        --> 3
0011    -   --> 3
---------
0000        --> 0,余数
一共减了3次,所以商是3,而最后一次减出来的结果是0,所以余数为0

式二:
1011        --> 11
0011    -   --> 3
---------
1000        --> 8
0011    -   --> 3
---------
0101        --> 5
0011    -   --> 3
---------
0010        --> 2,余数
一共减了3次,所以商是3,而最后一次减出来的结果是2,所以余数为2

看明白了吧?很好,let’s go on!

二进制减法运算的规则是,如果遇到0-1的情况,那么要从高位借1,就变成了(10+0)-1=1
CRC运算有什么不同呢?让我们看下面的例子:

这次用式子30 / 9,不过请读者注意最后的余数:

11110        --> 30
1001    -    --> 9
---------
 1100        --> 12    (很奇怪吧?为什么不是21呢?)
 1001   -    --> 9
 --------
  101        --> 5,余数 --> the CRC!

这个式子的计算过程是不是很奇怪呢?它不是直接减的,而是用XOR的方式来运算(程序员应该都很熟悉XOR吧),最后得到一个余数。

对啦,这个就是CRC的运算方法,明白了吗?CRC的本质是进行XOR运算,运算的过程我们不用管它,因为运算过程对最后的结果没有意义;我们真正感兴趣的只是最终得到的余数,这个余数就是CRC值。

进行一个CRC运算我们需要选择一个除数,这个除数我们叫它为“poly”,宽度W就是最高位的位置,所以我刚才举的例子中的除数9,这个poly 1001的W是3,而不是4,注意最高位总是1。(别问为什么,这个是规定)

如果我们想计算一个位串的CRC码,我们想确定每一个位都被处理过,因此,我们要在目标位串后面加上W个0位。现在让我们根据CRC的规范来改写一下上面的例子:

Poly                    =    1001,宽度W = 3
位串Bitstring           =    11110
Bitstring + W zeroes    =    11110 + 000 = 11110000

11110000
1001||||    -
-------------
 1100|||
 1001|||    -
 ------------
  1010||
  1001||    -
  -----------
   0110|
   0000|    -
   ----------
    1100
    1001    -
    ---------
     101        --> 5,余数 --> the CRC!

还有两点重要声明如下:
1、只有当Bitstring的最高位为1,我们才将它与poly进行XOR运算,否则我们只是将Bitstring左移一位。
2、XOR运算的结果就是被操作位串Bitstring与poly的低W位进行XOR运算,因为最高位总为0。

呵呵,是不是有点头晕脑胀的感觉了?看不懂的话,再从头看一遍,其实是很好理解的。(就是一个XOR运算嘛!)


好啦,原理介绍到这里,下面我讲讲具体怎么编程。

由于速度的关系,CRC的实现主要是通过查表法,对于CRC-16和CRC-32,各自有一个现成的表,大家可以直接引入到程序中使用。(由于这两个表太长,在这里不列出来了,请读者自行在网络上查找,很容易找到的。)

如果我们没有这个表怎么办呢?或者你跟我一样,懒得自己输入?不用急,我们可以“自己动手,丰衣足食”。
你可能会说,自己编程来生成这个表,会不会太慢了?其实大可不必担心,因为我们是在汇编代码的级别进行运算的,而这个表只有区区256个双字,根本影响不了速度。

这个表的C语言描述如下:
 

for (= 0; i < 256; i++)
{
    crc = i;
    for (= 0; j < 8; j++)
    {
        if (crc & 1)
            crc = (crc >> 1) ^ 0xEDB88320;
        else
            crc >>= 1;
    }
    crc32tbl[i] = crc;
}


生成表之后,就可以进行运算了。
我们的算法如下:
1、将寄存器向右边移动一个字节。
2、将刚移出的那个字节与我们的字符串中的新字节进行XOR运算,得出一个指向值表table[0..255]的索引。
3、将索引所指的表值与寄存器做XOR运算。
4、如果数据没有全部处理完,则跳到步骤1。

这个算法的C语言描述如下:
 

    temp = (oldcrc ^ abyte) & 0x000000FF;
    crc  = (( oldcrc >> 8) & 0x00FFFFFF) ^ crc32tbl[temp];
    return crc;


好啦,所有的东东都说完啦,最后献上一个完整的Win32Asm例子,请读者仔细研究吧!
(汇编方面的CRC-32资料极少啊,我个人认为下面给出的是很宝贵的资料。)

 

;****************************************************
;程序名称:演示CRC32原理
;作者:罗聪
;日期:2002-8-24
;出处:http://laoluoc.yeah.net(老罗的缤纷天地)
;注意事项:如欲转载,请保持本程序的完整,并注明:转载自“老罗的缤纷天地”(http://laoluoc.yeah.net)
;
;特别感谢Win32ASM高手—— dREAMtHEATER 为我的代码作了相当好的优化!
;请各位前去 http://NoteXPad.yeah.net 下载他的小巧的“cool 记事本”—— NoteXPad 来试用!(100% Win32ASM 编写)
;
;****************************************************

.386
.model flat, stdcall
option casemap:none

include windows.inc
include kernel32.inc
include user32.inc
includelib kernel32.lib
includelib user32.lib

WndProc            proto :DWORD, :DWORD, :DWORD, :DWORD
init_crc32table    proto
arraycrc32         proto

.const
IDC_BUTTON_OPEN        equ    3000
IDC_EDIT_INPUT         equ    3001

.data
szDlgName         db    "lc_dialog", 0
szTitle           db    "CRC demo by LC", 0
szTemplate        db    "字符串 ""%s"" 的 CRC32 值是:%X", 0
crc32tbl          dd    256 dup(0)    ;CRC-32 table
szBuffer          db    255 dup(0)

.data?
szText            db    300 dup(?)

.code
main:
    invoke GetModuleHandle, NULL
    invoke DialogBoxParam, eax, offset szDlgName, 0, WndProc, 0
    invoke ExitProcess, eax

WndProc proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    .if uMsg == WM_CLOSE
        invoke EndDialog, hWnd, 0
        
    .elseif uMsg == WM_COMMAND
        mov eax,wParam
        mov edx,eax
        shr edx,16
        movzx eax, ax
        .if edx == BN_CLICKED
            .IF eax == IDCANCEL
                invoke EndDialog, hWnd, NULL
            .ELSEIF eax == IDC_BUTTON_OPEN || eax == IDOK        
                ;******************************************
                ;关键代码开始:(当当当当……)
                ;******************************************
                ;取得用户输入的字符串:
                invoke GetDlgItemText, hWnd, IDC_EDIT_INPUT, addr szBuffer, 255

                ;初始化crc32table:
                invoke init_crc32table

                ;下面赋值给寄存器ebx,以便进行crc32转换:
                ;EBX是待转换的字符串的首地址:
                lea ebx, szBuffer

                ;进行crc32转换:
                invoke arraycrc32

                ;格式化输出:
                invoke wsprintf, addr szText, addr szTemplate, addr szBuffer, eax

                ;好啦,让我们显示结果:
                invoke MessageBox, hWnd, addr szText, addr szTitle, MB_OK
            .ENDIF
        .endif
    .ELSE
        mov eax,FALSE
        ret
    .ENDIF
    mov eax,TRUE
    ret
WndProc endp

;**********************************************************
;函数功能:生成CRC-32表
;**********************************************************
init_crc32table    proc

        ;如果用C语言来表示,应该如下:
        ;
        ;    for (i = 0; i < 256; i++)
        ;    {
        ;        crc = i;
        ;        for (j = 0; j < 8; j++)
        ;        {
        ;            if (crc & 1)
        ;                crc = (crc >> 1) ^ 0xEDB88320;
        ;            else
        ;                crc >>= 1;
        ;        }
        ;        crc32tbl[i] = crc;
        ;    }
        ;
        ;呵呵,让我们把上面的语句改成assembly的:

        mov     ecx, 256        ; repeat for every DWORD in table
        mov     edx, 0EDB88320h
$BigLoop:
        lea     eax, [ecx-1]
        push    ecx
        mov     ecx, 8
$SmallLoop:
        shr     eax, 1
        jnc     @F
        xor     eax, edx
@@:
        dec     ecx
        jne     $SmallLoop
        pop     ecx
        mov     [crc32tbl+ecx*4-4], eax
        dec     ecx
        jne     $BigLoop

        ret
init_crc32table      endp


;**************************************************************
;函数功能:计算CRC-32
;**************************************************************
arraycrc32    proc

        ;计算 CRC-32 ,我采用的是把整个字符串当作一个数组,然后把这个数组的首地址赋值给 EBX,把数组的长度赋值给 ECX,然后循环计算,返回值(计算出来的 CRC-32 值)储存在 EAX 中:
        ;
        ; 参数:
        ;       EBX = address of first byte
        ; 返回值:
        ;       EAX = CRC-32 of the entire array
        ;       EBX = ?
        ;       ECX = 0
        ;       EDX = ?

        mov     eax, -1 ; 先初始化eax
        or      ebx, ebx
        jz      $Done   ; 避免出现空指针
@@:
        mov     dl, [ebx]
        or      dl, dl
        je      $Done    ;判断是否对字符串扫描完毕
        
        ;这里我用查表法来计算 CRC-32 ,因此非常快速:
        ;因为这是assembly代码,所以不需要给这个过程传递参数,只需要把oldcrc赋值给EAX,以及把byte赋值给DL:
        ;
        ; 在C语言中的形式:
        ;
        ;   temp = (oldcrc ^ abyte) & 0x000000FF;
        ;   crc  = (( oldcrc >> 8) & 0x00FFFFFF) ^ crc32tbl[temp];
        ;
        ; 参数:
        ;       EAX = old CRC-32
        ;        DL = a byte
        ; 返回值:
        ;       EAX = new CRC-32
        ;       EDX = ?
               
        xor     dl, al
        movzx   edx, dl
        shr     eax, 8
        xor     eax, [crc32tbl+edx*4]
        
        inc     ebx        
        jmp     @B

$Done:
        not     eax
        ret
arraycrc32      endp

end main
;********************    over    ********************
;by LC



下面是它的资源文件:
 


#include "resource.h"

#define IDC_BUTTON_OPEN    3000
#define IDC_EDIT_INPUT 3001
#define IDC_STATIC -1

LC_DIALOG DIALOGEX 10, 10, 195, 60
STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | 
    WS_SYSMENU
CAPTION "lc’s assembly framework"
FONT 9, "宋体", 0, 0, 0x0
BEGIN
    LTEXT           "请输入一个字符串(区分大小写):",IDC_STATIC,11,7,130,10
    EDITTEXT        IDC_EDIT_INPUT,11,20,173,12,ES_AUTOHSCROLL
    DEFPUSHBUTTON   "Ca&lc",IDC_BUTTON_OPEN,71,39,52,15
END



如果你能够完全理解本节的内容,那么请留意我的下一讲,我将具体介绍如何运用CRC-32对你的文件进行保护。(呵呵,好戏在后头……)

 

老罗
2002-8-26

 

写在2011

最近这十年好像过得格外的快,回想10年前,才刚刚考上大学,生活还是那么简单。

转眼10后都工作了好几年了,这十年来也是最丰富的十年,经历了许多也成长了很多,也许是工作性质是研发的缘故,总有还没走出象牙塔的感觉,大学生活还总是在眼前。

在这年末最后一天回顾一下我的十年,十年大事记:

2001-2005 在一所重点大学里读着本科,学着一个非重点但是自认为喜欢的专业---电气自动化。人生中第一次大量的接受很多新鲜事情。经历了初恋。四年的生活还算快乐,也奠定了人生中第一块敲门砖。不断的后悔于高考的失败,于是下定决心考研。

2005-2008 如愿以偿进入了向往已久的西安电子科技大学读研。也就在这段时间里感受到电子技术的日新月异。其实真正享受学校的培养也就第一年,不过接下来的近两年的生活才是真正丰富多彩和值得回忆的。在企业里实习一年,学到了很多东西也见识了很多新的电子技术,在中科院深圳所里实习一年,第一次到深圳,认识了这个活力城市和中科院,见过学多大师,感觉到了技术的殿堂那份骄傲。

2008来到北京,进入普源开始了第一份真正的工作,至今任在在这里工作。在北京接近四年了,买了一套不大的房,结了婚,买了车,贷了款。成功跻身于房奴车奴族。这四年里的工作也使自己在技术上提高了很多,找到了自己的技术方向和爱好,也发现了技术的路还那么漫长。

生活还是那么丰富多彩,希望接下来的十年可以收获更多,希望我的家庭越来越和睦,亲人们都健健康康,工作也顺顺利利。

DOS 遍历目录及子目录,删除特定名称文件夹或文件

1、遍历某个文件夹及其子文件夹目录,删除全部名为CVS的目录:

      进入CMD界面执行以下两句,第一句进入特定待查找的目录,第二句删除当前目录及子目录下全部名为CVS的文件夹

cd "E:\3_DSP"

for /R  %s in (.,*) do  rd /q /s %s\CVS

参考:http://blog.sina.com.cn/s/blog_517d1cb00100se0v.html

DOS命令:用For命令遍历文件夹

(2011-04-02 21:29:38)

Image转载

标签:

dos
command
batch
programming

分类: 技术探究

Syntax:

FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters]

Description:

Walks the directory tree rooted at [drive:]path, executing the FOR statement in each directory of the tree.

  • If no directory specification is specified after /R then the current directory is assumed. 
  • If set is just a single period (.) character then it will just enumerate the directory tree.
  • If set is just a single asterisk (*) character then it will just enumerate the files.
  • If set is a single period (.) character followed by a single asterisk (*) character then it will enumerate the sub-folders firstly and then files under the sub-folder recursively.
  • If set is a single asterisk (*) character followed by a single period (.) character then it will enumerate the files firstly then the sub-folder recursively.
  • Wildcards characters asterisk(*) and period (.) can be repeated and the loop will also repeated appropriately.
  • Question mark(?) is also supported and usually used to filter interested files or sub-folders under specified folder or the current folder.

Example:

@echo off

REM recursively print absolute path of sub-folders and files under drive D:

for /R "D:\" %%s in (.,*) do (

  echo %%s

  sleep 0.3

)

REM recursively print absolute path of only sub-folders under current folder

for /R %%s in (.) do (

  echo %%s

  sleep 0.3

)

REM recursively print absolute path of only files under current folder

for /R %%s in (*) do (

  echo %%s

  sleep 0.3

)

REM recursively print absolute path of files under current folder and sub-folders but only for those files with name matching pattern "list?.xul" (i.e. list0.xul or listA.xul).

for /R %%s in (list?.xul) do (

  echo %%s

  sleep 0.3

)

REM do the same with the above but without sleeping

for /R %%s in (list?.xul) do echo %%s

The above examples just perform echo command. However, you can do more complex commands as needed. For example, the following example is used to perform the following operations on all *.xul file under the current folder and sub-folders

  • open Firefox with *.xul file
  • open *.xul files with notepad
  • sleep for 5 seconds
  • forcibly kill firefox and notepad

@echo off

set FIREFOX_ROOT="C:\Program Files\Mozilla Firefox\"

set FIREFOX="firefox.exe"

for /R %%s in (*.xul) do (

  start /D%FIREFOX_ROOT% FIREFOX -chrome %%s

  start notepad %%s

  sleep 5

  taskkill /f /fi "imagename eq firefox.exe"

  taskkill /f /fi "imagename eq notepad.exe"

)

报告晓松老师

老师出狱了,报告晓松老师:这六个月:拉登被爆头了;乔帮主去世了,没有爱疯5了;凤姐移民了,芙蓉85斤了;峰芝还是离了,冠希老师宣扬新艳照了;广电开始限娱了,潘币发行了;城管依旧威武,国足还是老样子;老人还是不敢扶,动车的事也没回应了;我们都还好,就是2012世界末日了还没船票。

linux中安装NI VISA

1、加载NI visa for linux.iso 或 将关盘文件里面的东西复制到一个普通目录中。

2、在终端里,进入安装文件的目录,运行 “./INSTALL”脚本即可。

     可能出现拒绝运行的提示,这是因为INSTALL文件没有运行权限所致,有一点击右键增加可执行权限或使用“sh ./INSTALL”来强制运行脚本。

3、中间遇到提示回答即可,一路安装完成。

4、安装完成后查看设备:http://zone.ni.com/devzone/cda/tut/p/id/2816

NI-VISA comes with a utility called VISA Interactive Control (VISAIC) on Linux. This utility gives you access to all VISA functionality interactively, in an easy-to-use graphical environment. It is a convenient way to view available ports and get started developing instrument control applications. Note that this utility cannot be used to change settings on your instrument.
To launch VISAIC on Linux, use the NIvisaic command. When VISAIC runs, it automatically finds all of the available resources in the system and lists the instrument descriptors for each of these resources under the appropriate resource type. This information is displayed on the VISA I/O tab. The following figure shows the VISAIC opening window:

Figure 1. The VISA Interactive Control (VISAIC) Utility on Linux is a convenient way to view available ports and get started developing instrument control applications.

Another utility, VISA Configuration, can be used to add and configure instrument interfaces in your system. To run this utility, use the visaconf command. The following figure shows the visaconf opening window:

Figure 2. The VISA Configuration utility can be used to add and configure instrument interfaces on your Linux system.

If your instrument is not initially displayed under the Resource Editor tab, you can use the Add Static button to manually add it to your system.

Locating and Installing Existing Instrument Drivers

While it is possible to use NI-VISA to communicate with an instrument directly, using instrument drivers can abstract the lower level details and accelerate software development. The NI Instrument Driver Network provides access to drivers for thousands of instruments many third-party vendors.

If you want to use an existing instrument driver in Linux, it must be a Plug and Play driver. Interchangeable Virtual Instrument (IVI) drivers run only on Windows operating systems. To download an instrument driver for use with LabVIEW on Linux, you must make sure it is packaged as a *.vi or *.llb file. You cannot open or install a *.exe instrument driver file on Linux.

To access the instrument driver VIs in LabVIEW, save the *.llb and menu files in theLabVIEW\instr.lib folder. You will now have access to the VIs from the Functions>>Instrument I/O>>Instrument Drivers palette. You can use these VIs to communicate with your instrument.

Instrument Driver Development on Linux Using the NI-VISA API

If an instrument driver does not exist for your instrument, you can use NI-VISA functions in LabVIEW or LabWindows/CVI to control your instrument. Make sure to have your instrument user manual available, because you will need to be familiar with the commands that your instrument responds to. Refer to the VISA API section in the following document: Instrument Control in LabVIEW Tutorial, for more help on using VISA in LabVIEW. You can access the VISA VIs from theFunction>>Instrument I/O>>VISA palette in LabVIEW.

For information on accessing instruments from LabWindows/CVI applications using NI-VISA, see the Instrument Control in LabWindows/CVI Tutorial. Note that LabWindows/CVI applications require the LabWindows/CVI Run-Time Module for Linux to execute on a Linux OS.

Steve Jobs

    帮主Steve Jobs去世,在Iphone4S发布之后的一个重磅新闻,不知道是巧合还是安排~ 不过这位IT的神人的确值得纪念。

    Steve Jobs 1955-2011

Vmware虚拟机中添加新硬盘

Vmware虚拟机中的硬盘和操作系统经场面临崩溃和人为损坏的特点(其实用虚拟机往往就是为了进行破坏性的实验)。

因此我希望把操作系统建立在一个独立虚拟磁盘上,而其他文件或资料单独在建立在一个虚拟磁盘上,然后把安装了操作系统的虚拟磁盘备份一下。这样在虚拟机崩溃后可以迅速恢复和重建。

但是遇到一个问题:在Vmware中添加了一块虚拟磁盘后,虚拟机内的操作系统却无法识别,在“我的电脑”中看不到另一块硬盘。于是google了一下,应该是新添加的虚拟磁盘没有被格式化的缘故,找到解决方法如下:

1、在“设备管理器中”的“磁盘驱动器”下看一下,确认是否已存在第二块硬盘。如果没有就在控制面板里添加新硬件,添加上他即可。如果已经存在,则跳过此步骤。

image

2、“我的电脑”点右键,选择“管理”选择左侧的“磁盘管理”。此时系统会检测到有磁盘没有被格式化,自动弹出对向导话框,按照向导对话框一步步格式化磁盘即可。(如果格式化完了卷标名称有误,可以在这里右键点击对应磁盘即可修改)

image

修改磁盘卷标等信息:

image

Linux kernel释放出3.0版本

参见:http://kernel.org/ Linux20周年之际,Linux kernel释放出3.0版本,也许算是一种纪念的方式~

截图如下:

linux

访问Google doc/plus方法

由于相关地址被禁国内很难访问,可是Docs的确需要呀~ 在网上找到一个方法如下:

1、打开:C:\WINDOWS\system32\drivers\etc 目录,用记事本打开其中的 hosts 文件。

2、在文件末尾加入以下文本即可。

#google plus
203.208.46.29 picadaweb.google.com
203.208.46.29 lh1.ggpht.com
203.208.46.29 lh2.ggpht.com
203.208.46.29 lh3.ggpht.com
203.208.46.29 lh4.ggpht.com
203.208.46.29 lh5.ggpht.com
203.208.46.29 lh6.ggpht.com
203.208.46.29 lh6.googleusercontent.com
203.208.46.29 lh5.googleusercontent.com
203.208.46.29 lh4.googleusercontent.com
203.208.46.29 lh3.googleusercontent.com
203.208.46.29 lh2.googleusercontent.com
203.208.46.29 lh1.googleusercontent.com
203.208.46.29 plus.google.com
203.208.46.29 talkgadget.google.com
203.208.46.30 profiles.google.com
#google docs
74.125.227.2 docs.google.com
74.125.227.2 docs0.google.com
74.125.227.2 docs1.google.com
74.125.227.2 docs2.google.com
74.125.227.2 docs3.google.com
74.125.227.2 spreadsheets.google.com
74.125.227.2 spreadsheets0.google.com
74.125.227.2 spreadsheets1.google.com
74.125.227.2 spreadsheets2.google.com
74.125.227.2 spreadsheets3.google.com

#picasaweb.google.com
74.125.91.99 www.picasa.com
74.125.91.103 picasa.google.com
203.208.39.104 lh1.ggpht.com
203.208.39.104 lh2.ggpht.com
203.208.39.104 lh3.ggpht.com
203.208.39.104 lh4.ggpht.com
203.208.39.104 lh5.ggpht.com
203.208.39.104 lh6.ggpht.com
203.208.39.104 lh7.ggpht.com
203.208.39.104 lh8.ggpht.com
203.208.39.104 lh9.ggpht.com

203.208.39.104 picadaweb.google.com
203.208.39.104 lh1.ggpht.com
203.208.39.104 lh2.ggpht.com
203.208.39.104 lh3.ggpht.com
203.208.39.104 lh4.ggpht.com
203.208.39.104 lh5.ggpht.com
203.208.39.104 lh6.ggpht.com

程序员与妓女

程序员与妓女基本一样,以下为证:

1、都是靠出卖为生。

2、吃青春饭,人老珠黄肯定混不下去。

3、越高级收入越高,当然中间人的抽头会更高。

4、生活没有规律。以夜生活为主,如果需要,凌晨也要加班。

5、名声越大,越容易受到青睐。

6、必须尽最大可能满足客户各种各样非正常的需求。

7、鼓励创新精神。

8、喜欢扎堆。程序员集中的地方称为软件园,妓女集中的地方叫红灯区。

9、流动性较大,正常情况下没有工会。

10、如果怀孕了,既不能做程序员,也不能做妓女。

11、都为防病毒的问题而烦恼...

12、当然, 个中高手还专门以制毒传毒为乐。

13、一个是Microsoft,一个是Plug & Play。

14、工作状态相同。工作时精神高度集中,最怕外界干扰。工作完毕身心放松,体会到一种不可替代的工作快乐。

15、女孩子最好还是不要做这两个职业,但还是有很多女孩子做。

16、除非在转行以后,否则都不愿意结婚......没空儿啊。

17、程序员怕查户口的。妓女怕查房的。

18、妓女工作的地方(床)是程序员最向往的地方。

19、程序界的高手通常很讨厌微软,妓女界的高手嗯...这个...恐怕也如此。

20、都是吃青春饭,不过到人老珠黄后,凭着混个脸熟,程序员可以混个管理员,妓女也行,不过俗称老鸨。

21、妓女靠的本钱是三围,程序员靠的可是四围(思维)。

22、程序员为了拉客,通常会在交易前提供一个DEMO,妓女提供的那叫PHOTO。

23、程序员现在出的活时兴叫吃霸、结霸,妓女大姐一律叫波霸。

24、心不在焉的妓女可以一边工作一边do { beep(1); sleep(9) } until overflow。心不在焉的程序员也可以一边工作一边navigate到成人网站上去。

25、程序员手册:一套好的人机操作界面要求,对于新手,能够一步一步的引导他进入功能,相反对于熟客,能够直奔主题;妓女同样要遵守程序员手册对人鸡界面的规定。

26、妓女在工作中最怕的是临检,程序员最怕的是停电。

27、新上手的程序员叫菜鸟,刚入行的妓女叫雏鸡,都是好可怜的小动物。

28、程序界现在流行OO的方法,虽然在XXXX年前妓女已在床上掌握了O~O~~~的技术。

29、程序员为了拉客,无奈之时,也可以先让客人试玩,妓女当然有时也会先给你甜头。

不过总之程序员比妓女还惨,补充如下:

1、妓女每个月总有几天可以理直气壮的说不,程序员如果老板不发话,可要一年干到黑。

2、女人做程序那叫奇女、才女,男人要是做妓,那就叫鸭了。

3、妓女不干了人家那叫从良,程序员如果不干了,估计是下了岗。

4、程序员有千年虫问题,妓女好象没听说有。

5、妓女的工作隐蔽性很强,程序员的工作只怕亲戚朋友都知道,所以更加没脸皮。

6、程序员做的越好,要做的程序越多,妓女做的好,就可以挑三拣四。