标签存档: ASM

[ASM] 是男人就下100层,交出序列号来

某天(前天:),看了两个小时高数(具体不详:)之后,想放松一下,找到《是男人就下100层》,打开,玩了两把,突然发现今天怎么看那个“注册”的按钮特别不爽,OK,Crack it!

常规步骤,用PEid侦壳,用VC4.x写的,这样方便了,脱壳都不用。接着祭出用户级调试法宝——Ollydbg,加载《是男人就下100层》,万事俱备,踏上征途!

按F9运行程序,等程序窗口出来,点一下任务栏上按钮,结果竟然中断了,CPU窗口一看,原来是一条INT3指令,麻烦的东西,NOP掉。继续F9运行,好,这下可以把程序窗口调到前面了。切换到程序窗口,点注册,出现填写用户名和序列号的窗口。用户名填HotHeart,序列号填一个123456,确定,当然不会成功,要不我就可以去买彩票了^_^。弹出一个错误提示框,内容是日文的……意思大概是序列号无效。既然有提示框,那就好办,回到Ollydbg,下断点bp MessageBoxA,再次换到程序窗口点确定。YES!顺利中断,切回Ollydbg看看代码:

; 获取对话框中控件文本

00407A78  PUSH 100

00407A7D  LEA EAX,DWORD PTR SS:[EBP-204]

00407A83  PUSH EAX

00407A84  PUSH 3EB

00407A89  MOV EAX,DWORD PTR SS:[EBP+8]

00407A8C  PUSH EAX

00407A8D  CALL DWORD PTR DS:[<&USER32.GetDlgItemText>]

00407A93  LEA EAX,DWORD PTR SS:[EBP-204]

00407A99  PUSH EAX

00407A9A  CALL 是男人就.00407C5F

00407A9F  ADD ESP,4

00407AA2  TEST EAX,EAX

00407AA4  JNZ 是男人就.00407AE1

; 从资源中载入字符串,是注册失败的提示信息

00407AAA  PUSH 100

00407AAF  LEA EAX,DWORD PTR SS:[EBP-104]

00407AB5  PUSH EAX

00407AB6  PUSH 4

00407AB8  MOV EAX,DWORD PTR DS:[40E200]

00407ABD  PUSH EAX

00407ABE  CALL DWORD PTR DS:[<&USER32.LoadStringA>]

; 下面就是弹出出错提示框的代码了

00407AC4  PUSH 10

00407AC6  PUSH 是男人就.0040D270

00407ACB  LEA EAX,DWORD PTR SS:[EBP-104]

00407AD1  PUSH EAX

00407AD2  MOV EAX,DWORD PTR SS:[EBP+8]

00407AD5  PUSH EAX

00407AD6  CALL DWORD PTR DS:[<&USER32.MessageBoxA>]

00407ADC  JMP 是男人就.00407B22

看到407AD6这里调用了MessageBoxA,往上看,很舒服地看到这一句:

00407AA4  JNZ 是男人就.00407AE1

一个很值得注意的跳转,是不是关键跳转呢,把JNZ改成JZ,再试试,点确定,出错提示是没有了,不过那个注册按钮还是在的,也就是说这句不是关键跳转,没有涉及到序列号的部分。再往上看,发现看在这个跳转之前有获取对话框中数据:

00407A8D  CALL DWORD PTR DS:[<&USER32.GetDlgItemText>]

那么在这里中断看看它得到了什么。F2下断点,切到程序窗口,输入用户名和序列号,确定,预料之中断下来。接着F8单步运行,再来一个YES!它得到的是123456,也就是我填的序列号,接着把序列号所在的地址存到EAX中,再将EAX压入堆栈,紧接着又一个call,嗯,这个call很值得怀疑,跟进!下面就是整个call的函数的代码了,有点长:)

00407C5F  PUSH EBP ; C函数标准开头

00407C60  MOV EBP,ESP

00407C62  SUB ESP,4   ; 局部变量,分析后面的代码可知是用来作计数器的

00407C65  PUSH EBX ; 保护寄存器

00407C66  PUSH ESI

00407C67  PUSH EDI

从代码开始可明显看出这是一个标准的C函数,接着往下看。碰到堆栈操作,这值得注意,因为call过来时有压入过一个参数,指向序列号起始地址:

00407C68  MOV EAX,DWORD PTR SS:[EBP+8]  ; 这句之后EAX指向序列号

00407C6B  XOR ECX,ECX

00407C6D  MOV CL,BYTE PTR DS:[EAX+7]    ; 得到序列号第8位

00407C70  TEST ECX,ECX ; 判断是否为0

00407C72  JE 是男人就.00407C7F    ; 是刚跳转

00407C78  XOR EAX,EAX ; EAX清零

00407C7A  JMP 是男人就.00407DC4    ; 这里跳到函数结尾

00407C7F  MOV DWORD PTR SS:[EBP-4],0    ; 计数器清零

00407C86  JMP 是男人就.00407C8E

上面的代码的作用是得到序列号第8位,并判断是否为0,学过C的人都知道在C里字符串是以0表示结束的,那么就知道这里三句是用来判断用户输入的序列号是否大于7位的了,如果大于7位,就会将EAX清零并跳到函数结束的地方,而前面已经知道,在调用这个函数之后,如果返回值为0则序列号验证失败,所以序列号是7位或7位以内的。我填的123456,是6位,所以没有跳到函数结束,接着往下分析:

00407C8B  INC DWORD PTR SS:[EBP-4]    ; 计数器加1

