怎样将DES加密、解密的汇编程序转变为C语言的程序?

tydyg 2008-03-23 04:20:26
哪位大侠精通汇编和C51,看能否将下面一段DES加密、解密的函数转变为C51的,函数能有入口参数、出口参数,需要的密钥参数。
这段汇编函数我在KEIL里面编译通过了。
;---------------------------------------------------------------
; DES加密解密算法
;---------------------------------------------------------------
NAME DES64



; PUBLIC DESMETHOD

;DESCONST SEGMENT CODE
;DES64 SEGMENT CODE


; RSEG DESCONST
;---------------------------------------------------------------
; 初始置换表,共64B
;---------------------------------------------------------------
Data_IP: DB 58,50,42,34,26,18,10,2
DB 60,52,44,36,28,20,12,4
DB 62,54,46,38,30,22,14,6
DB 64,56,48,40,32,24,16,8
DB 57,49,41,33,25,17, 9,1
DB 59,51,43,35,27,19,11,3
DB 61,53,45,37,29,21,13,5
DB 63,55,47,39,31,23,15,7

;---------------------------------------------------------------
; 终止置换表,共64B
;---------------------------------------------------------------
Data_FP:
DB 40,8,48,16,56,24,64,32
DB 39,7,47,15,55,23,63,31
DB 38,6,46,14,54,22,62,30
DB 37,5,45,13,53,21,61,29
DB 36,4,44,12,52,20,60,28
DB 35,3,43,11,51,19,59,27
DB 34,2,42,10,50,18,58,26
DB 33,1,41, 9,49,17,57,25

;---------------------------------------------------------------
; 扩充置换表,共48B
;---------------------------------------------------------------
DATA_EP: DB 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9
DB 8, 9,10,11,12,13,12,13,14,15,16,17
DB 16,17,18,19,20,21,20,21,22,23,24,25
DB 24,25,26,27,28,29,28,29,30,31,32, 1
;---------------------------------------------------------------
; PBOX置换表,共32B
;---------------------------------------------------------------
Data_PBox: DB 16,7,20,21,29,12,28,17, 1,15,23,26, 5,18,31,10
DB 2,8,24,14,32,27, 3, 9,19,13,30, 6,22,11, 4,25
;---------------------------------------------------------------
; SBOX置换表,共256B
;---------------------------------------------------------------
Data_SBox: DB 0E4H,0D1H,02FH,0B8H,03AH,06CH,059H,007H
DB 00FH,074H,0E2H,0D1H,0A6H,0CBH,095H,038H
DB 041H,0E8H,0D6H,02BH,0FCH,097H,03AH,050H
DB 0FCH,082H,049H,017H,05BH,03EH,0A0H,06DH
DB 0F1H,08EH,06BH,034H,097H,02DH,0C0H,05AH
DB 03DH,047H,0F2H,08EH,0C0H,01AH,069H,0B5H
DB 00EH,07BH,0A4H,0D1H,058H,0C6H,093H,02FH
DB 0D8H,0A1H,03FH,042H,0B6H,07CH,005H,0E9H
DB 0A0H,09EH,063H,0F5H,01DH,0C7H,0B4H,028H
DB 0D7H,009H,034H,06AH,028H,05EH,0CBH,0F1H
DB 0D6H,049H,08FH,030H,0B1H,02CH,05AH,0E7H
DB 01AH,0D0H,069H,087H,04FH,0E3H,0B5H,02CH
DB 07DH,0E3H,006H,09AH,012H,085H,0BCH,04FH
DB 0D8H,0B5H,06FH,003H,047H,02CH,01AH,0E9H
DB 0A6H,090H,0CBH,07DH,0F1H,03EH,052H,084H
DB 03FH,006H,0A1H,0D8H,094H,05BH,0C7H,02EH
DB 02CH,041H,07AH,0B6H,085H,03FH,0D0H,0E9H
DB 0EBH,02CH,047H,0D1H,050H,0FAH,039H,086H
DB 042H,01BH,0ADH,078H,0F9H,0C5H,063H,00EH
DB 0B8H,0C7H,01EH,02DH,06FH,009H,0A4H,053H
DB 0C1H,0AFH,092H,068H,00DH,034H,0E7H,05BH
DB 0AFH,042H,07CH,095H,061H,0DEH,00BH,038H
DB 09EH,0F5H,028H,0C3H,070H,04AH,01DH,0B6H
DB 043H,02CH,095H,0FAH,0BEH,017H,060H,08DH
DB 04BH,02EH,0F0H,08DH,03CH,097H,05AH,061H
DB 0D0H,0B7H,049H,01AH,0E3H,05CH,02FH,086H
DB 014H,0BDH,0C3H,07EH,0AFH,068H,005H,092H
DB 06BH,0D8H,014H,0A7H,095H,00FH,0E2H,03CH
DB 0D2H,084H,06FH,0B1H,0A9H,03EH,050H,0C7H
DB 01FH,0D8H,0A3H,074H,0C5H,06BH,00EH,092H
DB 07BH,041H,09CH,0E2H,006H,0ADH,0F3H,058H
DB 021H,0E7H,04AH,08DH,0FCH,090H,035H,06BH
;---------------------------------------------------------------
; 密钥第一置换表,共56B
; 56-56位密钥置换表
;---------------------------------------------------------------
KEY_PC1: DB 50, 43, 36, 29, 22, 15, 08, 01
DB 51, 44, 37, 30, 23, 16, 09, 02
DB 52, 45, 38, 31, 24, 17, 10, 03
DB 53, 46, 39, 32, 56, 49, 42, 35
DB 28, 21, 14, 07, 55, 48, 41, 34
DB 27, 20, 13, 06, 54, 47, 40, 33
DB 26, 19, 12, 05, 25, 18, 11, 04
;64-56位密钥置换表
; DB 57,49,41,33,25,17, 9, 1,58,50,42,34,26,18
; DB 10, 2,59,51,43,35,27,19,11, 3,60,52,44,36
; DB 63,55,47,39,31,23,15, 7,62,54,46,38,30,22
; DB 14, 6,61,53,45,37,29,21,13, 5,28,20,12, 4

