Tag Archives: 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  ; 判断是否大于0×24 00407CB1  JLE 是男人就.00407CBE  ; 大于就出错了 00407CB7  XOR EAX,EAX ; 大于0×24,跳到函数结束,返回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 ; 判断是否大于0×61,如果对ASCII码表熟悉的话可以知道0×61对应的是a ; 小于则跳转,好,上面那个判断应该不是用来判断这个字符是否为小写字母了 ; 不是则跳到后面继续处理,是则进行下面的处理过程 00407DDD  XOR EAX,EAX ; EAX清零 00407DDF  MOV AL,BYTE PTR SS:[EBP+8]    ; 取字符 00407DE2  SUB EAX,20     ; 减0×20,变大写字母 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 ; 判断是否大于,0×41对应的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     ; 减去0×30 00407E09  JMP 是男人就.00407E0E  ; 函数结束 00407E0E  POP EDI 00407E0F  POP ESI 00407E10  POP EBX 00407E11  LEAVE 00407E12  RETN 连贯起来看可以知道这个处理函数的过程是这样的: char是否为小写字母 -> 是则减去0×20变成大写字母,不是则保持不变 -> char是否为大写字母 -> 是则减去7,不是则保持不变 -> char减0×30 再联系数字和字母的ASCII码值,9为0×39,A为0×41,0×41-0×39=7,OK,可以知道这个函数是把1位序列号变成二进制值,字符0到9对应数值0到9,字母全部转换成大写字母,并且字母A对应10,B对应11,后面依次类推。 弄明白了字符到数字的函数,继续看序列号验证的代码,可以看到在得到二进制值之后,又来了一句: 00407CB1  JLE 是男人就.00407CBE  ; 大于就出错了 00407CB7  XOR EAX,EAX ; 大于0×24,跳到函数结束,返回0 00407CB9  JMP 是男人就.00407DC4 00407CBE  JMP 是男人就.00407C8B  ; 继续处理下一位 0×24转换成十进制就是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=0×24 00407CEE  LEA EAX,DWORD PTR DS:[ECX+EBX*2+1D] ; EAX=ECX+EBX*2+1D 00407CF2  CDQ ; 扩展成64位 00407CF3  IDIV ESI ; 除以0×24 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)%0×24==第1位 ; 跟着又是两段代码类似的,不难发现也是判断,不过规则有小小的不同 ; 下面这段的判断规则是 ; (第2位+第5位*2+0x1D)%0×24==第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)%0×24==第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%0×24) break; tmp = b2 + b5*2 + 0x1D; if (b7!=tmp%0×24) break; tmp = b1 + b7*2 + 0x1D; if (b4!=tmp%0×24) 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+0×30); else return(bin-10+0×41); } 函数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    0×0 db ‘MENUET01′ ; 8 byte id dd 0×01                        ; header version dd START                        ; start of code dd I_END                        ; size of image dd 0×300000                    ; memory for app dd 0xffff                        ; esp dd 0×0 , 0×0                    ; I_Param , I_Icon START:                                ; start of execution mov eax,14                    ; 获取屏幕分辨率 int 0×40 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,0×36                ; BMP header size mov [imgf_s],eax mov [f_s],eax mov esi,bminfo                ; fill the header mov edi,filestart mov ecx,0×36/4 cld repz movsd call draw_window                ; draw the window still: mov eax,10                    ; wait here for event int 0×40 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 0×40 jmp still button:                            ; button mov eax,17                    ; get id int 0×40 cmp ah,1                    ; button id=1 ? jne .cmdcap mov eax,-1                    ; close this program int 0×40 .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 0×40 ; 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 0×40 ; 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 0×40 ; 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 0×40 ; 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 0×40 ; 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,0×00444444                ; font 1 & color ( 0xF0RRGGBB ) mov edx,btext                    ; pointer to text beginning mov esi,btexte-btext            ; text length int 0×40 mov eax,12                    ; function 12:tell os about windowdraw mov ebx,2                    ; 2, end of draw int 0×40 ret mov eax,47                    ; display the screen size mov ebx,4*65536 mov ecx,[scr_x] mov edx,120*65536+30 mov esi,0×0 int 0×40 mov eax,47 mov ebx,4*65536 mov ecx,[scr_y] mov edx,120*65536+40 mov esi,0×0 int 0×40 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 0×40 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 0×40 ret ; DATA AREA win_w        equ 250 win_h        equ 50 cmdcap        equ 2 cmdsave        equ 3 filestart    equ 0×20000 imgarea        equ filestart+0×36 scr_x    dd 0 scr_y    dd 0 zoom    dd 2 fileinfo: dd 1                    ; 1=WRITE dd 0×0                    ; not used f_s  dd 800*600*3+0×36        ; bytes to write dd filestart            ; source data pointer dd 0×10000                ; 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+0×36    ; 0002-0005 文件大小 dd 0×0                ; 0006-0009 保留 db 0×36,0×0,0×0,0×0; 000A-000D 文件头信息块大小,图像描述信息块的大小,图像颜色表的大小,保留(为01) dd 0×28            ; 000E-0011 图像描述信息块的大小,常为28H。 img_w    dd 800                ; 0012-0015    图像宽度。 img_h    dd 600                ; 0016-0019    图像高度。 dw 0×1                ; 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 0×0                ; 0026-0029    水平每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。 dd 0×0                ; 002A-002D    垂直每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。 dd 0×0                ; 002E-0031    此图像所用的颜色数,如值为0,表示所有颜色一样重要。 dd 0×0                ; 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    0×0 db ‘MENUET01′ ; 8 byte id dd 0×01                    ; header version dd START                   ; start of code dd I_END                   ; size of image dd 0×100000                ; memory for app dd 0x7fff0                 ; esp dd 0×0 , 0×0               ; I_Param , I_Icon START:                          ; start of execution ; 计算窗口位置 mov eax,14 int 0×40 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 0×40 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 0×40 jmp still button:                       ; button mov eax,17                 ; get id int 0×40 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 0×0 dir      dd 0×0 addr dd 0×0 ya       dd 0×0 yb       dd 0×0 ;========================================================== close: ;结束程序 mov eax,-1 int 0×40 inputfile: ;输入文件名 call read_string_file jmp still inputdir: ;输入路径 call read_string_dir jmp still ;========================================================== runp: ;运行程序 mov eax,58        ;运行 mov ebx,file_start int 0×40 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 0×40 cmp eax,0        ; eax=0 无事件 je file_fun    ; 继续等待事件过程 cmp eax,2        ; eax=2 按钮事件 jne file_read_done    ; 跳到读取键盘事件结束 mov eax,2        ; 获取击键 ascii 码 int 0×40 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 0×40 cmp eax,0        ; eax=0 无事件 je dir_fun    ; 继续等待事件过程 cmp eax,2        ; eax=2 按钮事件 jne dir_read_done    ; 跳到读取键盘事件结束 mov eax,2        ; 获取击键 ascii 码 int 0×40 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 0×40 mov eax,4        ; 显示路径 mov edx,[dir] mov ebx,56*65536 add ebx,[ya] mov ecx,0×444444 mov esi,6 int 0×40 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 0×40 mov eax,4        ; 显示文件名 mov edx,[filestr] mov ebx,56*65536 add ebx,[yb] mov ecx,0×444444 mov esi,30 int 0×40 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 0×40 ; 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 0×40 ; 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 0×40 mov eax,8                ; 路径输入 id=5 mov ebx,13*65536+40 mov ecx,50*65536+12 mov edx,5 mov esi,0xaabbcc int 0×40 mov eax,8                ; 文件名输入 id=2 mov ebx,13*65536+40 mov ecx,66*65536+12 mov edx,2 mov esi,0xaabbcc int 0×40 mov eax,8                ; 确定 id=3 mov ebx,128*65536+48 mov ecx,84*65536+18 mov edx,3 mov esi,0xaabbcc int 0×40 mov eax,8                ; 取消 id=4 mov ebx,182*65536+48 mov ecx,84*65536+18 mov edx,4 mov esi,0xaabbcc int 0×40 mov ebx,13*65536+34    ; 提示信息 mov ecx,0×666666 mov edx,txttxt mov esi,30 mov eax,4 int 0×40 mov eax,4                ; Path按钮文本 mov ebx,21*65536+53 mov ecx,0×666666 mov edx,txtdir mov esi,4 int 0×40 mov ebx,21*65536+69    ; File按钮文本 mov ecx,0×444444 mov edx,txtfile mov esi,4 mov eax,4 int 0×40 mov ebx,140*65536+89    ; 确定取消按钮文本 mov ecx,0×444444 mov edx,txtbutton mov esi,13 mov eax,4 int 0×40 call print_dir call print_file mov eax,12                ; function 12:tell os about windowdraw mov ebx,2                ; 2, end of draw int 0×40 ret ; DATA AREA tcolor      dd 0×000000 txttxt      db ‘请输入要运行程序的路径和文件名’,‘x’ txtdir      db ‘Path’,‘x’ txtfile     db ‘File’,‘x’ txtbutton   db ‘确定     取消’,‘x’ screenysize dw 0×0 winy        dd 0×0 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  0×64,al

