SadlyCodes的个人主页

QQ2011低级键盘钩子分析

0

004DF8B1    55              PUSH EBP

004DF8B2    8BEC            MOV EBP,ESP
004DF8B4    81EC 98000000   SUB ESP,98
004DF8BA    C745 FC E8F24D0>MOV DWORD PTR SS:[EBP-4],4DF2E8
004DF8C1    8B55 FC         MOV EDX,DWORD PTR SS:[EBP-4]
004DF8C4    8B4A 3C         MOV ECX,DWORD PTR DS:[EDX+3C]
004DF8C7    8B01            MOV EAX,DWORD PTR DS:[ECX]
004DF8C9    3C CC           CMP AL,0CC
004DF8CB    75 07           JNZ SHORT 004DF8D4
004DF8CD    C742 08 0100000>MOV DWORD PTR DS:[EDX+8],1
004DF8D4    B8 FF441A6A     MOV EAX,6A1A44FF
004DF8D9    8B55 FC         MOV EDX,DWORD PTR SS:[EBP-4]
004DF8DC    81C2 0A060000   ADD EDX,60A
004DF8E2    B9 27000000     MOV ECX,27
004DF8E7    8D148A          LEA EDX,DWORD PTR DS:[EDX+ECX*4]
004DF8EA    83EA 04         SUB EDX,4
004DF8ED    0302            ADD EAX,DWORD PTR DS:[EDX]
004DF8EF    49              DEC ECX
004DF8F0  ^ 75 F8           JNZ SHORT 004DF8EA
004DF8F2    8B55 FC         MOV EDX,DWORD PTR SS:[EBP-4]
004DF8F5    0142 08         ADD DWORD PTR DS:[EDX+8],EAX
004DF8F8    C785 68FFFFFF 9>MOV DWORD PTR SS:[EBP-98],94
004DF902    8D85 68FFFFFF   LEA EAX,DWORD PTR SS:[EBP-98]
004DF908    50              PUSH EAX
004DF909    8B4D FC         MOV ECX,DWORD PTR SS:[EBP-4]
004DF90C    FF51 30         CALL DWORD PTR DS:[ECX+30]
004DF90F    83BD 78FFFFFF 0>CMP DWORD PTR SS:[EBP-88],2
004DF916    75 70           JNZ SHORT 004DF988
004DF918    6A 00           PUSH 0
004DF91A    8B55 FC         MOV EDX,DWORD PTR SS:[EBP-4]
004DF91D    FF52 28         CALL DWORD PTR DS:[EDX+28]
004DF920    6A 00           PUSH 0
004DF922    50              PUSH EAX
004DF923    8B4D FC         MOV ECX,DWORD PTR SS:[EBP-4]
004DF926    8B51 64         MOV EDX,DWORD PTR DS:[ECX+64]
004DF929    52              PUSH EDX
004DF92A    6A 0D           PUSH 0D
004DF92C    8B45 FC         MOV EAX,DWORD PTR SS:[EBP-4]
004DF92F    FF50 3C         CALL DWORD PTR DS:[EAX+3C]               ; 安装WH_KEYBOARD_LL钩子
004DF932    8B4D FC         MOV ECX,DWORD PTR SS:[EBP-4]
004DF935    8941 14         MOV DWORD PTR DS:[ECX+14],EAX
004DF938    8B4D FC         MOV ECX,DWORD PTR SS:[EBP-4]
004DF93B    8379 14 00      CMP DWORD PTR DS:[ECX+14],0
004DF93F    EB 47           JE SHORT 004DF988           ;改为jmp后可以眺过安装WH_DEBUG钩子
004DF941    8B55 FC         MOV EDX,DWORD PTR SS:[EBP-4]             ; 以后安装WH_DEBUG钩子
004DF944    FF52 34         CALL DWORD PTR DS:[EDX+34]
004DF947    50              PUSH EAX
004DF948    6A 00           PUSH 0
004DF94A    8B45 FC         MOV EAX,DWORD PTR SS:[EBP-4]
004DF94D    8B48 60         MOV ECX,DWORD PTR DS:[EAX+60]
004DF950    51              PUSH ECX
004DF951    6A 09           PUSH 9
004DF953    8B55 FC         MOV EDX,DWORD PTR SS:[EBP-4]
004DF956    FF52 3C         CALL DWORD PTR DS:[EDX+3C]
004DF959    8B4D FC         MOV ECX,DWORD PTR SS:[EBP-4]
004DF95C    8941 10         MOV DWORD PTR DS:[ECX+10],EAX
004DF95F    8B55 FC         MOV EDX,DWORD PTR SS:[EBP-4]
004DF962    837A 10 00      CMP DWORD PTR DS:[EDX+10],0
004DF966    75 19           JNZ SHORT 004DF981
004DF968    8B45 FC         MOV EAX,DWORD PTR SS:[EBP-4]
004DF96B    8B48 14         MOV ECX,DWORD PTR DS:[EAX+14]
004DF96E    51              PUSH ECX
004DF96F    8B55 FC         MOV EDX,DWORD PTR SS:[EBP-4]
004DF972    FF52 40         CALL DWORD PTR DS:[EDX+40]
004DF975    8B45 FC         MOV EAX,DWORD PTR SS:[EBP-4]
004DF978    C740 14 0000000>MOV DWORD PTR DS:[EAX+14],0
004DF97F    EB 07           JMP SHORT 004DF988
004DF981    B8 01000000     MOV EAX,1
004DF986    EB 02           JMP SHORT 004DF98A
004DF988    33C0            XOR EAX,EAX
004DF98A    8BE5            MOV ESP,EBP
004DF98C    5D              POP EBP
004DF98D    C3              RETN
特征码:
004DF932    8B4D FC         MOV ECX,DWORD PTR SS:[EBP-4]
004DF935    8941 14         MOV DWORD PTR DS:[ECX+14],EAX
004DF938    8B4D FC         MOV ECX,DWORD PTR SS:[EBP-4]
base:8B 4D FC 89 41 14 8B 4D FC xx xx xx xx
   +13:要修改的指令(2个字节)