00407C8E  CMP DWORD PTR SS:[EBP-4],7  ; 判断计数器是否大于7

00407C92  JGE 是男人就.00407CC3       ; 大于等于就

00407C98  MOV EAX,DWORD PTR SS:[EBP-4] ; 取计数器值到EAX

00407C9B  MOV ECX,DWORD PTR SS:[EBP+8] ; 取序列号地址到ECX

00407C9E  MOV AL,BYTE PTR DS:[EAX+ECX; 取序列号第N位到AL,N为计数器值

00407CA1  PUSH EAX ; 压入堆栈,调用处理函数

00407CA2  CALL 是男人就.00407DC9  ; 将1位序列号从ASCII转换成二进制值

00407CA7  ADD ESP,4   ; 恢复堆栈

00407CAA  XOR ECX,ECX ; ECX清零

00407CAC  MOV CL,AL ; 将处理过的1位序列号存到CL

00407CAE  CMP ECX,24  ; 判断是否大于0x24

00407CB1  JLE 是男人就.00407CBE  ; 大于就出错了

00407CB7  XOR EAX,EAX ; 大于0x24,跳到函数结束,返回0

00407CB9  JMP 是男人就.00407DC4

00407CBE  JMP 是男人就.00407C8B  ; 继续处理下一位

很清楚的,上面这段代码是用来判断序列号中每一位是否符合要求,是什么要求呢,看到在每取得1位序列号之后有一句call:

00407CA2  CALL 是男人就.00407DC9

这个调用得看看,F7跟进:

00407DC9  PUSH EBP

00407DCA  MOV EBP,ESP

00407DCC  PUSH EBX

00407DCD  PUSH ESI

00407DCE  PUSH EDI

00407DCF  XOR EAX,EAX

00407DD1  MOV AL,BYTE PTR SS:[EBP+8]    ; 取参数,即1位序列号

00407DD4  CMP EAX,61

00407DD7  JL 是男人就.00407DE8

; 判断是否大于0x61,如果对ASCII码表熟悉的话可以知道0x61对应的是a

; 小于则跳转,好,上面那个判断应该不是用来判断这个字符是否为小写字母了

; 不是则跳到后面继续处理,是则进行下面的处理过程

00407DDD  XOR EAX,EAX ; EAX清零

00407DDF  MOV AL,BYTE PTR SS:[EBP+8]    ; 取字符

00407DE2  SUB EAX,20     ; 减0x20,变大写字母

00407DE5  MOV BYTE PTR SS:[EBP+8],AL ; 保存

; 不管是否为小字字母,经过上面的判断和处理都会变成大写字母,继续进行处理

00407DE8  XOR EAX,EAX ; EAX清零

00407DEA  MOV AL,BYTE PTR SS:[EBP+8]    ; 取字符

00407DED  CMP EAX,41

00407DF0  JL 是男人就.00407E01

; 判断是否大于,0x41对应的ASCII字符为A

; 小于则跳转,说明这里是判断字符是否为大写字母

; 不是则跳到后面继续处理,是则进行下面的处理过程

00407DF6  XOR EAX,EAX ; EAX清零

00407DF8  MOV AL,BYTE PTR SS:[EBP+8]    ; 取字符

00407DFB  SUB EAX,7      ; 减去7

00407DFE  MOV BYTE PTR SS:[EBP+8],AL ; 保存

; 如果不是大写字母就跳到这里了,当然如果是大写字母的话

; 经过上面的处理过程同样要进行下面的处理过程

00407E01  XOR EAX,EAX ; EAX清零

00407E03  MOV AL,BYTE PTR SS:[EBP+8]    ; 取字符

00407E06  SUB EAX,30     ; 减去0x30

00407E09  JMP 是男人就.00407E0E  ; 函数结束

00407E0E  POP EDI

00407E0F  POP ESI

00407E10  POP EBX

00407E11  LEAVE

00407E12  RETN

连贯起来看可以知道这个处理函数的过程是这样的:

char是否为小写字母 -> 是则减去0x20变成大写字母,不是则保持不变 -> char是否为大写字母 -> 是则减去7,不是则保持不变 -> char减0x30

再联系数字和字母的ASCII码值,9为0x39,A为0x41,0x41-0x39=7,OK,可以知道这个函数是把1位序列号变成二进制值,字符0到9对应数值0到9,字母全部转换成大写字母,并且字母A对应10,B对应11,后面依次类推。

弄明白了字符到数字的函数,继续看序列号验证的代码,可以看到在得到二进制值之后,又来了一句:

00407CB1  JLE 是男人就.00407CBE  ; 大于就出错了

00407CB7  XOR EAX,EAX ; 大于0x24,跳到函数结束,返回0

00407CB9  JMP 是男人就.00407DC4

00407CBE  JMP 是男人就.00407C8B  ; 继续处理下一位

0x24转换成十进制就是36,刚好是10个数字加26个字母,也就是说序列号只允许数字与字母,并且要7位,否则就通不过这个验证了。弄明白了这个,继续往下看:

00407CC3  MOV EAX,DWORD PTR SS:[EBP+8]

00407CC6  MOV AL,BYTE PTR DS:[EAX+5]  ; 取序列号第6位

00407CC9  PUSH EAX

00407CCA  CALL 是男人就.00407DC9      ; 转换成数值

00407CCF  ADD ESP,4

00407CD2  XOR EBX,EBX

00407CD4  MOV BL,AL ; 序列号第6位值保存到EBX

00407CD6  MOV EAX,DWORD PTR SS:[EBP+8]

00407CD9  MOV AL,BYTE PTR DS:[EAX+2]  ; 取序列号第3位

00407CDC  PUSH EAX

00407CDD  CALL 是男人就.00407DC9      ; 转换成数值

00407CE2  ADD ESP,4

00407CE5  XOR ECX,ECX

00407CE7  MOV CL,AL ; 序列号第3位值保存到ECX

00407CE9  MOV ESI,24   ; ESI=0x24

00407CEE  LEA EAX,DWORD PTR DS:[ECX+EBX*2+1D] ; EAX=ECX+EBX*2+1D

00407CF2  CDQ ; 扩展成64位

00407CF3  IDIV ESI ; 除以0x24

00407CF5  MOV EBX,EDX ; EDX为余数,存到EBX

00407CF7  MOV EAX,DWORD PTR SS:[EBP+8]

00407CFA  MOV AL,BYTE PTR DS:[EAX]  ; 取序列号第1位

00407CFC  PUSH EAX

00407CFD  CALL 是男人就.00407DC9    ; 转换成数值

00407D02  ADD ESP,4

00407D05  XOR ECX,ECX

00407D07  MOV CL,AL ; 序列号第1位值存到ECX

00407D09  CMP EBX,ECX ; EBX=ECX?

00407D0B  JNZ 是男人就.00407DBD  ; 不等,验证失败,跳到函数结束

; 头有些大了,用心看发现这里在判断序列号是否符合规则

; (第3位+第6位*2+0x1D)%0x24==第1位

; 跟着又是两段代码类似的,不难发现也是判断,不过规则有小小的不同

; 下面这段的判断规则是

; (第2位+第5位*2+0x1D)%0x24==第7位

00407D11  MOV EAX,DWORD PTR SS:[EBP+8]

00407D14  MOV AL,BYTE PTR DS:[EAX+4]

00407D17  PUSH EAX

00407D18  CALL 是男人就.00407DC9

00407D1D  ADD ESP,4

00407D20  XOR EBX,EBX

00407D22  MOV BL,AL

00407D24  MOV EAX,DWORD PTR SS:[EBP+8]

00407D27  MOV AL,BYTE PTR DS:[EAX+1]

00407D2A  PUSH EAX

00407D2B  CALL 是男人就.00407DC9

00407D30  ADD ESP,4

00407D33  XOR ECX,ECX

00407D35  MOV CL,AL

00407D37  MOV ESI,24

00407D3C  LEA EAX,DWORD PTR DS:[ECX+EBX*2+1D]

00407D40  CDQ

00407D41  IDIV ESI

00407D43  MOV EBX,EDX

00407D45  MOV EAX,DWORD PTR SS:[EBP+8]

00407D48  MOV AL,BYTE PTR DS:[EAX+6]

00407D4B  PUSH EAX

00407D4C  CALL 是男人就.00407DC9

00407D51  ADD ESP,4

00407D54  XOR ECX,ECX

00407D56  MOV CL,AL

00407D58  CMP EBX,ECX

00407D5A  JNZ 是男人就.00407DBD

; 下面这段的判断规则是

; (第1位+第7位*2+0x1D)%0x24==第4位

00407D60  MOV EAX,DWORD PTR SS:[EBP+8]

00407D63  MOV AL,BYTE PTR DS:[EAX+6]

00407D66  PUSH EAX

00407D67  CALL 是男人就.00407DC9

00407D6C  ADD ESP,4

00407D6F  XOR EBX,EBX

00407D71  MOV BL,AL

00407D73  MOV EAX,DWORD PTR SS:[EBP+8]

00407D76  MOV AL,BYTE PTR DS:[EAX]

00407D78  PUSH EAX

00407D79  CALL 是男人就.00407DC9

00407D7E  ADD ESP,4

00407D81  XOR ECX,ECX

00407D83  MOV CL,AL

00407D85  MOV ESI,24

00407D8A  LEA EAX,DWORD PTR DS:[ECX+EBX*2+1D]

00407D8E  CDQ

00407D8F  IDIV ESI

00407D91  MOV EBX,EDX

00407D93  MOV EAX,DWORD PTR SS:[EBP+8]

00407D96  MOV AL,BYTE PTR DS:[EAX+3]

00407D99  PUSH EAX

00407D9A  CALL 是男人就.00407DC9

00407D9F  ADD ESP,4

00407DA2  XOR ECX,ECX

00407DA4  MOV CL,AL

00407DA6  CMP EBX,ECX

00407DA8  JNZ 是男人就.00407DBD

上面这三段就是序列号验证的主要部分了,写个示意表达式,用b1~b7表示序列号第1至7位转换后的数值,T为临时变量:

T = b3 + b6*2 + 1D

b1 = T mod 24 ?

T = b2 + b5*2 + 1D

b7 = T mod 24 ?

T = b1 + b7*2 + 1D

b4 = T mod 24 ?

如果三次判断均通过,那么整个序列号验证函数就回返回1,表示验证通过:

00407DAE  MOV EAX,1   ; EAX=1

00407DB3  JMP 是男人就.00407DC4  ; 跳到返回

00407DB8  JMP 是男人就.00407DC4

00407DBD  XOR EAX,EAX ; 任何验证的情况下均返回0

00407DBF  JMP 是男人就.00407DC4

00407DC4  POP EDI

00407DC5  POP ESI

00407DC6  POP EBX

00407DC7  LEAVE

00407DC8  RETN

到这里就差不多了,序列号验证函数已经分析完成,它的验证规则也知道了,接着是写注册机了,不过我一开始没想到怎么随机产生一个序列号,只好把正确的序列号全列出来了,看了一下,有4万多个……我用C写了个列序列号的程序,用的穷举法,列出每一个序列号,判断是否可用,如果可用就打印出来,程序清单如下:

#include <stdio.h>

int main()

{

char bin2char(int bin);

int tmp,b1,b2,b3,b4,b5,b6,b7;

printf(“register codes:\n”);

for(b1=0;b1<36;b1++)

for(b2=0;b2<36;b2++)

for(b3=0;b3<36;b3++)

for(b4=0;b4<36;b4++)

for(b5=0;b5<36;b5++)

for(b6=0;b6<36;b6++)

for(b7=0;b7<36;b7++)

{

tmp = b3 + b6*2 + 0x1D;

if (b1!=tmp%0x24) break;

tmp = b2 + b5*2 + 0x1D;

if (b7!=tmp%0x24) break;

tmp = b1 + b7*2 + 0x1D;

if (b4!=tmp%0x24) break;

printf(“%c%c%c%c%c%c%c\n”,

bin2char(b1),bin2char(b2),bin2char(b3),bin2char(b4),bin2char(b5),bin2char(b6),bin2char(b7));

}

}

char bin2char(int bin)

{

if(bin<10) return(bin+0x30);

else return(bin-10+0x41);

}

函数bin2char是序列号验证过程中字符转数值的逆过程。

是男人就下100层的破解就到此结束了,后来我又看了一下序列号的规律,发现第7位总是为0,而第4位决定第1位,而第3位与第6位,第2位与第5位分别为一组。这样,随机产生b2,b3,b4就可以生成一个序列号了。另外,也可用暴力破解的方式,在序列号验证函数中直接将EXA=1并返回,这样不论什么序列号都能通过验证了:)