;---------------------------------------------------------------
; 密钥第二置换表,共48B
;---------------------------------------------------------------
KEY_PC2: DB 14,17,11,24,01,05,03,28,15,06,21,10
DB 23,19,12,04,26,08,16,07,27,20,13,02
DB 41,52,31,37,47,55,30,40,51,45,33,48
DB 44,49,39,56,34,53,46,42,50,36,29,32

;---------------------------------------------------------------
; 密钥16次循环移位次数表,共16B
;---------------------------------------------------------------
SHIFTBITS: DB 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1

; RSEG DES64
USING 0
...全文
446 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
cjnmm 2008-05-19
  • 打赏
  • 举报
回复
你是在哪里调试的?是在KEILC51里么?怎么调试的,我不会啊,教教我吧,QQ373625819
用户 昵称 2008-04-17
  • 打赏
  • 举报
回复
用 0102030405060708 为密钥,以1122334455667788为数据,俺所有的工具直接加密都是 6C5E94DCADD39F1D

俺也听说des有cbc模式和ecb模式,俺不知道俺用的是哪种模式,如果您有一种算法和俺的一样,不妨就用那种。
tydyg 2008-04-17
  • 打赏
  • 举报
回复
我找到好几个DES加密算法的例程,独自用它们自己加密,然后解密都可以恢复原数据,可是他们加密后产生的数据都不一样,我只能利用这个51汇编的DES算法,我要用430单片机,本人对51和430汇编都不太清楚,抱着书看了好多天,还是丈二和尚摸不着头脑;怎样才能把它转为430的汇编被C调用的函数呢?望大侠赐教!!
用户 昵称 2008-04-16
  • 打赏
  • 举报
回复
加密后的结果都不一样?那你找一个pc上运行des工具的进行结果比较一下,应该有一样的。
tydyg 2008-04-16
  • 打赏
  • 举报
回复
上述的DES数法我找到好几个版本的,用它们进行加密和解密都可以,但是加密后生成的结果都不一样。我这个加密算法是以前别人加密好的数据,现在我要用它进行对数据解密,没有其他办法,只有利用这个汇编程序改为可以被C调用的,在430上面用,那位大侠有高招帮俺指点一下,小弟先谢谢了!!
tydyg 2008-03-25
  • 打赏
  • 举报
