2026-07-28 14:35:14 +08:00
|
|
|
|
/* Deshab CMD — Windows 风格命令行(PE/EXE 兼容层前端)
|
|
|
|
|
|
*
|
|
|
|
|
|
* 黑底白字 cmd.exe 风格,PS/2 键盘输入。
|
|
|
|
|
|
* 内置命令:dir/cd/type/copy/del/ren/mkdir/rmdir/echo/cls/ver/set/help/exit
|
|
|
|
|
|
* PE 执行:输入 foo.exe 或 foo → 从 FAT32 根读取 → 调用 UTSM PE 服务执行
|
|
|
|
|
|
* - PE32+ (64位):UTSM 原生 Ring0 执行
|
|
|
|
|
|
* - PE32 (32位):UTSM 内嵌 x86-32 解释器执行
|
|
|
|
|
|
* Esc 返回桌面。
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "../UTSM/include/utsm/dsk.h"
|
|
|
|
|
|
#include "../UTSM/include/utsm/pe.h"
|
2026-07-28 21:11:10 +08:00
|
|
|
|
#include "../UTSM/pe/pe_loader.h" /* PE 头结构体(image_dos_header 等),供 peinfo 解析 */
|
2026-07-28 14:35:14 +08:00
|
|
|
|
|
|
|
|
|
|
typedef unsigned char u8;
|
|
|
|
|
|
typedef unsigned short u16;
|
|
|
|
|
|
typedef unsigned int u32;
|
|
|
|
|
|
typedef unsigned long long u64;
|
|
|
|
|
|
typedef long long i64;
|
|
|
|
|
|
|
|
|
|
|
|
#define COM1 0x3F8
|
|
|
|
|
|
|
|
|
|
|
|
static __inline__ void outb(u16 port, u8 value) { __asm__ volatile("outb %0,%1"::"a"(value),"Nd"(port)); }
|
|
|
|
|
|
static __inline__ u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; }
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- TSC 计时 ---- */
|
|
|
|
|
|
static u64 g_tsc_per_ms = 0;
|
|
|
|
|
|
|
|
|
|
|
|
static __inline__ u64 rdtsc_cmd(void) {
|
|
|
|
|
|
u32 lo, hi;
|
|
|
|
|
|
__asm__ volatile("rdtsc" : "=a"(lo), "=d"(hi));
|
|
|
|
|
|
return ((u64)hi << 32) | lo;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void tsc_calibrate_cmd(void) {
|
|
|
|
|
|
outb(0x43, 0x30);
|
|
|
|
|
|
outb(0x40, 0x7c);
|
|
|
|
|
|
outb(0x40, 0x2e);
|
|
|
|
|
|
u64 tsc_start = rdtsc_cmd();
|
|
|
|
|
|
u16 prev = 0; u64 loops = 0;
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
|
outb(0x43, 0x00);
|
|
|
|
|
|
u16 cur = (u16)inb(0x40) | ((u16)inb(0x40) << 8);
|
|
|
|
|
|
if (cur > prev && loops > 10) break;
|
|
|
|
|
|
prev = cur; loops++;
|
|
|
|
|
|
}
|
|
|
|
|
|
u64 tsc_end = rdtsc_cmd();
|
|
|
|
|
|
g_tsc_per_ms = (tsc_end - tsc_start) / 10;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void serial_wait_tx(void) {
|
|
|
|
|
|
if (g_tsc_per_ms == 0) tsc_calibrate_cmd();
|
|
|
|
|
|
if (g_tsc_per_ms == 0) {
|
|
|
|
|
|
for (u32 i = 0; i < 100000; i++) { if (inb(COM1 + 5) & 0x20) break; }
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
u64 deadline = rdtsc_cmd() + g_tsc_per_ms / 10;
|
|
|
|
|
|
while (rdtsc_cmd() < deadline) {
|
|
|
|
|
|
if (inb(COM1 + 5) & 0x20) break;
|
|
|
|
|
|
__asm__ volatile("pause");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void sputc(char c) { serial_wait_tx(); outb(COM1, (unsigned char)c); }
|
|
|
|
|
|
static void swrite(const char *s) { while (*s) { if (*s == '\n') sputc('\r'); sputc(*s++); } }
|
|
|
|
|
|
static void logl(const char *s) { swrite(s); swrite("\n"); }
|
|
|
|
|
|
|
|
|
|
|
|
/* ASCII 字体 — 必须在 deshab_ui.h 之前包含 */
|
|
|
|
|
|
#include "../firstInit/ascii_bitmaps.c"
|
|
|
|
|
|
#include "../UTSM/include/utsm/deshab_ui.h"
|
2026-07-31 13:53:49 +08:00
|
|
|
|
/* FAT32 共享读写库 — PE64 程序(如 deaicup.exe ~2.2MB)需要大文件缓冲 */
|
|
|
|
|
|
#define F32_DATA_BYTES (4u * 1024u * 1024u)
|
2026-07-28 14:35:14 +08:00
|
|
|
|
#include "../tools/fat32_io.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* 颜色定义 — Windows cmd.exe 风格(黑底浅灰字)
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
#define CMD_BG 0xFF000000u /* 纯黑背景 */
|
|
|
|
|
|
#define CMD_TEXT 0xFFC0C0C0u /* 浅灰文字(cmd.exe 默认) */
|
|
|
|
|
|
#define CMD_PROMPT 0xFFFFFFFFu /* 白色提示符 */
|
|
|
|
|
|
#define CMD_ERROR 0xFFFF6060u /* 浅红错误 */
|
|
|
|
|
|
#define CMD_OK 0xFF60FF60u /* 浅绿成功 */
|
|
|
|
|
|
#define CMD_INFO 0xFF60A0FFu /* 浅蓝信息 */
|
|
|
|
|
|
#define CMD_DIM 0xFF808080u /* 灰色暗淡 */
|
|
|
|
|
|
#define CMD_WARN 0xFFFFFF60u /* 黄色警告 */
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* 终端缓冲区与渲染
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
#define TERM_COLS 100
|
|
|
|
|
|
#define TERM_ROWS 40
|
|
|
|
|
|
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
|
|
|
|
|
|
|
|
|
|
|
|
static du_context g_ctx;
|
|
|
|
|
|
static u64 fb_a, fb_w, fb_h, fb_p;
|
|
|
|
|
|
|
|
|
|
|
|
static u8 term_ch[TERM_MAX_CHARS];
|
|
|
|
|
|
static u32 term_fg[TERM_MAX_CHARS];
|
|
|
|
|
|
static int term_w = TERM_COLS;
|
|
|
|
|
|
static int term_h = TERM_ROWS;
|
|
|
|
|
|
static int cur_col = 0;
|
|
|
|
|
|
static int cur_row = 0;
|
|
|
|
|
|
static int cur_visible = 1;
|
|
|
|
|
|
static int term_start_x, term_start_y;
|
|
|
|
|
|
|
|
|
|
|
|
static const dsk_boot_context *g_boot_ctx = 0;
|
|
|
|
|
|
static u64 g_kernel_api = 0;
|
|
|
|
|
|
static const pe_service *g_pe_svc = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* dev_mode 串口镜像 */
|
|
|
|
|
|
static int g_serial_mirror = 0;
|
|
|
|
|
|
|
|
|
|
|
|
static void term_init_pos(void) {
|
|
|
|
|
|
int margin = 4;
|
|
|
|
|
|
term_start_x = margin;
|
|
|
|
|
|
term_start_y = margin;
|
|
|
|
|
|
int avail_w = (int)fb_w - margin * 2;
|
|
|
|
|
|
int avail_h = (int)fb_h - margin * 2;
|
|
|
|
|
|
term_w = avail_w / (int)DU_ASCII_STEP;
|
|
|
|
|
|
term_h = avail_h / (int)DU_ASCII_LINE_H;
|
|
|
|
|
|
if (term_w < 10) term_w = 10;
|
|
|
|
|
|
if (term_h < 5) term_h = 5;
|
|
|
|
|
|
if (term_w > TERM_COLS) term_w = TERM_COLS;
|
|
|
|
|
|
if (term_h > TERM_ROWS) term_h = TERM_ROWS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void term_clear_buf(void) {
|
|
|
|
|
|
for (int i = 0; i < TERM_MAX_CHARS; i++) {
|
|
|
|
|
|
term_ch[i] = ' ';
|
|
|
|
|
|
term_fg[i] = CMD_TEXT;
|
|
|
|
|
|
}
|
|
|
|
|
|
cur_col = 0;
|
|
|
|
|
|
cur_row = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void term_scroll(void) {
|
|
|
|
|
|
for (int r = 1; r < term_h; r++) {
|
|
|
|
|
|
for (int c = 0; c < term_w; c++) {
|
|
|
|
|
|
int src = r * TERM_COLS + c;
|
|
|
|
|
|
int dst = (r - 1) * TERM_COLS + c;
|
|
|
|
|
|
term_ch[dst] = term_ch[src];
|
|
|
|
|
|
term_fg[dst] = term_fg[src];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
for (int c = 0; c < term_w; c++) {
|
|
|
|
|
|
term_ch[(term_h - 1) * TERM_COLS + c] = ' ';
|
|
|
|
|
|
term_fg[(term_h - 1) * TERM_COLS + c] = CMD_TEXT;
|
|
|
|
|
|
}
|
|
|
|
|
|
cur_row = term_h - 1;
|
|
|
|
|
|
cur_col = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void term_newline(void) {
|
|
|
|
|
|
cur_col = 0;
|
|
|
|
|
|
cur_row++;
|
|
|
|
|
|
if (cur_row >= term_h) term_scroll();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void term_putc_color(char c, u32 color) {
|
|
|
|
|
|
if (g_serial_mirror) { if (c == '\n') sputc('\r'); sputc(c); }
|
|
|
|
|
|
if (c == '\n') { term_newline(); return; }
|
|
|
|
|
|
if (c == '\r') { cur_col = 0; return; }
|
|
|
|
|
|
if (c == '\t') {
|
|
|
|
|
|
int spaces = 8 - (cur_col % 8);
|
|
|
|
|
|
for (int i = 0; i < spaces; i++) term_putc_color(' ', color);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (c < ' ' || c > '~') c = '.';
|
|
|
|
|
|
if (cur_col >= term_w) term_newline();
|
|
|
|
|
|
int idx = cur_row * TERM_COLS + cur_col;
|
|
|
|
|
|
term_ch[idx] = (u8)c;
|
|
|
|
|
|
term_fg[idx] = color;
|
|
|
|
|
|
cur_col++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void term_putc(char c) { term_putc_color(c, CMD_TEXT); }
|
|
|
|
|
|
static void term_puts_color(const char *s, u32 color) { while (*s) term_putc_color(*s++, color); }
|
|
|
|
|
|
static void term_puts(const char *s) { term_puts_color(s, CMD_TEXT); }
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- 数字转字符串 ---- */
|
|
|
|
|
|
static void term_putu(u32 v) {
|
|
|
|
|
|
char buf[12];
|
|
|
|
|
|
int i = 11;
|
|
|
|
|
|
buf[i--] = 0;
|
|
|
|
|
|
if (v == 0) { buf[i--] = '0'; }
|
|
|
|
|
|
while (v > 0 && i >= 0) { buf[i--] = (char)('0' + (v % 10)); v /= 10; }
|
|
|
|
|
|
term_puts(&buf[i + 1]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void term_putx(u32 v) {
|
|
|
|
|
|
static const char hex[] = "0123456789ABCDEF";
|
|
|
|
|
|
term_putc('0'); term_putc('x');
|
|
|
|
|
|
for (int i = 28; i >= 0; i -= 4) {
|
|
|
|
|
|
u32 nib = (v >> i) & 0xF;
|
|
|
|
|
|
if (nib != 0 || i <= 12) term_putc(hex[nib]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-28 21:11:10 +08:00
|
|
|
|
/* 64 位十六进制输出(前导零抑制),用于 PE ImageBase 等宽字段 */
|
|
|
|
|
|
static void term_putx64(u64 v) {
|
|
|
|
|
|
static const char hex[] = "0123456789ABCDEF";
|
|
|
|
|
|
term_putc('0'); term_putc('x');
|
|
|
|
|
|
int started = 0;
|
|
|
|
|
|
for (int i = 60; i >= 0; i -= 4) {
|
|
|
|
|
|
u64 nib = (v >> i) & 0xF;
|
|
|
|
|
|
if (nib) started = 1;
|
|
|
|
|
|
if (started || i == 0) term_putc(hex[nib]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-28 14:35:14 +08:00
|
|
|
|
/* ---- 终端全屏渲染 ---- */
|
|
|
|
|
|
static void term_redraw_all(void) {
|
|
|
|
|
|
du_fill_rect(&g_ctx, 0, 0, (i64)fb_w, (i64)fb_h, CMD_BG);
|
|
|
|
|
|
for (int r = 0; r < term_h; r++) {
|
|
|
|
|
|
i64 y = term_start_y + (i64)r * (i64)DU_ASCII_LINE_H;
|
|
|
|
|
|
for (int c = 0; c < term_w; c++) {
|
|
|
|
|
|
int idx = r * TERM_COLS + c;
|
|
|
|
|
|
i64 x = term_start_x + (i64)c * (i64)DU_ASCII_STEP;
|
|
|
|
|
|
du_draw_char(&g_ctx, (char)term_ch[idx], x, y, term_fg[idx], CMD_BG);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void term_redraw_cursor(void) {
|
|
|
|
|
|
if (cur_col >= term_w) return;
|
|
|
|
|
|
i64 x = term_start_x + (i64)cur_col * (i64)DU_ASCII_STEP;
|
|
|
|
|
|
i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H;
|
|
|
|
|
|
du_fill_rect(&g_ctx, x, y, (i64)DU_ASCII_STEP - 1, (i64)DU_ASCII_CELL_H, CMD_PROMPT);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void term_clear_cursor(void) {
|
|
|
|
|
|
if (cur_col >= term_w) return;
|
|
|
|
|
|
i64 x = term_start_x + (i64)cur_col * (i64)DU_ASCII_STEP;
|
|
|
|
|
|
i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H;
|
|
|
|
|
|
int idx = cur_row * TERM_COLS + cur_col;
|
|
|
|
|
|
du_fill_rect(&g_ctx, x, y, (i64)DU_ASCII_STEP - 1, (i64)DU_ASCII_CELL_H, CMD_BG);
|
|
|
|
|
|
du_draw_char(&g_ctx, (char)term_ch[idx], x, y, term_fg[idx], CMD_BG);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* 键盘输入(PS/2 Scan Code Set 1)
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
static char scan_to_ascii(u8 sc, int shift) {
|
|
|
|
|
|
static const char normal[58] = {
|
|
|
|
|
|
0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t',
|
|
|
|
|
|
'q','w','e','r','t','y','u','i','o','p','[',']','\n',0,
|
|
|
|
|
|
'a','s','d','f','g','h','j','k','l',';','\'', '`',0,'\\',
|
|
|
|
|
|
'z','x','c','v','b','n','m',',','.','/',0,'*',0,' '
|
|
|
|
|
|
};
|
|
|
|
|
|
static const char shifted[58] = {
|
|
|
|
|
|
0, 27, '!','@','#','$','%','^','&','*','(',')','_','+', 8, '\t',
|
|
|
|
|
|
'Q','W','E','R','T','Y','U','I','O','P','{','}','\n',0,
|
|
|
|
|
|
'A','S','D','F','G','H','J','K','L',':','"','~',0,'|',
|
|
|
|
|
|
'Z','X','C','V','B','N','M','<','>','?',0,'*',0,' '
|
|
|
|
|
|
};
|
|
|
|
|
|
if (sc >= 58) return 0;
|
|
|
|
|
|
return shift ? shifted[sc] : normal[sc];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* 字符串工具
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
static int str_eq(const char *a, const char *b) {
|
|
|
|
|
|
while (*a && *b) { if (*a != *b) return 0; a++; b++; }
|
|
|
|
|
|
return *a == *b;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int str_len(const char *s) {
|
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
while (s[n]) n++;
|
|
|
|
|
|
return n;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void str_copy(char *dst, const char *src, int max) {
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
while (src[i] && i < max - 1) { dst[i] = src[i]; i++; }
|
|
|
|
|
|
dst[i] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int str_ieq(const char *a, const char *b) {
|
|
|
|
|
|
while (*a && *b) {
|
|
|
|
|
|
char ca = *a, cb = *b;
|
|
|
|
|
|
if (ca >= 'a' && ca <= 'z') ca -= 32;
|
|
|
|
|
|
if (cb >= 'a' && cb <= 'z') cb -= 32;
|
|
|
|
|
|
if (ca != cb) return 0;
|
|
|
|
|
|
a++; b++;
|
|
|
|
|
|
}
|
|
|
|
|
|
return *a == *b;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* 输入行编辑
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
static char input_buf[256];
|
|
|
|
|
|
static int input_len = 0;
|
|
|
|
|
|
static int input_cursor = 0;
|
|
|
|
|
|
|
|
|
|
|
|
static const char *PROMPT = "C:\\> ";
|
|
|
|
|
|
|
|
|
|
|
|
static void redraw_input_line(void) {
|
|
|
|
|
|
int prompt_len = str_len(PROMPT);
|
|
|
|
|
|
i64 y = term_start_y + (i64)cur_row * (i64)DU_ASCII_LINE_H;
|
|
|
|
|
|
du_fill_rect(&g_ctx, term_start_x, y,
|
|
|
|
|
|
(i64)term_w * (i64)DU_ASCII_STEP,
|
|
|
|
|
|
(i64)DU_ASCII_CELL_H, CMD_BG);
|
|
|
|
|
|
for (int i = 0; i < prompt_len; i++) {
|
|
|
|
|
|
i64 x = term_start_x + (i64)i * (i64)DU_ASCII_STEP;
|
|
|
|
|
|
du_draw_char(&g_ctx, PROMPT[i], x, y, CMD_PROMPT, CMD_BG);
|
|
|
|
|
|
}
|
|
|
|
|
|
for (int i = 0; i < input_len; i++) {
|
|
|
|
|
|
i64 x = term_start_x + (i64)(prompt_len + i) * (i64)DU_ASCII_STEP;
|
|
|
|
|
|
du_draw_char(&g_ctx, input_buf[i], x, y, CMD_TEXT, CMD_BG);
|
|
|
|
|
|
}
|
|
|
|
|
|
cur_col = prompt_len + input_cursor;
|
|
|
|
|
|
term_redraw_cursor();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void insert_char(char c) {
|
|
|
|
|
|
if (input_len >= 255) return;
|
|
|
|
|
|
for (int i = input_len; i > input_cursor; i--)
|
|
|
|
|
|
input_buf[i] = input_buf[i - 1];
|
|
|
|
|
|
input_buf[input_cursor] = c;
|
|
|
|
|
|
input_len++;
|
|
|
|
|
|
input_cursor++;
|
|
|
|
|
|
redraw_input_line();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void delete_char_back(void) {
|
|
|
|
|
|
if (input_cursor == 0) return;
|
|
|
|
|
|
for (int i = input_cursor - 1; i < input_len - 1; i++)
|
|
|
|
|
|
input_buf[i] = input_buf[i + 1];
|
|
|
|
|
|
input_len--;
|
|
|
|
|
|
input_cursor--;
|
|
|
|
|
|
input_buf[input_len] = 0;
|
|
|
|
|
|
redraw_input_line();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cursor_left(void) {
|
|
|
|
|
|
if (input_cursor > 0) { input_cursor--; redraw_input_line(); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cursor_right(void) {
|
|
|
|
|
|
if (input_cursor < input_len) { input_cursor++; redraw_input_line(); }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cursor_home(void) {
|
|
|
|
|
|
input_cursor = 0;
|
|
|
|
|
|
redraw_input_line();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cursor_end(void) {
|
|
|
|
|
|
input_cursor = input_len;
|
|
|
|
|
|
redraw_input_line();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void kill_line(void) {
|
|
|
|
|
|
input_len = 0;
|
|
|
|
|
|
input_cursor = 0;
|
|
|
|
|
|
input_buf[0] = 0;
|
|
|
|
|
|
redraw_input_line();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* 内置命令
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_help(void) {
|
|
|
|
|
|
term_puts_color("Deshab CMD — 命令列表:\r\n", CMD_INFO);
|
|
|
|
|
|
term_puts(" dir 列出文件\r\n");
|
|
|
|
|
|
term_puts(" cd 显示当前目录\r\n");
|
|
|
|
|
|
term_puts(" type <file> 显示文件内容\r\n");
|
|
|
|
|
|
term_puts(" copy <a> <b> 复制文件\r\n");
|
|
|
|
|
|
term_puts(" del <file> 删除文件\r\n");
|
|
|
|
|
|
term_puts(" ren <a> <b> 重命名文件\r\n");
|
|
|
|
|
|
term_puts(" echo <text> 显示文本 (echo text > file 写入)\r\n");
|
|
|
|
|
|
term_puts(" cls 清屏\r\n");
|
|
|
|
|
|
term_puts(" ver 显示版本\r\n");
|
|
|
|
|
|
term_puts(" set 显示环境变量\r\n");
|
|
|
|
|
|
term_puts(" help 显示此帮助\r\n");
|
|
|
|
|
|
term_puts(" exit 返回桌面\r\n");
|
|
|
|
|
|
term_puts_color("\nPE 程序执行:\r\n", CMD_WARN);
|
|
|
|
|
|
term_puts(" <name>.exe 运行 PE32+/PE32 程序 (如 hello.exe)\r\n");
|
|
|
|
|
|
term_puts(" <name> 自动查找 name.exe 并运行\r\n");
|
2026-07-28 21:11:10 +08:00
|
|
|
|
term_puts(" pe <name> 显式运行 PE 程序 (如 pe hello64)\r\n");
|
|
|
|
|
|
term_puts(" peinfo <nm> 显示 PE 头信息 (不执行)\r\n");
|
2026-07-28 14:35:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_ver(void) {
|
|
|
|
|
|
term_puts_color("Deshab CMD [Version 0.1.0]\r\n", CMD_OK);
|
|
|
|
|
|
term_puts_color("(c) 2026 Deshab Project. PE/EXE 兼容层 by UTSM.\r\n", CMD_DIM);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_cls(void) {
|
|
|
|
|
|
term_clear_buf();
|
|
|
|
|
|
term_redraw_all();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_cd(void) {
|
|
|
|
|
|
term_puts("C:\\\r\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_set(void) {
|
|
|
|
|
|
term_puts_color("环境变量:\r\n", CMD_INFO);
|
|
|
|
|
|
term_puts(" PATH=C:\\;C:\\bin\r\n");
|
|
|
|
|
|
term_puts(" PROMPT=$P$G\r\n");
|
|
|
|
|
|
term_puts(" OS=Deshab\r\n");
|
|
|
|
|
|
term_puts(" PE_COMPAT=UTSM_PE32+_PE32\r\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* dir — 列出 FAT32 根目录文件 */
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
|
int count;
|
|
|
|
|
|
u32 total_bytes;
|
|
|
|
|
|
} dir_ctx_static;
|
|
|
|
|
|
|
|
|
|
|
|
static int dir_list_cb(const char *name, u32 size, u8 attr, void *ud) {
|
|
|
|
|
|
dir_ctx_static *st = (dir_ctx_static *)ud;
|
|
|
|
|
|
if (attr & 0x10) {
|
|
|
|
|
|
term_puts_color(name, CMD_INFO);
|
|
|
|
|
|
term_puts_color(" <DIR> ", CMD_DIM);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
term_puts(name);
|
|
|
|
|
|
term_puts(" ");
|
|
|
|
|
|
/* 右对齐文件大小 */
|
|
|
|
|
|
u32 sz = size;
|
|
|
|
|
|
char numbuf[12];
|
|
|
|
|
|
int ni = 11;
|
|
|
|
|
|
numbuf[ni--] = 0;
|
|
|
|
|
|
if (sz == 0) numbuf[ni--] = '0';
|
|
|
|
|
|
while (sz > 0 && ni >= 0) { numbuf[ni--] = (char)('0' + sz % 10); sz /= 10; }
|
|
|
|
|
|
while (ni >= 0) numbuf[ni--] = ' ';
|
|
|
|
|
|
term_puts(&numbuf[0]);
|
|
|
|
|
|
st->total_bytes += size;
|
|
|
|
|
|
}
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
st->count++;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_dir(void) {
|
|
|
|
|
|
if (!f32_blk_read) {
|
|
|
|
|
|
term_puts_color("dir: 无块设备\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
term_puts_color(" C 盘的卷是 DESHAB\r\n", CMD_DIM);
|
|
|
|
|
|
term_puts_color(" 卷序列号为 DE5A-B002\r\n", CMD_DIM);
|
|
|
|
|
|
term_puts("\r\n C:\\ 的目录\r\n\r\n");
|
|
|
|
|
|
|
|
|
|
|
|
dir_ctx_static st = {0, 0};
|
|
|
|
|
|
f32_list_root(dir_list_cb, &st);
|
|
|
|
|
|
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
term_putu((u32)st.count);
|
|
|
|
|
|
term_puts(" 个文件 ");
|
|
|
|
|
|
term_putu(st.total_bytes);
|
|
|
|
|
|
term_puts(" 字节\r\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* type <file> — 显示文件内容 */
|
|
|
|
|
|
static void cmd_type(const char *args) {
|
|
|
|
|
|
if (!*args) { term_puts_color("type: 缺少文件名\r\n", CMD_ERROR); return; }
|
|
|
|
|
|
if (!f32_blk_read) { term_puts_color("type: 无块设备\r\n", CMD_ERROR); return; }
|
|
|
|
|
|
|
|
|
|
|
|
char name11[11];
|
|
|
|
|
|
if (f32_name_to_83(args, name11) != 0) {
|
|
|
|
|
|
term_puts_color("type: 无效文件名\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
u8 *data = 0; u32 size = 0;
|
|
|
|
|
|
if (f32_read_root_file(name11, &data, &size) != 0) {
|
|
|
|
|
|
term_puts_color("type: 文件不存在\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* 输出文件内容(按字符) */
|
|
|
|
|
|
for (u32 i = 0; i < size; i++) {
|
|
|
|
|
|
char c = (char)data[i];
|
|
|
|
|
|
if (c == '\n') term_putc('\r');
|
|
|
|
|
|
term_putc(c);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (size > 0 && data[size - 1] != '\n') term_putc('\r');
|
|
|
|
|
|
term_putc('\n');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* echo <text> — 显示文本或重定向到文件 */
|
|
|
|
|
|
static void cmd_echo(const char *args) {
|
|
|
|
|
|
/* 检查重定向: echo text > file */
|
|
|
|
|
|
char text[256];
|
|
|
|
|
|
str_copy(text, args, sizeof(text));
|
|
|
|
|
|
|
|
|
|
|
|
/* 查找 > */
|
|
|
|
|
|
int redir = -1;
|
|
|
|
|
|
for (int i = 0; text[i]; i++) {
|
|
|
|
|
|
if (text[i] == '>') { redir = i; break; }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (redir >= 0) {
|
|
|
|
|
|
/* 重定向到文件 */
|
|
|
|
|
|
text[redir] = 0;
|
|
|
|
|
|
/* 去尾部空格 */
|
|
|
|
|
|
int tl = str_len(text);
|
|
|
|
|
|
while (tl > 0 && text[tl - 1] == ' ') text[--tl] = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* 获取文件名 */
|
|
|
|
|
|
char *fn = text + redir + 1;
|
|
|
|
|
|
while (*fn == ' ') fn++;
|
|
|
|
|
|
if (!*fn) { term_puts_color("echo: 重定向缺少文件名\r\n", CMD_ERROR); return; }
|
|
|
|
|
|
|
|
|
|
|
|
char name11[11];
|
|
|
|
|
|
if (f32_name_to_83(fn, name11) != 0) {
|
|
|
|
|
|
term_puts_color("echo: 无效文件名\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!f32_blk_write) { term_puts_color("echo: 无块设备写能力\r\n", CMD_ERROR); return; }
|
|
|
|
|
|
|
|
|
|
|
|
/* 构造写入内容 */
|
|
|
|
|
|
char content[256];
|
|
|
|
|
|
int ci = 0;
|
|
|
|
|
|
for (int i = 0; i < tl && ci < 254; i++) content[ci++] = text[i];
|
|
|
|
|
|
content[ci++] = '\r';
|
|
|
|
|
|
content[ci++] = '\n';
|
|
|
|
|
|
|
|
|
|
|
|
if (f32_write_root_file(name11, (const u8 *)content, (u32)ci) != 0) {
|
|
|
|
|
|
term_puts_color("echo: 写入失败\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
term_puts_color("echo: 已写入 ", CMD_OK);
|
|
|
|
|
|
term_puts(fn);
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
/* 直接显示 */
|
|
|
|
|
|
if (*args) {
|
|
|
|
|
|
term_puts(args);
|
|
|
|
|
|
}
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* copy <src> <dst> — 复制文件 */
|
|
|
|
|
|
static void cmd_copy(const char *args) {
|
|
|
|
|
|
char src[64], dst[64];
|
|
|
|
|
|
int si = 0, di = 0;
|
|
|
|
|
|
const char *p = args;
|
|
|
|
|
|
while (*p == ' ') p++;
|
|
|
|
|
|
while (*p && *p != ' ' && si < 63) src[si++] = *p++;
|
|
|
|
|
|
src[si] = 0;
|
|
|
|
|
|
while (*p == ' ') p++;
|
|
|
|
|
|
while (*p && di < 63) dst[di++] = *p++;
|
|
|
|
|
|
dst[di] = 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (!src[0] || !dst[0]) {
|
|
|
|
|
|
term_puts_color("copy: 用法 copy <源文件> <目标文件>\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!f32_blk_read || !f32_blk_write) {
|
|
|
|
|
|
term_puts_color("copy: 无块设备\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char s83[11], d83[11];
|
|
|
|
|
|
if (f32_name_to_83(src, s83) != 0 || f32_name_to_83(dst, d83) != 0) {
|
|
|
|
|
|
term_puts_color("copy: 无效文件名\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
u8 *data = 0; u32 size = 0;
|
|
|
|
|
|
if (f32_read_root_file(s83, &data, &size) != 0) {
|
|
|
|
|
|
term_puts_color("copy: 源文件不存在\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (f32_write_root_file(d83, data, size) != 0) {
|
|
|
|
|
|
term_puts_color("copy: 写入失败\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
term_puts_color(" 1 个文件已复制。\r\n", CMD_OK);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* del <file> — 删除文件 */
|
|
|
|
|
|
static void cmd_del(const char *args) {
|
|
|
|
|
|
if (!*args) { term_puts_color("del: 缺少文件名\r\n", CMD_ERROR); return; }
|
|
|
|
|
|
if (!f32_blk_write) { term_puts_color("del: 无块设备写能力\r\n", CMD_ERROR); return; }
|
|
|
|
|
|
|
|
|
|
|
|
char name11[11];
|
|
|
|
|
|
if (f32_name_to_83(args, name11) != 0) {
|
|
|
|
|
|
term_puts_color("del: 无效文件名\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (f32_delete_root_file(name11) != 0) {
|
|
|
|
|
|
term_puts_color("del: 文件不存在或删除失败\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
term_puts_color("del: 已删除\r\n", CMD_OK);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ren <old> <new> — 重命名文件 */
|
|
|
|
|
|
static void cmd_ren(const char *args) {
|
|
|
|
|
|
char old[64], nw[64];
|
|
|
|
|
|
int oi = 0, ni = 0;
|
|
|
|
|
|
const char *p = args;
|
|
|
|
|
|
while (*p == ' ') p++;
|
|
|
|
|
|
while (*p && *p != ' ' && oi < 63) old[oi++] = *p++;
|
|
|
|
|
|
old[oi] = 0;
|
|
|
|
|
|
while (*p == ' ') p++;
|
|
|
|
|
|
while (*p && ni < 63) nw[ni++] = *p++;
|
|
|
|
|
|
nw[ni] = 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (!old[0] || !nw[0]) {
|
|
|
|
|
|
term_puts_color("ren: 用法 ren <旧名> <新名>\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!f32_blk_read || !f32_blk_write) {
|
|
|
|
|
|
term_puts_color("ren: 无块设备\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char o83[11], n83[11];
|
|
|
|
|
|
if (f32_name_to_83(old, o83) != 0 || f32_name_to_83(nw, n83) != 0) {
|
|
|
|
|
|
term_puts_color("ren: 无效文件名\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
u8 *data = 0; u32 size = 0;
|
|
|
|
|
|
if (f32_read_root_file(o83, &data, &size) != 0) {
|
|
|
|
|
|
term_puts_color("ren: 源文件不存在\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (f32_write_root_file(n83, data, size) != 0) {
|
|
|
|
|
|
term_puts_color("ren: 写入新文件失败\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (f32_delete_root_file(o83) != 0) {
|
|
|
|
|
|
term_puts_color("ren: 删除旧文件失败(警告)\r\n", CMD_WARN);
|
|
|
|
|
|
}
|
|
|
|
|
|
term_puts_color("ren: 已重命名\r\n", CMD_OK);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_mkdir(void) { term_puts_color("mkdir: FAT32 根目录不支持子目录\r\n", CMD_WARN); }
|
|
|
|
|
|
static void cmd_rmdir(void) { term_puts_color("rmdir: FAT32 根目录不支持子目录\r\n", CMD_WARN); }
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* PE/EXE 程序执行
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
/* 检查文件名是否以 .exe/.com/.bat 结尾(不区分大小写) */
|
|
|
|
|
|
static int has_exec_ext(const char *name) {
|
|
|
|
|
|
int len = str_len(name);
|
|
|
|
|
|
if (len < 4) return 0;
|
|
|
|
|
|
const char *ext = name + len - 4;
|
|
|
|
|
|
if (ext[0] != '.') return 0;
|
|
|
|
|
|
if (str_ieq(ext, ".exe")) return 1;
|
|
|
|
|
|
if (str_ieq(ext, ".com")) return 1;
|
|
|
|
|
|
if (str_ieq(ext, ".bat")) return 2;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 执行 PE/EXE 程序。
|
|
|
|
|
|
* prog_name = 程序名(可能带或不带 .exe 后缀)
|
|
|
|
|
|
* full_cmdline = 完整命令行(程序名 + 参数,即 GetCommandLineA 返回值)
|
|
|
|
|
|
*/
|
2026-07-28 21:11:10 +08:00
|
|
|
|
static int cmd_read_pe_from_bin(const char *name11, u8 **data, u32 *size); /* 前向声明 */
|
|
|
|
|
|
|
2026-07-28 14:35:14 +08:00
|
|
|
|
static void exec_pe(const char *prog_name, const char *full_cmdline) {
|
|
|
|
|
|
if (!g_pe_svc) {
|
|
|
|
|
|
term_puts_color("PE 兼容层不可用(UTSM PE 服务未初始化)\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!f32_blk_read) {
|
|
|
|
|
|
term_puts_color("无块设备,无法读取 PE 文件\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 构造 8.3 文件名 */
|
|
|
|
|
|
char base[64];
|
|
|
|
|
|
str_copy(base, prog_name, sizeof(base));
|
|
|
|
|
|
|
|
|
|
|
|
/* 如果没有扩展名,自动补 .exe */
|
|
|
|
|
|
int has_dot = 0;
|
|
|
|
|
|
for (int i = 0; base[i]; i++) {
|
|
|
|
|
|
if (base[i] == '.') { has_dot = 1; break; }
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!has_dot) {
|
|
|
|
|
|
int bl = str_len(base);
|
|
|
|
|
|
if (bl > 59) bl = 59;
|
|
|
|
|
|
base[bl] = '.'; base[bl + 1] = 'e'; base[bl + 2] = 'x';
|
|
|
|
|
|
base[bl + 3] = 'e'; base[bl + 4] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char name11[11];
|
|
|
|
|
|
if (f32_name_to_83(base, name11) != 0) {
|
|
|
|
|
|
term_puts_color("无效文件名\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-28 21:11:10 +08:00
|
|
|
|
/* PATH 查找:先根目录,再 /bin/ */
|
2026-07-28 14:35:14 +08:00
|
|
|
|
u8 *pe_data = 0; u32 pe_size = 0;
|
2026-07-28 21:11:10 +08:00
|
|
|
|
int rc = f32_read_root_file(name11, &pe_data, &pe_size);
|
|
|
|
|
|
if (rc != 0) {
|
|
|
|
|
|
rc = cmd_read_pe_from_bin(name11, &pe_data, &pe_size);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (rc != 0) {
|
2026-07-28 14:35:14 +08:00
|
|
|
|
term_puts_color("'", CMD_ERROR);
|
|
|
|
|
|
term_puts_color(prog_name, CMD_ERROR);
|
|
|
|
|
|
term_puts_color("' 不是内部或外部命令,也不是可运行的程序。\r\n", CMD_ERROR);
|
2026-07-28 21:11:10 +08:00
|
|
|
|
term_puts_color(" (已搜索 C:\\ 及 C:\\bin\\)\r\n", CMD_DIM);
|
2026-07-28 14:35:14 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 验证 PE 头(MZ 签名) */
|
|
|
|
|
|
if (pe_size < 2 || pe_data[0] != 'M' || pe_data[1] != 'Z') {
|
|
|
|
|
|
term_puts_color("不是有效的 PE/EXE 文件(缺少 MZ 签名)\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
term_puts_color("正在执行 ", CMD_INFO);
|
|
|
|
|
|
term_puts_color(prog_name, CMD_WARN);
|
|
|
|
|
|
term_puts_color(" ...\r\n", CMD_INFO);
|
|
|
|
|
|
term_redraw_all();
|
|
|
|
|
|
|
|
|
|
|
|
/* 调用 UTSM PE 服务执行 */
|
|
|
|
|
|
logl("[cmd] PE run begin");
|
|
|
|
|
|
swrite("[cmd] cmdline: "); swrite(full_cmdline); swrite("\n");
|
|
|
|
|
|
|
|
|
|
|
|
u64 exit_code = 0;
|
2026-07-28 21:11:10 +08:00
|
|
|
|
rc = g_pe_svc->run(pe_data, (u64)pe_size, full_cmdline, &exit_code);
|
2026-07-28 14:35:14 +08:00
|
|
|
|
|
|
|
|
|
|
if (rc != 0) {
|
|
|
|
|
|
term_puts_color("PE 执行失败,错误码: ", CMD_ERROR);
|
|
|
|
|
|
term_putx((u32)rc);
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
term_puts_color("程序退出,返回码: ", CMD_DIM);
|
|
|
|
|
|
term_putu((u32)exit_code);
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logl("[cmd] PE run end");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-28 21:11:10 +08:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* /bin 子目录支持 — 用于查找 PE 可执行程序
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
/* 在根目录簇链中查找名为 name11 的子目录(attr & 0x10),返回首簇号。
|
|
|
|
|
|
* 使用 f32_disk 缓存(来自 fat32_io.h)。返回 0 成功,-1 未找到。 */
|
|
|
|
|
|
static int cmd_find_subdir(u32 dir_clus, const char *name11, u32 *out_clus) {
|
|
|
|
|
|
if (!f32_disk_loaded && f32_disk_load() != 0) return -2;
|
|
|
|
|
|
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
|
|
|
|
|
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
|
|
|
|
|
u32 fat_byte_off = bpb->rsvd * 512;
|
|
|
|
|
|
u32 spc = bpb->spc;
|
|
|
|
|
|
u32 clus = dir_clus;
|
|
|
|
|
|
while (clus >= 2 && clus < 0x0FFFFFF8) {
|
|
|
|
|
|
u32 lba = data_lba + (clus - 2) * spc;
|
|
|
|
|
|
const u8 *cb;
|
|
|
|
|
|
if ((u64)lba * 512 + (u64)spc * 512 <= sizeof(f32_disk))
|
|
|
|
|
|
cb = f32_disk + (u64)lba * 512;
|
|
|
|
|
|
else {
|
|
|
|
|
|
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
|
|
|
|
|
|
cb = f32_cluster;
|
|
|
|
|
|
}
|
|
|
|
|
|
const f32_dirent *dir = (const f32_dirent *)cb;
|
|
|
|
|
|
for (u32 e = 0; e * 32 < spc * 512; e++) {
|
|
|
|
|
|
if (dir[e].name[0] == 0) goto done_dir;
|
|
|
|
|
|
if ((u8)dir[e].name[0] == 0xE5) continue;
|
|
|
|
|
|
if (dir[e].attr == 0x0F) continue;
|
|
|
|
|
|
if (dir[e].attr & 0x08) continue;
|
|
|
|
|
|
if (!(dir[e].attr & 0x10)) continue; /* 只匹配目录 */
|
|
|
|
|
|
if (f32_neq11(dir[e].name, name11)) {
|
|
|
|
|
|
*out_clus = f32_r16((const u8*)&dir[e].clow) | ((u32)f32_r16((const u8*)&dir[e].chigh) << 16);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
u32 fo = fat_byte_off + clus * 4;
|
|
|
|
|
|
if (fo + 4 > sizeof(f32_disk)) break;
|
|
|
|
|
|
clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
|
|
|
|
|
}
|
|
|
|
|
|
done_dir:
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 从指定目录首簇读取文件到 f32_data。
|
|
|
|
|
|
* dir_clus: 目录起始簇号。name11: 8.3 名。
|
|
|
|
|
|
* 返回 0 成功,*out_data 指向 f32_data,*out_size 为字节数;非 0 失败。 */
|
|
|
|
|
|
static int cmd_read_file_in_dir(u32 dir_clus, const char *name11, u8 **out_data, u32 *out_size) {
|
|
|
|
|
|
if (!f32_disk_loaded && f32_disk_load() != 0) return -1;
|
|
|
|
|
|
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
|
|
|
|
|
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
|
|
|
|
|
u32 fat_byte_off = bpb->rsvd * 512;
|
|
|
|
|
|
u32 spc = bpb->spc;
|
|
|
|
|
|
|
|
|
|
|
|
/* 先在目录中查找文件 */
|
|
|
|
|
|
u32 clus = dir_clus;
|
|
|
|
|
|
u32 found_clus = 0, found_size = 0;
|
|
|
|
|
|
int found = 0;
|
|
|
|
|
|
while (clus >= 2 && clus < 0x0FFFFFF8 && !found) {
|
|
|
|
|
|
u32 lba = data_lba + (clus - 2) * spc;
|
|
|
|
|
|
const u8 *cb;
|
|
|
|
|
|
if ((u64)lba * 512 + (u64)spc * 512 <= sizeof(f32_disk))
|
|
|
|
|
|
cb = f32_disk + (u64)lba * 512;
|
|
|
|
|
|
else {
|
|
|
|
|
|
if (f32_read_sectors(lba, 8, f32_cluster) != 0) return -4;
|
|
|
|
|
|
cb = f32_cluster;
|
|
|
|
|
|
}
|
|
|
|
|
|
const f32_dirent *dir = (const f32_dirent *)cb;
|
|
|
|
|
|
for (u32 e = 0; e * 32 < spc * 512; e++) {
|
|
|
|
|
|
if (dir[e].name[0] == 0) goto done_find;
|
|
|
|
|
|
if ((u8)dir[e].name[0] == 0xE5) continue;
|
|
|
|
|
|
if (dir[e].attr == 0x0F) continue;
|
|
|
|
|
|
if (dir[e].attr & 0x08) continue;
|
|
|
|
|
|
if (dir[e].attr & 0x10) continue; /* 跳过目录 */
|
|
|
|
|
|
if (f32_neq11(dir[e].name, name11)) {
|
|
|
|
|
|
found_clus = f32_r16((const u8*)&dir[e].clow) | ((u32)f32_r16((const u8*)&dir[e].chigh) << 16);
|
|
|
|
|
|
found_size = dir[e].fsize;
|
|
|
|
|
|
found = 1;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
u32 fo = fat_byte_off + clus * 4;
|
|
|
|
|
|
if (fo + 4 > sizeof(f32_disk)) break;
|
|
|
|
|
|
clus = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
|
|
|
|
|
}
|
|
|
|
|
|
done_find:
|
|
|
|
|
|
if (!found) return -5;
|
|
|
|
|
|
if (found_size > sizeof(f32_data)) return -6;
|
|
|
|
|
|
|
|
|
|
|
|
/* 读取文件数据 */
|
|
|
|
|
|
u8 *dst = f32_data; u32 remaining = found_size; u32 fc = found_clus;
|
|
|
|
|
|
while (fc >= 2 && fc < 0x0FFFFFF8 && remaining > 0) {
|
|
|
|
|
|
u32 fc_lba = data_lba + (fc - 2) * spc;
|
|
|
|
|
|
u32 fc_bytes = spc * 512;
|
|
|
|
|
|
if (fc_bytes > remaining) fc_bytes = remaining;
|
|
|
|
|
|
const u8 *fb;
|
|
|
|
|
|
if ((u64)fc_lba * 512 + (u64)spc * 512 <= sizeof(f32_disk))
|
|
|
|
|
|
fb = f32_disk + (u64)fc_lba * 512;
|
|
|
|
|
|
else {
|
|
|
|
|
|
if (f32_read_sectors(fc_lba, 8, f32_cluster) != 0) return -7;
|
|
|
|
|
|
fb = f32_cluster;
|
|
|
|
|
|
}
|
|
|
|
|
|
for (u32 b = 0; b < fc_bytes; b++) dst[b] = fb[b];
|
|
|
|
|
|
dst += fc_bytes; remaining -= fc_bytes;
|
|
|
|
|
|
u32 fo = fat_byte_off + fc * 4;
|
|
|
|
|
|
if (fo + 4 > sizeof(f32_disk)) break;
|
|
|
|
|
|
fc = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
|
|
|
|
|
}
|
|
|
|
|
|
*out_data = f32_data; *out_size = found_size;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static u32 g_cmd_bin_clus = 0;
|
|
|
|
|
|
static int g_cmd_bin_checked = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* 获取 /bin 目录首簇号。返回 0 成功,-1 未找到。 */
|
|
|
|
|
|
static int cmd_get_bin_cluster(u32 *out_clus) {
|
|
|
|
|
|
if (g_cmd_bin_checked) {
|
|
|
|
|
|
if (g_cmd_bin_clus == 0) return -1;
|
|
|
|
|
|
*out_clus = g_cmd_bin_clus;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
g_cmd_bin_checked = 1;
|
|
|
|
|
|
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
|
|
|
|
|
char bin83[11] = {'B','I','N',' ',' ',' ',' ',' ',' ',' ',' '};
|
|
|
|
|
|
if (cmd_find_subdir(bpb->root_clus, bin83, &g_cmd_bin_clus) != 0) {
|
|
|
|
|
|
g_cmd_bin_clus = 0;
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
*out_clus = g_cmd_bin_clus;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 从 /bin 子目录读取 PE 文件。返回 0 成功。 */
|
|
|
|
|
|
static int cmd_read_pe_from_bin(const char *name11, u8 **data, u32 *size) {
|
|
|
|
|
|
u32 bin_clus;
|
|
|
|
|
|
if (cmd_get_bin_cluster(&bin_clus) != 0) return -1;
|
|
|
|
|
|
return cmd_read_file_in_dir(bin_clus, name11, data, size);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* peinfo — 解析并显示 PE 头信息(不执行)
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
/* 把程序名转为 8.3 名(自动补 .exe),返回 0 成功,-1 失败。
|
|
|
|
|
|
* 内联自 exec_pe 的同名逻辑,供 cmd_peinfo 复用。 */
|
|
|
|
|
|
static int make_pe_name83(const char *prog, char name11[11]) {
|
|
|
|
|
|
char base[64];
|
|
|
|
|
|
str_copy(base, prog, sizeof(base));
|
|
|
|
|
|
int has_dot = 0;
|
|
|
|
|
|
for (int i = 0; base[i]; i++) {
|
|
|
|
|
|
if (base[i] == '.') { has_dot = 1; break; }
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!has_dot) {
|
|
|
|
|
|
int bl = str_len(base);
|
|
|
|
|
|
if (bl > 59) bl = 59;
|
|
|
|
|
|
base[bl] = '.'; base[bl + 1] = 'e'; base[bl + 2] = 'x';
|
|
|
|
|
|
base[bl + 3] = 'e'; base[bl + 4] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
return f32_name_to_83(base, name11);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void cmd_peinfo(const char *args) {
|
|
|
|
|
|
if (!*args) {
|
|
|
|
|
|
term_puts_color("用法: peinfo <程序名> (如 peinfo hello64)\r\n", CMD_INFO);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!f32_blk_read) {
|
|
|
|
|
|
term_puts_color("peinfo: 无块设备\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char name11[11];
|
|
|
|
|
|
if (make_pe_name83(args, name11) != 0) {
|
|
|
|
|
|
term_puts_color("peinfo: 无效文件名\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* PATH 查找:先根目录,再 /bin/ */
|
|
|
|
|
|
u8 *pe = 0; u32 pe_size = 0;
|
|
|
|
|
|
int rc = f32_read_root_file(name11, &pe, &pe_size);
|
|
|
|
|
|
if (rc != 0) {
|
|
|
|
|
|
rc = cmd_read_pe_from_bin(name11, &pe, &pe_size);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (rc != 0) {
|
|
|
|
|
|
term_puts_color("peinfo: 文件不存在: ", CMD_ERROR);
|
|
|
|
|
|
term_puts_color(args, CMD_ERROR);
|
|
|
|
|
|
term_puts_color(" (已搜索 C:\\ 及 C:\\bin\\)\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- DOS 头 ---- */
|
|
|
|
|
|
if (pe_size < sizeof(image_dos_header)) {
|
|
|
|
|
|
term_puts_color("peinfo: 文件过小,无 DOS 头\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const image_dos_header *dos = (const image_dos_header *)pe;
|
|
|
|
|
|
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
|
|
|
|
|
|
term_puts_color("peinfo: 缺少 MZ 签名\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
u32 lfanew = dos->e_lfanew;
|
|
|
|
|
|
if ((u64)lfanew + 4 + sizeof(image_file_header) > pe_size) {
|
|
|
|
|
|
term_puts_color("peinfo: e_lfanew 越界\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
term_puts_color("=== PE 头信息 ===\r\n", CMD_INFO);
|
|
|
|
|
|
term_puts(" DOS: MZ OK, e_lfanew=");
|
|
|
|
|
|
term_putx(lfanew);
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- NT 签名 + File Header ---- */
|
|
|
|
|
|
const u8 *nt = pe + lfanew;
|
|
|
|
|
|
if (*(const u32 *)nt != IMAGE_NT_SIGNATURE) {
|
|
|
|
|
|
term_puts_color("peinfo: 缺少 PE\\0\\0 签名\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const image_file_header *fh = (const image_file_header *)(nt + 4);
|
|
|
|
|
|
u16 machine = fh->machine;
|
|
|
|
|
|
u16 nsec = fh->number_of_sections;
|
|
|
|
|
|
u16 opt_size = fh->size_of_optional_header;
|
|
|
|
|
|
|
|
|
|
|
|
term_puts(" NT: PE\\0\\0 OK\r\n");
|
|
|
|
|
|
term_puts(" Machine: ");
|
|
|
|
|
|
term_putx(machine);
|
|
|
|
|
|
if (machine == 0x8664) term_puts_color(" (AMD64)\r\n", CMD_OK);
|
|
|
|
|
|
else if (machine == 0x014c) term_puts_color(" (i386)\r\n", CMD_OK);
|
|
|
|
|
|
else term_puts(" (未知)\r\n");
|
|
|
|
|
|
term_puts(" 节区数: ");
|
|
|
|
|
|
term_putu(nsec);
|
|
|
|
|
|
term_puts(" OptHdr 大小: ");
|
|
|
|
|
|
term_putu(opt_size);
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- Optional Header ---- */
|
|
|
|
|
|
if (opt_size < 2) {
|
|
|
|
|
|
term_puts_color("peinfo: Optional Header 过小\r\n", CMD_WARN);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ((u64)lfanew + 4 + sizeof(image_file_header) + opt_size > pe_size) {
|
|
|
|
|
|
term_puts_color("peinfo: Optional Header 越界\r\n", CMD_ERROR);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const void *opt = (const void *)(nt + 4 + sizeof(image_file_header));
|
|
|
|
|
|
u16 magic = *(const u16 *)opt;
|
|
|
|
|
|
int is_pe32_plus = (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC);
|
|
|
|
|
|
|
|
|
|
|
|
u64 img_base = 0;
|
|
|
|
|
|
u32 entry_rva = 0;
|
|
|
|
|
|
u32 img_size = 0;
|
|
|
|
|
|
u32 subsys = 0;
|
|
|
|
|
|
const image_data_directory *import_dir = 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (is_pe32_plus) {
|
|
|
|
|
|
if (opt_size >= sizeof(image_optional_header64)) {
|
|
|
|
|
|
const image_optional_header64 *oh = (const image_optional_header64 *)opt;
|
|
|
|
|
|
img_base = oh->image_base;
|
|
|
|
|
|
entry_rva = oh->address_of_entry_point;
|
|
|
|
|
|
img_size = oh->size_of_image;
|
|
|
|
|
|
subsys = oh->subsystem;
|
|
|
|
|
|
import_dir = &oh->data_directory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
|
|
|
|
|
}
|
|
|
|
|
|
term_puts_color(" 格式: PE32+ (64位 原生)\r\n", CMD_OK);
|
|
|
|
|
|
} else if (magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
|
|
|
|
if (opt_size >= sizeof(image_optional_header32)) {
|
|
|
|
|
|
const image_optional_header32 *oh = (const image_optional_header32 *)opt;
|
|
|
|
|
|
img_base = oh->image_base;
|
|
|
|
|
|
entry_rva = oh->address_of_entry_point;
|
|
|
|
|
|
img_size = oh->size_of_image;
|
|
|
|
|
|
subsys = oh->subsystem;
|
|
|
|
|
|
import_dir = &oh->data_directory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
|
|
|
|
|
}
|
|
|
|
|
|
term_puts_color(" 格式: PE32 (32位 解释器)\r\n", CMD_OK);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
term_puts_color("peinfo: 未知 Optional Header magic=", CMD_ERROR);
|
|
|
|
|
|
term_putx(magic);
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
term_puts(" ImageBase: ");
|
|
|
|
|
|
term_putx64(img_base);
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
term_puts(" EntryPoint: ");
|
|
|
|
|
|
term_putx(entry_rva);
|
|
|
|
|
|
term_puts(" (RVA)\r\n");
|
|
|
|
|
|
term_puts(" SizeOfImage: ");
|
|
|
|
|
|
term_putx(img_size);
|
|
|
|
|
|
term_puts(" (");
|
|
|
|
|
|
term_putu(img_size);
|
|
|
|
|
|
term_puts(" bytes)\r\n");
|
|
|
|
|
|
term_puts(" Subsystem: ");
|
|
|
|
|
|
term_putu(subsys);
|
|
|
|
|
|
term_puts(subsys == 3 ? " (CUI 控制台)\r\n" : "\r\n");
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- 节区表 ---- */
|
|
|
|
|
|
const image_section_header *secs = (const image_section_header *)
|
|
|
|
|
|
((const u8 *)opt + opt_size);
|
|
|
|
|
|
if ((u64)((const u8 *)secs - pe) + (u64)nsec * sizeof(image_section_header) > pe_size) {
|
|
|
|
|
|
term_puts_color("peinfo: 节区表越界,跳过\r\n", CMD_WARN);
|
|
|
|
|
|
nsec = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (nsec > 0) {
|
|
|
|
|
|
term_puts_color(" 节区表:\r\n", CMD_INFO);
|
|
|
|
|
|
term_puts(" Name VAddr VSize RawSize Flg\r\n");
|
|
|
|
|
|
for (int i = 0; i < nsec && i < 96; i++) {
|
|
|
|
|
|
char nm[9];
|
|
|
|
|
|
for (int j = 0; j < 8; j++) nm[j] = (char)secs[i].name[j];
|
|
|
|
|
|
nm[8] = 0;
|
|
|
|
|
|
term_puts(" ");
|
|
|
|
|
|
term_puts(nm);
|
|
|
|
|
|
int pad = 8 - str_len(nm);
|
|
|
|
|
|
for (int k = 0; k < pad; k++) term_putc(' ');
|
|
|
|
|
|
term_puts(" ");
|
|
|
|
|
|
term_putx(secs[i].virtual_address);
|
|
|
|
|
|
term_puts(" ");
|
|
|
|
|
|
term_putx(secs[i].virtual_size);
|
|
|
|
|
|
term_puts(" ");
|
|
|
|
|
|
term_putx(secs[i].size_of_raw_data);
|
|
|
|
|
|
term_puts(" ");
|
|
|
|
|
|
char flags[4];
|
|
|
|
|
|
flags[0] = (secs[i].characteristics & IMAGE_SCN_MEM_EXECUTE) ? 'X' : '-';
|
|
|
|
|
|
flags[1] = (secs[i].characteristics & IMAGE_SCN_MEM_READ) ? 'R' : '-';
|
|
|
|
|
|
flags[2] = (secs[i].characteristics & IMAGE_SCN_MEM_WRITE) ? 'W' : '-';
|
|
|
|
|
|
flags[3] = 0;
|
|
|
|
|
|
term_puts_color(flags, CMD_WARN);
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ---- 导入表 ---- */
|
|
|
|
|
|
if (import_dir && import_dir->virtual_address != 0 && import_dir->size != 0) {
|
|
|
|
|
|
u32 imp_rva = import_dir->virtual_address;
|
|
|
|
|
|
if (imp_rva >= pe_size) {
|
|
|
|
|
|
term_puts_color("peinfo: 导入表 RVA 越界\r\n", CMD_WARN);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
term_puts_color(" 导入表:\r\n", CMD_INFO);
|
|
|
|
|
|
int dll_count = 0;
|
|
|
|
|
|
int func_total = 0;
|
|
|
|
|
|
int truncated = 0;
|
|
|
|
|
|
for (int di = 0; !truncated; di++) {
|
|
|
|
|
|
u32 desc_off = imp_rva + (u32)di * sizeof(image_import_descriptor);
|
|
|
|
|
|
if ((u64)desc_off + sizeof(image_import_descriptor) > pe_size) break;
|
|
|
|
|
|
const image_import_descriptor *desc =
|
|
|
|
|
|
(const image_import_descriptor *)(pe + desc_off);
|
|
|
|
|
|
if (desc->name == 0 && desc->first_thunk == 0) break;
|
|
|
|
|
|
|
|
|
|
|
|
const char *dll_name = 0;
|
|
|
|
|
|
if (desc->name < pe_size) dll_name = (const char *)(pe + desc->name);
|
|
|
|
|
|
term_puts(" DLL: ");
|
|
|
|
|
|
term_puts_color(dll_name ? dll_name : "(?)", CMD_WARN);
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
|
|
|
|
|
|
u32 int_rva = desc->original_first_thunk;
|
|
|
|
|
|
u32 iat_rva = desc->first_thunk;
|
|
|
|
|
|
if (int_rva == 0) int_rva = iat_rva;
|
|
|
|
|
|
if (int_rva == 0) { dll_count++; continue; }
|
|
|
|
|
|
|
|
|
|
|
|
for (int fi = 0; ; fi++) {
|
|
|
|
|
|
u32 thunk_off = int_rva + (u32)fi * 4;
|
|
|
|
|
|
if ((u64)thunk_off + 4 > pe_size) break;
|
|
|
|
|
|
u32 thunk = *(const u32 *)(pe + thunk_off);
|
|
|
|
|
|
if (thunk == 0) break;
|
|
|
|
|
|
|
|
|
|
|
|
term_puts(" ");
|
|
|
|
|
|
if (thunk & 0x80000000u) {
|
|
|
|
|
|
term_puts("#ordinal ");
|
|
|
|
|
|
term_putu(thunk & 0xFFFF);
|
|
|
|
|
|
} else if (thunk + 2 < pe_size) {
|
|
|
|
|
|
const char *fn = (const char *)(pe + thunk + 2);
|
|
|
|
|
|
term_puts(fn);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
term_puts("(?)");
|
|
|
|
|
|
}
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
func_total++;
|
|
|
|
|
|
if (func_total > 64) {
|
|
|
|
|
|
term_puts_color(" ... (导入过多,截断)\r\n", CMD_DIM);
|
|
|
|
|
|
truncated = 1;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
dll_count++;
|
|
|
|
|
|
if (dll_count > 32) {
|
|
|
|
|
|
term_puts_color(" ... (DLL 过多,截断)\r\n", CMD_DIM);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
term_puts(" 共 ");
|
|
|
|
|
|
term_putu(dll_count);
|
|
|
|
|
|
term_puts(" 个 DLL, ");
|
|
|
|
|
|
term_putu(func_total);
|
|
|
|
|
|
term_puts(" 个导入函数\r\n");
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
term_puts_color(" 导入表: 无\r\n", CMD_DIM);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
term_puts_color("=== PE 头信息结束 ===\r\n", CMD_INFO);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* pe — 显式运行 PE 程序(等价于直接输入名字,但可在 help 中列出)
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
static void cmd_pe(const char *args) {
|
|
|
|
|
|
if (!*args) {
|
|
|
|
|
|
term_puts_color("用法: pe <程序名> (如 pe hello64)\r\n", CMD_INFO);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
exec_pe(args, args);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-28 14:35:14 +08:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* 命令解析与分发
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
static int g_should_exit = 0;
|
|
|
|
|
|
|
|
|
|
|
|
static void execute_command(const char *cmd) {
|
|
|
|
|
|
while (*cmd == ' ') cmd++;
|
|
|
|
|
|
if (*cmd == 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
/* 提取命令名 */
|
|
|
|
|
|
const char *args = cmd;
|
|
|
|
|
|
while (*args && *args != ' ') args++;
|
|
|
|
|
|
int cmd_name_len = (int)(args - cmd);
|
|
|
|
|
|
char name[64];
|
|
|
|
|
|
if (cmd_name_len >= 64) cmd_name_len = 63;
|
|
|
|
|
|
for (int i = 0; i < cmd_name_len; i++) name[i] = cmd[i];
|
|
|
|
|
|
name[cmd_name_len] = 0;
|
|
|
|
|
|
while (*args == ' ') args++;
|
|
|
|
|
|
|
|
|
|
|
|
/* 内置命令(不区分大小写) */
|
|
|
|
|
|
if (str_ieq(name, "help") || str_eq(name, "?")) { cmd_help(); return; }
|
|
|
|
|
|
if (str_ieq(name, "ver")) { cmd_ver(); return; }
|
|
|
|
|
|
if (str_ieq(name, "cls") || str_ieq(name, "clear")) { cmd_cls(); return; }
|
|
|
|
|
|
if (str_ieq(name, "dir") || str_ieq(name, "ls")) { cmd_dir(); return; }
|
|
|
|
|
|
if (str_ieq(name, "cd")) { cmd_cd(); return; }
|
|
|
|
|
|
if (str_ieq(name, "type") || str_ieq(name, "cat")) { cmd_type(args); return; }
|
|
|
|
|
|
if (str_ieq(name, "copy") || str_ieq(name, "cp")) { cmd_copy(args); return; }
|
|
|
|
|
|
if (str_ieq(name, "del") || str_ieq(name, "rm")) { cmd_del(args); return; }
|
|
|
|
|
|
if (str_ieq(name, "ren") || str_ieq(name, "mv")) { cmd_ren(args); return; }
|
|
|
|
|
|
if (str_ieq(name, "mkdir") || str_ieq(name, "md")) { cmd_mkdir(); return; }
|
|
|
|
|
|
if (str_ieq(name, "rmdir") || str_ieq(name, "rd")) { cmd_rmdir(); return; }
|
|
|
|
|
|
if (str_ieq(name, "echo")) { cmd_echo(args); return; }
|
|
|
|
|
|
if (str_ieq(name, "set")) { cmd_set(); return; }
|
|
|
|
|
|
if (str_ieq(name, "exit") || str_ieq(name, "quit")) {
|
|
|
|
|
|
term_puts_color("返回桌面...\r\n", CMD_DIM);
|
|
|
|
|
|
g_should_exit = 1;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-07-28 21:11:10 +08:00
|
|
|
|
if (str_ieq(name, "pe")) { cmd_pe(args); return; }
|
|
|
|
|
|
if (str_ieq(name, "peinfo")) { cmd_peinfo(args); return; }
|
2026-07-28 14:35:14 +08:00
|
|
|
|
if (str_ieq(name, "reboot")) {
|
|
|
|
|
|
term_puts_color("重启中...\r\n", CMD_WARN);
|
|
|
|
|
|
term_redraw_all();
|
|
|
|
|
|
for (u64 i = 0; i < 100000000ULL; i++) __asm__ volatile("pause");
|
|
|
|
|
|
/* 触发三重故障重启 */
|
|
|
|
|
|
__asm__ volatile("int $0x03");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* PE/EXE 执行:检查是否为可执行文件 */
|
|
|
|
|
|
int ext_type = has_exec_ext(name);
|
|
|
|
|
|
if (ext_type == 1) {
|
|
|
|
|
|
/* .exe/.com → PE 执行 */
|
|
|
|
|
|
exec_pe(name, cmd);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ext_type == 2) {
|
|
|
|
|
|
/* .bat → 批处理(暂不支持) */
|
|
|
|
|
|
term_puts_color("批处理文件 (.bat) 暂不支持\r\n", CMD_WARN);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
/* 无扩展名 → 尝试自动补 .exe 执行 */
|
|
|
|
|
|
if (cmd_name_len > 0 && name[0] != '.') {
|
|
|
|
|
|
exec_pe(name, cmd);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 未知命令 */
|
|
|
|
|
|
term_puts_color("'", CMD_ERROR);
|
|
|
|
|
|
term_puts_color(name, CMD_ERROR);
|
|
|
|
|
|
term_puts_color("' 不是内部或外部命令,也不是可运行的程序。\r\n", CMD_ERROR);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-31 13:53:49 +08:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* AUTOEXEC.BAT — 启动时自动执行根目录下的批处理(每行一条命令)
|
|
|
|
|
|
* 空行与以 :: 或 rem 开头的行跳过。
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
static void run_autoexec(void) {
|
|
|
|
|
|
if (!f32_blk_read) return;
|
|
|
|
|
|
char name11[11];
|
|
|
|
|
|
if (f32_name_to_83("AUTOEXEC.BAT", name11) != 0) return;
|
|
|
|
|
|
u8 *data = 0; u32 size = 0;
|
|
|
|
|
|
if (f32_read_root_file(name11, &data, &size) != 0) {
|
|
|
|
|
|
logl("[cmd] no AUTOEXEC.BAT");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
logl("[cmd] running AUTOEXEC.BAT");
|
|
|
|
|
|
term_puts_color("执行 AUTOEXEC.BAT ...\r\n", CMD_DIM);
|
|
|
|
|
|
|
|
|
|
|
|
u32 pos = 0;
|
|
|
|
|
|
while (pos < size && !g_should_exit) {
|
|
|
|
|
|
/* 取一行 */
|
|
|
|
|
|
char line[128];
|
|
|
|
|
|
int li = 0;
|
|
|
|
|
|
while (pos < size && data[pos] != '\n' && data[pos] != '\r' && li < 127) {
|
|
|
|
|
|
line[li++] = (char)data[pos++];
|
|
|
|
|
|
}
|
|
|
|
|
|
while (pos < size && (data[pos] == '\n' || data[pos] == '\r')) pos++;
|
|
|
|
|
|
line[li] = 0;
|
|
|
|
|
|
/* 去前导空格 */
|
|
|
|
|
|
const char *p = line;
|
|
|
|
|
|
while (*p == ' ' || *p == '\t') p++;
|
|
|
|
|
|
if (!*p) continue;
|
|
|
|
|
|
if (p[0] == ':' && p[1] == ':') continue;
|
|
|
|
|
|
if ((p[0] == 'r' || p[0] == 'R') && (p[1] == 'e' || p[1] == 'E')
|
|
|
|
|
|
&& (p[2] == 'm' || p[2] == 'M') && (p[3] == ' ' || p[3] == 0)) continue;
|
|
|
|
|
|
|
|
|
|
|
|
/* 回显并执行 */
|
|
|
|
|
|
term_puts_color(PROMPT, CMD_PROMPT);
|
|
|
|
|
|
term_puts(p);
|
|
|
|
|
|
term_puts("\r\n");
|
|
|
|
|
|
term_redraw_all();
|
|
|
|
|
|
execute_command(p);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-28 14:35:14 +08:00
|
|
|
|
/* ============================================================
|
|
|
|
|
|
* 主入口
|
|
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
__attribute__((visibility("default")))
|
|
|
|
|
|
void dsk_entry(const dsk_boot_context *ctx) {
|
|
|
|
|
|
__asm__ volatile("cli");
|
|
|
|
|
|
logl("[cmd] boot");
|
|
|
|
|
|
|
|
|
|
|
|
if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) {
|
|
|
|
|
|
logl("[cmd] bad context");
|
|
|
|
|
|
for (;;) __asm__("hlt");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fb_a = ctx->framebuffer_address;
|
|
|
|
|
|
fb_w = ctx->framebuffer_width;
|
|
|
|
|
|
fb_h = ctx->framebuffer_height;
|
|
|
|
|
|
fb_p = ctx->framebuffer_pitch;
|
|
|
|
|
|
|
|
|
|
|
|
g_kernel_api = ctx->dkm_kernel_api;
|
|
|
|
|
|
g_boot_ctx = ctx;
|
|
|
|
|
|
|
|
|
|
|
|
/* 获取 block 设备 read/write */
|
|
|
|
|
|
{
|
|
|
|
|
|
u64 api = ctx->dkm_kernel_api;
|
|
|
|
|
|
u64 blk = *(u64 *)(api + 0xA8);
|
|
|
|
|
|
f32_block_read_fn bread = blk ? (f32_block_read_fn)*(u64 *)(blk + 16) : 0;
|
|
|
|
|
|
f32_block_write_fn bwrite = blk ? (f32_block_write_fn)*(u64 *)(blk + 24) : 0;
|
|
|
|
|
|
f32_init(bread, bwrite);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (f32_blk_read) logl("[cmd] block_read ok");
|
|
|
|
|
|
if (f32_blk_write) logl("[cmd] block_write ok");
|
|
|
|
|
|
|
|
|
|
|
|
/* 获取 PE 服务(reserved[4]) */
|
|
|
|
|
|
{
|
|
|
|
|
|
u64 pe_addr = ctx->reserved[4];
|
|
|
|
|
|
if (pe_addr) {
|
|
|
|
|
|
const pe_service *pe = (const pe_service *)pe_addr;
|
|
|
|
|
|
if (pe->magic == PE_SERVICE_MAGIC) {
|
|
|
|
|
|
g_pe_svc = pe;
|
|
|
|
|
|
logl("[cmd] PE service ok");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
logl("[cmd] PE service magic mismatch");
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
logl("[cmd] PE service not available");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* dev_mode 串口镜像 */
|
|
|
|
|
|
if (ctx->reserved[3] == 1) {
|
|
|
|
|
|
g_serial_mirror = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 初始化渲染上下文 */
|
|
|
|
|
|
du_context_init(&g_ctx, fb_a, fb_w, fb_h, fb_p);
|
|
|
|
|
|
term_init_pos();
|
|
|
|
|
|
term_clear_buf();
|
|
|
|
|
|
term_redraw_all();
|
|
|
|
|
|
|
|
|
|
|
|
/* 启动 banner */
|
|
|
|
|
|
term_puts_color("Deshab CMD [Version 0.1.0]\r\n", CMD_OK);
|
|
|
|
|
|
term_puts_color("(c) 2026 Deshab Project. PE/EXE 兼容层 by UTSM.\r\n", CMD_DIM);
|
|
|
|
|
|
term_putc('\r');
|
|
|
|
|
|
term_putc('\n');
|
|
|
|
|
|
if (g_pe_svc) {
|
|
|
|
|
|
term_puts_color("PE 兼容层已就绪 (PE32+ 原生 + PE32 解释器)\r\n", CMD_OK);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
term_puts_color("警告: PE 兼容层不可用\r\n", CMD_WARN);
|
|
|
|
|
|
}
|
|
|
|
|
|
term_putc('\r');
|
|
|
|
|
|
term_putc('\n');
|
|
|
|
|
|
term_redraw_all();
|
|
|
|
|
|
|
2026-07-31 13:53:49 +08:00
|
|
|
|
/* AUTOEXEC.BAT 自动执行(存在则逐行运行) */
|
|
|
|
|
|
run_autoexec();
|
|
|
|
|
|
if (g_should_exit) return;
|
|
|
|
|
|
|
2026-07-28 14:35:14 +08:00
|
|
|
|
/* 主循环 */
|
|
|
|
|
|
int shift = 0;
|
|
|
|
|
|
int e0 = 0;
|
|
|
|
|
|
u64 blink_start = rdtsc_cmd();
|
|
|
|
|
|
g_should_exit = 0;
|
|
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
|
/* 绘制提示符 */
|
|
|
|
|
|
term_puts_color(PROMPT, CMD_PROMPT);
|
|
|
|
|
|
input_len = 0;
|
|
|
|
|
|
input_cursor = 0;
|
|
|
|
|
|
input_buf[0] = 0;
|
|
|
|
|
|
int prompt_len = str_len(PROMPT);
|
|
|
|
|
|
cur_col = prompt_len;
|
|
|
|
|
|
cur_visible = 1;
|
|
|
|
|
|
redraw_input_line();
|
|
|
|
|
|
|
|
|
|
|
|
int cmd_done = 0;
|
|
|
|
|
|
while (!cmd_done && !g_should_exit) {
|
|
|
|
|
|
/* 光标闪烁 */
|
|
|
|
|
|
if (g_tsc_per_ms) {
|
|
|
|
|
|
u64 now = rdtsc_cmd();
|
|
|
|
|
|
u64 elapsed = now - blink_start;
|
|
|
|
|
|
int want_vis = (elapsed / (g_tsc_per_ms * 530)) & 1;
|
|
|
|
|
|
if (want_vis != cur_visible) {
|
|
|
|
|
|
if (want_vis) term_redraw_cursor();
|
|
|
|
|
|
else term_clear_cursor();
|
|
|
|
|
|
cur_visible = want_vis;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
u8 st = inb(0x64);
|
|
|
|
|
|
if (!(st & 1)) { __asm__("pause"); continue; }
|
|
|
|
|
|
u8 data = inb(0x60);
|
|
|
|
|
|
if (st & 0x20) continue; /* 忽略鼠标数据 */
|
|
|
|
|
|
u8 sc = data;
|
|
|
|
|
|
|
|
|
|
|
|
blink_start = rdtsc_cmd();
|
|
|
|
|
|
if (!cur_visible) { cur_visible = 1; term_redraw_cursor(); }
|
|
|
|
|
|
|
|
|
|
|
|
if (sc == 0xE0) { e0 = 1; continue; }
|
|
|
|
|
|
if (sc == 0x2A || sc == 0x36) { shift = 1; continue; }
|
|
|
|
|
|
if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; }
|
|
|
|
|
|
|
|
|
|
|
|
if (sc & 0x80) {
|
|
|
|
|
|
/* 按键释放 */
|
|
|
|
|
|
e0 = 0;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (e0) {
|
|
|
|
|
|
if (sc == 0x4B) cursor_left();
|
|
|
|
|
|
else if (sc == 0x4D) cursor_right();
|
|
|
|
|
|
else if (sc == 0x47) cursor_home();
|
|
|
|
|
|
else if (sc == 0x4F) cursor_end();
|
|
|
|
|
|
else if (sc == 0x53) { /* Delete */ }
|
|
|
|
|
|
e0 = 0;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (sc == 0x1C) {
|
|
|
|
|
|
/* Enter — 提交命令 */
|
|
|
|
|
|
term_clear_cursor();
|
|
|
|
|
|
input_buf[input_len] = 0;
|
|
|
|
|
|
term_putc('\r');
|
|
|
|
|
|
term_putc('\n');
|
|
|
|
|
|
execute_command(input_buf);
|
|
|
|
|
|
input_len = 0;
|
|
|
|
|
|
input_cursor = 0;
|
|
|
|
|
|
input_buf[0] = 0;
|
|
|
|
|
|
cmd_done = 1;
|
|
|
|
|
|
term_redraw_all();
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (sc == 0x0E) { delete_char_back(); continue; }
|
|
|
|
|
|
if (sc == 0x01) {
|
|
|
|
|
|
/* Esc — 返回桌面 */
|
|
|
|
|
|
logl("[cmd] esc -> return to DSK");
|
|
|
|
|
|
g_should_exit = 1;
|
|
|
|
|
|
cmd_done = 1;
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (sc == 0x15 && !shift) { kill_line(); continue; }
|
|
|
|
|
|
|
|
|
|
|
|
char c = scan_to_ascii(sc, shift);
|
|
|
|
|
|
if (c && c >= 32 && c <= 126) {
|
|
|
|
|
|
insert_char(c);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (g_should_exit) break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logl("[cmd] exit");
|
|
|
|
|
|
}
|