OK,大功告成。写的有些啰嗦,第一次写破解的文章,有错误也是难免的,欢迎指正^_^,vipxjw#163.com。

用汇编实现符串操作函数

不管是在系统开发还是在平时的编程当中,字符串操作都是很重要的一部分。在C中,已经有库提供了strcpy、strcmp、strcat等函数, 而在开发用汇编开发自己的系统时,并没有现在的库可用,这就要求我们自己来实现字符串操作了。以下如果没有特别说明,字符串均以0为结束标志。

strcpy 字 符串复制

在字符串复制当中,为了简便,可以像在C中一样,不考虑边界问题,把这个问题交调用者,不过这样就有可能产生缓冲区溢出 了:)字符串复制还是比较容易实现的,只要在复制每一个字节之前判断是不是0,如果是就结束,不是则继续复制下一个字节。我给出一个简单的例子,当然,你 可以把它优化以产生更好的性能。

strcpy:

; in  si 源字符串起始地址

;     di 目标地 址

; out 无

push si

push di  ; 保护寄存器

next:

lodsb    ; 载 入一个字节

or al,al  ; 是0吗?

je end  ; 是则结束

stosb   ; 不 是则放入目标中

jmp next ; 继续下一个字节

end:

mov [di],byte 0  ; 结 束标志