回复
这个程序我试过了,好像有问题,不能加密和解密,加密过程就不对。我主要想用在430单片机上,哪位老哥有能在430上使用的DES加密算法程序支援一下!!我的QQ:513845925
用户 昵称 2008-03-24
  • 打赏
  • 举报
回复
http://www.smth.edu.cn/bbsanc.php?path=%2Fgroups%2Fsci.faq%2FMCUTech%2Fcodelist%2FM.1073305018.30

网上针对51的就有的事。
tydyg 2008-03-24
  • 打赏
  • 举报
回复
没有找到现成的C源程序啊?哪位有支援一下啊?小弟先谢谢了!!
用户 昵称 2008-03-23
  • 打赏
  • 举报
回复
你要在51上实现软des?占用的空间肯定很多。
用户 昵称 2008-03-23
  • 打赏
  • 举报
回复
有现成的源程序,不如直接使用吧,汇编转C实现已有算法,有点浪费精力。
tydyg 2008-03-23
  • 打赏
  • 举报
回复
DES22: ANL A,#0FH ;如R0==1则A=SBOX表值低4位
DES23: MOV 1CH,A ;1CH的低4位存放6位源经SBOX变换后的结果
MOV A,R2
ANL A,#1
PUSH ACC
MOV A,R2
ANL A,#0FEH
RR A
ADD A,#10H
MOV R0,A ;R0=目标绝对地址
POP ACC
JNZ DES24 ;如R2%2==0 则R0(H4BIT)=(1CH)
MOV A,1CH
SWAP A ;A=SBOX结果
JMP DES25
DES24: MOV A,@R0
ORL A,1CH
DES25: MOV @R0,A
INC R2
CJNE R2,#8,DES17
; CJNE R2,#8,DES33 ;SBOX变换完毕结果在10H..13H
; JMP DES34
; DES33:
; JMP DES17
;进行PBOX变换
; DES34:
MOV R0,#10H
MOV R1,#14H
MOV DPTR,#DATA_PBOX
MOV R2,#0
DES26: MOV R3,#8
DES27: CALL CALA ;进行置换
DJNZ R3,DES27
INC R2
CJNE R2,#4,DES26 ;PBOX置换完毕结果在14H..17H
MOV R0,#14H ;14H..17H结果和8单元引用的起始地址相异或,结果放入14..17H中
MOV R1,8
MOV R2,#4
DES28: MOV A,@R1
XRL A,@R0
MOV @R0,A
INC R0
INC R1
DJNZ R2,DES28
MOV A,8 ;将源码的后32位移到前32位
ADD A,#4
MOV R0,A
MOV R1,8
MOV R2,#4
CALL MOVRAMBYTE
MOV R0,#14H ;将14H..17H移到源码的后32位
MOV A,8
ADD A,#4
MOV R1,A
MOV R2,#4
CALL MOVRAMBYTE
INC R6
CJNE R6,#10H,DES32 ;16次迭代运算完毕
MOV A,10 ;判断加密解密
JNZ DES29 ;加密跳转
MOV R0,8
MOV A,8
ADD A,#4
MOV R1,A
MOV R2,#4
CALL XCHRAMBYTE
JMP DES29
DES32: JMP DES6
DES29: MOV R0,8 ;对源作逆置换到10H..17H
MOV R1,#10H
MOV DPTR,#DATA_FP
MOV R2,#0
DES30: MOV R3,#8
DES31: CALL CALA ;进行置换
DJNZ R3,DES31
INC R2
CJNE R2,#8,DES30
MOV R0,#10H ;将置换后的数转移到源地址
MOV R1,8
MOV R2,#8
CALL MOVRAMBYTE
RET
;ENDP

END
tydyg 2008-03-23
  • 打赏
  • 举报
