[转载]helloPe的android项目实战之连连看

mikel阅读(1172)

[转载]helloPe的android项目实战之连连看 – HelloPe – 博客园.

此篇为本系列文章第一篇,只为记下Android小项目的是设计,实现过程。本文将首先介绍连连看项目的设计,以及涉及到的相关的内容与项目中所需算法.

本系列文章记录只是为了增加Android项目实战经验,将所学的知识用于相应的项目开发当中。首先介绍一下android中连连看项目的架构及所用到的技术进行简要分析,框架基本如下图所示:

本程序主要包含两大模块:即(1)表示层模块;(2)后台逻辑模块;

其中表示层模块可以理解为游戏的UI及一些游戏辅助效果,表示层模块中,重要的是实现游戏的布局地图,项目中实 现中,游戏的布局将使用自定义View的方式,在屏幕上贴图实现。而菜单模块及选关的dialog,只是为用户提供一些常见的选择,如重玩,过关继续,音 效开关等等,为了有一个更好的用户交互环境,dialog的实现将通过自定义dialog的方式。而游戏音效是MediaPlayer在不同的状态场景下 播放不同的游戏音效。

后台逻辑模块中,即时对于程序计算的实现与程序各种状态的监听,将是整个程序运行的基础。此模块中将实现对于 游戏剩余时间限制和游戏状态的监听与处理。对于游戏剩余时间的监听,将开启单独的线程进行处理,从而不至于影响主程序逻辑的运行;游戏的状态的监控处理 中,将会实现对于连通的两个图标的消除(即游戏界面的更新),游戏输赢的监听判断,游戏暂停与否等(暂停状态需要同时将剩余时间暂停,而时间监听线程需要 知道所处状态,此二者紧密联系)。对于本程序中最重要的还是程序中核心算法模块的实现,在游戏中,最主要的算法是判断两个选中的图标是否能够连通,其余两个算法也依赖于此算法而进行。下面着重介绍一下连接算法:

在介绍连接算法之前,先简单介绍一下连连看的布局算法,为了简单起见,我们使用4*4的棋盘,假设棋子有四种:

首先在程序初始化时,我们先将要加载的图片在棋盘上按序绘制出来,注意每一种图标我们绘制的时候需要一次性绘制两次,这样,才能保重绘制出来的每种图标的个数都是偶数个。假设最初如下图(1):

图(1)最初绘制                     图(2)调换后棋盘

这样绘制后,我们进行一次遍历,随机的调换棋盘中的图标(是现有棋盘中的图标之间的调换,并不是更改成为其他的图标)。经过调换的棋盘可能如图 (2)所示这样就完成了棋盘的初始化,当然我们的棋盘在最外面一层中是不添加图标的,为的是我们连线时候能够使用最外层画线,而不会出现穿过图标画线的情 况,棋盘如下图:

现在看看连接算法的原理:

首先两个图标能够连接的充分条件是:(1)两个图标是相同的;(2)两个图标之间有一条路相连,其中这条“路上”没有其他的图标“阻碍”;(3)这 一条路不能有两个以上的拐角;满足这三个条件即可认为两个图标是相连通的;对于连通的判断中,图标连通时有三种情况,分为以下:

(1)直线型:这就是横向或者是纵向方向判断即可,这种情况最容易判断,只要两者之间没有其他图标即可,就不多说;

(2)一折型:其实在两个选中图标确定的两个对角顶点画一个矩形,若是其余两个顶点中有能够满足与两个选中图标都能够“直线型”相连的,即可认为此两个选中图标可以相连,实例如下,判断两个红色的图标相连的情况:

一折型的示例

(3)二折型:对于二折型的判断是重点。判断二折型主要是做两个方向的扫描,即横向扫描与纵向扫描:

首先说横向扫描:

需要判断的两个红色图标                进行横向扩展                              扩展中两点能够满足直线型相连

横向扫描中:首先将两个需要判断的比图标进行横向的扩展,扩张规则是在没有遇到其他图标时一直扩展,知道遇到此行的其他图标或者到达棋盘的边缘,扩 展后的点如图中X表示,如果扩展后的点种能够存在两点满足直线型相连通的情况,即可判断两个图标是可以相连通的,连通的画线也是根据这两个辅助点相连而成 的;

类似的,对于纵向扫描中:

需要判断的两个红色图标                  进行纵向扩展                           扩展中存在两点能够满足”直线型“

连连看连接算法到此也算完成了,至于hint的帮助算法,判断当前棋盘是否还有解的算法都是依赖于次算法。

此项目文章下一步将是进行每一个功能模块及算法的一步步实现。之所以写本系列的文章,为了记录android小项目的经历,增加实战的能力,做个总结。并不是为了做出多么新颖的项目,当然也向不少的网友学习了!

[转载]WordPress分享500多款国外WordPress经典主题 其之二

mikel阅读(777)

[转载]【Wordpress】分享500多款国外WordPress经典主题 其之二 – 悠游人生 – 博客园.

从国外网站上淘来的WordPress主题,以前都是放在本人博客xcodeland.mooo.com上的后来空间挂掉了就转到这里来与大家分享。

3dgreen

3dgreen

下载地址

3dcomputer1

3dcomputer1

下载地址

beauty-2dblog

beauty-2dblog

下载地址

pronews

pronews

下载地址

mp-tech1

mp-tech1

下载地址

greenzine

greenzine

下载地址

carspower

carspower

下载地址

load-2dmusic

load-2dmusic

下载地址

make-20money-20online

make-20money-20online

下载地址

business-2dideas

business-2dideas

下载地址

merc-20race

merc-20race

下载地址

art-2dstudio

art-2dstudio

下载地址

paradisecove

paradisecove

下载地址

elementary-2dfire

elementary-2dfire

下载地址

blue-20stripes

blue-20stripes

下载地址

lake

lake

下载地址

gamers-2dtheme

gamers-2dtheme

下载地址

moderninterior-202

moderninterior-202

下载地址

moderninterior-201

moderninterior-201

下载地址

vital

vital

下载地址

lightnight

lightnight

下载地址

moneytizeme

moneytizeme

下载地址

airplane-theme

airplane-theme

下载地址

freedom

freedom

下载地址

hiddensky

hiddensky

下载地址

lime

lime

下载地址

3color

3color

下载地址

zpoon-theme

zpoon-theme

下载地址

simplejojo

simplejojo

下载地址

mobilecity-pack

mobilecity-pack

下载地址

motorcycles

motorcycles

下载地址

wp-2dbridal-2djewelry

wp-2dbridal-2djewelry

下载地址

magonwood

magonwood

下载地址

photography-bliss-theme

photography-bliss-theme

下载地址

fashionmod

fashionmod

下载地址

lazyday

lazyday

下载地址

itheme-2dblue

itheme-2dblue

下载地址

wprefreshing

wprefreshing

下载地址

vintagebleau

vintagebleau

下载地址

flowers

flowers

下载地址

green-2dblog

green-2dblog

下载地址

time

time

下载地址

aim

aim

下载地址

ladybug

ladybug

下载地址

lazyman

lazyman

下载地址

flint-2dmountain

flint-2dmountain

下载地址

business-2dblue-2d20

business-2dblue-2d20

下载地址

vcreative

vcreative

下载地址

aquarium

aquarium

下载地址

grey-tutu

grey-tutu

下载地址

myfs-2dwildtux

myfs-2dwildtux

下载地址

sexyround

sexyround

下载地址

crispy-2dclean1

crispy-2dclean1

下载地址

business-magazine

business-magazine

下载地址

bigblueheaven-2d7

bigblueheaven-2d7

下载地址

greensense

greensense

下载地址

darksweetblogger

darksweetblogger

下载地址

777blue-2ddream

777blue-2ddream

下载地址

redworld

redworld

下载地址

come-2dmy-2dlady-2d11

come-2dmy-2dlady-2d11

下载地址

ultraweb

ultraweb

下载地址

business-2dtrend

business-2dtrend

下载地址

multiplayer-tech-1

multiplayer-tech-1

下载地址

orbitalred

orbitalred

下载地址

fresh

fresh

下载地址

mywall

mywall

下载地址

blackabstract

blackabstract

下载地址

dreamhome

dreamhome

下载地址

royal

royal

下载地址

digi-2dnet-2dblog

digi-2dnet-2dblog

下载地址

blue-2dcolumns

blue-2dcolumns

下载地址

healthy-living-202

healthy-living-202

下载地址

SEOblog2.0

seoblog2.0

下载地址

blacker

blacker

下载地址

nature

nature

下载地址

cute-2dred

cute-2dred

下载地址

ecoleaf

ecoleaf

下载地址

beauty

beauty

下载地址

blackwire

blackwire

下载地址

adulttheme001

adulttheme001

下载地址

cool-green-v1

cool-green-v1

下载地址

SEO-executive

seo-executive

下载地址

discovery-11

discovery-11

下载地址

sl-2ddocumenting

sl-2ddocumenting

下载地址

semlaguna-2dhalo

semlaguna-2dhalo

下载地址

areebu

areebu

下载地址

kayupress

kayupress

下载地址

orangehighlights

orangehighlights

下载地址

greenswirl

greenswirl

下载地址

interiordecor

interiordecor

下载地址

elegance

elegance

下载地址

realius-2d10

realius-2d10

下载地址

greenlog

greenlog

下载地址

metropolis

metropolis

下载地址

praca-2dpremium

praca-2dpremium

下载地址

cloudyday

cloudyday

下载地址

fish

fish

下载地址

simply-2dnews

simply-2dnews

下载地址

calmbreeze

calmbreeze

下载地址

lightbreaker

lightbreaker

下载地址

[转载]WordPress分享500多款国外WordPress经典主题 其之一

mikel阅读(1075)

[转载]【Wordpress】分享500多款国外WordPress经典主题 其之一 – 悠游人生 – 博客园.

从国外网站上淘来的WordPress主题,以前都是放在本人博客xcodeland.mooo.com上的后来空间挂掉了就转到这里来与大家分享。

wedding-2drose

wedding-2drose

下载地址

royale-20blue

royale-20blue

下载地址

lilyinterior

lilyinterior

下载地址

diarykey

diarykey

下载地址

wma

wma

下载地址

arc

arc

下载地址

mobileflow

mobileflow

下载地址

nickoftime

nickoftime

下载地址

black-buttons-theme

black-buttons-theme

下载地址

clubbing

clubbing

下载地址

wine

wine

下载地址

vintagemedical

vintagemedical

下载地址

soccerpro

soccerpro

下载地址

victorian

victorian

下载地址

conference

conference

下载地址

oldphotos

oldphotos

下载地址

wp-20vista

wp-20vista

下载地址

coffeecup

coffeecup

下载地址

stealthbike

stealthbike

下载地址

mom-2dblog

mom-2dblog

下载地址

benz

benz

下载地址

melodias

melodias

下载地址

xclure

xclure

下载地址

tuned-car-theme

tuned-car-theme

下载地址

the-simpsons-theme

the-simpsons-theme

下载地址

food-wordpress-theme

food-wordpress-theme

下载地址

diet-wordpress-theme

diet-wordpress-theme

下载地址

mybay

mybay

下载地址

chante

chante

下载地址

hydrophile

hydrophile

下载地址

tennis-8

tennis-8

下载地址

leaving

leaving

下载地址

nature

nature

下载地址

finalfantasy

finalfantasy

下载地址

wildkitty

wildkitty

下载地址

pconcept

pconcept

下载地址

pod-20girl

pod-20girl

下载地址

gadgets

gadgets

下载地址

newage

newage

下载地址

popidol

popidol

下载地址

diet1

diet1

下载地址

lilicious

lilicious

下载地址

liberty

liberty

下载地址

dog-2dlover

dog-2dlover

下载地址

sugaredspring

sugaredspring

下载地址

frozenlotus

frozenlotus

下载地址

niceday

niceday

下载地址

freshcrust

freshcrust

下载地址

dirty-green-wall-theme

dirty-green-wall-theme

下载地址

1st-2dclass-2dblue

1st-2dclass-2dblue

下载地址

puzzle

puzzle

下载地址

ultimateblogger

ultimateblogger

下载地址

old-paper-theme

old-paper-theme

下载地址

maidensofheavenlymountain

maidensofheavenlymountain

下载地址

battlefield-20report

battlefield-20report

下载地址

whitewires

whitewires

下载地址

fish

fish

下载地址

host2

host2

下载地址

funkylilly

funkylilly

下载地址

dog

dog

下载地址

cat2

cat2

下载地址

cat

cat

下载地址

webcatalog

webcatalog

下载地址

host1

host1

下载地址

host4

host4

下载地址

carbon-20page

carbon-20page

下载地址

protegidas

protegidas

下载地址

wow-20planet

wow-20planet

下载地址

street-20biker

street-20biker

下载地址

classica-20news

classica-20news

下载地址

wp-20final-20fantasy

wp-20final-20fantasy

下载地址

surf-20passion

surf-20passion

下载地址

hockey-20dreams

hockey-20dreams

下载地址

spider-2dblogger

spider-2dblogger

下载地址

shooter

shooter

下载地址

techmag

techmag

下载地址

shopping-mall-template

shopping-mall-template

下载地址

computers

computers

下载地址

greenxpro

greenxpro

下载地址

sea-2dedge

sea-2dedge

下载地址

design-2dpro-2d20

design-2dpro-2d20

下载地址

iorange

iorange

下载地址

adsense-2dready

adsense-2dready

下载地址

wp-2dgraduation-2dthemes

wp-2dgraduation-2dthemes

下载地址

greentec

greentec

下载地址

recipeforentrepreneur

recipeforentrepreneur

下载地址

trippygreen

trippygreen

下载地址

green-skin-care

green-skin-care

下载地址

wp-2dmedical-2ddoctor

wp-2dmedical-2ddoctor

下载地址

firetec

firetec

下载地址

watermelon3d

watermelon3d

下载地址

beach-2dday

beach-2dday

下载地址

amazing-2dlife

amazing-2dlife

下载地址

earth

earth

下载地址

lush-2dliving

lush-2dliving

下载地址

soccer

soccer

下载地址

tourist

tourist

下载地址

medicine

medicine

下载地址

abstractblu

abstractblu

下载地址

[转载]T-SQL查询进阶--基于列的逻辑表达式

mikel阅读(918)

[转载]T-SQL查询进阶–基于列的逻辑表达式 – CareySon – 博客园.

引言