call  Empty_8042

mov  al,0xdf

out  0×60,al

call  Empty_8042

; 进入保护模式

mov  eax,cr0 ;置PE位

or   eax,1

mov  cr0,eax

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

Empty_8042:

in   al,0×64

test al,0×2

jnz  Empty_8042

ret

; GDT的内容

gdt:

gdt_null:

dd  0×0000

dd  0×0000

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 0×0200

SectorsPerCluster     db 0×01

ReservedSectors       dw 0×0001

TotalFATs             db 0×02

MaxRootEntries        dw 0x00E0

TotalSectorsSmall     dw 0x0B40

MediaDescriptor       db 0xF0

SectorsPerFAT         dw 0×0009

SectorsPerTrack       dw 0×0012

NumHeads              dw 0×0002

HiddenSectors         dd 0×00000000

TotalSectorsLarge     dd 0×00000000

DriveNumber           db 0×00

Flags                 db 0×00

Signature             db 0×29

VolumeID              dd 0xFFFFFFFF

VolumeLabel           db ”OSexample  ”

SystemID              db ”FAT12   ”

print_mesg:  ;打印信息调用

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

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

mov bx,0×0007 ; 设置显示属性

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

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

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

ret ; 返回调用程序

get_key:  ;等待按键

mov ah,0×00

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

ret

clrscr:   ;清屏

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

mov cx,0×0000 ; 清屏

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

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

int 0×10 ; 调用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],0×641 ; 显示

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,0×01 ; 设置保护模式位

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],0×0400+’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 0×0000

db 0×00

db 0x09a

db 0x0cf

db 0×00

datasel equ $-gdt

data_gdt:

dw 0x0ffff

dw 0×0000

db 0×00

db 0×092

db 0x0cf

db 0×00

videosel equ $-gdt

dw 3999

dw 0×8000 ; 基址是0xb8000

db 0x0b

db 0×92

db 0xcf

db 0×00

gdt_end:

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

pm_mesg  db ”Switching to protected mode…  ”

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

dw 0x0AA55  ;引导扇区标志

.