实验证明找到的特征代码有两处,低地址为执行的代码,高地址的为备份代码,
当程序检测到执行代码改变时,用备份代码恢复之。所以修改时,应先将备份
代码修改,然后再修改可执行代码,这样安全些.
keyoard_ll hook proc 特征码
003CF513    B9 07000000     MOV ECX,7
003CF518    33C0            XOR EAX,EAX
003CF51A    FC              CLD
特征码
base:B9 07 00 00 00 33 C0 FC
   -12:回调函数头地址
MOV ECX,7
XOR EAX,EAX
CLD
分析过程:
@首先通过工具软件(XueTr)找到QQ安装的硬件钩子句柄和回调函数地址,用CE定位该句柄地址
  结果发现,当光标放入密码框后,句柄的值不断变化,而工具软件的显示也支持这个
  结论。然而,回调函数的基址确没有发生变化。这说明QQ为了保持自己的硬件键盘钩子
  保持在最前边,除了应用WH_DEBUG钩子外,还在不断地给自己安装(卸载)键盘硬件钩子
@用OD在CE找到的存放键盘硬件钩子句柄的内存地址下内存写入的硬件断点,经调试,主要在
  两个地方断下,一个为给该地址赋值0,一个为赋值为非零(一个call的返回值),很
  显然,这是函数得到的钩子句柄值了。这两段代码会在输入焦点在密码框后不断被反复
  执行的。
@调试发现QQ的可执行代码大部分放在了数据段(也有的可能在堆栈里),这些区域非常
  灵活,程序不断刷新这些区域,最终导致OD的断点失灵,断不不来。处理的方法这里有
  一种,比较烦,就是结合CE,将相应下断点的地址处的内存锁定,值保持为0xcc (int 3)
  这样,CE配合OD就可以正常下断了。
@用前边的方法可以定位到QQ安装钩子的函数处,虽然每次QQ启动,这些函数的地址是不
  相同的,但代码的特征没有发生变化,我们可以用暴力搜索特征码(不知道CE用的什么方
法搜索的那么快,高手指点一下。我的搜索算法效率太低!)的方法定位这些函数
  包括钩子安装的函数以及相应的钩子回调函数。定位函数基址以后,就可以用写入内存的
  方法来修改相应代码。注意有些代码是不断被QQ更新修正的。我这里有两个思路,一个是
  通过系统调用更改相应页面的保护属性,使其在我们写入数据后变为只读的页面。别一个
  时找到QQ的备份代码位置,本人运气不错,无意间在搜索内存的过程中发现了,这样的
  备份代码在内存里确实存在。也就是说,只要备份代码被我们改变,那么实际执行的代码
  即使我们不动,它也会被QQ更新为我们需要的。
@0×90确实是个好东西,它可以让程序的好多调用失效。我们可以在QQ安装钩子的函数附近nop
  一些代码,这样其安装钩子就会失败。然而并不是我们就解决了所有问题,它自己安不上
  钩子,密码也就没法接收了。我们无法输入密码了。原因是它通过自己的硬件钩子获取用户
  输入的密码,而不是常规的消息循环机制。我们必须解决这个问题。当我们给QQ打上内存
  补丁后,给它安装个钩子也不是什么难事了。我一开始想到的便是安装WH_KEYBOARD_LL钩子,
  开始实验时安装在系统范围内,结果别处的击键操作可以正常钩到,但QQ的密码框确不秆,
  钩到的信息除了有我们键入的以外还有好多乱码按键(这时QQ显然不会给加密,因为此时
  钩子是我们的在最前边,也因而我们能找到自己按的键,如果不打内存补丁,则经QQ的钩子函数
  处理后,我们得到的真的就全是乱码了!)。我分析,QQ应该采用了模拟按键一类的方法,
  那些不是我们按下键的信息自然是它做的干扰。怎么解决?试想,假如钩子是QQ安的,它的
  回调函数怎么做?它自然可以区分正常按键和自己的干扰按键。在键盘硬件钩子的回调函数里
  有KBDLLHOOKSTRUCT结构。其中的虚拟码,扫描码按一般的认识,其不会被利用。不然会影响
  正常按键的判别。最大的可能就是那个flags成员以及那个附加信息域了。我采集了一些相关的
  数据,结果让人心喜。flags成员出现情况了。正常按键取值为0×00(按下)和0×80(释放),而
  那些乱码按键则为0×10或0×90,这样一来,我们便可以非常容易的过虑了。利用一些函数,我们
  能够很容易的得到相应键的ascii码。然而我们这时还是没有办法输入密码。想正常输入密码
  还得另想办法。
