22.1 帮助发现bug
按照上一章节对一般性保护异常现象的分析,如果出现对数组赋值时超出范围应该作何处理呢?下面将根据函数asm_inthandler0d编写函数asm_inthandler0c用于处理栈异常现象。
inthandler0c函数中的esp[11]是取esp栈中的第11号元素,如果想要得到产生异常时其他寄存器的值,可按照下面显示相应元素即可。
esp[ 0]
:
EDI
esp[ 1]
:
ESI esp[0
~
7]
为
_asm_inthandler
中
PUSHAD
的结果
esp[ 2]
:
EBP
esp[ 4]
:
EBX
esp[ 5]
:
EDX
esp[ 6]
:
ECX
esp[ 7]
:
EAX
esp[ 8
] :
DS esp[8
~
9]
为
_asm_inthandler
中
PUSH
的结果
esp[ 9]
:
ES
esp[10]
:
错误编号(基本上是
0
,显示出来也没什么意思)
esp[11]
:
EIP
esp[12]
:
CS esp[10
~
15]
为异常产生时
CPU
自动
PUSH
的结果
esp[13]
:
EFLAGS
esp[14]
:
ESP
(应用程序用
ESP
)
esp[15]
:
SS
(应用程序用
SS
)
/* dsctbl.c */
set_gatedesc(idt + 0x0c, (int) asm_inthandler0c, 2 * 8, AR_INTGATE32);
/* naskfunc.nas */
_asm_inthandler0c:
STI
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS
MOV DS,AX
MOV ES,AX
CALL _inthandler0c
CMP EAX,0
JNE end_app
POP EAX
POPAD
POP DS
POP ES
ADD ESP,4
IRETD
/* console.c */
int *inthandler0c(int *esp){
struct CONSOLE *cons = (struct CONSOLE *) *((int *) 0x0fec);
struct TASK *task = task_now();
char s[30];
cons_putstr0(cons, "\nINT 0D :\n Stack Exception.\n");
sprintf(s, "EIP = %08X\n", esp[11]);
cons_putstr0(cons, s);
return &(task->tss.esp0); /*强制结束程序*/
}
22.2 强制结束应用程序
如果遇到一个死循环的程序,应该如何应对呢?下面实现按下“shift+F1”组合键,结束应用程序。读键盘数据是在文件bootpack.c中实现的,所以将组合键的代码也放在该文件中。
void HariMain(void){
(中略)
struct CONSOLE *cons;
(中略)
for (;;) {
(中略)
if (fifo32_status(&fifo) == 0) {
task_sleep(task_a);
io_sti();
} else {
i = fifo32_get(&fifo);
io_sti();
if (256 <= i && i <= 511) {/* 键盘数据 */
(中略)
if(i == 256 + 0x3b && key_shift != 0 && task_cons->tss.ss0 != 0){/* Shift+F1 */
cons = (struct CONSOLE *)*((int*)0x0fec);
cons_putstr0(cons, "\nBreak(key):\n");
io_cli();/*不能在改变寄存器值时切换到其他任务*/
task_cons->tss.eax = (int) &(task_cons->tss.esp0);
task_cons->tss.eip = (int) asm_end_app;/*指向下一条指令*/
io_sti();
}
(中略)
} else if (512 <= i && i <= 767) {/* 鼠标数据 */
(中略)
} else if (i <= 1) {/* 光标用计时器 */
(中略)
}
}
}
}
_asm_end_app:
; EAX为tss.esp0的地址
MOV ESP,[EAX]
MOV DWORD [EAX+4],0 ; 这里!
POPAD
RET ; 返回cmd_app
22.3 用C语言显示字符串(1)
按照函数api_putchar能够调用中断打印字符,依葫芦画瓢可以写出函数api_putstr0能够调用中断打印字符串。
_api_putstr0: ; void api_putstr0(char *s);
PUSH EBX ;用于后续对EBX寄存器中的数据进行恢复
MOV EDX,2 ;EDX=2时cons_putstr0(cons, (char*)ebx+cs_base);
MOV EBX,[ESP+8] ; s
INT 0x40
POP EBX
RET
22.4 用C语言显示字符串(2)
.hrb
文件,开头的
36
个字节不是程序,而是存放了下列这些信息:
0x0000 (DWORD)
……
请求操作系统为应用程序准备的数据段的大小
0x0004 (DWORD)
……“
Hari
”
(
.hrb
文件的标记)
0x0008 (DWORD)
……
数据段内预备空间的大小
0x000c (DWORD)
……
ESP
初始值
&
数据部分传送目的地址
0x0010 (DWORD)
……
hrb文件内数据部分的大小,向数据段传送的部分的字节数
0x0014 (DWORD) .......
向数据段传送的部分在
.hrb
文件中的起始地址
0x0018 (DWORD)
……0xe9000000 E9是JMP指令的机器语言编码,
0x001c (DWORD) ……存放的是应用程序运行入口地址 -
0x20
0x0020 (DWORD)
……
malloc
空间的起始地址
根据上述对hrb文件的分析,可以修改console.c函数
int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
{
int segsiz, datsiz, esp, dathrb;
(中略)
if (finfo != 0) {
/*找到文件的情况*/
p = (char *) memman_alloc_4k(memman, finfo->size);
file_loadfile(finfo->clustno, finfo->size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));
if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
segsiz = *((int *) (p + 0x0000));
esp = *((int *) (p + 0x000c));//数据传送目的地址
datsiz = *((int *) (p + 0x0010));//数据段部分大小
dathrb = *((int *) (p + 0x0014));//放在hrb文件中的起始位置,复制从该地址起的datsiz个数据
q = (char *) memman_alloc_4k(memman, segsiz);
*((int *) 0xfe8) = (int) q;
set_segmdesc(gdt + 1003, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);
set_segmdesc(gdt + 1004, segsiz - 1, (int) q, AR_DATA32_RW + 0x60);
for (i = 0; i < datsiz; i++) /*将.hrb文件中的数据部分先复制到数据段*/
q[esp + i] = p[dathrb + i];
start_app(0x1b, 1003 * 8, esp, 1004 * 8, &(task->tss.esp0));
memman_free_4k(memman, (int) q, segsiz);
} else {
cons_putstr0(cons, ".hrb file format error.\n");
}
memman_free_4k(memman, (int) p, finfo->size);
cons_newline(cons);
return 1;
}
/*没有找到文件的情况*/
return 0;
}
22.5 显示窗口
EDX = 5
EBX =
窗口缓冲区
ESI =
窗口在
x
轴方向上的大小(即窗口宽度)
EDI =
窗口在
y
轴方向上的大小(即窗口高度)
EAX =
透明色
ECX =
窗口名称
调用后,返回值如下:
EAX =
用于操作窗口的句柄(用于刷新窗口等操作)
在
asm_hrb_api
中执行了两次
PUSHAD,第一次是为了保存寄存器的值,第二次是为了向hrb_api传递值。因此如果查出被传递的变量的地址,在那个地址的后面应该正好存放着相同的寄存器的值。然后只要修改那个值,就可以由
POPAD
获取修改后的值,实现将值返回给应用程序的功能。
/* a_nask.nas */
_api_openwin: ; int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
PUSH EDI
PUSH ESI
PUSH EBX
MOV EDX,5
MOV EBX,[ESP+16] ; buf 由于buf的大小为7500,2^12=4096<7500,所以得用2^16个字节
MOV ESI,[ESP+20] ; xsiz
MOV EDI,[ESP+24] ; ysiz
MOV EAX,[ESP+28] ; col_inv
MOV ECX,[ESP+32] ; title
INT 0x40
POP EBX
POP ESI
POP EDI
RET
/* winhelo.c */
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_end(void);
char buf[150 * 50];
void HariMain(void){
int win;
win = api_openwin(buf, 150, 50, -1, "hello");
api_end();
}
/* console.c */
//根据输入参数打印数据 eax为当前字符 ebx为字符串 ecx长度
int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax){
int ds_base = *((int *) 0xfe8);
struct TASK *task = task_now();
struct CONSOLE *cons = (struct CONSOLE *) *((int *) 0x0fec);
struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);/*从这开始*/
struct SHEET *sht;
int *reg = &eax + 1;/* eax后面的地址*/
/* reg[0] : EDI, reg[1] : ESI, reg[2] : EBP, reg[3] : ESP */
/* reg[4] : EBX, reg[5] : EDX, reg[6] : ECX, reg[7] : EAX *//*到这结束*/
if(edx == 1)
cons_putchar(cons, eax&0xff, 1);
else if(edx == 2)
cons_putstr0(cons, (char*)ebx+ds_base);
else if(edx == 3)
cons_putstr1(cons, (char*)ebx+ds_base, ecx);
else if(edx == 4)
return &(task->tss.esp0);
else if(edx == 5){/*从这开始*/
sht = sheet_alloc(shtctl);
sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax);
make_window8((char *) ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0);
sheet_slide(sht, 100, 50);
sheet_updown(sht, 3); /*背景层高度3位于task_a之上*/
reg[7] = (int) sht;
}/*到这结束*/
return 0;
}
22.6 在窗口中描绘字符和方块
在窗口上显示字符的API |
描绘方块的
API
|
EDX = 6
EBX =
窗口句柄
ESI =
显示位置的
x
坐标
EDI =
显示位置的
y
坐标
EAX =
色号
ECX =
字符串长度
EBP =
字符串
|
EDX = 7
EBX =
窗口句柄
EAX = x0
ECX = y0
ESI = x1
EDI = y1
EBP =
色号
|
/* a_nask.nas */
_api_putstrwin: ; void api_putstrwin(int win, int x, int y, int col, int len, char *str);
PUSH EDI ;保存4个寄存器的值
PUSH ESI
PUSH EBP
PUSH EBX
MOV EDX,6
MOV EBX,[ESP+20] ; win
MOV ESI,[ESP+24] ; x
MOV EDI,[ESP+28] ; y
MOV EAX,[ESP+32] ; col
MOV ECX,[ESP+36] ; len
MOV EBP,[ESP+40] ; str
INT 0x40
POP EBX
POP EBP
POP ESI
POP EDI
RET
_api_boxfilwin: ; void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
PUSH EDI
PUSH ESI
PUSH EBP
PUSH EBX
MOV EDX,7
MOV EBX,[ESP+20] ; win
MOV EAX,[ESP+24] ; x0
MOV ECX,[ESP+28] ; y0
MOV ESI,[ESP+32] ; x1
MOV EDI,[ESP+36] ; y1
MOV EBP,[ESP+40] ; col
INT 0x40
POP EBX
POP EBP
POP ESI
POP EDI
RET
/* console.c */
}else if(edx == 6){
sht = (struct SHEET *) ebx;
putfonts8_asc(sht->buf, sht->bxsize, esi, edi, eax, (char *) ebp + ds_base);
sheet_refresh(sht, esi, edi, esi + ecx * 8, edi + 16);
}else if(edx == 7){
sht = (struct SHEET *) ebx;
boxfill8(sht->buf, sht->bxsize, ebp, eax, ecx, esi, edi);
sheet_refresh(sht, eax, ecx, esi + 1, edi + 1);
}