pop di  ; 恢复寄存器

pop si

ret

strlen 取 字符串长度

应该说,这一个比上一个容易,因为这个只需要考虑什么结束,而不需要去复制字节。

strlen:

; in si 源 字符串

; out ax 字符串长度

push si

push cx    ; 保护寄存器

xor cx,cx  ; 计 数器清零

next:

lodsb       ; 载入一个字节

or al,al     ; 是0?

je end     ; 是 则结束

inc cx      ; 字符串长度+1

jmp next ; 继续下一个字节

end:

mov ax,cx ; 将 字符串长度放到AX中作为返回值

pop bx     ; 恢复寄存器

pop si

ret

strcat 字 符串连接

就个人来说,字符串连接用得并不是很多,但既然在C中有这个函数,就必然有它存在的理由,所以我们还是有必要来实现它的。 同样的,在字符串连接时不考虑目标缓冲区是否足够的问题,把这个交给调用者。

要将一个字符串连接到另一个字符之后,就需要先找出目 标字符串的结尾,即0的地址,然后就可以把这个地址做为目标,把需要连接的字符串的首地址做为源,调用strcpy即可完成。而找出0的地址,原理和取字 符串长度一样,载入一个字节然后判断是否为0。这个留大家自己去实现。

上面的例子使用的寄存器都是16位的,所以只能用在16位的 程序当中,当然,如果是你自己写的,那么自己懂得它实现的原理,移植到32位就是件很容易的事。

今天这篇就到这里喽~~难得写教 程之类的文章,今天还算顺手^-^~~

[MenuetOS] HotCats 0.1 代码分析

用来在MEOS里抓取屏幕,不过这个版本并不完善。

在这个版本里,程序所用的方法是扫描屏幕上的点然后再写到缓存最后写到文件,这样就会导致速度很慢。

BMP格式文档可以去我的网络硬盘下载http://osdev.ys168.com

;=======================================

;    HotCats

;    Ver : 0.1

;    BLOG: http://hotheart.go.3322.org

;=======================================

bits 32

org    0x0

db ‘MENUET01’ ; 8 byte id

dd 0x01                        ; header version

dd START                        ; start of code

dd I_END                        ; size of image

dd 0x300000                    ; memory for app

dd 0xffff                        ; esp

dd 0x0 , 0x0                    ; I_Param , I_Icon

START:                                ; start of execution

mov eax,14                    ; 获取屏幕分辨率

int 0x40

xor ebx,ebx

mov bx,ax

shr eax,16

inc eax ; X size

inc ebx ; Y size