@起始时认为WH_KEYBOARD_LL钩子子是安装在系统每个进程里的,然而实验失败了。查MSDN得知该钩子
  用本地线程环境执行回调函数,系统通过给本地程序发消息来实现钩子回调函数的调用。所以想
  通过这种类型的钩子访问目标进程内部的数据(原始回调函数)已经是不可能的了。我们不能直接
  安装这个钩子。
@首先肯定一点,如果我们想调用原始的回调函数,我们必须注入它的进程里。WH_KEYBOARD)LL是无法注入
  到其进程里的。为了达到这个目的,我们不妨用WH_KEYBOAR安装远线程钩子,这样我们就成功注入了
  安装这个钩子时需要传入原始回调函数地址(线程Id号就不说了)。这样我们可以在该钩子的注册
  回调函数(WH_KEYBOARD的)安装WH_KEYBOARD_LL,这时钩子就被安装在QQ的进程空间里了。如果
  我们在注册的回调函数里直接调用原始回调函数,结果证明一切正常。试想,我们多些代码,设置
  一些过滤什么的又如何呢?我的注册函数名称为Filter!实现将拦到的按键写到文件里。呵呵!用心良
  苦呀!
@通过对QQ的分析,我们应该深入了解钩子的工作机制,动态库的相关知识,一些相应的调试和反调试技术
  等。
附加说明:附件中有源代码,vc2005可直接编译。一个项目为dll,一个为测试程序。特征码在我的机器上目前仍有效,
win7+qq2011最新版本。当然,关键还是我们解决问题的思路。代码可以任意复制和修改。有问题希望一起讨论下。
vista,win7系统上程序要以管理员权限运行。由于特征码搜索算法效率不高,点击button1后,等待的时间稍长些.

QQ密码输入框(防键盘钩子)原理分析

0

1.网上看到的一些防星号查看器的代码大多是在后台维护一个字符串(真实的密码),界面则不显示真实的密码,这样做,维护那个字符串很费事,就因为我之前那样做过,我才在看到WM_GETTEXT后想要重做一个

2.防键盘钩子,在做上面的功能是,突然想到,如果自己模拟发一些乱七八糟的按键消息,然后自己区分哪些是垃圾哪些是用户真正的按键,那钩子就到不取密码了,所以又加了这个功能.后来自己做了一个钩子看了一下效果不错,试了试QQ的竟然也是这样,开始以为QQ用了什么高科技

3.自我感觉貌似实现了QQ密码框的功能,不知道会有什么问题,所以帖出来,谁有兴趣自己看吧

//////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

 

//safeedit.h

// CSafeEdit

 

class CSafeEdit : public CEdit

{

public:

//设置取密码时用的KEY(大于5)

BOOL SetPassWordKey(CString & astrKey);//取密码时用的KEY

DECLARE_DYNAMIC(CSafeEdit)

int GetWindowTextEx(CString & astrText);//astrText 传入KEY 传出密码

public:

CSafeEdit();

virtual ~CSafeEdit();

 

protected:

DECLARE_MESSAGE_MAP()

LRESULT OnGetText(WPARAM wParam, LPARAM lParam);

 

private:

BOOL mbCanGetText;//GetWindowTextEx:astrText == mstrKey ? TRUE : FALSE

CString mstrKey;

char mcLastVKey;//Timer 模拟的键

public:

afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);

afx_msg void OnTimer(UINT nIDEvent);

afx_msg void OnSetFocus(CWnd* pOldWnd);

afx_msg void OnKillFocus(CWnd* pNewWnd);

};

//////////////////////////////////////////////////////////////////////////////////////////////////////////

 

// SafeEdit.cpp : 实现文件

//

 

#include “stdafx.h”

#include “DlgTemp2.h”

#include “SafeEdit.h”

#include “.\safeedit.h”

 

 

// CSafeEdit

 

IMPLEMENT_DYNAMIC(CSafeEdit, CEdit)

CSafeEdit::CSafeEdit()

{

mstrKey = “jiawei@hotmail.com”;

mbCanGetText = FALSE;

mcLastVKey = 0;

}

 

CSafeEdit::~CSafeEdit()

{

}

 

 

BEGIN_MESSAGE_MAP(CSafeEdit, CEdit)

ON_MESSAGE(WM_GETTEXT, OnGetText)

ON_WM_CHAR()

ON_WM_TIMER()

ON_WM_SETFOCUS()

ON_WM_KILLFOCUS()

END_MESSAGE_MAP()

 

 

// CSafeEdit 消息处理程序

 

LRESULT CSafeEdit::OnGetText(WPARAM wParam, LPARAM lParam)

{

long lnStyle = GetWindowLong(GetSafeHwnd(),GWL_STYLE);

if(lnStyle & ES_PASSWORD)

{

if(mbCanGetText)

return DefWindowProc(WM_GETTEXT,wParam,lParam);

else

return -1;

}

 

return DefWindowProc(WM_GETTEXT,wParam,lParam);

}

//************************************

// Method:    SetPassWordKey

// FullName: CSafeEdit::SetPassWordKey

// Access:    public

// Returns:   BOOL