回复
DES11: MOV A,R6
JZ DES13 ;是解密过程,解密时第一轮不移位
MOV A,9 ;9存放56位密钥起始地址
ADD A,#6
MOV R0,A ;R0=56位密钥字节6
MOV B,A ;B=56位密钥字节6
MOV A,#0 ;第0位
CALL GETBIT ;R7=为56位密钥55位
PUSH AR7 ;保存R7
MOV R4,#7 ;循环变量 56位=7字节 循环次数=7 从0到6
DES12: MOV A,@R0
RR A
MOV @R0,A ;右移一位
MOV A,R0
MOV R1,A
DEC R1
MOV A,@R1 ;A=上一字节内容
ANL A,#1
MOV R7,A ;R7=上一字节BIT0
MOV B,R0
MOV A,#7
CALL SETBIT ;本字节BIT7=R7
DEC R0
DJNZ R4,DES12 ;循环右移完毕
MOV A,9
ADD A,#3
MOV R0,A
MOV A,@R0 ;A=密钥3字节
ANL A,#8
MOV R7,A ;R7=密钥3字节3位即移位后的28位
MOV B,9
MOV A,#7
CALL SETBIT ;移位后56位密钥第0位=R7
POP AR7 ;R7:移位前为56位密钥55位
MOV A,9
ADD A,#3
MOV B,A
MOV A,#3
CALL SETBIT ;移位后的密钥28位=R7,完成一次KEY的循环右移
MOV A,#10H
CLR C
SUBB A,R6
MOV DPTR,#SHIFTBITS
MOVC A,@A+DPTR ;A=本次循环移位次数
INC R5
CJNE A,5,DES11 ;全部循环完毕获得本次运算密钥

DES13: MOV R0,9 ;R0=源表基地址
MOV R1,#16H ;R1=目的表基地址
MOV DPTR,#KEY_PC2 ;DPTR=位置换表基地址
MOV R2,#0 ;R2=字节循环变量0..7
DES14: MOV R3,#8 ;R3=位循环变量8..1
DES15: CALL CALA ;进行置换
DJNZ R3,DES15
INC R2
CJNE R2,#6,DES14 ;对本次运算密钥作缩位运算获得48位子金钥,结果存到16H..1BH
MOV R0,#10H ;将16H..1BH的内容和10H..15H的内容相异或然后放到16H..1BH中10H..15H的内容可以抛弃
MOV R1,#16H
MOV R2,#6
DES16: MOV A,@R0
XRL A,@R1
MOV @R1,A
INC R0
INC R1
DJNZ R2,DES16

MOV DPTR,#DATA_SBOX ;对16H..1BH的内容作S-BOX置换结果放入10H..13H
MOV R2,#0 ;循环变量 从0..7
DES17: MOV 1CH,#0 ;(1CH)=0=SBOX表寻址单元
MOV R3,#0 ;循环变量 从0..5
DES18: MOV A,R2
MOV B,#6
MUL AB ;A=R2*6
ADD A,R3 ;A=R2*6+R3
PUSH ACC
ANL A,#0F8H
RR A
RR A
RR A ;A=(R2*6+R3)/8
ADD A,#16H
MOV B,A ;B存放源表地址字节索引
POP ACC
ANL A,#7
CPL A
INC A
ADD A,#7 ;A=7-(R2*6+R3)%8 存放源表位索引
CALL GETBIT ;R7=源表的B字节A位
CJNE R3,#0,DES19
MOV R0,#5 ;如R3==0,R0=SBOX表寻址单元1位
JMP DES21
DES19: CJNE R3,#5,DES20
MOV R0,#4 ;如R3==5,R0=SBOX表寻址单元0位
JMP DES21
DES20: MOV A,#4
CLR C
SUBB A,R3
MOV R0,A ;如0<R3<5,R0=SBOX寻址单元8-R3位
DES21: MOV B,#1CH ;B=SBOX寻址单元地址=1CH
MOV A,R0
CALL SETBIT
INC R3
CJNE R3,#6,DES18
MOV A,1CH ;计算SBOX寻址单元
ANL A,#1
PUSH ACC
MOV A,1CH
ANL A,#0FEH
RR A
MOV 1CH,A
MOV A,R2 ;第R2个6BIT
MOV B,#32
MUL AB
ADD A,1CH ;(1CH)=SBOX地址?
MOVC A,@A+DPTR ;A=查SBOX表值
POP AR0
CJNE R0,#0,DES22
ANL A,#0F0H
SWAP A ;如R0==0则A=SBOX表值高4位
JMP DES23
tydyg 2008-03-23
  • 打赏
  • 举报