T-SQL不仅仅是一个用于查询数据库的语言,还是一个可以对数据进行操作的语言。基于列的CASE表达式就是其中一种,不像其他查询语句可以互相替代 (比如用子查询实现的查询也可以使用Join实现),CASE表达式在控制基于列的逻辑大部分是无法替代的。下面文中会详细讲解CASE表达式。

简介

基于列的逻辑表达式,其实就是CASE表达式.可以用在SELECT,UPDATE,DELETE,SET以及IN,WHERE,ORDER BY和HAVING子句之后。由于这里讲的是T-SQL查询,所以只说到CASE表达式在SELECT子句和ORDER BY子句中的使用。

CASE表达式实现的功能类似于编程语言中的IF…THEN…ELSE逻辑。只是CASE表达式在T-SQL中并不能控制T-SQL程序的流程,只是作为基于列的逻辑使用.

一个简单的CASE表达式如下:

我已经知道员工ID对应的姓名,我想获得员工ID,并将员工ID以姓名的方式展现出来,我不知道的员工ID则显示UNKNOW:

SELECT TOP 4 CASE EmployeeID
	WHEN 1 THEN 'CareySon'
	WHEN 2 THEN 'Jack'
	WHEN 3 THEN 'Tom'
	ELSE 'UNKNOW'
	END AS NameList,EmployeeID
  FROM [AdventureWorks].[HumanResources].[Employee]
  ORDER BY EmployeeID

显示结果如下:

1

上面代码中,CASE后面跟选择的列名,后面的WHEN所取得值都为EmployeeID这一列,THEN后面的值为对应前面WHEN后面列中,实际在结果中显示的值。

CASE表达式实际情况可以分为两种:

CASE简单表达式(CASE Simple Expression):将某个表达式与一组简单表达式进行比较以确定结果。

CASE 搜索表达式(CASE Searched Expression):计算一组布尔表达式以确定结果。

下面会按照这两种CASE表达式来阐述

CASE简单表达式(CASE Simple Expression)

在CASE简单表达式中,整个表达式只会取一列的值做相应的判断,上面那个查询例子就是一个CASE简单表达式,可以用下图表示:

2

CASE表达式也可以用这样的写法:

SELECT TOP 4 NameList=CASE EmployeeID
	WHEN 1 THEN 'CareySon'
	WHEN 2 THEN 'Jack'
	WHEN 3 THEN 'Tom'
	ELSE 'UNKNOW'
	END,EmployeeID
  FROM [AdventureWorks].[HumanResources].[Employee]
  ORDER BY EmployeeID

上面代码和前面代码所达到的效果是一模一样的,从这个代码可以看出,CASE表达式的结果实际上只局限在一列当中,这也是为什么CASE表达式是“基于列的逻辑表达式”

因为CASE表达式的值只局限在一列当中,所以THEN后面的值数据类型必须相同,或者兼容,否则就会报错。

在上面语句中,还有一个可选的“ELSE”语句,这个语句可以省略,但最好的做法是保留ELSE,否则不在匹配值范围内的所有值都会为“NULL”。

CASE搜索表达式(CASE Searched Expression)

与CASE简单表达式不同,CASE搜索表达式提供了更强大的功能,CASE搜索表达式不仅可以使用更复杂的逻辑表达式,并且还能根据多列中的数据确定所显示列的值。与上面CASE简单表达式等效的CASE搜索表达式为:

SELECT TOP 4 NameList=CASE 
	WHEN EmployeeID=1 THEN 'CareySon'
	WHEN EmployeeID=1 THEN 'Jack'
	WHEN EmployeeID=3 THEN 'Tom'
	ELSE 'UNKNOW'
	END,EmployeeID
  FROM [AdventureWorks].[HumanResources].[Employee]
  ORDER BY EmployeeID

CASE搜索表达式更复杂的应用比如:

公司规定每个人病假或者休假每年都不应该超过30个小时,现在我想取得所有男性员工的ID,其中员工病假或者是休假任意一项超过了30个小时,标记为“Exceed the time”,两项都不超过30个小时的,标记为“Not Exceed the time”

SELECT EmployeeID,
      CASE 
      WHEN VacationHours>30 AND Gender='M' THEN 'Exceed The Time'
      WHEN SickLeaveHours>30 AND Gender='M' THEN 'Exceed The Time'
      ELSE 'Not Exceed The Time'
      END AS Condition
  FROM [AdventureWorks].[HumanResources].[Employee]

查询结果如下:

3

上面可以看到,搜索表达式一列的WHEN表达式可以取自不同列,甚至是不同列之间的运算(比如上面可以取WHEN VacationHours+SickLeaveHours>60),这大大增强了CASE表达式的功能,因为CASE搜索表达式可以完全实现 CASE简单表达式所能实现的功能,我个人认为所有的CASE表达式都应该写成CASE搜索表达式的形式。

还有要注意WHEN…THEN是以先后顺序出现,当第一个WHEN后面的表达式为FALSE时,则会去看第二个WHEN后的表达式,依次类推。当第一个WHEN后的表达式为TRUE时,则取第一个THEN后面的值,即使第二个WHEN表达式也为TRUE。

例如还是第一个例子:我已经知道员工ID对应的姓名,我想获得员工ID,并将员工ID以姓名的方式展现出来,我不知道的员工ID则显示UNKNOW:

SELECT  NameList=CASE
	WHEN EmployeeID=1 THEN 'CareySon'
	WHEN EmployeeID=1 THEN 'Jack'
	WHEN EmployeeID=3 THEN 'Tom'
	ELSE 'UNKNOW'
	END,EmployeeID
  FROM [AdventureWorks].[HumanResources].[Employee]
  ORDER BY EmployeeID

结果如下:

4

CASE表达式在ORDER BY中的使用

CASE表达式在ORDER BY中可以将排序结果分类,使符合某些条件的行(Row)采用一种排序方式,符合另一种条件的行采用另一种排序方式:

比如:我想查看省份ID为8和9的员工的地址,当省份ID为9时,按照AddressID降序排列,当省份ID为8时,按照AddressID升序排列

SELECT [AddressID]
      ,[AddressLine1]
      ,[City]
      ,[StateProvinceID]
      FROM [AdventureWorks].[Person].[Address]
WHERE StateProvinceID=9 OR StateProvinceID=8
ORDER BY  
         CASE WHEN StateProvinceID=9 THEN AddressID END DESC,
         CASE WHEN StateProvinceID=8 THEN AddressID END

结果如下:

5

注意这里,每一条排序规则都要写一个单独的CASE表达式,前面文章说了,因为CASE表达式是基于列的,一个CASE表达式只能返回一个值,所以基于多少个值排序,则需要多少个CASE表达式

总结

文章讲述了CASE表达式在SELECT子句中和ORDER BY子句中的使用,CASE表达式又进一步分为CASE简单表达式和CASE搜索表达式。掌握使用CASE表达式可以使程序员将一部分需要在程序中的业务 逻辑移到数据库中。掌握CASE表达式是深入学习T-SQL查询必不可少的。

[转载]疯狂的 Web 应用开源项目

mikel阅读(1241)

[转载]疯狂的 Web 应用开源项目 | 酷壳 – CoolShell.cn.

下面是一个Web应用的开源列表。没什么可说的,太疯狂了。尤其是Web 2.0那一堆。我不知道你怎么想,有些开源项目的源码写得挺不好的,尤其是性能方面。或许你会以为改一改他们就可以成为为自己所用,不过,改这些开源的项目还真不容易。玩玩还可以。

数字媒体

  • 相册(Flickr, Picasa)
    • Gallery,基于PHP + MySQL的Web相册。非常易于使用,包括一个配置向导,对于相片的操作包括自动生成缩略图、相片的大小改变、选择、排序等。
    • Piwigo,基于PHP + MySQL。配备了强大的功能,发布和管理您的照片,可扩展性和智能浏览功能,如类别,标签,或年表。这是网络和照片的标准要求。扩展使Piwigo更可扩展性和可定制的。
    • UberGallery,一个简单易用的相册。PHP。不需要数据库。
    • Zenphoto,一个简单的web相册程序,它能够简单的展示你的图片,并含有你所需要的所有功能和特点。可以和Wordpress集成。
  • 视频(YouTube)
    • Flowplayer,一个用Flash开发的在Web上的视频播放器,可以很容易将它集成在任何的网页上。支持HTTP以及流媒体传输。
    • Plumi,一个建立在Plone 内容管理系统上的视频分享系统,可帮助你轻松建立视频分享网站
  • 音乐电台社区(last.fm, ulike)
  • 视频电影社区(netflix, criticker)
  • 期刊参考论文数据库(Emerald Insight, Springer Link)
    • CiteSeerX, 采用机器自动识别技术搜集网上以Postscrip和PDF文件格式存在的学术论文,然后依照引文索引方法标引和链接每一篇文章。(其是CiteSeer 的换代产品。1997年,CiteSeer引文搜索引擎由NEC公司在美国普林斯顿研究所的三位研究人员Steve Lawrence, Lee Giles和Kurt Bollacker研制开发。它是利用自动引文标引系统ACI(Autonomous Citation Indexing)建立的第一个科学文献数字图书馆(Scientific Literature Digital Library))。
  • 地图(Google Maps)
    • OpenStreetMap,一个可供自由编辑的世界地图,它是由所有的用户创造的。OpenStreetMap允许您查看,编辑或者使用世界各地的地理数据来帮助您。其就像Wikipedia一样,全世界的人都可以编辑,据说其上面的数据超过了政府的数据。当然,目前其参与的人数还不够,大量的地方都是白板。

文件存储

  • 文件共享/同步(DropBox, drop.io, Ubuntu One)
    • Tahoe Least-Authority Filesystem,一个云存储分布式文件系统。
    • iFolder,一个简单安全的存储解决方案,可在计算机间文件的同步和分享。可以用来随时备份本地的文件。
  • 在线文件编辑(Google Docs)
    • AbiCollab,基于AbiWord的社群的线上文书处理协作服务。
    • Etherpad,基于开放软体的线上文书处理服务,最大的特色在于多人即时共同协作一份文件,软体组织不直接提供服务,而是透过其他没有连系的组织网站提供。
  • 虚拟机供应(Amazon EC2)
    • Eucalyptus (computing), 是一用来通过计算集群或工作站群实现弹性的、实用的云计算。它最初是美国加利福尼亚大学 Santa Barbara 计算机科学学院的一个研究项目,现在已经商业化,发展成为了 Eucalyptus Systems Inc。不过,Eucalyptus 仍然按开源项目那样维护和开发。Eucalyptus Systems 还在基于开源的 Eucalyptus 构建额外的产品;它还提供支持服务。
    • Globus Toolkit,Globus项目工具包,其可以在计算机上提供稳定、安全和对等网络的分布式运算,集群和其它高性能系统功能。
    • OpenNebula,一个虚拟基础设备引擎, 用来动态布署虚拟机器在一群实体资源上,OpenNEbula 最大的特色在于将虚拟平台从单一实体机器到一群实体资源。

内容服务

  • Wiki(Wikispaces)
    • Dokuwiki, 一个针对小公司文件需求而开发的Wiki引擎。DokuWiki是用程序设计语言PHP开发的并以GPL 2发布。DokuWiki基于文本存储,所以不需要数据库,其数据文件在Wiki系统外也是可读的。DokuWiki的功能齐全,支持UTF-8,最新版 支持中文链接。能够单独编辑页面中的某个章节,能够自动生成目录,适合中小企业、个人使用,用作资料归档、指南、读书笔记等。DokuWiki安装很简 单,默认提供配置工具。
    • Mediawiki, 是一套基于网络的Wiki引擎,维基媒体基金会的所有项目乃至众多wiki网站皆采用了这一软件。MediaWiki软件最初是为自由内容百科全书维基百 科所开发,今日已被一些公司机构部署为内部的知识管理和内容管理系统。Novell甚而还在多个高流量的网站中使用了该软件。
    • μWiki,一个小巧而功能齐全的wiki,所有的代码才3500行,可通过facebook和openID认证。
  • 出版
    • Topaz
    • Ambra,是一个期刊管理与发布系统。它具有一个高容量、高效、经济的系统来在所有科学领域发表研究文章。
    • Open Journal Systems,简称OJS,此系统是一个开源码的期刊管理与出版软件,由公共知识项目(PKP; Public Knowledge Project)研发与支持。(中国肺癌杂志使用了这个系统)
  • Blog
    • WordPress,这个不用说了吧。
    • LiveJournal,一个综合型SNS交友网站,有论坛,博客等功能,Brad Fitzpatrick始建于1999年4月15日,目的是为了与同学保持联系,之后发展为大型网络社区平台,是网友聚集的好地方,LJ支持多国语言,ALEXA综合排名84 ,日均访客可达6,288,000以上
  • 微博 (Twitter)
    • Jisko, 界面和Twitter很像,集成Twitter同步功能,它能够自动将你在Jisko平台上发布的内容发表到您的Twitter账户上。也能够自动读取您 的Twitter更新,但是并不能将这些内容发布到Jisko平台,只能在自己的好友Timeline里查看。Jisko平台还能够连接您的 Jabber/GTalk账户,让您通过IM发帖。并且有数个缩链服务供选择,十分实用。
    • Jaiku Engine,Google 曾经收购的类Twitter平台Jaiku现在已经完全开源并且切换AppEngine上运行,早前Google曾经宣布停止Jaiku等项目的维护和开 发,现在更将Jaiku完全开源提供用户免费下载,所有人都可以在自己的主机上建立和运行自己的Jaiku应用了。
    • Status.net,一个开源微博服务。同时,它又可将信息同步到Twitter。所以我们也可以把它理解为“开源的Twitter客户端”。但它与客户端又有本质的不同:拥有自己的数据库,只是把数据同步到推特而已。
  • 网页访问量统计(Google Analytics)
    • Piwik, 一套基于Php+MySQL技术构建,能够与Google Analytics相媲美的开源网站访问统计系统,前身是phpMyVisites。Piwik可以给你详细的统计信息,比如网页浏览人数, 访问最多的页面, 搜索引擎关键词等等,并且采用了大量的AJAX/Flash技术,使得在操作上更加便易。此外,它还采用了插件扩展及开放API架构,可以让开发人员根据 自已的实际需求创建更多的功能.
    • Open Web Analytics, 一个开源的网站流量统计系统。基于PHP/Open Flash Chart/Ajax技术开发,既可以单独使用也可以与WordPress、Gallery&MediaWiki集成使用。支持多个网站,集成 Google Maps,RSS/Atom订阅跟踪等功能。
  • 虚拟主机平台(Google AppEngine)
    • AppScale,是一个平台,允许用户发布和托管自己的 Google App Engine 的应用程序。支持 Python, Java, and Go Google App Engine 平台。
  • 办公(Google Docs)
    • Zimbra Collaboration Suite,其英文缩写为ZCA。全功能的通信及协作办公应用程序,提供可靠和高效能的邮件、地址簿、效率手册、任务列表以及网络文档制作功能。
    • PHPGroupware,功能强大,基于Web的Messaging ,Collaboration和企业管理平台。phpGroupWare包含50多个模块可根据你的需求进行搭配与组合。它提供了约50种基于网络的应用,有日历,通讯录,先进的项目经理,待办事项列表,笔记,电子邮件,新闻组和新闻阅读器,一个文件管理器和更多应用。
    • OpenGoo,Fengoffice, 基于ExtJs+XAMP(Apache、PHP、MySQL)开发的开源web office。它具备了主流在线协作系统所应具备的所有功能,包括任务管理、日程管理、文件管理、联系人管理以及email收发功能。其文件管理模块,实 现了文件版本管理的功能,能够方便的查找、下载同一文件的不同版本。适用于任何单位或个人创建,共享,协作维护和发布它们所有内部与外部文档。
    • Crabgrass,社会网络,小组协作,网络组织的Web应用程序。它由一组协作工具固体套件,如私人维基,任务列表,文件库,和决策工具。程序目前正在做了大量的用户界面改革,更完善的社会网络工具,博客和活动日程,以及更好的协作和决策制定各独立团体的支持。
    • Etherpad, 由两位Google 前员工所开发,已被Google 收购成为开放原始码项目。主要功能是让多个使用者透过网路来共同编辑一份文件,与先前介绍过的Sync.in 好用的线上即时文件协作平台类似。EtherPad 无须注册就能使用,建立文件后会产生一个网址,其它用户可以透过该网址与你编辑同一份文件,并标记出不同用户所编辑的位置,也有提供汇入汇出及时间轴等功 能。