// Qualifier: 设置取密码时用到的KEY,长度大于五

// Parameter: CString & astrKey

//************************************

BOOL CSafeEdit::SetPassWordKey(CString & astrKey)

{

if(astrKey.GetLength() < 5)

return FALSE;

mstrKey = astrKey;

return TRUE;

}

 

//************************************

// Method:    GetWindowTextEx

// FullName: CSafeEdit::GetWindowTextEx

// Access:    public

// Returns:   int

// Qualifier:

// Parameter: CString & astrText 传入取密码用的KEY,如果成功返回密码

//************************************

int CSafeEdit::GetWindowTextEx(CString & astrText)

{

if(astrText != mstrKey)

return -1;

 

astrText = “”;

DWORD ldwLen = ::SendMessage(GetSafeHwnd(),WM_GETTEXTLENGTH,0,0);

if(ldwLen <= 0)

return 0;

char *lpszBuf = new char[ldwLen+1];

if(lpszBuf == NULL)

return 0;

mbCanGetText = TRUE;

ldwLen = ::SendMessage(GetSafeHwnd(),WM_GETTEXT,ldwLen+1,(LPARAM)lpszBuf);

mbCanGetText = FALSE;

astrText = lpszBuf;

delete [] lpszBuf;

return ldwLen;

}

void CSafeEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)

{

if(mcLastVKey != nChar)

CEdit::OnChar(nChar, nRepCnt, nFlags);

}

 

void CSafeEdit::OnTimer(UINT nIDEvent)

{

//随机生成按键

UINT lnVKey = ’0′ + (rand()%(‘Z’-’0′+1));

UINT lnScan = MapVirtualKeyEx(lnVKey,0,NULL);

mcLastVKey = MapVirtualKeyEx(lnVKey,2,NULL);

if(mcLastVKey >= ‘A’ && mcLastVKey <= ‘Z’)

{

BOOL lbLowercase = TRUE;

if(GetKeyState(VK_CAPITAL) & 0×01 != 0)//CapsLock ON

lbLowercase = !lbLowercase;

if((GetKeyState(VK_LSHIFT) & 0×80) != 0 || (GetKeyState(VK_RSHIFT) & 0×80) != 0)//Shift DOWN

lbLowercase = !lbLowercase;

if(lbLowercase)

mcLastVKey += ‘a’ – ‘A’;

}

keybd_event(lnVKey,lnScan,KEYEVENTF_EXTENDEDKEY,0);

CEdit::OnTimer(nIDEvent);

keybd_event(lnVKey,lnScan,KEYEVENTF_KEYUP,0);

}

 

void CSafeEdit::OnSetFocus(CWnd* pOldWnd)

{

CEdit::OnSetFocus(pOldWnd);

long lnStyle = GetWindowLong(GetSafeHwnd(),GWL_STYLE);

if(lnStyle & ES_PASSWORD)

SetTimer(1,500,NULL);

}

 

void CSafeEdit::OnKillFocus(CWnd* pNewWnd)

{

CEdit::OnKillFocus(pNewWnd);

long lnStyle = GetWindowLong(GetSafeHwnd(),GWL_STYLE);

if(lnStyle & ES_PASSWORD)

KillTimer(1);

}

 

相关评论:qq好象不是这样做的 因为我在截取QQ密码的时候发现QQ密码输入框输入的内容什么都截取不到 而不是截取到一大片乱七八遭的数据

 

这几天好好地研究了一下QQ2010登陆窗口的密码输入框,细节没有完全弄清楚, 不过还是看出了大致的原理了。在这里发表一下我的看法,和有兴趣的朋友一起讨论。菜鸟水平,认识问题的程度很有限,大牛们就飘过吧。

 

我首先想到的,是要搞清楚,他的保护原始是基于R0还是R3?

这个问题的答案是:R3。理由很简单,因为找不到QQ有驱动啊,所以自然就是在R3进行的保护了。这个没什么好说的,不讲了。

 

接着是,QQ究竟如何在应用层实现密码输入保护的呢?

这个问题一开始我想来想去都不明白。我的运气很好,后来无意中看到了QQ2010安装了两个很特别的windows消息钩子:WH_DEBUG和WH_KEYBOARD_LL,于是线索出来了。

 

看到这两个钩子,好特别啊,它们是做什么用的呢?为什么要安装这两个钩子呢?

我马上翻了一下MSDN,找到答案了。

 

原来,WH_DEBUG可以用来管理很多种类型的钩子,有这么多种:

 

WH_CALLWNDPROC

WH_CALLWNDPROCRET

WH_CBT

WH_DEBUG

WH_JOURNALPLAYBACK

WH_JOURNALRECORD

WH_KEYBOARD

WH_MOUSE

WH_MSGFILTER

WH_SHELL

WH_SYSMSGFILTER

 

WH_DEBUG能管理这些类型的钩子,在这些钩子的钩子过程将要被系统调用的时候,系统总是先执行调试钩子的钩子过程。而MS的这个调试钩子,允许我们决定是否执行这些被管理的钩子。

 

我扫了一眼以上列表,在其中发现了WH_KEYBOARD钩子。原来普通键盘钩子是被调式钩子所管理的,也就是说,如果一个程序装上了调式钩子,那么在它上面的普通键盘钩子的钩子过程在执行之前,先要执行调式钩子,而在这个调式钩子里就可以拒绝执行即将执行的普通键盘钩子。