回复
;---------------------------------------------------------------
;根据位置换表进行位置换
;入口
; R0: 源表起始地址
; R1: 目的表起始地址
; R2: 目的子节地址索引0..N
; R3: 目的子节位索引8..1
; DPTR: 位置换表地址
;---------------------------------------------------------------
CALA: ; PROC
PUSH B
PUSH AR7
MOV A,R2 ;A=R2目的子节地址索引0..N
INC A ;A=R2+1
RL A
RL A
RL A
ANL A,#0F8H ;A=(R2+1)*8
CLR C
SUBB A,R3 ;A=R2*8-R3
MOVC A,@A+DPTR

DEC A ;A=源表位地址
PUSH ACC
ANL A,#0F8H
RR A
RR A
RR A ;A=源表字节地址索引=源表位地址/8
ADD A,R0
MOV B,A ;B=源字节绝对地址
POP ACC ;A=源表位地址
ANL A,#7
CPL A
INC A
ADD A,#7 ;源字节位索引A=7-(A%8)
CALL GETBIT ;R7为获得的置换位
MOV A,R1
ADD A,R2 ;A=目的字节地址
MOV B,A ;B=目的字节地址
MOV A,R3
DEC A ;A=目的字节位索引
CALL SETBIT
POP AR7
POP B
RET
;ENDP
;--------------------------------------
;DES64加解密子程序
;入口:
; 8: 存储源表和目的表基地址
; 9: 存储密钥地址
; 10: 存储加密解密方式0--解密1-加密
;--------------------------------
DESMETHOD:; PROC
MOV R0,8 ;对源进行初始置换,R0=源表基地址
MOV R1,#10H ;R1=目的表基地址
MOV DPTR,#DATA_IP ;DPTR=位置换表基地址
MOV R2,#0 ;R2=字节循环变量0..7
DES1: MOV R3,#8 ;R3=位循环变量8..1
DES2: CALL CALA ;进行置换
DJNZ R3,DES2
INC R2
CJNE R2,#8,DES1

MOV A,10
JNZ DES3 ;加密跳转

MOV R0,#10H ;(10)=0是解密过程将初始置换后的64位数据高低32位互换
MOV R1,#14H
MOV R2,#4
CALL XCHRAMBYTE



DES3: MOV R0,#10H ;将置换后的数转移到源地址
MOV R1,8
MOV R2,#8
CALL MOVRAMBYTE ;初始置换完毕,结果仍存到原位置(8)+0..(8)+7

MOV R0,9 ;对密钥进行缩位变换,R0=源表基地址
MOV R1,#10H ;R1=目的表基地址
MOV DPTR,#KEY_PC1 ;DPTR=位置换表基地址
MOV R2,#0 ;R2=字节循环变量0..7
DES4: MOV R3,#8 ;R3=位循环变量8..1
DES5: CALL CALA ;进行置换
DJNZ R3,DES5
INC R2
CJNE R2,#7,DES4

MOV R0,#10H ;将置换后的数转移到源地址
MOV R1,9
MOV R2,#7
CALL MOVRAMBYTE ;缩位置换完毕,结果仍存到原位置(9)+0..(9)+6

MOV R6,#0 ;进行16次迭代,R6=16次迭代循环变量

DES6: MOV A,8 ;对8字节数据的右32位作扩充冲变换扩充到48位,结果存到10H..15H中
ADD A,#4
MOV R0,A ;R0=源表基地址
MOV R1,#10H ;R1=目的表基地址
MOV DPTR,#DATA_EP ;DPTR=位置换表基地址
MOV R2,#0 ;R2=字节循环变量0..7
DES7: MOV R3,#8 ;R3=位循环变量8..1
DES8: CALL CALA ;进行置换
DJNZ R3,DES8
INC R2
CJNE R2,#6,DES7 ;对56位密钥进行移位结果仍在原位置(9)+0..(9)+6

MOV R5,#0 ;循环变量 循环次数=每一次需要移位的次数

MOV A,10
JZ DES11 ;解密跳转