mov [scr_x],eax

mov [scr_y],ebx

mov [img_w],eax

mov [img_h],ebx

imul ebx

imul eax,3                    ; image data size

mov [img_s],eax

add eax,0x36                ; BMP header size

mov [imgf_s],eax

mov [f_s],eax

mov esi,bminfo                ; fill the header

mov edi,filestart

mov ecx,0x36/4

cld

repz movsd

call draw_window                ; draw the window

still:

mov eax,10                    ; wait here for event

int 0x40

cmp eax,1                    ; redraw request ?

je red

cmp eax,2                    ; key in buffer ?

je key

cmp eax,3                    ; button in buffer ?

je button

jmp still

red:                                ; redraw

call draw_window

jmp still

key:                                ; key

mov eax,2                    ; just read it and ignore

int 0x40

jmp still

button:                            ; button

mov eax,17                    ; get id

int 0x40

cmp ah,1                    ; button id=1 ?

jne .cmdcap

mov eax,-1                    ; close this program

int 0x40

.cmdcap:

cmp ah,cmdcap                ; is the Capture button ?

jne .cmdsave

call capall                    ; capture the whole screen

jmp still

.cmdsave:

cmp ah,cmdsave                ; is the save button

jne .end

call savefile                ; save the file

jmp still

.end:

jmp still

;   *********************************************

;   *******  WINDOW DEFINITIONS AND DRAW ********

;   *********************************************

draw_window:

mov eax,12                    ; function 12:tell os about windowdraw

mov ebx,1                    ; 1, start of draw

int 0x40

; DRAW WINDOW

mov eax,0

mov ebx,[scr_x]

sub ebx,win_w+5

imul ebx,65536

add ebx,win_w

mov ecx,[scr_y]

sub ecx,win_h+25

imul ecx,65536

add ecx,win_h

mov edx,0x03ffffff

mov esi,0x40ffffff

mov edi,0x00ffffff

int 0x40

; WINDOW LABEL

mov eax,4                    ; function 4 : write text to window

mov ebx,8*65536+8            ; [x start] *65536 + [y start]

mov ecx,0x10ddeeff            ; font 1 & color ( 0xF0RRGGBB )

mov edx,labelt                ; pointer to text beginning

mov esi,labellen-labelt        ; text length

int 0x40

; Button Capture

mov eax,8

mov ebx,10*65536+40

mov ecx,(win_h-10-14)*65536+14

mov edx,cmdcap

mov esi,0xaabbcc

int 0x40

; Button Save

mov eax,8

mov ebx,(10+40+10)*65536+40

mov ecx,(win_h-10-14)*65536+14

mov edx,cmdsave

mov esi,0xaabbcc

int 0x40

; Button Text

mov eax,4                        ; function 4 : write text to window

mov ebx,18*65536+(win_h-10-14+4); [x start] *65536 + [y start]

mov ecx,0x00444444                ; font 1 & color ( 0xF0RRGGBB )

mov edx,btext                    ; pointer to text beginning

mov esi,btexte-btext            ; text length

int 0x40

mov eax,12                    ; function 12:tell os about windowdraw

mov ebx,2                    ; 2, end of draw

int 0x40

ret

mov eax,47                    ; display the screen size

mov ebx,4*65536

mov ecx,[scr_x]

mov edx,120*65536+30

mov esi,0x0

int 0x40

mov eax,47

mov ebx,4*65536

mov ecx,[scr_y]

mov edx,120*65536+40

mov esi,0x0

int 0x40

capall:

mov edi,imgarea

mov ecx,[scr_y]

cld

.nextline:

mov ebx,ecx

dec ebx

imul ebx,[scr_x]

push ecx

mov ecx,[scr_x]

.capline:

push edi

push ebx

mov eax,35                    ; get pixel

int 0x40

stosd

pop ebx

pop edi

inc ebx

add edi,3

loop .capline

pop ecx

loop .nextline

.end:

ret

savefile:                            ; save the file

mov eax,58

mov ebx,fileinfo

int 0x40

ret

; DATA AREA

win_w        equ 250

win_h        equ 50

cmdcap        equ 2

cmdsave        equ 3

filestart    equ 0x20000

imgarea        equ filestart+0x36

scr_x    dd 0

scr_y    dd 0

zoom    dd 2

fileinfo:

dd 1                    ; 1=WRITE

dd 0x0                    ; not used

f_s  dd 800*600*3+0x36        ; bytes to write

dd filestart            ; source data pointer

dd 0x10000                ; work area for os – 16384 bytes

db ‘/HD/1/TEST.BMP’,0    ; ASCIIZ dir & filename

btext:  db ‘ Cat     Save  ‘

btexte:

labelt:

db ‘HotCats 0.1 – HotHeart HotWorks 2005’

labellen:

bminfo:

db ‘BM’ ; 0000-0001 位图标志

imgf_s    dd 800*600*3+0x36    ; 0002-0005 文件大小

dd 0x0                ; 0006-0009 保留

db 0x36,0x0,0x0,0x0; 000A-000D 文件头信息块大小,图像描述信息块的大小,图像颜色表的大小,保留(为01)

dd 0x28            ; 000E-0011 图像描述信息块的大小,常为28H。

img_w    dd 800                ; 0012-0015    图像宽度。

img_h    dd 600                ; 0016-0019    图像高度。

dw 0x1                ; 001A-001B    图像的plane总数(恒为1)。

dw 24                ; 001C-001D    数据压缩方式(数值位0:不压缩;1:8位压缩;2:4位压缩)。