想到这里我恍然大悟了,难怪QQ装有一个调式钩子呢,难怪有全局键盘钩子记录不到QQ的密码呢!

 

还有一个低级键盘钩子,这个比较难一点,QQ保护原理核心都在这里面。

想贴一下这个钩子的钩子过程的反汇编代码,想想还是算了,太太长了。还是说一下我理解出来的意思吧,不全面,没有完全弄懂。

 

首先要知道的是,低级键盘钩子和普通简单钩子执行的先后顺序。

如果一个程序,同时被装有这两种钩子的话。在按键消息到来的时候,这两个钩子的执行顺序是:

低级键盘钩子 -》普通键盘钩子

为什么是这样的呢?我是看MSDN悟出来的,有疑问的话你也看看吧。

既然低级键盘钩子在普通键盘钩子的前面执行,自然它也有防御普通键盘钩子的能力了。

 

看到前面两个钩子,都是能够防御普通键盘钩子的。我自然想到,如果盗号者采用的是低级键盘钩子呢?是不是就能取得QQ的密码了呢?

带着好奇,我自己做了一个低级键盘钩子的测试程序。结果发现:

 

当我用鼠标点击密码输入框,把输入焦点定在这里时(并没有按键盘),测试程序就不停地输出按键信息。奇怪,我还没有开始输入密码啊,一个键都还没按呢!

 

后来冷静下来想了一想,这一定是TX为了防止盗号木马使用低级键盘钩子取走QQ的密码,而故意设置的虚假的按键信息,这样即使你用低级钩子记录到了某个正确的按键,其中必然混杂了很多虚假的按键信息,形成了干扰。

 

最后还有一点很重要的东西,可能大家很容易忘记,我在这里提醒一下大家。

细细地回想一下以前学习windows钩子学到的东西,记忆里会想起这么一个规律:

如果一个程序上被装上了多了同一类型的钩子,那么这些钩子的执行顺序是,先安装的后执行,

后安装的先执行。

 

也就是说,如果给某个程序同时装上两个低级键盘钩子A和B,安装的顺序是:A -》B,那么这个钩子的钩子过程执行的顺序就是:B -》A。

 

所以说,如果我们给QQ安装上一个低级键盘钩子,因为我门是在后面安装的,所以我们的钩子应该先执行,然后才执行QQ自己安装的低级键盘钩子。这不是成了QQ键盘保护的弱点了吗?我一边想着,一边在怀疑。

 

这个问题QQ是处理的呢?我在实践中探索到了答案,原来QQ会通过一个定时器,不停地对它的调式钩子和低级键盘钩子进行Unhook,然后马上再Hook。这样,不是就保证了QQ的钩子安装在盗号木马的钩子之后,从而先于木马的钩子执行了吗?哈哈

 

最后用一句话概括QQ自己的低级键盘钩子吧,尽管细节我也没有完全看明白,但还是能猜出来了。

QQ通过给自己在应用层设置这个低级键盘钩子,并不断脱钩、挂钩,来保证自己在应用层上是最先得到键盘消息的。既然是最先得到了,那么接下来要进行保护就容易了,自然就是先取得正确的按键信息,然后阻止这个消息继续传递下来给别人,就算传下去交给别人,也要修改按键信息,传一个错误的下去。:)

 

仔细回想一下整个过程,发现也不是很高深嘛,以前把QQ的键盘保护看得太神秘了

我知道的就这么多了,希望有深入理解的朋友继续报料啊

 

相关评论:

 

我想了一个在应用层实现的办法:

先把QQ进程里的SetWindowsHookEx和UnhookWindowsHookEx废了,然后再安装低级键盘钩子来记录

实验了一翻这个办法是成功的,可是不知道为什么后来QQ的进程崩溃了。

不错。QQ的密码框还会产生干扰,按了的键会变成另一个键信息,会生成一些随机的按键信息

 

QQ用了一个定时器,不停得卸载自己的钩子,又马上重新安装,如此来保证自己的钩子总是在最前面执行的,所以只要把SetWindowsHookEx和UnhookWindowsHookEx废了,那它就没办法了

 

自己装个键盘过滤驱动不就得了?反正是从下面传上来的。或者挂csrss.exe的win32k!RawInputThread

到了做选择的时候了

0

厌倦了每天在家泡着的生活了,出去走走也挺好的,找个地方坐下来翻翻杂志喝杯咖啡,也许慢慢就会忘记悲伤与不愉快,走在中关村的大街上总会有个想法,我什么时候能融入IT这个圈子?做个领跑者。或许这辈子都不会,越发的感觉在北京活着太难了,但我真的有勇气去南方发展吗?我也不知道…

介绍五种排序算法

0

在程序对于数据的处理中排序是最基本的也是最常用的,排序算法有很多种,这里SC贴出五种排序算法的VB.NET实现,包括我们最熟悉的冒泡排序。还有例如箱排序等出色的排序算法没有贴出下次整理后贴出来。

 

Public Class clsSorter