Groupware群件

  • Webmail (gmail, hotmail)
    • Zimbra,强大的开源协同办公套件包括WebMail,日历,通信录,Web文档管理和创作。它最大的特色在于其采用Ajax技术模仿CS桌面应用软件的风格开发的客户端兼容Firefox,Safari和IE浏览器。
    • Roundcube, 支持多国语言的IMAP客户端,操作界面看起像一个桌面应用程序。它提供一个e-mail客户端应该具备的所有功能包括MIME支持,地址薄,文件夹操 作,信息搜索和拼写检查。RoundCube Webmail采用PHP+Ajax开发并且需要MySQL数据库来存储数据。 用户界面采用XHTML+CSS2设计。
    • conjoon,基于Ext JS+PHP/MySQL开发的Webmail和RSS客户端阅读器。此外还包含一个联系人管理模块。
    • Tdah, 一个PHP Webmail系统。该系统采用POP3协议收邮件,可以配置使用SMTP、PHP mail、Sendmail或Qmail来发送邮件。T-dah还包含以下几个模块:事件日历、群组聊天、文件夹管理、邮件搜索等。T-dah使用 TinyMCE WYSIWYG编辑器来创建新邮件。
    • Funambol,世界领先的开源云同步和PUSHMAIL工具,支持诸多手提移动设备,包括苹果、黑莓、Android、Windows Mobile、索爱、三星、诺基亚等20余款。
    • Hastymail,一个使用方便快捷、安全,跨平台的IMAP/SMTP客户端。采用PHP语言编写,运行于PHP+MYSQL平台环境。提供一个简洁的Web界面来发送和读取E-mail。
    • Xuheki,一个很快的IMAP 使用AJAX 技术开发的客户端。你能想到的功能它基本上都有了。
    • Claros,一个比较简单的,采用pop3/smtp收发邮件的webMail系统。不需要数据库的支持。提供一个独立于SMTP服务器的垃圾邮件过滤机制。
  • Email 服务器(MS Exchange)
    • Archiveopteryx,一个互联网归档邮件服务器,支持强大的归档功能。可以运行在Linux, FreeBSD, NetBSD, OpenBSD 和 Mac OS X。
    • Roundcube,提供一个e-mail客户端应该具备的所有功能包括MIME支持,地址薄,文件夹操作,信息搜索和拼写检查。RoundCube Webmail采用PHP+Ajax开发并且需要MySQL数据库来存储数据。 用户界面采用XHTML+CSS2设计。
    • Squirrelmail, 一款由PHP语言编写,基于标准的webmail软件包。它包括内建的纯PHP支持的IMAP和SMTP协议,所生成的页面绝对支持HTML4.0标准 (无需JavaScript支持),这样可以运行在更多的平台和更多的浏览器上。它的系统安装要求非常低,但是非常容易安装和配置。 SquirrelMail拥有你的客户端邮件程序所拥有的一切,比如增强型的MIME支持、地址薄、文件夹操作等等功能。
    • Horde Groupware Suite,一个强大的邮件办公套件。
  • 邮件列表 (Google Groups, Yahoo Groups)
    • Freelists
    • Mailman,管理电子信箱讨论和自由软件电子通讯清单。 支持内置的归档,自动退回处理,内容过滤,消化交货,垃圾邮件过滤器等。
  • 论坛 (vBulletin)(注:国内的主要是用Discuz!)
    • phpBB,中文的在这里http://www.phpbbchina.com
    • Phorum,基于PHP+MySQL开发的开源论坛项目。它的特点是速度快,功能强大,面向模块化设计,安装简单。此外Phorum还集成电子报。
    • Vanilla,是很多外国牛人都在用的一款开源论坛程序,它不像我们熟知的phpBB之类的或是类似我国discuz,phpwind的模式,而是采取了全新的内核和界面,界面类似于stackflow,所以用它来做一个社交性的问答网站也是个不错的选择。
    • Ospo,是一项开源社交门户站点方案。它拥有标准功能(添加、删除好友,前十排行榜),论坛整合、音乐模块(带有艺术家目录的专辑和歌曲)、广播心情整合、日志(添加、删除、修改、检查)等等众多功能。
  • 日历(cf, 30boxes, Google calendars, ScheduleWorld)
    • Zimbra Collaboration Suite,功能的通信及协作办公应用程序,提供可靠和高效能的邮件、地址簿、效率手册、任务列表以及网络文档制作功能。
    • Web Calendar,一款漂亮的Flash日历,可以添加在网页上,它可以高亮显示事件,会议,节假日的日期。
    • Funambol,世界领先的开源云同步和PUSHMAIL工具,支持诸多手提移动设备,包括苹果、黑莓、Android、Windows Mobile、索爱、三星、诺基亚等20余款。
    • Joyent Connector,免费提供Office 2.0的功能,如团队电子邮件、日程安排、相互联系、文档和书签。
    • Horde Groupware Suite,协同办公套件。
  • 会议和评审管理
    • Openconf,开源的会议管理系统,主要提供以下功能:电子提交、评审、论文答辩,以及会议主席对整个过程的管理等。
    • MyReview,学术会议的论文提交和论文评审。
    • EasyChair,会议管理系统。
    • CyberChair,论文提交和评审系统。
    • iChair,会议系统,支持论文提交,评审,讨论等。
    • Indico,会议计划,组织,支持从简单到复杂的会议。
    • ICEcore,开放团队合作软件使用社交联网统一团队工作空间、实时网络会议、项目管理、实践团体以及远程操作。
  • 反馈(Pollmonkey, Google Forms)
    • Limesurvey,前身为PHPSurveyor)是一款在线问卷调查程序,它用PHP语言编写并可以使用MySQL,PostgreSQL或者MSSQL等多种数据库,它集成了调查程序开发、调查问卷的发布以及数据收集等功能,使用它,用户不必了解这些功能的编程细节。
  • 其它
    • AROUNDMe, 可以创建像Ning, Myspace, Yahoo or Google groups一样的合作网站。每个群组可以创建多个网页,网页上包括留言簿、博客、论坛、维基百科等功能。每个群组还可以通过xHTML, CSS, JavaScript and PHP来进行自定义。
    • InteractOLE,是一款网络学习的递交和支持平台。与其他在线学习平台不同,InteractOLE致力于教学与学习的社交和互动方面,而不是向学生们学习内容的提供。

纯Web 2.0服务

  • Feed操作(Yahoo Pipes)
    • Deri Pipes,像Yahoo Pipes一样,可视化的在线编程工具,它是一个用于过滤、转换和聚合网页内容的服务。
  • Feed 聚合(Bloglines, Google Reader)
    • Newsblur,像Google Reader一样的一个RSS在线阅读器。
    • rsslounge,基于PHP+MySQL开发的RSS供稿阅读器。可以分类,过滤供稿,设置优先权。除标准的供稿项目之外,还支持图片/照片。
    • Tiny Tiny RSS,基于Web的RSS/Atom新闻聚合器。它的UI基于Ajax技术开发所以看起非常像一个桌面应用程序。
    • Lilina,一个开源的RSS新闻聚合器实现,功能强大,方便易用,而且最大的好处是不需要数据库支持。
    • OpenWebReader,多用户的RSS聚合阅读。
    • Gregarius,RSS/RDF/ATOM新闻聚合器支持OPML导入/导出,XHTML/CSS输出。它包含一个基于Ajax的itemtagging系统。
    • Cheetah News,利用AJAX技术构建的RSS阅读器,完美支持中文。
    • Memephage,是一种自动化网络日志。它能搜集并总结从不同地方收集来的连接,目前是从IRC, 社交MUD,邮件和浏览器中搜集,并使用POE多任务处理和网络框架。
    • Ozcode,是Ozmozr.com背后的源代码, 一个微型RSS聚合器,可以进行网络社交、信息分享、身份聚合与展示的网站。
  • 社区聚合(FriendFeed)
    • Identi.ca,一个新的微网志服务, 其实现在微博服务真的是很多了, 不过这个比较特别一点的是, identi.ca 用PHP 开发, 可以用jabber/GTalk, 也可以用openid 来登录,主要的是,其是开源项目。
    • Noserub,提供的建站程序,可以创建属于你的微型门户,包括 Blog、网络摘录、图片分享、视频、Twitter 等等的,都可以罗列出来,并且通过 RSS 实时更新内容,你的朋友们可以方便的获知你在网络里经常去哪里,最近在关注一些什么,做些什么,想些什么。
  • 社区新闻(digg)
    • Meneame,程序是类似Digg的西班牙程序,网址是http://websvn.meneame.net/
    • Pligg,最灵活的类似Digg的Web2.0 CMS系统!网页设计师可以使用Pligg做他(她)想做的任何事情。稍微懂一些PHP和Mysql的知识即可安装Pligg
    • Drigg,基于Drupal 构建的PHP的Digg网站系统。
    • Reddit,其源码和文档在这里:https://github.com/reddit/reddit
    • CommunityNews,通过使用社交书签和贝叶斯定理技术向博客定期提供记录。用户可以通过投票支持或反对RSS来源以支持那些受欢迎的资源。
    • NewsCloud,是一款基于NewsCloud.com专为平民新闻业和社会新闻网络设计的开源传媒平台。
    • Jamss,是基于Digg.com的社交新闻网站, 其通过PHP/MySQL运行。.Jamss 考虑到了行内意见和网络文章的评论,还可以灵活适应多种主题。
  • 社区网络(Facebook, Twitter)
    • Friendika, 一个由PHP+MySQL的免费应用程式(Open Source),提供使用者一个单一的界面来控制社群网路服务。支援的社群网路应用服务包括Facebook,Twitter、WordPress、 Blogger、Identi.ca、RSS订阅与电子邮件等等的整合服务。
    • Diaspora,让你将您的关系分成多个视图(Google+的圈子),每个视图是您生活的不同部分。这是Diaspora首创,用来确保您的照片、经历和笑话,只与您所希望分享的人分享。
    • Buddypress, 是 WordPress 母公司的一个全新的开源程序,BuddyPress 从本质上说其实是 WordPress 的插件。BuddyPress 把 WordPress的关注点从博客转移到了社区。当然,用户还是能够使用WordPress的所有的博客功能,只是当用户使用 BuddyPress 时,第一要做的是创建他们个人档案,第二才是写博客。
    • GNU Social,GNU的SNS。
    • Elgg,一款免费开源的社会性网络脚本程序(php/mysql),以BLOG为中心实现社会网络化,从社会性来讲:Elgg以兴趣为核心的社交平台。它包括网络日志、资料存储、RSS集合、个人档案、FOAF功能等等。
    • SocialEngine,是一款由PHP和Zend控制的网络软件,其脚本让你可以轻松地创建属于你自己社交网站或是在线社区,包括自定义群组、相册、消息、用户档案、视频、新闻订阅,拖放群集邮箱服务器等等功能。
    • iSocial,是一款免费社交网络脚本平台,你可以用它建立像Friendster和Orkut那样可以一键使用书签,约会和建立群组的社交网站。
    • Mahara,有着电子档案、网络日志、简历编辑工具、联系用户的社交网络系统以及建立在线社区的齐全功能。
    • The PeopleAggregator,是全新一代的社交网站系统,它力求应用开放的标准、密切的网络互动和强大的灵活性。
    • Appleseed,是一款类似Friendster的社交网站软件。网站运行appleseed将互通,形成Appleseed的社交网站。该软件发展的重点是对隐私和安全,以及易用的配置。
    • Mugshot,则通过一系列的WEB CRM、照片、日志等等让你时刻了解朋友们的最新动态。
    • Clonesumating,是CONSUMATING.COM代码的开源版本, 其功能有用户档案、用户标签、配对并发现古怪标签合并、团队活动(比如每周照片评选、博客问答)、事件日历、PSS订阅等等。
    • BeWelcom Rox,是www.bewelcome.org等其他社交网站的运作平台,它将人们真实地聚集了在一起。在那里人们了解全球村庄以及其他文化,分享自己的所在地,组织旅游,写旅游博客等等。
    • OpenPNE,是由PHP写成的网络社交服务引擎,其功能有好友管理、好友邀请、日记、博客、订收件箱等等。
    • WorldSpace,是一款用户可拓展的共享虚拟空间,它致力于成为新一代的社交网络系统。
    • Zoints,这一款软件熟知在线社区是互联网中最重要的一部分,它所正是为帮助解决论坛版主所面临的三大问题(即获得会员,保留会员和盈利)而设计的。
  • 社区书签(Delicious)
    • Scuttle,开源Web书签系统,允许多个用户在线存储,共享和Tag他们喜欢的链接。
    • Semantic Scuttle,是一款基于Scuttle的社交书签工具。它可以试验像层次化标签、合作描述、OpenID认证这样的全新功能。
    • Sabros.us,一个基于互联网的书签系统。它与del.icio.us 是相似,您能在网上处理您的书签, 或者自己建立一个网站。
    • Connotea,是 NGP(Nature Publishing Group) 旗下的网站,借鉴当前流行的 del.icio.us 等社会书签的创意,专注于科研领域,并可导入桌面文献管理软件的数据,是当前比较流行的一款在线文献管理工具。
    • Pressmark
    • Shiftspace,让你的Wordpress成为像 del.icio.ussabros.us这样的站点。
    • Ma.gnolia 2,基于Ruby开发。它的界面比较漂亮,但速度比较慢,另外搜索仅限于tag。
    • Akarru,是一款用来建立像www.blogmemes.com网站的社交书签引擎。用户可以通过投票系统在首页上张贴链接并推销链接。
    • Monkey Chow,是一款带有社交书签、主题文章、来源标签、OPML、文章搜索、编辑来源属性等等众多功能的新闻聚合浏览器。
    • Feed Me Links,可以将你的书签存储在网上以便随时随地使用,输入你最喜爱的网址并和好友们分享,加标签来管理不同链接,还有更多新鲜事物等待你来发现。
  • 短网址服务(TinyURL)
    • tinyULL,不是tinyURL,别看错了。