dd 0                ; 001E-0021    记录像素的位数,很重要的数值,图像的颜色数由该值决定。

img_s    dd 800*600*3        ; 0022-0025    图像区数据的大小。

dd 0x0                ; 0026-0029    水平每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。

dd 0x0                ; 002A-002D    垂直每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。

dd 0x0                ; 002E-0031    此图像所用的颜色数,如值为0,表示所有颜色一样重要。

dd 0x0                ; 0032-0035 未用

I_END:

[MenuetOS] HotRun 0.4 代码分析

; Made by HotHeart

http://www.xujiwei.com

; vipxjw@tom.com

; MENUET RUN 0.4

; 1) 缺省目录 /RD/1/

; 2) 可以更改程序目录,在SETUP里设置硬盘后可以运行

;    硬盘上的程序

; 3) 只能访问根目录

; 4) 自动根据屏幕大小调整窗口位置(限任务栏为文字模式)

use32

org    0x0

db ‘MENUET01’ ; 8 byte id

dd 0x01                    ; header version

dd START                   ; start of code

dd I_END                   ; size of image

dd 0x100000                ; memory for app

dd 0x7fff0                 ; esp

dd 0x0 , 0x0               ; I_Param , I_Icon

START:                          ; start of execution

; 计算窗口位置

mov eax,14

int 0x40

mov [screenysize],ax

xor eax,eax

mov ax,[screenysize]

sub eax,116+20

imul eax,65536

add eax,116

mov [winy],eax

; 初始化变量

mov [filestr],dword runprogram

mov [dir],dword rundir

mov [ya],dword 53

mov [yb],dword 69

call draw_window

still:

mov eax,23                 ; wait here for event

int 0x40

cmp eax,1                  ; redraw request ?

je red

cmp eax,2                  ; key in buffer ?

je key

cmp eax,3                  ; button in buffer ?

je button

jmp still

red:                          ; redraw

call draw_window

jmp still

key:                          ; key

mov eax,2                  ; just read it and ignore

int 0x40

jmp still

button:                       ; button

mov eax,17                 ; get id

int 0x40

cmp ah,4                  ; id=4 结束

je close

cmp ah,1

je close

cmp ah,2                  ; id=2 输入文件名

je inputfile

cmp ah,5                  ; id=5 输入路径

je inputdir

cmp ah,3                  ; id=3 运行程序

je runp

jmp still

file_start: dd 16

dd 0,0,0,0×10000

rundir:     db ‘/RD/1/’ ; 缺省目录

runprogram: db ‘RUN’,0        ; 缺省程序

times 60 db 0

filestr  dd 0x0

dir      dd 0x0

addr dd 0x0

ya       dd 0x0

yb       dd 0x0

;==========================================================

close:

;结束程序

mov eax,-1

int 0x40

inputfile:

;输入文件名

call read_string_file

jmp still

inputdir:

;输入路径

call read_string_dir

jmp still

;==========================================================

runp:

;运行程序

mov eax,58        ;运行

mov ebx,file_start

int 0x40

jmp still

;==========================================================

read_string_file:

;输入文件名

mov edi,[filestr]

mov eax,0

mov ecx,40

cld

rep stosb

call print_file

mov edi,[filestr]

file_fun:

mov eax,23        ; 等待事件

mov ebx,100    ; 延时100毫秒

int 0x40

cmp eax,0        ; eax=0 无事件

je file_fun    ; 继续等待事件过程

cmp eax,2        ; eax=2 按钮事件

jne file_read_done    ; 跳到读取键盘事件结束

mov eax,2        ; 获取击键 ascii 码

int 0x40

shr eax,8

cmp eax,13        ; 是否为回车键

je file_read_done    ; 是则跳到读取结束

cmp eax,8        ; 是否为退格键

jnz file_nobsl    ; 不是则跳到 nobsl

cmp edi,[filestr]

jz file_fun

sub edi,1        ; 字符数量减 1

mov [edi],byte 32    ; 退格

call print_file    ; 显示字符串

jmp file_fun    ; 继续读取

file_nobsl:

cmp al,95        ; 判断是否为小写

jbe file_cok    ; 是则跳到 cok

sub al,32        ; 不是则将 ascii 减去 32

file_cok:

mov [edi],al ; 添加到字符串

call print_file    ; 显示字符串

add edi,1        ; 已读取字符数量

mov esi,[filestr]    ; 读入字符串地址

add esi,30        ; 加上 30

cmp esi,edi ; 比较

jnz file_fun    ; 未到最大长度

file_read_done:    ; 读取结束

mov [edi],byte 0    ; 结束标志

call print_file    ; 显示字符串

jmp still        ; 事件循环

;==========================================================

read_string_dir:

mov edi,[dir]

mov eax,0

mov ecx,6

cld

rep stosb

call print_dir

mov edi,[dir]

dir_fun:

mov eax,23        ; 等待事件

mov ebx,100    ; 延时100毫秒

int 0x40

cmp eax,0        ; eax=0 无事件

je dir_fun    ; 继续等待事件过程

cmp eax,2        ; eax=2 按钮事件

jne dir_read_done    ; 跳到读取键盘事件结束

mov eax,2        ; 获取击键 ascii 码

int 0x40

shr eax,8

cmp eax,13        ; 是否为回车键

je dir_read_done    ; 是则跳到读取结束

cmp eax,8        ; 是否为退格键

jnz dir_nobsl    ; 不是则跳到 nobsl

cmp edi,[filestr]

jz dir_fun

sub edi,1

mov [edi],byte 32

call print_dir    ; 显示字符串