Public Class BubbleSorter ‘冒泡排序
Public Shared Sub Sort(ByVal list As Byte())
Dim i As Integer, j As Integer, temp As Integer
Dim done As Boolean = False
j = 1
While (j < list.Length) AndAlso (Not done)
done = True
For i = 0 To list.Length – j – 1
If list(i) > list(i + 1) Then
done = False
temp = list(i)
list(i) = list(i + 1)
list(i + 1) = temp
End If
Next
j += 1
End While
End Sub
End Class

Public Class SelectionSorter ‘选择排序
Private Shared min As Integer
Public Shared Sub Sort(ByVal list As Byte())
For i As Integer = 0 To list.Length – 2
min = i
For j As Integer = i + 1 To list.Length – 1
If list(j) < list(min) Then
min = j
End If
Next
Dim t As Integer = list(min)
list(min) = list(i)
list(i) = t
Next
End Sub
End Class

Public Class InsertionSorter ‘插入排序
Public Shared Sub Sort(ByVal list As Byte())
For i As Integer = 1 To list.Length – 1
Dim t As Integer = list(i)
Dim j As Integer = i
While (j > 0) AndAlso (list(j – 1) > t)
list(j) = list(j – 1)
j -= 1
End While
list(j) = t
Next
End Sub
End Class

Public Class ShellSorter ‘希尔排序
Public Shared Sub Sort(ByVal list As Byte())
Dim inc As Integer
inc = 1
While inc <= list.Length \ 9
inc = 3 * inc + 1
End While
While inc > 0
Dim i As Integer = inc + 1
While i <= list.Length
Dim t As Integer = list(i – 1)
Dim j As Integer = i
While (j > inc) AndAlso (list(j – inc – 1) > t)
list(j – 1) = list(j – inc – 1)
j -= inc
End While
list(j – 1) = t
i += inc
End While
inc /= 3
End While
End Sub
End Class

Public Class quicksort’快速排序
Private Sub sort(ByRef arrValue() As Byte, ByVal intLx As Int32, ByVal intRx As Int32)
Dim strValue As Byte
Dim I As Int32
Dim j As Int32
I = intLx
j = intRx
Do
While I < j AndAlso arrValue(I) <= arrValue(j)
I += 1
End While
If I < j Then
strValue = arrValue(I)
arrValue(I) = arrValue(j)
arrValue(j) = strValue
End If
While I < j AndAlso arrValue(I) <= arrValue(j)
j -= 1
End While
If I < j Then
strValue = arrValue(I)
arrValue(I) = arrValue(j)
arrValue(j) = strValue
End If
Loop Until I = j
I -= 1 : j += 1
If I > intLx Then Call quicksort(arrValue, intLx, I)
If j < intRx Then Call quicksort(arrValue, j, intRx)

End Sub
End Class

End Class

不至于的吧。。。。

0

也不只是真发火了还是什么。。。。心里一震的感觉。

是,有过不愉快的过去。以我对你的了解,你应该不会记仇的吧。。。。

 

或许这才是禁不起一点的触碰。。。。

 

心乱了,好可怕。

程序员新年要做的10个决定

0

还剩不到一周的时间,我们将迎来2012年。我知道这是老生常谈,但今年你曾经有过什么样重要的经历?很自然的,我们现在正处于为新年许下目标的时候。也许你可能有“真实”生活中的一些目标,但是你的程序员生涯呢?

 

1 -学习一种新语言,框架或方法

我们必须保持学习最新的技术。

Web开发唯一不变的是变化。以NodeJS为例:两三年前它并不存在,只有很少(如果有的话)的JavaScript 代码运行于服务器之上。现在,你离不开它。每个Web开发人员都希望留在自己的作品里。要做到这一点,我们必须不停学习最新的技术。如果你是一个后端开 发,这可能代表着学习JavaScript和Node.js,类似于Ruby和Rails。对于前端开发,则可能意味着真正理解CSS3或掌握HTML5 新的API。当然,这并不意味着你必须经常使用它,只需要保持自己不断学习。

按照同样的思路,对于重新评估你的工作流程、学习更好的和不同的工具以更快速地完成工作来说,现在是一个很好的时间。

2 –让你掌握的内容更精深


程序员新年要做的10个决定


也要预留一段时间来关注现有的语言和软件。

当然,保持锋利比学习新的内容有更大意义。它也包括改善你使用日常工具的方式。我知道自己一直坚持使用熟悉的的模式和方法而不是学习在某个特定情形下更佳的方案是不对的。你知道关于JavaScript设计模式的那些事吗?你对PHP的面向对象和面向功能编程是否有扎实的理解?你是否曾经使用过SQL连接?你使用的文本编辑器里是否有没有用过的功能?这些都不是新技术,但是如果你不使用它们,它们对你来说就是全新的!也要在关注现有的语言和软件上预留一段时间。

3 -探索一个新的领域

这项内容与第一项并不相同。学习一种新的语言、框架,或自己领域里的方法是重要的,甚至对你的日常实践来说可能会有用。 但如果你像我一样,你会迷上网络上的每一部分。要尝试探索新的领域。对后台开发来说是深入研究前端开发,而对前端来说是探索可用性或用户体验。如果你喜欢 写作,你可能会对内容策略或培养设计灵感感兴趣。在网络上有几十个不同的领域,继续探索!

4 –参与社区



把它叫做参与,把它叫做结网,把它叫做任何你想要的。