身份和安全

  • 域名
    • Namecoin/.bit,基于bitcoin技术的分散、开放DNS系统。.bit域名到底靠不靠谱啊,是不是有P2P网络存在,.bit网站就能永远访问?会不会被墙?我们不得而之。
    • Social DNS
    • Distributed DNS
  • 身份凭证
    • OpenID, 一个去中心化的网上身份认证系统。对于支持OpenID的网站,用户不需要记住像用户名和密码这样的传统验证标记。取而代之的是,他们只需要预先在一个作 为OpenID身份提供者(identity provider, IdP)的网站上注册。OpenID是去中心化的,任何网站都可以使用OpenID来作为用户登录的一种方式,任何网站也都可以作为OpenID身份提供 者。OpenID既解决了问题而又不需要依赖于中心性的网站来确认数字身份。OpenID正在被越来越多的大网站采用
    • OAuth,(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
  • 加密
    • CACert,想给自己申请一份电子邮件证书或者给自己的网站、服务器申请一个SSL证书是很不容易的,你每年都得给CA(证书颁发验证组织)缴纳不少的证书申请费。有了CAcert,国外一个免费的数字证书颁发组织,你可以免费注册成为用户,申领个人证书和服务器证书等。证书被各种浏览器、邮件客户端所支持。

其它

  • 翻译(Google Translator)
    • Apertium,一个机器翻译平台,由西班牙政府和加泰罗尼亚自治政府拨款支持阿利坎特大学开发。
  • 桌面(iGoogle, netbives)
    • EyeOS, 一款web桌面环境, 俗称Web Operating System (Web OS)或者Web Office. eyeOS是一个开源的软件, 用户可以自由下载或者在eyeOS的服务器 eyeOS server 上使用. 基本的系统附带一些办公软件和 PIM 应用, 并且在官方http://eyeos.org可以找到完整的程序代码。其开发哲学是:Taking Your Life Everywhere!
    • CorneliOS,一款运行在服务器端、基于网络的网络虚拟操作系统,本身通过HTML和(或)XHTML为用户提供各种服务,这也就意味着用户只需要使用普通浏览器即可连接并使用这款操作系统。非常类似 eyeOS。
参考

[转载]在Visual Studio中使用GitHub(使用篇)

mikel阅读(772)

[转载]在Visual Studio中使用GitHub(使用篇) – ☆磊☆ – 博客园.

一、准备工具

上一篇中我们已经安装了Git Extensions和Tortoisegit。在这里我们要为Visual Studio安装一个插件,专门用于Git。

Git Source Control Provider,打开http://visualstudiogallery.msdn.microsoft.com/ 搜索”Git”关键字,即可搜索到该插件。

二、配置源码管理工具

imageimage

三、配置帐户关联

image

image

四、使用

后面的事情就很简单了,创建文件夹 pull 一个副本,修改文件,commit文件,push到服务器。

image

五、总结

ok,git这个算是写完了,其实用起来和其他的版本控制感觉差不多。用不惯的也可以直接用Git Bash直接写命令行。

[转载]代码注入的三种方法

mikel阅读(895)

[转载]代码注入的三种方法 – VC知识库文章.

代码注入的三种方法

作者:Robert Kuster
编译:VCKBASE

原文出处:Three Ways to Inject Your Code into Another Process

下载源代码
目录

简介

本文将讨论如何把代码注入不同的进程地址空间,然后在该进程的上下文中执行注入的代码。 我们在网上可以查到一些窗口/密码侦测的应用例子,网上的这些程序大多都依赖 Windows 钩子技术来实现。本文将讨论除了使用 Windows 钩子技术以外的其它技术来实现这个功能。如图一所示:

图一 WinSpy 密码侦测程序

为了找到解决问题的方法。首先让我们简单回顾一下问题背景。
要“读取”某个控件的内容——无论这个控件是否属于当前的应用程序——通常都是发送 WM_GETTEXT 消息来实现。这个技术也同样应用到编辑控件,但是如果该编辑控件属于另外一个进程并设置了 ES_PASSWORD 式样,那么上面讲的方法就行不通了。用 WM_GETTEXT 来获取控件的内容只适用于进程“拥有”密码控件的情况。所以我们的问题变成了如何在另外一个进程的地址空间执行:

::SendMessage( hPwdEdit, WM_GETTEXT, nMaxChars, psBuffer );

通常有三种可能性来解决这个问题。

  1. 将你的代码放入某个 DLL,然后通过 Windows 钩子映射该DLL到远程进程;
  2. 将你的代码放入某个 DLL,然后通过 CreateRemoteThread 和 LoadLibrary 技术映射该DLL到远程进程;
  3. 如果不写单独的 DLL,可以直接将你的代码拷贝到远程进程——通过 WriteProcessMemory——并用 CreateRemoteThread 启动它的执行。本文将在第三部分详细描述该技术实现细节;

第一部分: Windows 钩子

范例程序——参见HookSpy 和HookInjEx

Windows 钩子主要作用是监控某些线程的消息流。通常我们将钩子分为本地钩子和远程钩子以及系统级钩子,本地钩子一般监控属于本进程的线程的消息流,远程钩子是线程专用的,用于监控属于另外进程的线程消息流。系统级钩子监控运行在当前系统中的所有线程的消息流。
如果钩子作用的线程属于另外的进程,那么你的钩子过程必须驻留在某个动态链接库(DLL)中。然后系统映射包含钩子过程的DLL到钩子作用的线程的地址空间。Windows将映射整个 DLL,而不仅仅是钩子过程。这就是为什么 Windows 钩子能被用于将代码注入到别的进程地址空间的原因。
本文我不打算涉及钩子的具体细节(关于钩子的细节请参见 MSDN 库中的 SetWindowHookEx API),但我在此要给出两个很有用心得,在相关文档中你是找不到这些内容的:

  1. 在成功调用 SetWindowsHookEx 后,系统自动映射 DLL 到钩子作用的线程地址空间,但不必立即发生映射,因为 Windows 钩子都是消息,DLL 在消息事件发生前并没有产生实际的映射。例如:
    如果你安装一个钩子监控某些线程(WH_CALLWNDPROC)的非队列消息,在消息被实际发送到(某些窗口的)钩子作用的线程之前,该DLL 是不会被映射到远程进程的。换句话说,如果 UnhookWindowsHookEx 在某个消息被发送到钩子作用的线程之前被调用,DLL 根本不会被映射到远程进程(即使 SetWindowsHookEx 本身调用成功)。为了强制进行映射,在调用 SetWindowsHookEx 之后马上发送一个事件到相关的线程。
    在UnhookWindowsHookEx了之后,对于没有映射的DLL处理方法也一样。只有在足够的事件发生后,DLL才会有真正的映射。
  2. 当你安装钩子后,它们可能影响整个系统得性能(尤其是系统级钩子),但是你可以很容易解决这个问题,如果你使用线程专用钩子的DLL映射机制,并不截获消息。考虑使用如下代码:
    BOOL APIENTRY DllMain( HANDLE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved )
    {
        if( ul_reason_for_call == DLL_PROCESS_ATTACH )
        {
            // Increase reference count via LoadLibrary
            char lib_name[MAX_PATH]; 
            ::GetModuleFileName( hModule, lib_name, MAX_PATH );
            ::LoadLibrary( lib_name );
    
            // Safely remove hook
            ::UnhookWindowsHookEx( g_hHook );
        }    
        return TRUE;
    }

    那么会发生什么呢?首先我们通过Windows 钩子将DLL映射到远程进程。然后,在DLL被实际映射之后,我们解开钩子。通常当第一个消息到达钩子作用线程时,DLL此时也不会被映射。这里的处理技巧是调用LoadLibrary通过增加 DLLs的引用计数来防止映射不成功。
    现在剩下的问题是如何卸载DLL,UnhookWindowsHookEx 是不会做这个事情的,因为钩子已经不作用于线程了。你可以像下面这样做:

    • 就在你想要解除DLL映射前,安装另一个钩子;
    • 发送一个“特殊”消息到远程线程;
    • 在钩子过程中截获这个消息,响应该消息时调用 FreeLibrary 和 UnhookWindowsHookEx;

    目前只使用了钩子来从处理远程进程中DLL的映射和解除映射。在此“作用于线程的”钩子对性能没有影响。
    下面我们将讨论另外一种方法,这个方法与 LoadLibrary 技术的不同之处是DLL的映射机制不会干预目标进程。相对LoadLibrary 技术,这部分描述的方法适用于 WinNT和Win9x。
    但是,什么时候使用这个技巧呢?答案是当DLL必须在远程进程中驻留较长时间(即如果你子类化某个属于另外一个进程的控件时)以及你想尽可能少的干涉目标进程时。我在 HookSpy 中没有使用它,因为注入DLL 的时间并不长——注入时间只要足够得到密码即可。我提供了另外一个例子程序——HookInjEx——来示范。HookInjEx 将DLL映射到资源管理器“explorer.exe”,并从中/解除影射,它子类化“开始”按钮,并交换鼠标左右键单击“开始”按钮的功能。

HookSpy 和 HookInjEx 的源代码都可以从本文的下载源代码中获得。

第二部分:CreateRemoteThread 和 LoadLibrary 技术

范例程序——LibSpy

通常,任何进程都可以通过 LoadLibrary API 动态加载DLL。但是,如何强制一个外部进程调用这个函数呢?答案是:CreateRemoteThread。
首先,让我们看一下 LoadLibrary 和FreeLibrary API 的声明:

HINSTANCE LoadLibrary(
LPCTSTR lpLibFileName // 库模块文件名的地址
);

BOOL FreeLibrary(
HMODULE hLibModule // 要加载的库模块的句柄
);

现在将它们与传递到 CreateRemoteThread 的线程例程——ThreadProc 的声明进行比较。

DWORD WINAPI ThreadProc(
LPVOID lpParameter // 线程数据
);

你可以看到,所有函数都使用相同的调用规范并都接受 32位参数,返回值的大小都相同。也就是说,我们可以传递一个指针到LoadLibrary/FreeLibrary 作为到 CreateRemoteThread 的线程例程。但这里有两个问题,请看下面对CreateRemoteThread 的描述:

  1. CreateRemoteThread 的 lpStartAddress 参数必须表示远程进程中线程例程的开始地址。
  2. 如果传递到 ThreadFunc 的参数lpParameter——被解释为常规的 32位值(FreeLibrary将它解释为一个 HMODULE),一切OK。但是,如果 lpParameter 被解释为一个指针(LoadLibraryA将它解释为一个串指针)。它必须指向远程进程的某些数据。

第一个问题实际上是由它自己解决的。LoadLibrary 和 FreeLibray 两个函数都在 kernel32.dll 中。因为必须保证kernel32存在并且在每个“常规”进程中的加载地址要相同,LoadLibrary/FreeLibray 的地址在每个进程中的地址要相同,这就保证了有效的指针被传递到远程进程。
第二个问题也很容易解决。只要通过 WriteProcessMemory 将 DLL 模块名(LoadLibrary需要的DLL模块名)拷贝到远程进程即可。

所以,为了使用CreateRemoteThread 和 LoadLibrary 技术,需要按照下列步骤来做:

  1. 获取远程进程(OpenProcess)的 HANDLE;
  2. 为远程进程中的 DLL名分配内存(VirtualAllocEx);
  3. 将 DLL 名,包含全路径名,写入分配的内存(WriteProcessMemory);
  4. 用 CreateRemoteThread 和 LoadLibrary. 将你的DLL映射到远程进程;
  5. 等待直到线程终止(WaitForSingleObject),也就是说直到 LoadLibrary 调用返回。另一种方法是,一旦 DllMain(用DLL_PROCESS_ATTACH调用)返回,线程就会终止;
  6. 获取远程线程的退出代码(GetExitCodeThread)。注意这是一个 LoadLibrary 返回的值,因此是所映射 DLL 的基地址(HMODULE)。
    在第二步中释放分配的地址(VirtualFreeEx);
  7. 用 CreateRemoteThread 和 FreeLibrary从远程进程中卸载 DLL。传递在第六步获取的 HMODULE 句柄到 FreeLibrary(通过 CreateRemoteThread 的lpParameter参数);
  8. 注意:如果你注入的 DLL 产生任何新的线程,一定要在卸载DLL 之前将它们都终止掉;
  9. 等待直到线程终止(WaitForSingleObject);

此外,处理完成后不要忘了关闭所有句柄,包括在第四步和第八步创建的两个线程以及在第一步获取的远程线程句柄。现在让我们看一下 LibSpy 的部分代码,为了简单起见,上述步骤的实现细节中的错误处理以及 UNICODE 支持部分被略掉。

HANDLE hThread;
char    szLibPath[_MAX_PATH];  // “LibSpy.dll”模块的名称 (包括全路径);
void*   pLibRemote;   // 远程进程中的地址,szLibPath 将被拷贝到此处;
DWORD   hLibModule;   // 要加载的模块的基地址(HMODULE)
HMODULE hKernel32 = ::GetModuleHandle("Kernel32");

// 初始化szLibPath
//...
// 1. 在远程进程中为szLibPath 分配内存
// 2. 将szLibPath 写入分配的内存
pLibRemote = ::VirtualAllocEx( hProcess, NULL, sizeof(szLibPath),
                               MEM_COMMIT, PAGE_READWRITE );
::WriteProcessMemory( hProcess, pLibRemote, (void*)szLibPath,
                      sizeof(szLibPath), NULL );

// 将"LibSpy.dll" 加载到远程进程(使用CreateRemoteThread 和 LoadLibrary)
hThread = ::CreateRemoteThread( hProcess, NULL, 0,
            (LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32,
                                       "LoadLibraryA" ),
             pLibRemote, 0, NULL );
::WaitForSingleObject( hThread, INFINITE );

// 获取所加载的模块的句柄
::GetExitCodeThread( hThread, &hLibModule );

// 清除
::CloseHandle( hThread );
::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE );

假设我们实际想要注入的代码——SendMessage ——被放在DllMain (DLL_PROCESS_ATTACH)中,现在它已经被执行。那么现在应该从目标进程中将DLL 卸载:

// 从目标进程中卸载"LibSpy.dll"  (使用 CreateRemoteThread 和 FreeLibrary)
hThread = ::CreateRemoteThread( hProcess, NULL, 0,
            (LPTHREAD_START_ROUTINE) ::GetProcAddress( hKernel32,
                                       "FreeLibrary" ),
            (void*)hLibModule, 0, NULL );
::WaitForSingleObject( hThread, INFINITE );

// 清除
::CloseHandle( hThread );

进程间通信

到目前为止,我们只讨论了关于如何将DLL 注入到远程进程的内容,但是,在大多数情况下,注入的 DLL 都需要与原应用程序进行某种方式的通信(回想一下,我们的DLL是被映射到某个远程进程的地址空间里了,不是在本地应用程序的地址空间中)。比如秘密侦测 程序,DLL必须要知道实际包含密码的控件句柄,显然,编译时无法将这个值进行硬编码。同样,一旦DLL获得了秘密,它必须将它发送回原应用程序,以便能 正确显示出来。
幸运的是,有许多方法处理这个问题,文件映射,WM_COPYDATA,剪贴板以及很简单的 #pragma data_seg 共享数据段等,本文我不打算使用这些技术,因为MSDN(“进程间通信”部分)以及其它渠道可以找到很多文档参考。不过我在 LibSpy例子中还是使用了 #pragma data_seg。细节请参考 LibSpy 源代码。

第三部分:CreateRemoteThread 和 WriteProcessMemory 技术

范例程序——WinSpy

另外一个将代码拷贝到另一个进程地址空间并在该进程上下文中执行的方法是使用远程线程和 WriteProcessMemory API。这种方法不用编写单独的DLL,而是用 WriteProcessMemory 直接将代码拷贝到远程进程——然后用 CreateRemoteThread 启动它执行。先来看看 CreateRemoteThread 的声明:

HANDLE CreateRemoteThread(
  HANDLE hProcess,        // 传入创建新线程的进程句柄
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // 安全属性指针
  DWORD dwStackSize,      // 字节为单位的初始线程堆栈
  LPTHREAD_START_ROUTINE lpStartAddress,     // 指向线程函数的指针
  LPVOID lpParameter,     // 新线程使用的参数
  DWORD dwCreationFlags,  // 创建标志
  LPDWORD lpThreadId      // 指向返回的线程ID
);

如果你比较它与 CreateThread(MSDN)的声明,你会注意到如下的差别:

  • 在 CreateRemoteThread中,hProcess是额外的一个参数,一个进程句柄,新线程就是在这个进程中创建的;
  • 在 CreateRemoteThread中,lpStartAddress 表示的是在远程进程地址空间中的线程起始地址。线程函数必须要存在于远程进程中,所以我们不能简单地传递一个指针到本地的 ThreadFunc。必须得先拷贝代码到远程进程;
  • 同样,lpParameter 指向的数据也必须要存在于远程进程,所以也得将它拷贝到那。

综上所述,我们得按照如下的步骤来做:

  1. 获取一个远程进程的HANDLE (OpenProces) ;
  2. 在远程进程地址空间中为注入的数据分配内存(VirtualAllocEx);
  3. 将初始的 INDATA 数据结构的一个拷贝写入分配的内存中(WriteProcessMemory);
  4. 在远程进程地址空间中为注入的代码分配内存;
  5. 将 ThreadFunc 的一个拷贝写入分配的内存;
  6. 用 CreateRemoteThread启动远程的 ThreadFunc 拷贝;
  7. 等待远程线程终止(WaitForSingleObject);
  8. 获取远程来自远程进程的结果(ReadProcessMemory 或 GetExitCodeThread);
  9. 释放在第二步和第四步中分配的内存(VirtualFreeEx);
  10. 关闭在第六步和第一步获取的句柄(CloseHandle);

ThreadFunc 必须要遵循的原则:

  1. 除了kernel32.dll 和user32.dll 中的函数之外,ThreadFunc 不要调用任何其它函数,只有 kernel32.dll 和user32.dll被保证在本地和目标进程中的加载地址相同(注意,user32.dll并不是被映射到每个 Win32 的进程)。如果你需要来自其它库中的函数,将LoadLibrary 和 GetProcAddress 的地址传给注入的代码,然后放手让它自己去做。如果映射到目标进程中的DLL有冲突,你也可以用 GetModuleHandle 来代替 LoadLibrary。
    同样,如果你想在 ThreadFunc 中调用自己的子例程,要单独把每个例程的代码拷贝到远程进程并用 INJDATA为 ThreadFunc 提供代码的地址。
  2. 不要使用静态字符串,而要用 INJDATA 来传递所有字符串。之所以要这样,是因为编译器将静态字符串放在可执行程序的“数据段”中,可是引用(指针)是保留在代码中的。那么,远程进程中ThreadFunc 的拷贝指向的内容在远程进程的地址空间中是不存在的。
  3. 去掉 /GZ 编译器开关,它在调试版本中是默认设置的。
  4. 将 ThreadFunc 和 AfterThreadFunc 声明为静态类型,或者不启用增量链接。
  5. ThreadFunc 中的局部变量一定不能超过一页(也就是 4KB)。
    注意在调试版本中4KB的空间有大约10个字节是用于内部变量的。
  6. 如果你有一个开关语句块大于3个case 语句,将它们像下面这样拆分开:
    switch( expression ) {
        case constant1: statement1; goto END;
        case constant2: statement2; goto END;
        case constant3: statement2; goto END;
    }
    switch( expression ) {
        case constant4: statement4; goto END;
        case constant5: statement5; goto END;
        case constant6: statement6; goto END;
    }
    END:

    或者将它们修改成一个 if-else if 结构语句(参见附录E)。

如果你没有按照这些规则来做,目标进程很可能会崩溃。所以务必牢记。在目标进程中不要假设任何事情都会像在本地进程中那样 (参见附录F)。

GetWindowTextRemote(A/W)

要想从“远程”编辑框获得密码,你需要做的就是将所有功能都封装在GetWindowTextRemot(A/W):中。

int GetWindowTextRemoteA( HANDLE hProcess, HWND hWnd, LPSTR lpString );
int GetWindowTextRemoteW( HANDLE hProcess, HWND hWnd, LPWSTR lpString );

参数说明:
hProcess:编辑框控件所属的进程句柄; 
hWnd:包含密码的编辑框控件句柄; 
lpString:接收文本的缓冲指针; 
返回值:返回值是拷贝的字符数;

下面让我们看看它的部分代码——尤其是注入数据的代码——以便明白 GetWindowTextRemote 的工作原理。此处为简单起见,略掉了 UNICODE 支持部分。

INJDATA
typedef LRESULT (WINAPI *SENDMESSAGE)(HWND,UINT,WPARAM,LPARAM);

typedef struct { 
HWND hwnd; // 编辑框句柄
SENDMESSAGE fnSendMessage; // 指向user32.dll 中 SendMessageA 的指针

char psText[128]; // 接收密码的缓冲
} INJDATA;

INJDATA 是一个被注入到远程进程的数据结构。但在注入之前,结构中指向 SendMessageA 的指针是在本地应用程序中初始化的。因为对于每个使用user32.dll的进程来说,user32.dll总是被映射到相同的地址,因此,SendMessageA 的地址也肯定是相同的。这就保证了被传递到远程进程的是一个有效的指针。

ThreadFunc函数

static DWORD WINAPI ThreadFunc (INJDATA *pData) 
{
	pData->fnSendMessage( pData->hwnd, WM_GETTEXT, // Get password
				sizeof(pData->psText),
				(LPARAM)pData->psText ); 
	return 0;
}

// 该函数在ThreadFunc之后标记内存地址
// int cbCodeSize = (PBYTE) AfterThreadFunc - (PBYTE) ThreadFunc.
static void AfterThreadFunc (void)
{
}

ThradFunc 是被远程线程执行的代码。

  • 注释:注意AfterThreadFunc 是如何计算 ThreadFunc 大小的。通常这样做并不是一个好办法,因为链接器可以随意更改函数的顺序(也就是说ThreadFunc可能被放在 AfterThreadFunc之后)。这一点你可以在小项目中很好地保证函数的顺序是预先设想好的,比如 WinSpy 程序。在必要的情况下,你还可以使用 /ORDER 链接器选项来解决函数链接顺序问题。或者用反汇编确定 ThreadFunc 函数的大小。

如何使用该技术子类化远程控件

范例程序——InjectEx

下面我们将讨论一些更复杂的内容,如何子类化属于另一个进程的控件。

首先,你得拷贝两个函数到远程进程来完成此任务

  1. ThreadFunc实际上是通过 SetWindowLong子类化远程进程中的控件;
  2. NewProc是子类化控件的新窗口过程;

这里主要的问题是如何将数据传到远程窗口过程 NewProc,因为 NewProc 是一个回调函数,它必须遵循特定的规范和原则,我们不能简单地在参数中传递 INJDATA指针。幸运的是我找到了有两个方法来解决这个问题,只不过要借助汇编语言,所以不要忽略了汇编,关键时候它是很有用的!

方法一:

如下图所示:

在远程进程中,INJDATA 被放在NewProc 之前,这样 NewProc 在编译时便知道 INJDATA 在远程进程地址空间中的内存位置。更确切地说,它知道相对于其自身位置的 INJDATA 的地址,我们需要所有这些信息。下面是 NewProc 的代码:

static LRESULT CALLBACK NewProc(
  HWND hwnd,       // 窗口句柄
  UINT uMsg,       // 消息标示符
  WPARAM wParam,   // 第一个消息参数
  LPARAM lParam )  // 第二个消息参数
{
    INJDATA* pData = (INJDATA*) NewProc;  // pData 指向 NewProc
    pData--;              // 现在pData 指向INJDATA;
                          // 回想一下INJDATA 被置于远程进程NewProc之前;

    //-----------------------------
    // 此处是子类化代码
    // ........
    //-----------------------------

    // 调用原窗口过程;
    // fnOldProc (由SetWindowLong 返回) 被(远程)ThreadFunc初始化
    // 并被保存在(远程)INJDATA;中
    return pData->fnCallWindowProc( pData->fnOldProc, 
                                    hwnd,uMsg,wParam,lParam );
}

但这里还有一个问题,见第一行代码:

INJDATA* pData = (INJDATA*) NewProc;

这种方式 pData得到的是硬编码值(在我们的进程中是原 NewProc 的内存地址)。这不是我们十分想要的。在远程进程中,NewProc “当前”拷贝的内存地址与它被移到的实际位置是无关的,换句话说,我们会需要某种类型的“this 指针”。
虽然用 C/C++ 无法解决这个问题,但借助内联汇编可以解决,下面是对 NewProc的修改:

static LRESULT CALLBACK NewProc(
  HWND hwnd,       // 窗口句柄
  UINT uMsg,       // 消息标示符
  WPARAM wParam,   // 第一个消息参数
  LPARAM lParam )  // 第二个消息参数
{
    // 计算INJDATA 结构的位置
    // 在远程进程中记住这个INJDATA 
    // 被放在NewProc之前
    INJDATA* pData;
    _asm {
        call    dummy
dummy:
        pop     ecx         // <- ECX 包含当前的EIP
        sub     ecx, 9      // <- ECX 包含NewProc的地址
        mov     pData, ecx
    }
    pData--;

    //-----------------------------
    // 此处是子类化代码
    // ........
    //-----------------------------

    // 调用原来的窗口过程
    return pData->fnCallWindowProc( pData->fnOldProc, 
                                    hwnd,uMsg,wParam,lParam );
}

那么,接下来该怎么办呢?事实上,每个进程都有一个特殊的寄存器,它指向下一条要执行的指令的内存位置。即所谓的指令指针,在32位 Intel 和 AMD 处理器上被表示为 EIP。因为 EIP是一个专用寄存器,你无法象操作一般常规存储器(如:EAX,EBX等)那样通过编程存取它。也就是说没有操作代码来寻址 EIP,以便直接读取或修改其内容。但是,EIP 仍然还是可以通过间接方法修改的(并且随时可以修改),通过JMP,CALL和RET这些指令实现。下面我们就通过例子来解释通过 CALL/RET 子例程调用机制在32位 Intel 和 AMD 处理器上是如何工作的。
当你调用(通过 CALL)某个子例程时,子例程的地址被加载到 EIP,但即便是在 EIP杯修改之前,其旧的那个值被自动PUSH到堆栈(被用于后面作为指令指针返回)。在子例程执行完时,RET 指令自动将堆栈顶POP到 EIP。
现在你知道了如何通过 CALL 和 RET 实现 EIP 的修改,但如何获取其当前的值呢?下面就来解决这个问题,前面讲过,CALL PUSH EIP 到堆栈,所以,为了获取其当前值,调用“哑函数”,然后再POP堆栈顶。让我们用编译后的 NewProc 来解释这个窍门。

Address   OpCode/Params   Decoded instruction
--------------------------------------------------
:00401000  55              push ebp            ; entry point of
                                               ; NewProc