jmp dir_fun    ; 继续读取

dir_nobsl:

cmp al,95        ; 判断是否为小写

jbe dir_cok    ; 是则跳到 cok

sub al,32        ; 不是则将 ascii 减去 32

dir_cok:

mov [edi],al ; 添加到字符串

call print_dir    ; 显示字符串

add edi,1        ; 已读取字符数量

mov esi,[dir]    ; 读入字符串地址

add esi,6        ; 加上 6

cmp esi,edi ; 比较

jnz dir_fun    ; 未到最大长度

dir_read_done:    ; 读取结束

call print_dir    ; 显示字符串

jmp still        ; 事件循环

;==========================================================

print_dir:

;显示路径

pusha

mov eax,13        ; 画底纹

mov ebx,55*65536+31*6

mov ecx,[ya]

shl ecx,16

mov cx,12

sub ecx,2*65536

mov edx,0xeeeeee

int 0x40

mov eax,4        ; 显示路径

mov edx,[dir]

mov ebx,56*65536

add ebx,[ya]

mov ecx,0x444444

mov esi,6

int 0x40

popa

ret

;==========================================================

print_file:

;显示文件名

pusha

mov eax,13        ; 画底纹

mov ebx,55*65536+31*6

mov ecx,[yb]

shl ecx,16

mov cx,12

sub ecx,2*65536

mov edx,0xeeeeee

int 0x40

mov eax,4        ; 显示文件名

mov edx,[filestr]

mov ebx,56*65536

add ebx,[yb]

mov ecx,0x444444

mov esi,30

int 0x40

popa

ret

;==========================================================

;   *********************************************

;   *******  WINDOW DEFINITIONS AND DRAW ********

;   *********************************************

draw_window:

mov eax,12                    ; function 12:tell os about windowdraw

mov ebx,1                     ; 1, start of draw

int 0x40

; DRAW WINDOW

mov eax,0                ; function 0 : define and draw window

mov ebx,1*65536+250    ; [x start] *65536 + [x size]

mov ecx,[winy]            ; [y start] *65536 + [y size]

mov edx,0x03ffffff        ; color of work area RRGGBB,8->color gl

mov esi,0x805080d0        ; color of grab bar  RRGGBB,8->color gl

mov edi,0x005080d0        ; color of frames    RRGGBB

int 0x40

; WINDOW LABEL

mov eax,4                ; function 4 : write text to window

mov ebx,8*65536+8        ; [x start] *65536 + [y start]

mov ecx,0x10ddeeff        ; font 1 & color ( 0xF0RRGGBB )

mov edx,labelt            ; pointer to text beginning

mov esi,labellen-labelt; text length

int 0x40

mov eax,8                ; 路径输入 id=5

mov ebx,13*65536+40

mov ecx,50*65536+12

mov edx,5

mov esi,0xaabbcc

int 0x40

mov eax,8                ; 文件名输入 id=2

mov ebx,13*65536+40

mov ecx,66*65536+12

mov edx,2

mov esi,0xaabbcc

int 0x40

mov eax,8                ; 确定 id=3

mov ebx,128*65536+48

mov ecx,84*65536+18

mov edx,3

mov esi,0xaabbcc

int 0x40

mov eax,8                ; 取消 id=4

mov ebx,182*65536+48

mov ecx,84*65536+18

mov edx,4

mov esi,0xaabbcc

int 0x40

mov ebx,13*65536+34    ; 提示信息

mov ecx,0x666666

mov edx,txttxt

mov esi,30

mov eax,4

int 0x40

mov eax,4                ; Path按钮文本

mov ebx,21*65536+53

mov ecx,0x666666

mov edx,txtdir

mov esi,4

int 0x40

mov ebx,21*65536+69    ; File按钮文本

mov ecx,0x444444

mov edx,txtfile

mov esi,4

mov eax,4

int 0x40

mov ebx,140*65536+89    ; 确定取消按钮文本

mov ecx,0x444444

mov edx,txtbutton

mov esi,13

mov eax,4

int 0x40

call print_dir

call print_file

mov eax,12                ; function 12:tell os about windowdraw

mov ebx,2                ; 2, end of draw

int 0x40

ret

; DATA AREA

tcolor      dd 0x000000

txttxt      db ‘请输入要运行程序的路径和文件名’,‘x’

txtdir      db ‘Path’,‘x’

txtfile     db ‘File’,‘x’

txtbutton   db ‘确定     取消’,‘x’

screenysize dw 0x0

winy        dd 0x0

labelt:

db ‘运行’

labellen:

I_END:

[本日志由 xujiwei 于 2005-07-22 06:24 PM 编辑]

Comments (0), Views (2955), Pings (0), Leave a response!

实模式进保护模式

在写自己的操作系统时,不能总在实模式下,进入保护模式才是”正道”,不过怎么进入保护模式又是一个问题,我把

XuOS里进入保护模式的代码分析一下.

; 装入GDT

mov  eax,ds  ;设置GDT在物理内存中的正确位置

shl  eax,4

add  [gdt_addr+2],eax

cli  ; 关中断

lgdt [gdt_addr] ;载入GDT

; 下面打开A20地址线,这段代码可以在OSzone上找到相关解释.

call  Empty_8042

mov  al,0xd1

out  0x64,al

call  Empty_8042

mov  al,0xdf

out  0x60,al

call  Empty_8042

; 进入保护模式

mov  eax,cr0 ;置PE位

or   eax,1

mov  cr0,eax

jmp oscodesel:code_32  ; 跳到32位代码处执行