DES9: MOV B,9 ;是加密过程
MOV A,#7
CALL GETBIT ;R7=56位密钥第0位
PUSH AR7 ;保存R7
MOV R0,9
MOV R4,#0 ;循环变量 56位=7字节 循环次数=7 从0到6
DES10: MOV A,@R0
RL A
MOV @R0,A ;左移一?
MOV A,R0
MOV R1,A
INC R1
MOV A,@R1 ;A=下一字节内容
ANL A,#80H
MOV R7,A ;R7=下一字节BIT7
MOV B,R0
MOV A,#0
CALL SETBIT ;本字节BIT0=R7
INC R0
INC R4
CJNE R4,#7,DES10 ;循环左移完毕
MOV A,9
ADD A,#3
MOV R0,A
MOV A,@R0 ;A=密钥3字节
ANL A,#10H
MOV R7,A ;R7=密钥3字节4位即移位后的27位
MOV A,9
ADD A,#6
MOV B,A
MOV A,#0
CALL SETBIT ;移位后的密钥55位=移位后的密钥27位
POP AR7 ;移位前密钥0位
MOV A,9
ADD A,#3
MOV B,A
MOV A,#4
CALL SETBIT ;移位后的密钥27位=R7,完成一次KEY的循环左移
MOV A,R6
MOV DPTR,#SHIFTBITS
MOVC A,@A+DPTR ;A=本次迭代循环次数
INC R5
CJNE A,5,DES9 ;全部循环完毕获得本次运算密钥
JMP DES13 ;本次运算密钥计算完毕
tydyg 2008-03-23
  • 打赏
  • 举报
回复
;---------------------------------------------------------------
; 字节位置索引表,共48B
;---------------------------------------------------------------
BITPOSTBL: DB 1,2,4,8,10H,20H,40H,80H
;---------------------------------------------------------------
;设置字节某一位
;入口:
; B: 存储目的地址
; A: 存储第几位0..7
; R7: 存储要设置的值
;---------------------------------------------------------------
SETBIT: ; PROC
PUSH AR0
PUSH DPH
PUSH DPL ;保护现场
MOV R0,B ;R0存储源地址
MOV DPTR,#BITPOSTBL
MOVC A,@A+DPTR
CJNE R7,#0,SETBIT1
CPL A
ANL A,@R0
JMP SETBIT2
SETBIT1: ORL A,@R0
SETBIT2: MOV @R0,A
POP DPL ;恢复现场
POP DPH
POP AR0
RET
;ENDP
;---------------------------------------------------------------
;获得源地址字节的一位值
;入口:
; B: 存储源地址
; A: 存储第几位0..7
;出口: R7: 返回第几位的值
;---------------------------------------------------------------
GETBIT: ; PROC
PUSH AR0
PUSH DPH
PUSH DPL ;保护现场
MOV R0,B ;R0存储源地址
MOV DPTR,#BITPOSTBL
MOVC A,@A+DPTR
ANL A,@R0
MOV R7,A
POP DPL ;恢复现场
POP DPH
POP AR0
RET
;ENDP
;---------------------------------------------------------------
;移动程序数据到内存
;入口:
; R0: 存储源地址起始字节索引
; DPTR: 存储源地址
; R1: 存储目的地址
; R2: 存储移动长度
;占用资源:
; A,R0,R1,R2,DPTR
;---------------------------------------------------------------
MOVCODEBYTE: ; PROC
MOV A,R0
MOVC A,@A+DPTR
MOV @R1,A
INC R0
INC R1
DJNZ R2,MOVCODEBYTE
RET
;---------------------------------------------------------------
;移动内存块
;入口:
; R0: 存储源地址
; R1: 存储目的地址
; R2: 存储移动长度
;---------------------------------------------------------------
MOVRAMBYTE:; PROC
MOV A,@R0
MOV @R1,A
INC R0
INC R1
DJNZ R2,MOVRAMBYTE
RET
;---------------------------------------------------------------
;交换内存块
;入口:
; R0: 存储源地址
; R1: 存储目的地址
; R2: 存储交换长度
;---------------------------------------------------------------
XCHRAMBYTE:; PROC
MOV A,@R0
XCH A,@R1
MOV @R0,A
INC R0
INC R1
DJNZ R2,XCHRAMBYTE
RET

21,463

社区成员

发帖
与我相关
我的任务
社区描述
汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。
社区管理员
  • 汇编语言
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