:00401001  8BEC            mov ebp, esp
:00401003  51              push ecx
:00401004  E800000000      call 00401009       ; *a*    call dummy
:00401009  59              pop ecx             ; *b*
:0040100A  83E909          sub ecx, 00000009   ; *c*
:0040100D  894DFC          mov [ebp-04], ecx   ; mov pData, ECX
:00401010  8B45FC          mov eax, [ebp-04]
:00401013  83E814          sub eax, 00000014   ; pData--;
.....
.....
:0040102D  8BE5            mov esp, ebp
:0040102F  5D              pop ebp
:00401030  C21000          ret 0010
  • 哑函数调用;就是JUMP到下一个指令并PUSH EIP到堆栈;
  • 然后将堆栈顶POP到 ECX,ECX再保存EIP;这也是 POP EIP指令的真正地址;
  • 注意 NewProc 的入口点和 “POP ECX”之间的“距离”是9 个字节;因此为了计算 NewProc的地址,要从 ECX 减9。

这样一来,不管 NewProc 被移到什么地方,它总能计算出其自己的地址。但是,NewProc 的入口点和 “POP ECX”之间的距离可能会随着你对编译/链接选项的改变而变化,由此造成 RELEASE和Debug版本之间也会有差别。但关键是你仍然确切地知道编译时的值。

  1. 首先,编译函数
  2. 用反汇编确定正确的距离
  3. 最后,用正确的距离值重新编译

此即为 InjecEx 中使用的解决方案,类似于 HookInjEx,交换鼠标点击“开始”左右键时的功能。

方法二:

对于我们的问题,在远程进程地址空间中将 INJDATA 放在 NewProc 前面不是唯一的解决办法。看下面 NewProc的变异版本:

static LRESULT CALLBACK NewProc(
  HWND hwnd,      // 窗口句柄
  UINT uMsg,      // 消息标示符
  WPARAM wParam,  // 第一个消息参数
  LPARAM lParam ) // 第二个消息参数
{
    INJDATA* pData = 0xA0B0C0D0;    // 虚构值

    //-----------------------------
    // 子类化代码
    // ........
    //-----------------------------

    // 调用原来的窗口过程
    return pData->fnCallWindowProc( pData->fnOldProc, 
                                    hwnd,uMsg,wParam,lParam );
}

此处 0xA0B0C0D0 只是远程进程地址空间中真实(绝对)INJDATA地址的占位符。前面讲过,你无法在编译时知道该地址。但你可以在调用 VirtualAllocEx (为INJDATA)之后得到 INJDATA 在远程进程中的位置。 编译我们的 NewProc 后,可以得到如下结果:

 Address   OpCode/Params     Decoded instruction
--------------------------------------------------
:00401000  55                push ebp
:00401001  8BEC              mov ebp, esp
:00401003  C745FCD0C0B0A0    mov [ebp-04], A0B0C0D0
:0040100A  ...
....
:0040102D  8BE5              mov esp, ebp
:0040102F  5D                pop ebp
:00401030  C21000            ret 0010

因此,其编译的代码(十六进制)将是:

558BECC745FCD0C0B0A0......8BE55DC21000.

现在你可以象下面这样继续:

  1. 将INJDATA,ThreadFunc和NewProc 拷贝到目标进程;
  2. 修改 NewProc 的代码,以便 pData 中保存的是 INJDATA 的真实地址。
    例如,假设 INJDATA 的地址(VirtualAllocEx返回的值)在目标进程中是 0x008a0000。然后象下面这样修改NewProc的代码:

    	558BECC745FCD0C0B0A0......8BE55DC21000 <- 原来的NewProc (注1) 
    	558BECC745FC00008A00......8BE55DC21000 <- 修改后的NewProc,使用的是INJDATA的实际地址。

    也就是说,你用真正的 INJDATA(注2) 地址替代了虚拟值 A0B0C0D0(注2)。

  3. 开始执行远程的 ThreadFunc,它负责子类化远程进程中的控件。
  • 注1、有人可能会问,为什么地址 A0B0C0D0 和 008a0000 在编译时顺序是相反的。因为 Intel 和 AMD 处理器使用 little-endian 符号来表示(多字节)数据。换句话说,某个数字的低位字节被存储在内存的最小地址处,而高位字节被存储在最高位地址。
    假设“UNIX”这个词存储用4个字节,在 big-endian 系统中,它被存为“UNIX”,在 little-endian 系统中,它将被存为“XINU”。
  • 注2、某些破解(很糟)以类似的方式修改可执行代码,但是一旦加载到内存,一个程序是无法修改自己的代码的(代码驻留在可执行程序的“.text” 区域,这个区域是写保护的)。但仍可以修改远程的 NewProc,因为它是先前以 PAGE_EXECUTE_READWRITE 许可方式被拷贝到某个内存块中的。

何时使用 CreateRemoteThread 和 WriteProcessMemory 技术

与其它方法比较,使用 CreateRemoteThread 和 WriteProcessMemory 技术进行代码注入更灵活,这种方法不需要额外的 dll,不幸的是,该方法更复杂并且风险更大,只要ThreadFunc出现哪怕一丁点错误,很容易就让(并且最大可能地会)使远程进程崩溃(参见附录 F),因为调试远程 ThreadFunc 将是一个可怕的梦魇,只有在注入的指令数很少时,你才应该考虑使用这种技术进行注入,对于大块的代码注入,最好用 I.和II 部分讨论的方法。

WinSpy 以及 InjectEx 请从这里下载源代码

结束语

到目前为止,有几个问题是我们未提及的,现总结如下:

解决方案 OS 进程
I、Hooks Win9x 和 WinNT 仅仅与 USER32.DLL (注3)链接的进程
II、CreateRemoteThread & LoadLibrary 仅 WinNT(注4) 所有进程(注5), 包括系统服务(注6)
III、CreateRemoteThread & WriteProcessMemory 仅 WinNT 所有进程, 包括系统服务
  • 注3:显然,你无法hook一个没有消息队列的线程,此外,SetWindowsHookEx不能与系统服务一起工作,即使它们与 USER32.DLL 进行链接;
  • 注4:Win9x 中没有 CreateRemoteThread,也没有 VirtualAllocEx (实际上,在Win9x 中可以仿真,但不是本文讨论的问题了);
  • 注5:所有进程 = 所有 Win32 进程 + csrss.exe
    本地应用 (smss.exe, os2ss.exe, autochk.exe 等)不使用 Win32 API,所以也不会与 kernel32.dll 链接。唯一一个例外是 csrss.exe,Win32 子系统本身,它是本地应用程序,但其某些库(~winsrv.dll)需要 Win32 DLLs,包括 kernel32.dll;
  • 注6:如果你想要将代码注入到系统服务中(lsass.exe, services.exe, winlogon.exe 等)或csrss.exe,在打开远程句柄(OpenProcess)之前,将你的进程优先级置为 “SeDebugPrivilege”(AdjustTokenPrivileges)。

最后,有几件事情一定要了然于心:你的注入代码很容易摧毁目标进程,尤其是注入代码本身出错的时候,所以要记住:权力带来责任!
因为本文中的许多例子是关于密码的,你也许还读过 Zhefu Zhang 写的另外一篇文章“Super Password Spy++” ,在该文中,他解释了如何获取IE 密码框中的内容,此外,他还示范了如何保护你的密码控件免受类似的攻击。

附录A

为什么 kernel32.dll 和user32.dll 总是被映射到相同的地址。

我的假定:因为Microsoft 的程序员认为这样做有助于速度优化,为什么呢?我的解释是——通常一个可执行程序是由几个部分组成,其中包括“.reloc” 。当链接器创建 EXE 或者 DLL文件时,它对文件被映射到哪个内存地址做了一个假设。这就是所谓的首选加载/基地址。在映像文件中所有绝对地址都是基于链接器首选的加载地址,如果 由于某种原因,映像文件没有被加载到该地址,那么这时“.reloc”就起作用了,它包含映像文件中的所有地址的清单,这个清单中的地址反映了链接器首选 加载地址和实际加载地址的差别(无论如何,要注意编译器产生的大多数指令使用某种相对地址寻址,因此,并没有你想象的那么多地址可供重新分配),另一方 面,如果加载器能够按照链接器首选地址加载映像文件,那么“.reloc”就被完全忽略掉了。
但kernel32.dll 和user32.dll 及其加载地址为何要以这种方式加载呢?因为每一个 Win32 程序都需要kernel32.dll,并且大多数Win32 程序也需要 user32.dll,那么总是将它们(kernel32.dll 和user32.dll)映射到首选地址可以改进所有可执行程序的加载时间。这样一来,加载器绝不能修改kernel32.dll and user32.dll.中的任何(绝对)地址。我们用下面的例子来说明:
将某个应用程序 App.exe 的映像基地址设置成 KERNEL32的地址(/base:”0x77e80000″)或 USER32的首选基地址(/base:”0x77e10000″),如果 App.exe 不是从 USER32 导入方式来使用 USER32,而是通过LoadLibrary 加载,那么编译并运行App.exe 后,会报出错误信息(”Illegal System DLL Relocation”——非法系统DLL地址重分配),App.exe 加载失败。
为什么会这样呢?当创建进程时,Win 2000、Win XP 和Win 2003系统的加载器要检查 kernel32.dll 和user32.dll 是否被映射到首选基地址(实际上,它们的名字都被硬编码进了加载器),如果没有被加载到首选基地址,将发出错误。在 WinNT4中,也会检查ole32.dll,在WinNT 3.51 和较低版本的Windows中,由于不会做这样的检查,所以kernel32.dll 和user32.dll可以被加载任何地方。只有ntdll.dll总是被加载到其基地址,加载器不进行检查,一旦ntdll.dll没有在其基地址,进程就无法创建。

总之,对于 WinNT 4 和较高的版本中

  • 一定要被加载到基地址的DLLs 有:kernel32.dll、user32.dll 和ntdll.dll;
  • 每个Win32 程序都要使用的 DLLs+ csrss.exe:kernel32.dll 和ntdll.dll;
  • 每个进程都要使用的DLL只有一个,即使是本地应用:ntdll.dll;

附录B

/GZ 编译器开关

在生成 Debug 版本时,/GZ 编译器特性是默认打开的。你可以用它来捕获某些错误(具体细节请参考相关文档)。但对我们的可执行程序意味着什么呢?
当打开 /GZ 开关,编译器会添加一些额外的代码到可执行程序中每个函数所在的地方,包括一个函数调用(被加到每个函数的最后)——检查已经被我们的函数修改的 ESP堆栈指针。什么!难道有一个函数调用被添加到 ThreadFunc 吗?那将导致灾难。ThreadFunc 的远程拷贝将调用一个在远程进程中不存在的函数(至少是在相同的地址空间中不存在)

附录C

静态函数和增量链接

增量链接主要作用是在生成应用程序时缩短链接时间。常规链接和增量链接的可执行程序之间的差别是——增量链接时,每个函数调用经由一个额外的JMP指令,该指令由链接器发出(该规则的一个例外是函数声明为静态)。这些 JMP 指令允许链接器在内存中移动函数,这种移动无需修改引用函数的 CALL指令。但这些JMP指令也确实导致了一些问题:如 ThreadFunc 和 AfterThreadFunc 将指向JMP指令而不是实际的代码。所以当计算ThreadFunc 的大小时:

const int cbCodeSize = ((LPBYTE) AfterThreadFunc - (LPBYTE) ThreadFunc)

你实际上计算的是指向 ThreadFunc 的JMPs 和AfterThreadFunc之间的“距离” (通常它们会紧挨着,不用考虑距离问题)。现在假设 ThreadFunc 的地址位于004014C0 而伴随的 JMP指令位于 00401020。

:00401020   jmp  004014C0
 ...
:004014C0   push EBP          ; ThreadFunc 的实际地址
:004014C1   mov  EBP, ESP
 ...

那么

WriteProcessMemory( .., &ThreadFunc, cbCodeSize, ..);

将拷贝“JMP 004014C0”指令(以及随后cbCodeSize范围内的所有指令)到远程进程——不是实际的 ThreadFunc。远程进程要执行的第一件事情将是“JMP 004014C0” 。它将会在其最后几条指令当中——远程进程和所有进程均如此。但 JMP指令的这个“规则”也有例外。如果某个函数被声明为静态的,它将会被直接调用,即使增量链接也是如此。这就是为什么规则#4要将 ThreadFunc 和 AfterThreadFunc 声明为静态或禁用增量链接的缘故。(有关增量链接的其它信息参见 Matt Pietrek的文章“Remove Fatty Deposits from Your Applications Using Our 32-bit Liposuction Tools” )

附录D

为什么 ThreadFunc的局部变量只有 4k?

局部变量总是存储在堆栈中,如果某个函数有256个字节的局部变量,当进入该函数时,堆栈指针就减少256个字节(更精确地说,在函数开始处)。例如,下面这个函数:

void Dummy(void) {
    BYTE var[256];
    var[0] = 0;
    var[1] = 1;
    var[255] = 255;
}

编译后的汇编如下:

:00401000   push ebp
:00401001   mov  ebp, esp
:00401003   sub  esp, 00000100           ; change ESP as storage for
                                         ; local variables is needed
:00401006   mov  byte ptr [esp], 00      ; var[0] = 0;
:0040100A   mov  byte ptr [esp+01], 01   ; var[1] = 1;
:0040100F   mov  byte ptr [esp+FF], FF   ; var[255] = 255;
:00401017   mov  esp, ebp                ; restore stack pointer
:00401019   pop  ebp
:0040101A   ret

注意上述例子中,堆栈指针是如何被修改的?而如果某个函数需要4KB以上局部变量内存空间又会怎么样呢?其实,堆栈指针并不是被直接修改,而是通过另一个函数调用来修改的。就是这个额外的函数调用使得我们的 ThreadFunc “被破坏”了,因为其远程拷贝会调用一个不存在的东西。
我们看看文档中对堆栈探测和 /Gs编译器选项是怎么说的:
——“/GS是一个控制堆栈探测的高级特性,堆栈探测是一系列编译器插入到每个函数调用的代码。当函数被激活时,堆栈探测需要的内存空间来存储相关函数的局部变量。
如果函数需要的空间大于为局部变量分配的堆栈空间,其堆栈探测被激活。默认的大小是一个页面(在80×86处理器上4kb)。这个值允许在Win32 应用程序和Windows NT虚拟内存管理器之间进行谨慎调整以便增加运行时承诺给程序堆栈的内存。”
我确信有人会问:文档中的“……堆栈探测到一块需要的内存空间来存储相关函数的局部变量……”那些编译器选项(它们的描述)在你完全弄明白之前有时真的让 人气愤。例如,如果某个函数需要12KB的局部变量存储空间,堆栈内存将进行如下方式的分配(更精确地说是“承诺” )。