该网站极度令人不可思议:我想不出有任何其他的现象,人与人之间相隔那么远,却结下如此深厚的友谊。2012年,你为什 么不更多的尝试参与到这个令人惊讶的团体中呢?在Twitter上与他们交谈,阅读他们博客的文章和评论,或者通过自己写文章来作为回应;通过 Github或其他代码共享网站贡献自己的代码。或者加入自由讨论会,用户组和各种会议。把它叫做参与,把它叫做结网,把它叫做任何你想要的,但有一点是 肯定的:它会在大多数情况下让你和其他人受益。此外,建设新的重要人际关系,你可能会获得新的转机!

5 –教导他人

 

最有益的评论是伤害你感情的那些。

你应该认同我们之前的观点,在2012年更多的教导他人。为什么呢?它将会带来些什么呢?“教导是最好的学习”?我已经 为Nettuts +写作了近三年,我可以证明,这种说法是完全正确的。写下一个原理是如何工作的能够迫使你完全理解它,当你尝试教导别人时,你会惊讶对这个话题更了解了多 少。最重要的是,当你知道曾经帮助别人学习新的技能时你会有不可思议的感觉。毫无疑问,你将遇到几个反对的声音,例如指出你语法的错误(或只是一些害群之 马的评论)。不用有太多的担心;教学是一个学习的过程,你这样做会提高你自己。最有益的评论是伤害你感情的那些。

6 -更好地照顾自己

我们是网络的受害者。

我们作为Web开发似乎非常自豪,因为我们献出自己的劳动。我们工作时间超长,弓着腰在黑暗中使用计算机,我们忘记了洗澡或吃饭。我们是网络的受害者,忍受着痛苦来让互联网更加美好。

听起来很勇敢,但确实不是这样。

为你的风险考虑,我建议你在2012年照顾好自己。除了睡得好,吃得好,确保你的工作场所符合人体工学。按理说,如果你花了生命中的三分之一在办公室,使其尽可能舒适是很有道理的!

7 –更好的管理自己的时间(以及其他资源)

也许我并不是指和Web开发人员有很大关系,但尽管如此,几乎每一个“知识工作者”可以做得更好。对于我们里的很多人,尤其是自由职业者来说,你要用你的时间来做的事可能是灯红酒绿和饥肠辘辘之间的差异。记住所有的这些乐趣,以及我向你推荐学习的Web新技术了吗?好了,不要让它们的引诱限制你的学习时间。当然互联网越大越会有更多诱惑。我敢肯定,你最近看到了下面的内容,当我这么做时它给了一个暂停。

在过去的一年里,超过1万亿美元的视频在Youtube上被观看。

假设平均YouTube视频是2 – 3分钟之久,我们正在寻找的东西可以花上一整天的时间。在知识方面我还没有足够精深。

当然,“只是工作,不要娱乐”,完全按此行事是正确的吗?我不建议你成为客户的一个奴隶或者是不能忍受沉闷的工作狂。我只是说,我们需要更明智的来明确了解我们每小时是如何度过的,并努力的更好利用时间。

8 –采用更好的编程实践

过度的文档永远不会是坏事。

不,我不是在用两个不同的词汇重复同一个决定。这一次,我谈论的做法围绕编码本身的实践。我不能告诉你我有多少次热切地 开始了新的项目,然后一个半小时后说,“嗯。。。我要为尝试这个功能创建一个分支。噢,等一下,我忘了开始时初始化Git。。“确保我记得从一开始就使用 代码版本是我在2012年还要去面对的工作,它能使你的项目历史更清晰。

另一个我经常忽略的基本编码实践是注释。我可以变出许多行巧妙的代码,然后在剩下的时间里为其他事情分心。第二周我回到 工作里时花了20分钟试图弄清楚它。这会困扰你吗?请你给自己帮个忙,为自己和其他人留下有用的注释。文档和注释是同样正确的事情。我最近在学习 Dojo,我发现它的代码文档是无价的。当然,文件的水平将取决于你的项目的公开程度,但是我认为过度的文档永远不会是坏事。

9 -产生被动收入

我猜Nettuts +的绝大多数读者做客户端的工作,无论是作为一个自由职业者或以其他方式。那么为什么不另外获取一些被动收入呢?Envato(译者注:自由职业者创建的著名公司,旗下有销售类、教程类和博客类的网站)有十个市场,在这里任何具有合适技能的人都可以获利。为ThemeForest(译者注:国外知名设计网站)构建一个主题,为CodeCanyon写一个脚本,其潜在价值永无止境。当然,如果你的技能不适合Envato市场,或者即便可以,还有其他许多方法可以产生被动收入。例如如果你是一个作家,可以试试Tuts+ Premium(译者注:一个学习设计、Web开发技术的网站)。他们一直在寻找充满激情的新教师。


在市场或个人网站上销售项目是一个聪明的方式,可以被动地获取一些额外的现金,同时你还能做自己享受的事情。

10 –给自己放个假

戴上一顶完全不同的帽子…在有些时候

到目前为止,前面的每一个决定是你可以做的一些事情来改善你作为开发者的技能。我会密切指出,你要成为一个优秀开发者可 以做的最好的事情之一就是不要成为一个开发者。。。在有些时候。有时候需要戴上一顶完全不同的帽子。保持甚至和开发毫不相关的一项爱好,而且最好不要涉及 电脑。玩某种乐器,阅读,写作,开拓厨艺。不管你做什么,留下一些放松的时间。当你这样做时会发现,编程问题的解决方案往往出现在休息时间。