Empty_8042:

in   al,0x64

test al,0x2

jnz  Empty_8042

ret

; GDT的内容

gdt:

gdt_null:

dd  0x0000

dd  0x0000

gdt_system_code:

oscodesel equ $-gdt  ; 段选择子

dd  0x0000ffff,0x00cf9a00

gdt_system_data:

osdatasel equ $-gdt

dd  0x1000ffff,0x00cf9200

videosel equ $-gdt

dd  0x0000ffff,0x00cf920a

gdt_addr:

dw  gdt_addr-gdt-1

dd  gdt        ; ;GDT表的位置

当然,由于XuOS目前还比较简单,还没有设置IDT,GDT中段也比较少,况且我的理解也有可能有些偏差或者表述

有些不清楚,这里只提供一个思路,更多更强的功能还是要靠自己才行.

写你自己的操作系统

这是转载的,原文可以在http://www.xemean.net的文档中心里找到.

因为原文中的代码编译后运行有错误,这里我把改过后能正确运行的代码讲一下

org 0x07c00     ; 起始地址是0000:7c00

jmp begin_boot ; 跳过其它的数据,跳转到引导程序的开始处

OEM_ID                db “OSeg    ”   ;软盘信息,具体请参考”FAT格式”

BytesPerSector        dw 0x0200

SectorsPerCluster     db 0x01

ReservedSectors       dw 0x0001

TotalFATs             db 0x02

MaxRootEntries        dw 0x00E0

TotalSectorsSmall     dw 0x0B40

MediaDescriptor       db 0xF0

SectorsPerFAT         dw 0x0009

SectorsPerTrack       dw 0x0012

NumHeads              dw 0x0002

HiddenSectors         dd 0x00000000

TotalSectorsLarge     dd 0x00000000

DriveNumber           db 0x00

Flags                 db 0x00

Signature             db 0x29

VolumeID              dd 0xFFFFFFFF

VolumeLabel           db “OSexample  ”

SystemID              db “FAT12   ”

print_mesg:  ;打印信息调用

mov ah,0x13 ; 使用中断10h的功能13,在屏幕上写一个字符串

mov al,0x00 ; 决定调用函数后光标所处的位置

mov bx,0x0007 ; 设置显示属性

mov cx,0x20 ; 在此字符串长度为32

mov dx,0x0000 ; 光标的起始行和列

int 0x10 ; 调用BIOS的中断10h

ret ; 返回调用程序

get_key:  ;等待按键

mov ah,0x00

int 0x16 ; Get_key使用中断16h的功能0,读取下一个字符

ret

clrscr:   ;清屏

mov ax,0x0600 ; 使用中断10h的功能6,实现卷屏,如果al=0则清屏

mov cx,0x0000 ; 清屏

mov dx,0x174f ; 卷屏至23,79

mov bh,0 ; 使用颜色0来填充

int 0x10 ; 调用10h中断

ret  ;返回

begin_boot:  ;;引导程序开始

mov  ax,cs    ;设置段寄存器

mov  ds,ax

mov  es,ax

call clrscr ; 先清屏

mov bp,bootmesg ; 提供串地址

call print_mesg ; 输出信息

call get_key ; 等待用户按下任一键

bits 16   ;以16位方式编译

call clrscr ; 清屏

mov ax,0xb800 ; 使gs指向显示内存

mov gs,ax ; 在实模式下显示一个棕色的A

mov word [gs:0],0x641 ; 显示

call get_key ; 调用Get_key等待用户按下任一键

mov bp,pm_mesg ; 设置串指针

call print_mesg ; 调用print_mesg子程序

call get_key ; 等待按键

call clrscr ; 清屏

cli ; 关中断

mov  eax,ds   ;设置GDT物理地址

shl  eax,4

add  [gdtr+2],eax

lgdt [gdtr] ; 加载GDT

mov eax,cr0 ;进入保护模式

or  al,0x01 ; 设置保护模式位

mov cr0,eax ; 将更改后的字送至控制寄存器中

jmp codesel:go_pm  ;跳至32位代码

bits 32  ;以32位方式编译

go_pm:

mov ax,datasel  ;设置段寄存器

mov ds,ax ; 初始化ds和es,使其指向数据段

mov es,ax

mov ax,videosel  ;使gs指向显存

mov gs,ax

mov word [gs:0],0x0400+’B’ ; 在保护模式下显示一个红色的字符B

jmp $ ; 无限循环

bits 16

gdtr:

dw gdt_end-gdt-1 ; gdt的长度

dd gdt ; gdt的物理地址

gdt:   ;具体GDT信息请参考GDT格式

nullsel equ $-gdt ; $指向当前位置,所以nullsel = 0h

dd 0

dd 0 ; 所有的段描述符都是64位的

codesel equ $-gdt ; 这是8h也就是gdt的第二个描述符

code_gdt:

dw 0x0ffff ; 段描述符的界限是4Gb

dw 0x0000

db 0x00

db 0x09a

db 0x0cf

db 0x00

datasel equ $-gdt

data_gdt:

dw 0x0ffff

dw 0x0000

db 0x00

db 0x092

db 0x0cf

db 0x00

videosel equ $-gdt

dw 3999

dw 0x8000 ; 基址是0xb8000

db 0x0b

db 0x92

db 0xcf

db 0x00

gdt_end:

bootmesg db “Our OS boot sector loading…   ”   ;信息数据

pm_mesg  db “Switching to protected mode…  ”

times 510-($-$$) db 0  ;填满512个字节

dw 0x0AA55  ;引导扇区标志