sub    esp, 0x1000    ; "分配" 第一次 4 Kb
test  [esp], eax      ; 承诺一个新页内存(如果还没有承诺)
sub    esp, 0x1000    ; "分配" 第二次4 Kb
test  [esp], eax      ; ...
sub    esp, 0x1000
test  [esp], eax

注意4KB堆栈指针是如何被修改的,更重要的是,每一步之后堆栈底是如何被“触及”(要经过检查)。这样保证在“分配”(承诺)另一页面之前,当前页面承诺的范围也包含堆栈底。

注意事项
“每一个线程到达其自己的堆栈空间,默认情况下,此空间由承诺的以及预留的内存组成,每个线程使用 1 MB预留的内存,以及一页承诺的内存,系统将根据需要从预留的堆栈内存中承诺一页内存区域” (参见 MSDN CreateThread > dwStackSize > Thread Stack Size)
还应该清楚为什么有关 /GS 的文档说在堆栈探针在 Win32 应用程序和Windows NT虚拟内存管理器之间进行谨慎调整。

现在回到我们的ThreadFunc以及 4KB 限制
虽然你可以用 /Gs 防止调用堆栈探测例程,但在文档对于这样的做法给出了警告,此外,文件描述可以用 #pragma check_stack 指令关闭或打开堆栈探测。但是这个指令好像一点作用都没有(要么这个文档是垃圾,要么我疏忽了其它一些信息?)。总之,CreateRemoteThread 和 WriteProcessMemory 技术只能用于注入小块代码,所以你的局部变量应该尽量少耗费一些内存字节,最好不要超过 4KB限制。

附录E

为什么要将开关语句拆分成三个以上?

用下面这个例子很容易解释这个问题,假设有如下这么一个函数:

int Dummy( int arg1 ) 
{
    int ret =0;

    switch( arg1 ) {
    case 1: ret = 1; break;
    case 2: ret = 2; break;
    case 3: ret = 3; break;
    case 4: ret = 0xA0B0; break;
    }
    return ret;
}

编译后变成下面这个样子:

地址      操作码/参数       解释后的指令
--------------------------------------------------
                                             ; arg1 -> ECX
:00401000  8B4C2404         mov ecx, dword ptr [esp+04]
:00401004  33C0             xor eax, eax     ; EAX = 0
:00401006  49               dec ecx          ; ECX --
:00401007  83F903           cmp ecx, 00000003
:0040100A  771E             ja 0040102A

; JMP 到表***中的地址之一
; 注意 ECX 包含的偏移
:0040100C  FF248D2C104000   jmp dword ptr [4*ecx+0040102C]

:00401013  B801000000       mov eax, 00000001   ; case 1: eax = 1;
:00401018  C3               ret
:00401019  B802000000       mov eax, 00000002   ; case 2: eax = 2;
:0040101E  C3               ret
:0040101F  B803000000       mov eax, 00000003   ; case 3: eax = 3;
:00401024  C3               ret
:00401025  B8B0A00000       mov eax, 0000A0B0   ; case 4: eax = 0xA0B0;
:0040102A  C3               ret
:0040102B  90               nop

; 地址表***
:0040102C  13104000         DWORD 00401013   ; jump to case 1
:00401030  19104000         DWORD 00401019   ; jump to case 2
:00401034  1F104000         DWORD 0040101F   ; jump to case 3
:00401038  25104000         DWORD 00401025   ; jump to case 4

注意如何实现这个开关语句?

与其单独检查每个CASE语句,不如创建一个地址表,然后通过简单地计算地址表的偏移量而跳转到正确的CASE语句。这实际上是一种改进。假设你有50个CASE语句。如果不使用上述的技巧,你得执行50次 CMP和JMP指令来达到最后一个CASE。相反,有了地址表后,你可以通过表查询跳转到任何CASE语句,从计算机算法角度和时间复杂度看,我们用O(5)代替了O(2n)算法。其中:

  1. O表示最坏的时间复杂度;
  2. 我们假设需要5条指令来进行表查询计算偏移量,最终跳到相应的地址;

现在,你也许认为出现上述情况只是因为CASE常量被有意选择为连续的(1,2,3,4)。幸运的是,它的这个方案可以应用于大多数现实例子中,只有偏移量的计算稍微有些复杂。但有两个例外:

  • 如果CASE语句少于等于三个;
  • 如果CASE 常量完全互不相关(如:“”case 1” ,“case 13” ,“case 50” , 和“case 1000” );

显然,单独判断每个的CASE常量的话,结果代码繁琐耗时,但使用CMP和JMP指令则使得结果代码的执行就像普通的if-else 语句。
有趣的地方:如果你不明白CASE语句使用常量表达式的理由,那么现在应该弄明白了吧。为了创建地址表,显然在编译时就应该知道相关地址。

现在回到问题!
注意到地址 0040100C 处的JMP指令了吗?我们来看看Intel关于十六进制操作码 FF 的文档是怎么说的:

操作码 指令     描述
FF /4  JMP r/m32  Jump near, absolute indirect,
           address given in r/m32

原来JMP 使用了一种绝对寻址方式,也就是说,它的操作数(CASE语句中的 0040102C)表示一个绝对地址。还用我说什么吗?远程 ThreadFunc 会盲目地认为地址表中开关地址是 0040102C,JMP到一个错误的地方,造成远程进程崩溃。

附录F

为什么远程进程会崩溃呢?

当远程进程崩溃时,它总是会因为下面这些原因:

  1. 在ThreadFunc 中引用了一个不存在的串;
  2. 在在ThreadFunc 中 中一个或多个指令使用绝对寻址(参见附录E);
  3. ThreadFunc 调用某个不存在的函数(该调用可能是编译器或链接器添加的)。你在反汇编器中可以看到这样的情形:
    :004014C0    push EBP         ; ThreadFunc 的入口点
    :004014C1    mov EBP, ESP
     ...
    :004014C5    call 0041550     ;  这里将使远程进程崩溃
     ...
    :00401502    ret

    如果 CALL 是由编译器添加的指令(因为某些“禁忌” 开关如/GZ是打开的),它将被定位在 ThreadFunc 的开始的某个地方或者结尾处。

不管哪种情况,你都要小心翼翼地使用 CreateRemoteThread 和 WriteProcessMemory 技术。尤其要注意你的编译器/链接器选项,一不小心它们就会在 ThreadFunc 添加内容。

参考资料

[转载]百度地图API建立全国银行位置查询系统(五)——如何更改百度地图的信息窗口内容?

mikel阅读(928)

转载【百度地图API】建立全国银行位置查询系统(五)——如何更改百度地图的信息窗口内容? – 酸奶小妹 – 博客园.

摘要:

酷讯、搜房、去哪儿网等大型房产、旅游酒店网站的是百度的数据库,却显示自定义的信息窗口内容,这是如何实现的呢?

——————————————————————————————————————

零、先来看看百度地图上的信息窗口长个什么样子

在来看看房产网站的信息窗口是什么样子的

怎么样,信息窗口的内容不一样吧。

但是它们都是用的百度地图的数据库哦~~~

怎么更改百度地图默认的信息窗口呢?快来学习吧~~

一、百度地图的数据覆盖率

据了解,截止到2011年6月底,百度地图的数据覆盖率为80.73%,达到国内第一的水平。

所以,使用百度强大的数据库,对与开发者来说,收益匪浅。

但如何利用百度的数据库,展现自己的信息窗口内容呢?




二、如何自定义信息窗口内容?

我们先来看一个简单的例子

var infoWindow = new BMap.InfoWindow("World", opts);  // 创建信息窗口对象

例子中,“world”是信息窗口的内容,opts是信息窗口的设置选项。

“”引号中,可以书写任意的htm,已达到自定义信息窗口的目的。

opts的属性见官网的类参考,有如下设置。注意,设置是可选项,可以不写。

三、如何将自定义信息窗口与百度的数据库相联系

首先,我们需要获取到百度数据库里的内容。可以使用localsearch来搜索出数据。例如,我创建了一个搜索对象,搜索“招商银行”。

var local = new BMap.LocalSearch(map,{onSearchComplete: searchComplete});   //构造一个查询
local.search('招商银行');

看了我上一篇文章的朋友们,应该都知道,如何把那些小红点变成招商银行的图标了吧?

对啦,没错,就是修改marker的一个icon属性,更改图标即可。

// 创建招商银行的标注图标
var zsIcon = new BMap.Icon("http://ui-love.com/baidumap/bank/marker.gif",   //图片地址
new BMap.Size(40, 64),                // 标注显示大小
{
offset: new BMap.Size(20, 64),     // 标注底部小尖尖的偏移量
imageOffset: new BMap.Size(0, 0)   // 这里相当于CSS sprites
});

这时,标注们就是招商银行(左图)了,而不是小红点(右图)。

对于搜索出来的数据,我们可以找到结果(result)的各种属性,见类参考,选择一些填入信息窗口,并且,可以自己修改内容:

比如,我选择了名称、地址和电话3个属性,然后自己写了几颗星,以及详情链接,代码如下:

var infoWindow = new BMap.InfoWindow("<div style='line-height:1.8em;font-size:12px;'><b>名称:</b>"+point.title+"</br><b>地址:</b>"+point.address+"</br><b>电话:</b>"+point.phoneNumber+"</br><b>口碑:</b><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><a style='text-decoration:none;color:#2679BA;float:right' target='_blank' href='http://www.ui-love.com'>详情>></a></div>");  // 创建信息窗口对象

四、效果图和源代码

看见了麼?数据是真实可靠的,并且我加上了自己的内容在里面。可以做评价、详情链接,甚至价格、图片等等。

源代码里,我加入了其他两家银行,让大家做个对比。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>三家银行搜索</title>
<script type="text/javascript" src="http://api.map.baidu.com/api?v=1.2">
</script>
</head>
<body>
<div style="width:520px;height:340px;border:1px solid gray" id="container"></div>
<input type="button" onclick="milk_zs();" value="招商银行" />
<input type="button" onclick="milk_zg();" value="中国银行" />
<input type="button" onclick="milk_js();" value="建设银行" />

<div style="position:absolute;right:100px;top:10px;">
<script type="text/javascript"><!--
google_ad_client = "ca-pub-5845154460812025";
/* 180&#42;150 */
google_ad_slot = "5267666065";
google_ad_width = 180;
google_ad_height = 150;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</body>
</html>
<script type="text/javascript">
//查询完毕添加自定义标注
function addMarker(results,point, index){
    // 创建招商银行的标注图标
    var zsIcon = new BMap.Icon("http://ui-love.com/baidumap/bank/marker.gif",   //图片地址
                 new BMap.Size(40, 64),                // 标注显示大小
                {
                    offset: new BMap.Size(20, 64),     // 标注底部小尖尖的偏移量
                    imageOffset: new BMap.Size(0, 0)   // 这里相当于CSS sprites
                });
    // 创建中国银行的标注图标
    var zgIcon = new BMap.Icon("http://ui-love.com/baidumap/bank/marker.gif",   //图片地址
                 new BMap.Size(40, 64),                // 标注显示大小
                {
                    offset: new BMap.Size(20, 64),     // 标注底部小尖尖的偏移量
                    imageOffset: new BMap.Size(0, -64)   // 这里相当于CSS sprites
                });
    // 创建建设银行的标注图标
    var jsIcon = new BMap.Icon("http://ui-love.com/baidumap/bank/marker.gif",   //图片地址
                 new BMap.Size(40, 64),                // 标注显示大小
                {
                    offset: new BMap.Size(20, 64),     // 标注底部小尖尖的偏移量
                    imageOffset: new BMap.Size(0, -128)   // 这里相当于CSS sprites
                });
    var myIcon = "";
    if(results.keyword == "招商银行"){
        myIcon = zsIcon;
    }else if(results.keyword == "中国银行"){
        myIcon = zgIcon;
    }else if(results.keyword == "建设银行"){
        myIcon = jsIcon;
    }else{
        myIcon = zsIcon;
    }
    var marker = new BMap.Marker(point.point, {icon: myIcon});

    var infoWindow = new BMap.InfoWindow("<div style='line-height:1.8em;font-size:12px;'><b>名称:</b>"+point.title+"</br><b>地址:</b>"+point.address+"</br><b>电话:</b>"+point.phoneNumber+"</br><b>口碑:</b><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><a style='text-decoration:none;color:#2679BA;float:right' target='_blank' href='http://www.ui-love.com'>详情>></a></div>");  // 创建信息窗口对象
    marker.addEventListener("click",
        function(){
            marker.openInfoWindow(infoWindow);
        }
    );
    map.addOverlay(marker);
}

//查询完毕的回调函数
var searchComplete = function (results){   
    if (local.getStatus() != BMAP_STATUS_SUCCESS){
        return ;
    }
    for(var cnt = 0; cnt < results.getCurrentNumPois(); cnt++){
        var point = results.getPoi(cnt);
        addMarker(results,point, cnt);
    }
}

var map = new BMap.Map("container");    //创建地图容器
map.centerAndZoom(new BMap.Point(116.404, 39.915), 11); //初始化地图
var local = new BMap.LocalSearch(map,{onSearchComplete: searchComplete});   //构造一个查询

//定义三个不同的查询
function milk_zs(){
    map.clearOverlays();
    local.search('招商银行');
}
function milk_zg(){
    map.clearOverlays();
    local.search('中国银行');
}
function milk_js(){
    map.clearOverlays();
    local.search('建设银行');
}
</script>

[转载]如何利用百度地图API,制作房产酒店地图?(下)

mikel阅读(727)

[转载]如何利用【百度地图API】,制作房产酒店地图?(下) – 酸奶小妹 – 博客园.

摘要:

很多房产网、旅游酒店网上,都有一个列表,鼠标经过列表上的数据时,地图上就会打开相应的信息窗口。

如何实现这一功能呢?

快来学习吧。

———————————————————————————————–

一、制作列表

接着上一篇文章来讲,我们已经拥有了一张能显示自定义标注,和信息窗口的地图了。现在,我们来加上列表。

首先,需要写上一段htm,来展示列表。