我曾经用在开车回家路上的思考解决了许多代码问题。

当然,定时休息是很重要的,还有那些一年几次更长时间的休假。此外扔了那些年度计划!

你的决定?

好了,这就是Web开发者应做的十项决定的清单。你觉得有哪些不在我的名单上?让我们在评论中来听听这些意见!

蒋宇捷译自:http://net.tutsplus.com/articles/general/ten-new-years-resolutions-every-web-developer-should-make/
安德鲁 伯吉斯于2011年12月27日
原文链接:http://blog.jobbole.com/11810/

[信息图表]谁搞死了你的软件?

0

谁搞死了你的软件?讽刺的是,恰恰是软件本身。大量的软件在未得授权的情况下被使用,也即是我们常说的盗版。它们增加了每一个人的安全风险,并与黑客产业链紧密相连,让众多“程序猿”兄弟面临下岗的窘境。我们可能每个人暂时节省了一笔小钱,但对社会来说,亏大了。

1956年的5MB“大”硬盘 来自IBM

0

还在抱怨 3.5 吋的硬盘盒不方便携带或是云端空间一点都不够用吗?这台IBM 的 305 RAMAC(Random Access Method of Accounting and Control)想必可以完全改变你的想法。

 

照片中这「块」嗯… 应该说是这台 1956 年的 IBM 硬盘,内部主要是由 50 片 24 吋的磁盘所构成。这个 16 平方英尺的庞然大碟,除了每年固定耗费 US$35,000 外,而且还仅具有 5MB 的储存空间,姑且不论当时的高科技的在现在看来有多么渺小(一首 MP3 顶多吧?),但 RAMAC 其超过一吨的重量却肯定让你无法忽略它的存在。现在,让我们开始感谢科技的进步吧!

全球范围60秒钟内发生的与科技相关的有趣事情

0

在60秒钟内,苹果可以卖出925 部iPhone 4s和85台iPad。是不是觉得这不可能?上图是Go-Gulf网站给我们做出的一张信息图表,它向我们体现了在60秒钟内会发生的19件有趣的事情。60秒钟在全球范围内发生的事情:

 

-全球售出710台电脑,其中有555台用的是Intel的处理器
-生成1820TB的数据,足以录入2600万张CD之中
-232台电脑受恶意软件感染

-450张Windows 7 Cd售出
-925 部iPhone 4s和85台iPad售出
-11台Xbox 360售出

-18台Kindle Fire售出
-产生38吨电子垃圾
-4000部USB设备售出

-2500个墨盒售出
-Foursquare网站有2100次用户注册
-谷歌收入增加75,000美元

-200万网络用户在线收看色情内容
-即时通许工具上发生110万次对话
-103部黑莓手机售出

-玩家在Farmville农场游戏中耕种1100英亩的“土地”
-PayPal处理交易额高达219,000美元,其中有10,000是通过PayPal mobile完成
-eBay上完成950次购买,其中有180次购买是通过eBay Mobile完成

-黑客攻击12个网站,而Facbook账户遭受416次尝试攻击
-在线电影租借服务Redox租出1400张电影光盘

当string LoveYou(char* Me){}返回的不是”LoveYou2″

0
有的女人就像Windows,虽然很优秀,但是安全隐患太大.
有的女人就像UNIX,她条件很好,然而不是谁都能玩的起.
有的女人就像C#长的很漂亮,但是家务活不行.
有的女人就像C++,她会默默的为你做很多的事情.
有的女人就像JAVA,只需一点付出她就会为你到处服务.
有的女人就像JAVA Script,虽然对她处处小心但最终还是没有结果.
有的女人就像汇编 虽然很麻烦,但是有的时候还得求它.
有的女人就像 SQL,她会为你的发展带来莫大的帮助.
爱情就是死循环,一旦执行就陷进去了.
爱上一个人,就是内存泄露,你永远无法将他释放.
真正爱上一个人的时候,就像是常量限定,永远不会改变.
女朋友就是私有变量,只有我这个类才能调用.
情人就是指针用的时候一定要注意,要不然就带来巨大的灾难
我能抽象出整个世界….
但是我不能抽象出你….
因为你在我心中是那么的具体….
所以我的世界并不完整….
我可以重载甚至覆盖这个世界里的任何一种方法….
但是我却不能重载对你的思念….
也许命中注定了 你在我的世界里永远的烙上了静态的属性….
而我不慎调用了爱你这个方法….
当我义无返顾的把自己作为参数传进这个方法时….
我才发现爱上你是一个死循环….
它不停的返回对你的思念压入我心里的堆栈….
在这无尽的黑夜中….
我的内存里已经再也装不下别人….
我不停的向系统申请空间….
但却捕获一个异常——你爱的人不爱你….
为了解决这个异常….
我愿意虚拟出最后一点内存….
把所有我能实现的方法地址压入堆栈….
并且在栈尾压入最后一个方法——将字符串"我爱你,你爱我吗?"传递给你….
如果返回值为真--我将用尽一生去爱你….
否则——我将释放掉我所有的资源….
Go to Top