<div style="float: left; width: 200px; height: 340px; padding: 0pt 10px 0pt 0pt; line-height: 1.8em;">
<ul>
	<li><span style="float: right;">120元</span><a href="#">如家快捷酒店</a></li>
	<li><span style="float: right;">2370元</span><a href="#">昆仑大厦</a></li>
	<li><span style="float: right;">50元</span><a href="#">华夏银行</a></li>
	<li><span style="float: right;">16元</span><a href="#">成都小吃</a></li>
	<li><span style="float: right;">300元</span><a href="#">锦绣大饭店</a></li>
	<li><span style="float: right;">180元</span><a href="#">七天快捷酒店</a></li>
	<li><span style="float: right;">9元</span><a href="#">中央民族大学</a></li>
	<li><span style="float: right;">3300元</span><a href="#">昌平汽车专修学院</a></li>
	<li><span style="float: right;">20元</span><a href="#">百度大厦</a></li>
	<li><span style="float: right;">1000元</span><a href="#">海尔电器销售点</a></li>
</ul>
</div>

对了,不要忘记给列表中的链接加上样式。

二、打开对应的信息窗口

因为这里没有用到对应的marker,不能用上一篇文章中的打开信息窗口的方法了。

我们需要在地图上打开信息窗口。

需要两个参数,第一是信息窗口的id,第二是信息窗口的坐标。如下:

function openMyWin(id,p){
map.openInfoWindow(id,p);
}

最后,只需要对列表中的数据,加上打开对应信息窗口的js语句即可。

如下,当鼠标滑过时,打开信息一的窗口。
onmouSEOver=”openMyWin(infoWindow1,point[1])”

三、截图

四、源代码

为了方便大家学习,以下代码包含了上一章的全部代码,并做了改进。

全部源代码如下:





酷讯酒店地图
<script src="http://api.map.baidu.com/api?v=1.2" type="text/javascript"></script> <!-- a{text-decoration:none;color:#6ce;font-size:14px;} a:hover{text-decoration:underline;} -->

<div style="float: left; width: 200px; height: 340px; padding: 0pt 10px 0pt 0pt; line-height: 1.8em;">
<ul>
	<li><span style="float: right;">120元</span><a onmouseover="openMyWin(infoWindow1,point&#91;1&#93;)" href="#">如家快捷酒店</a></li>
	<li><span style="float: right;">2370元</span><a onmouseover="openMyWin(infoWindow2,point&#91;2&#93;)" href="#">昆仑大厦</a></li>
	<li><span style="float: right;">50元</span><a onmouseover="openMyWin(infoWindow3,point&#91;3&#93;)" href="#">华夏银行</a></li>
	<li><span style="float: right;">16元</span><a onmouseover="openMyWin(infoWindow4,point&#91;4&#93;)" href="#">成都小吃</a></li>
	<li><span style="float: right;">300元</span><a onmouseover="openMyWin(infoWindow5,point&#91;5&#93;)" href="#">锦绣大饭店</a></li>
	<li><span style="float: right;">180元</span><a onmouseover="openMyWin(infoWindow6,point&#91;6&#93;)" href="#">七天快捷酒店</a></li>
	<li><span style="float: right;">9元</span><a onmouseover="openMyWin(infoWindow7,point&#91;7&#93;)" href="#">中央民族大学</a></li>
	<li><span style="float: right;">3300元</span><a onmouseover="openMyWin(infoWindow8,point&#91;8&#93;)" href="#">昌平汽车专修学院</a></li>
	<li><span style="float: right;">20元</span><a onmouseover="openMyWin(infoWindow9,point&#91;9&#93;)" href="#">百度大厦</a></li>
	<li><span style="float: right;">1000元</span><a onmouseover="openMyWin(infoWindow0,point&#91;0&#93;)" href="#">海尔电器销售点</a></li>
</ul>
</div>
<script type="text/javascript">// <!&#91;CDATA&#91;
var map = new BMap.Map("container");
var point = new BMap.Point(116.404, 39.915);
map.centerAndZoom(point, 14);

var myIcon = new BMap.Icon("http://dev.baidu.com/wiki/static/map/API/examples/images/Mario.png", new BMap.Size(32, 70), {    //小车图片
    imageOffset: new BMap.Size(0, 0)    //图片的偏移量。为了是图片底部中心对准坐标点。
  });

var point = &#91;new BMap.Point(116.411776,39.942833),new BMap.Point(116.320791,40.003682),new BMap.Point(116.275186,39.896095),new BMap.Point(116.425098,39.946249),new BMap.Point(116.359823,39.984761),new BMap.Point(116.316479,39.98323),new BMap.Point(116.385986,39.946124),new BMap.Point(116.427545,40.00796),new BMap.Point(116.446965,39.911603),new BMap.Point(116.454579,39.946652)&#93;;   //10个坐标点

var marker1 = new BMap.Marker(point&#91;1&#93;,{icon:myIcon});  // 创建10个标注
var marker2 = new BMap.Marker(point&#91;2&#93;,{icon:myIcon});
var marker3 = new BMap.Marker(point&#91;3&#93;,{icon:myIcon});
var marker4 = new BMap.Marker(point&#91;4&#93;,{icon:myIcon});
var marker5 = new BMap.Marker(point&#91;5&#93;,{icon:myIcon});
var marker6 = new BMap.Marker(point&#91;6&#93;,{icon:myIcon});
var marker7 = new BMap.Marker(point&#91;7&#93;,{icon:myIcon});
var marker8 = new BMap.Marker(point&#91;8&#93;,{icon:myIcon});
var marker9 = new BMap.Marker(point&#91;9&#93;,{icon:myIcon});
var marker0 = new BMap.Marker(point&#91;0&#93;,{icon:myIcon});

map.addOverlay(marker1);              // 将标注添加到地图中
map.addOverlay(marker2);
map.addOverlay(marker3);
map.addOverlay(marker4);
map.addOverlay(marker5);
map.addOverlay(marker6);
map.addOverlay(marker7);
map.addOverlay(marker8);
map.addOverlay(marker9);
map.addOverlay(marker0);

map.setViewport(point);         //调整地图的最佳视野为显示标注数组point

var opts1 = {offset : new BMap.Size(0, -25), title : '<span style="font-size:14px;color:#0A8021">如家快捷酒店</span>'};
var opts2 = {offset : new BMap.Size(0, -25), title : '<span style="font-size:14px;color:#0A8021">昆仑大厦</span>'};
var opts3 = {offset : new BMap.Size(0, -25), title : '<span style="font-size:14px;color:#0A8021">华夏银行</span>'};
var opts4 = {offset : new BMap.Size(0, -25), title : '<span style="font-size:14px;color:#0A8021">成都小吃</span>'};
var opts5 = {offset : new BMap.Size(0, -25), title : '<span style="font-size:14px;color:#0A8021">锦绣大饭店</span>'};
var opts6 = {offset : new BMap.Size(0, -25), title : '<span style="font-size:14px;color:#0A8021">七天快捷酒店</span>'};
var opts7 = {offset : new BMap.Size(0, -25), title : '<span style="font-size:14px;color:#0A8021">中央民族大学</span>'};
var opts8 = {offset : new BMap.Size(0, -25), title : '<span style="font-size:14px;color:#0A8021">昌平汽车专修学院</span>'};
var opts9 = {offset : new BMap.Size(0, -25), title : '<span style="font-size:14px;color:#0A8021">百度大厦</span>'};
var opts0 = {offset : new BMap.Size(0, -25), title : '<span style="font-size:14px;color:#0A8021">海尔电器销售点</span>'};

var infoWindow1 = new BMap.InfoWindow("
<div style='line-height:1.8em;font-size:12px;'><b>地址:</b>北京市朝阳区高碑店小学旁</br><b>电话:</b>010-59921010</br><b>口碑:</b><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><a style='text-decoration:none;color:#2679BA;float:right' href='#'>详情>></a></div>
", opts1);  // 创建信息窗口对象,引号里可以书写任意的html语句。
var infoWindow2 = new BMap.InfoWindow("
<div style='line-height:1.8em;font-size:12px;'><b>地址:</b>北京市海淀区紫竹院123号</br><b>电话:</b>010-59921010</br><b>口碑:</b><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><a style='text-decoration:none;color:#2679BA;float:right' href='#'>详情>></a></div>
", opts2);
var infoWindow3 = new BMap.InfoWindow("
<div style='line-height:1.8em;font-size:12px;'><b>地址:</b>北京市海淀区紫竹院123号</br><b>电话:</b>010-59921010</br><b>口碑:</b><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><a style='text-decoration:none;color:#2679BA;float:right' href='#'>详情>></a></div>
", opts3);
var infoWindow4 = new BMap.InfoWindow("
<div style='line-height:1.8em;font-size:12px;'><b>地址:</b>北京市海淀区紫竹院123号</br><b>电话:</b>010-59921010</br><b>口碑:</b><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><a style='text-decoration:none;color:#2679BA;float:right' href='#'>详情>></a></div>
", opts4);
var infoWindow5 = new BMap.InfoWindow("
<div style='line-height:1.8em;font-size:12px;'><b>地址:</b>北京市朝阳区高碑店小学旁</br><b>电话:</b>010-59921010</br><b>口碑:</b><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><a style='text-decoration:none;color:#2679BA;float:right' href='#'>详情>></a></div>
", opts5);
var infoWindow6 = new BMap.InfoWindow("
<div style='line-height:1.8em;font-size:12px;'><b>地址:</b>北京市大钟寺沧澜大厦</br><b>电话:</b>010-59921010</br><b>口碑:</b><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><a style='text-decoration:none;color:#2679BA;float:right' href='#'>详情>></a></div>
", opts6);
var infoWindow7 = new BMap.InfoWindow("
<div style='line-height:1.8em;font-size:12px;'><b>地址:</b>北京市朝阳区高碑店小学旁</br><b>电话:</b>010-59921010</br><b>口碑:</b><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><a style='text-decoration:none;color:#2679BA;float:right' href='#'>详情>></a></div>
", opts7);
var infoWindow8 = new BMap.InfoWindow("
<div style='line-height:1.8em;font-size:12px;'><b>地址:</b>北京市哇哈哈路鲜鱼一条街</br><b>电话:</b>010-59921010</br><b>口碑:</b><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><a style='text-decoration:none;color:#2679BA;float:right' href='#'>详情>></a></div>
", opts8);
var infoWindow9 = new BMap.InfoWindow("
<div style='line-height:1.8em;font-size:12px;'><b>地址:</b>北京市朝阳区高碑店小学旁</br><b>电话:</b>010-59921010</br><b>口碑:</b><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><a style='text-decoration:none;color:#2679BA;float:right' href='#'>详情>></a></div>
", opts9);
var infoWindow0 = new BMap.InfoWindow("
<div style='line-height:1.8em;font-size:12px;'><b>地址:</b>北京市朝阳区高碑店小学旁</br><b>电话:</b>010-59921010</br><b>口碑:</b><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><img src='http://cdn2.iconfinder.com/data/icons/diagona/icon/16/031.png' /><a style='text-decoration:none;color:#2679BA;float:right' href='#'>详情>></a></div>
", opts0);

marker1.addEventListener("mouseover", function(){this.openInfoWindow(infoWindow1);});
marker2.addEventListener("mouseover", function(){this.openInfoWindow(infoWindow2);});
marker3.addEventListener("mouseover", function(){this.openInfoWindow(infoWindow3);});
marker4.addEventListener("mouseover", function(){this.openInfoWindow(infoWindow4);});
marker5.addEventListener("mouseover", function(){this.openInfoWindow(infoWindow5);});
marker6.addEventListener("mouseover", function(){this.openInfoWindow(infoWindow6);});
marker7.addEventListener("mouseover", function(){this.openInfoWindow(infoWindow7);});
marker8.addEventListener("mouseover", function(){this.openInfoWindow(infoWindow8);});
marker9.addEventListener("mouseover", function(){this.openInfoWindow(infoWindow9);});
marker0.addEventListener("mouseover", function(){this.openInfoWindow(infoWindow0);});

function openMyWin(id,p){
    map.openInfoWindow(id,p);
}
// &#93;&#93;></script>

[转载]sina微博与dedecms文章同步

mikel阅读(1101)

看到这篇文章的时候本代码首页已经关联其应用了,让新浪微薄的访问量到站上来,让站上的流量到微博上去,都可以的哦,大家都知道dedecms本身没有生成全站xml的,可是让sina微博自动关联站上的文章就不会成功,分两步来实现这个事,

1.让dedecms全站输出xml,2.在sina微博上关联上站点,弄好后站上每发表了文章sina微博就自动发表一篇关于文章的微博。

1.让dedecms全站输出xml。

①,建立php全站输出文件,新建一个记事本把以下代码贴上然后保存,重命名这个文件为 rss.php 重命名后确保是个php文件,

<!--?php     require_once (dirname(__FILE__) . "/include/common.inc.php");  require_once DEDEINC."/arc.partview.class.php";  $pv = new PartView(); $pv--->SetTemplet($cfg_basedir . $cfg_templets_dir . "/default/rss.htm");
header("Content-type:application/xml");
$pv-&gt;Display();
?&gt;

②,建立再新建一个记事本把一下代码贴上,保存,重命名为rss.htm,记住把你的网址填上。

<!--?xml version="1.0" encoding="utf-8" ?-->


{dede:global.cfg_webname/}

{dede:global.cfg_basehost/} {dede:global.cfg_description/}
zh-cn
{dede:global.cfg_webname/}
{dede:global.cfg_adminemail/}
{dede:arclist row='60' col='1' titlelen='100' orderby='pubdate'}

http://www.你的网址.com[field:arcurl/] 		&lt;!--[CDATA[[field:title function='html2text(@me)'/]]]&gt;
[field:writer/]
[field:typename/]

[field:pubdate function='strftime("%a, %d %b %Y %H:%M:%S +0800",@me)'/] 		http://www.你的网址.com[field:arcurl/]

<!--&#91;CDATA&#91;&#91;field:description function='html2text(@me)'/&#93; ... <br /--><strong>文章分类</strong>:[field:typename/]
<a href="http://www.你的网址.com&#91;field:arcurl/&#93;" target="_blank">阅读全文</a> | <a href="http://www.你的网址.com&#91;field:arcurl/&#93;" target="_blank">评论回复</a>]]--&gt;


{/dede:arclist}


③,修改你的主页模板index.htm在<head>和</head>之间加入一下标记。

<link rel=”alternate” type=”application/rss+xml” href=”http://www.你的网址.com/rss.php” title=”网站标题” /> ④,将rss.php文件上传至根目录,将rss.htm上传至模版文件夹下default文件夹里。

这样你从新生成一下主页,就可以全站输出啦。

2.让sina微博关联本站。

①,登陆sina微博,进入上面 工具 》 关联博客 》 选择其他博客 》 把你的网址贴在框里 确定。