PC逆向之代码还原技术,第五讲汇编中乘法的代码还原

一丶简介乘法指令

1.乘法指令

在汇编中,乘法指令使用 IMUL 或者 MUL指令. 一般有两种形式

IMUL reg,imm 这种指令格式是 reg * imm的结果 重新放到reg中.

mul同上

第二种指令格式:

IMUL reg,reg1,imm 这种形式是 reg1寄存器 * imm的结果.放到reg中.

IMUL MUL 一个带有I 一个没有. 这个是有符号相乘跟无符号相乘. 有符号相乘结果是有符号的.无符号相乘

结果是无符号的. 一定要注意.因为在代码还原中.可能一个有无符号没有注意就会吃大亏.博主吃过.

所以一定要注意.

2.代码还原注意问题

我们知道了汇编的乘法指令.那么为什么还要注意产生的问题.原因是这样的.乘法指令在CPU运行的时候

运行周期特别的大. 比如 x * 8 x的是任意一个变量. 8 是一个常量. 那么如果产生以下指令(当然不会产生.举个例子)

mov reg,[ebp - ?] 获得x变量的值
imul reg,8 x * 8结果重新放到reg当中.

假设这样产生的时间周期是100.那么cup就要损耗100.那么有没有什么办法可以优化,有办法.我们可以用

位运算. 我们知道8是2的3次方. 那么完全可以使用下方汇编指令来代替

shl reg,3

shl时钟周期特别低.所以就优化了乘法.

二丶乘法的汇编代码产生的格式

通过上方我们简介了乘法的缺点(时间周期大)我们知道.乘法可以进行优化的.所以我们下方就专门讲解几种

特别的优化方式

1.高级代码观看

int main(int argc, char* argv[])
{
int nValue1 = 3 * 4; //常量 * 常量
scanf("%d",&nValue1); //放置Release优化,所以对变量取地址.这样优化就不会很厉害
printf("值 = %d \r\n",nValue1); int nValue2 = nValue1 * 16; //变量 * 常量 其中常量是2的幂
scanf("%d",&nValue2);
printf("值 = %d \r\n",nValue2); nValue1 = argc;
int nValue3 = nValue1 * 3; //变量 * 常量 常量不是2的幂
scanf("%d",&nValue3);
printf("值 = %d \r\n",nValue3); int nValue5 = nValue1 * nValue2; //变量 * 变量
scanf("%d",&nValue5);
printf("值 = %d \r\n",nValue5); int nValue6 = nValue5 * 3 + 12; //常量 变量 混合运算
return 0;
}

其实观看以上代码,我们可以总结一下乘法的几种方式

1.常量 * 常量

2.变量 * 常量 常量是2的幂

3.变量 * 常量 常量不是2的幂

4.变量 * 变量

总共4中方式.每种方式进行解析

2.乘法的汇编代码还原.

1.常量*常量 汇编代码解析,以及两种新的优化方式的识别

观看过我们以前博客的童鞋应该知道. 编译器在编译的时候.有个优化选项,速度优先还是效率优先

也就是我们说的 o1 跟 o2 如果是o2模式.那么汇编代码就给我们进行最大程度的优化.

常量*常量 在优化中属于常量折叠. 也就是说 常量 * 常量直接可以计算出来了. 就不会产生汇编代码了.

Debug下的汇编

Debug下的汇编并不进行优化.所以直接看着汇编代码进行优化即可.

.text:00401268                 mov     [ebp+var_4], 0Ch
.text:0040126F lea eax, [ebp+var_4]
.text:00401272 push eax
.text:00401273 push offset Format ; "%d"
.text:00401278 call _scanf
.text:0040127D add esp, 8
.text:00401280 mov ecx, [ebp+var_4]
.text:00401283 push ecx
.text:00401284 push offset aD_0 ; "值 = %d \r\n"
.text:00401289 call _printf
.text:0040128E add esp, 8

Release下的汇编

text:00401080 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401080 _main proc near ; CODE XREF: start+AFp
.text:00401080
.text:00401080 var_10 = dword ptr -10h
.text:00401080 var_C = dword ptr -0Ch
.text:00401080 var_8 = dword ptr -8
.text:00401080 var_4 = dword ptr -4
.text:00401080 argc = dword ptr 4
.text:00401080 argv = dword ptr 8
.text:00401080 envp = dword ptr 0Ch
.text:00401080
.text:00401080 sub esp, 10h 开辟局部变量空间
.text:00401083 lea eax, [esp+10h+var_10] 注意从这里开始下方三条汇编指令
.text:00401087 mov [esp+10h+var_10], 0Ch 穿插的流水线优化代码.应该提到上方.
.text:0040108F push eax
.text:00401090 push offset aD_0 ; "%d"
.text:00401095 call _scanf
.text:0040109A mov ecx, [esp+18h+var_10]
.text:0040109E push ecx
.text:0040109F push offset aD ; "值 = %d \r\n"
.text:004010A4 call _printf

在Releas汇编下.常量 * 常量 直接进行优化了. 也就是产生的汇编指令

mov [esp +10h + var_10],0ch

但是上方为什么说让我们注意三条汇编指令

原因是这里CPU又产生了优化方式,以及汇编为什么是esp寻址.而不是ebp寻址.

优化方式: 流水线优化

什么是流水线优化.流水线优化就是 A运行B,B运行C,C进行完成. 原本是这样一条线.但是这样会产生问题

原因?: 因为A在完成B的过程中. B 跟 C是不能运行的,必须等待A进行完成之后才能运行.此时就要进行优化

就是说A在做事的时候.不能占用别人时间.别人也要进行做事.

所以上方的汇编代码我们可以改变一下.不影响结果

优化方式: 平栈优化

关于平栈优化.我们有没有注意到.在使用 scanf printf这种C调用约定的函数.并没有产生Add esp,8

这种操作代码.而Debug下产生了.原因是其实已经产生了.不过可以进行统一优化.在一个函数内.我们可以计算出所有需要优化

的这种C平栈. 在函数底部进行统一的平栈即可.并不会影响程序运行.

高级代码伪代码:
nvalue1 = 3 * 4;
scanf(&nvalue1)
printf(nvalue1) .text:00401087 mov [esp+10h+var_10], 0Ch
.text:00401083 lea eax, [esp+10h+var_10] 这里使用lea 使用了eax下方使用eax这样才配套.
.text:0040108F push eax
.text:00401090 push offset aD_0 ; "%d"
.text:00401095 call _scanf .text:0040109A mov ecx, [esp+18h+var_10]
.text:0040109E push ecx
.text:0040109F push offset aD ; "值 = %d \r\n"
.text:004010A4 call _printf

经过上面我们调整之后,是不是我们观看汇编代码的时候就觉着顺眼了. 比如scanf.这个函数是两个参数.

那么汇编中.就要进行push 两个参数. 并且要传入地址. 观看上方汇编代码.我们得知. lea是取地址.

下面接着push.然后调用scanf完成函数功能. 这个就是流水线优化. 在以后的汇编代码还原中.一定要准确的

定位正确的汇编代码.这样才能最好的进行还原.

注意: 上面是流水线优化代码.但是我们有没有发现.其实我们提到下面.一样不影响程序结果.

2.常量*变量 /变量 * 常量 常量是2的幂 汇编代码解析

高级代码:

int nValue2 = nValue1 * 16; //变量 * 常量 其中常量是2的幂
scanf("%d",&nValue2);
printf("值 = %d \r\n",nValue2);

看上边高级代码.我们知道,常量是一个2的幂. 也就是2的四次方是16.那么这种情况,底层汇编也不会使用

IMUL 指令.原因就是指令周期太长.所以进行优化. 如果是2的幂.我们完全可以进行位操作.左移一位,相当于 *2

Debug下的汇编:

.text:00401291                 mov     edx, [ebp+var_4]  这三行代码是主要代码.
.text:00401294 shl edx, 4
.text:00401297 mov [ebp+var_8], edx .text:0040129A lea eax, [ebp+var_8]
.text:0040129D push eax
.text:0040129E push offset Format ; "%d"
.text:004012A3 call _scanf
.text:004012A8 add esp, 8
.text:004012AB mov ecx, [ebp+var_8]
.text:004012AE push ecx
.text:004012AF push offset aD_0 ; "值 = %d \r\n"
.text:004012B4 call _printf
.text:004012B9 add esp, 8

通过Debug下的汇编.我们可以进行很好的代码还原.例如我们如果根据汇编.则可以还原高级代码为:

var_8 = var_4 << 4; //第一种还原方式. 但是可读性不好.所以我们可以进行更高的代码还原.(这个就是经验了)
var_8 = var_4 * 16; //第二种还原方式. 第二种还原方式才是真正的还原.但是他隐藏了一个2的幂.我们知道的左移4位.那么心里就要知道,左移四位.其实可以还原成 2^4次方.
上方两种还原方式都可以.不过如果还原的代码以后是很有用的.那么必须强迫自己还原为第二种方式.可以锻炼自己.也可以在逆向中学习更好的经验.

Release下的汇编:

.text:004010A9                 mov     edx, [esp+20h+var_10]
.text:004010AD lea eax, [esp+20h+var_C]
.text:004010B1 shl edx, 4
.text:004010B4 push eax
.text:004010B5 push offset aD_0 ; "%d"
.text:004010BA mov [esp+28h+var_C], edx
.text:004010BE call _scanf
.text:004010C3 mov ecx, [esp+28h+var_C]
.text:004010C7 push ecx
.text:004010C8 push offset aD ; "值 = %d \r\n"
.text:004010CD call _printf

Release下的代码是有流水线优化的.我们可以自己提出代码.观看汇编上下文提出代码进行还原.

汇编代码如下:

.text:004010A9                 mov     edx, [esp+20h+var_10]
.text:004010B1 shl edx, 4 代码外提. edx使用,下方也接着对edx操作.进行还原
.text:004010BA mov [esp+28h+var_C], edx .text:004010AD lea eax, [esp+20h+var_C]
.text:004010B4 push eax
.text:004010B5 push offset aD_0 ; "%d" .text:004010BE call _scanf
.text:004010C3 mov ecx, [esp+28h+var_C]
.text:004010C7 push ecx
.text:004010C8 push offset aD ; "值 = %d \r\n"
.text:004010CD call _printf

我们优化后的Release汇编代码.其实自己代码外提之后,跟Debug下汇编一样. 所以还原Releas下的汇编的

时候.有一个小技巧. 比如流水线优化. 我们自己提的时候. 可以观看汇编上下文. 比如上方汇编指令

mov edx,[var_10]; 如果是流水线优化.那么下方肯定跟edx寄存器无关的汇编指令.这个就是优化.

不过我们可以使用IDA打开.点中edx.那么edx就会高亮.就可以看出操作edx的汇编指令. 我们提出来.

根据上下文.只要不会影响结果就没有事.

Releas下汇编可以还原的高级代码为:

var_c = edx << 4;
var_c = edx * 16;

3. 变量 * 常量 常量是非2的幂

高级代码如下:

int main(int argc, char* argv[])
{ int nCount = 0;
scanf("%d",&nCount);
nCount = nCount * 15; printf("%d",nCount);
return 0;
}

着重讲解 Release Debug版本直接对着汇编还原即可.

.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main proc near ; CODE XREF: start+AF↓p
.text:00401000
.text:00401000 var_4 = dword ptr -4
.text:00401000 argc = dword ptr 4
.text:00401000 argv = dword ptr 8
.text:00401000 envp = dword ptr 0Ch
.text:00401000
.text:00401000 push ecx
.text:00401001 lea eax, [esp+4+var_4]
.text:00401005 mov [esp+4+var_4], 0
.text:0040100D push eax
.text:0040100E push offset aD ; "%d"
.text:00401013 call _scanf .text:00401018 mov eax, [esp+0Ch+var_4]
.text:0040101C lea eax, [eax+eax*2] //核心代码位置
.text:0040101F lea eax, [eax+eax*4] .text:00401022 push eax
.text:00401023 push offset aD ; "%d"
.text:00401028 mov [esp+14h+var_4], eax
.text:0040102C call _printf
.text:00401031 xor eax, eax
.text:00401033 add esp, 14h
.text:00401036 retn
.text:00401036 _main endp

提取出的核心汇编如下:

.text:00401018                 mov     eax, [esp+0Ch+var_4]
.text:0040101C lea eax, [eax+eax*2] //核心代码位置
.text:0040101F lea eax, [eax+eax*4]

首先 Var_4 设为 我们的局部变量

lea eax,[eax + eax * 2] 这个公式其实实在计算. eax + eax *2 按照数学公式可以转换为 3eax

lea eax,[eax + eax * 4] 一样是进行计算.上面的eax我们已经知道是 3eax 带入公式则得到 3eax + 3eax * 4 ===> 提取出来 = (3 * 4)eax + 3eax = 12eax + 3eax 继续优化 => 15 eax

此时eax我们知道是我们的局部变量. 所以求的就是 15 * 局部变量. 在高级代码中的表现形式也就是 nCount * 15

VS2019中的优化

.text:00401099                 mov     ecx, [ebp+var_4]
.text:0040109C shl ecx, 4
.text:0040109F sub ecx, [ebp+var_4]

ecx = nCount

ecx << 4 ====> nCount * 2^4

sub ecx,nCount

这种优化方式也是很巧妙的. 首先编译器尝试 + 1 + 1之后 = 16 16就可以优化为2^4次方.

但是最终结果是
15 .所以计算出的结果 -去自己本身. 也是*15

公式:

x * 2^n -x

4.乘法的混合运算

高级代码:

nValue1 = argc;
int nValue3 = nValue1 * 3; //变量 * 常量 常量不是2的幂
scanf("%d",&nValue3);
printf("值 = %d \r\n",nValue3);

Debug下的汇编:

.text:004012BC                 mov     edx, [ebp+argc]
.text:004012BF mov [ebp+var_4], edx
.text:004012C2 mov eax, [ebp+var_4]
.text:004012C5 imul eax, 3
.text:004012C8 mov [ebp+var_C], eax
.text:004012CB lea ecx, [ebp+var_C]
.text:004012CE push ecx
.text:004012CF push offset Format ; "%d"
.text:004012D4 call _scanf
.text:004012D9 add esp, 8
.text:004012DC mov edx, [ebp+var_C]
.text:004012DF push edx
.text:004012E0 push offset aD_0 ; "值 = %d \r\n"
.text:004012E5 call _printf
.text:004012EA add esp, 8

Debug下的汇编.代码不进行优化. 因为不是2的幂.所以直接使用指令Imul指令.

Releas下的汇编

.text:004010D2                 mov     eax, [esp+30h+argc]
.text:004010D6 mov [esp+30h+var_10], eax
.text:004010DA lea edx, [eax+eax*2]
.text:004010DD lea eax, [esp+30h+var_8]
.text:004010E1 push eax
.text:004010E2 push offset aD_0 ; "%d"
.text:004010E7 mov [esp+38h+var_8], edx
.text:004010EB call _scanf
.text:004010F0 mov ecx, [esp+38h+var_8]
.text:004010F4 push ecx
.text:004010F5 push offset aD ; "值 = %d \r\n"
.text:004010FA call _printf
.text:004010FF mov edx, [esp+40h+var_C]
.text:00401103 lea eax, [esp+40h+var_4]
.text:00401107 imul edx, [esp+40h+var_10]
.text:0040110C push eax
.text:0040110D push offset aD_0 ; "%d"
.text:00401112 mov [esp+48h+var_4], edx
.text:00401116 call _scanf

首先Release下的汇编,乘法直接使用lea指令进行计算了.

lea指令:

lea是运算指令.效率还是比IMUL MUL指令周期短. 它的特点是计算地址.算数运算.

如下代码:

mov eax,[00401000]
lea eax,[00401000]

上面两个指令一个是mov 一个是lea.指令不一样,效果也不一样.

mov eax,[00401000] 是获取00401000这个地址里面的值. 所以eax = [00401000]

lea eax,[00401000] 是直接将00401000给eax保存了.并不获取里面的值.虽然有[]取值运算符.

指令明白了.那么观看Release下的汇编就明白了.

去掉流水线优化:

.text:004010D2                 mov     eax, [esp+30h+argc]
.text:004010D6 mov [esp+30h+var_10], eax .text:004010DA lea edx, [eax+eax*2]
.text:004010E7 mov [esp+38h+var_8], edx 更改过得代码. .text:004010DD lea eax, [esp+30h+var_8]
.text:004010E1 push eax
.text:004010E2 push offset aD_0 ; "%d"
.text:004010EB call _scanf .text:004010F0 mov ecx, [esp+38h+var_8]
.text:004010F4 push ecx
.text:004010F5 push offset aD ; "值 = %d \r\n"
.text:004010FA call _printf

根据汇编代码我们可以进行还原:

.text:004010D2                 mov     eax, [esp+30h+argc]
.text:004010D6 mov [esp+30h+var_10], eax
这两句还原为:
nVar10 = argc; .text:004010DA lea edx, [eax+eax*2]
.text:004010E7 mov [esp+38h+var_8], edx 更改过得代码.
这两句可以还原为:
edx = argc + argc * 2; 第一种方式
edx = argc * 3; 第二种方式 为什么这里是3. 原因是 argc + argc * 2;等价于就是argc *3;
因为在数学上 * 一个数.都可以用加法去替换.
比如:
2 * 3;
我们可以替换为: 2 + 2 + 2 所以我们按照第二种方式进行还原的时候.主要也是看经验.慢慢提升自己.

4.变量*变量

高级代码:

int nValue5 = nValue1 * nValue2; //变量 * 变量
scanf("%d",&nValue5);
printf("值 = %d \r\n",nValue5);

Debug下的汇编:

.text:004012ED                 mov     eax, [ebp+var_4]
.text:004012F0 imul eax, [ebp+var_8]
.text:004012F4 mov [ebp+var_10], eax
.text:004012F7 lea ecx, [ebp+var_10]
.text:004012FA push ecx
.text:004012FB push offset Format ; "%d"
.text:00401300 call _scanf
.text:00401305 add esp, 8
.text:00401308 mov edx, [ebp+var_10]
.text:0040130B push edx
.text:0040130C push offset aD_0 ; "值 = %d \r\n"
.text:00401311 call _printf
.text:00401316 add esp, 8

Debug下.汇编代码就很简单了.直接对着进行还原就行.如上面汇编代码我们可以还原为:

var_10 = var_4 * var_8;

Releas下的汇编:

在Releas下.除了进行流水线优化.等必要的优化.变量 * 变量是无法进行优化了.也是直接使用指令了.

我们去掉流水线优化进行汇编代码还原即可.

有流水线的汇编代码:

.text:004010FF                 mov     edx, [esp+40h+var_C]
.text:00401103 lea eax, [esp+40h+var_4] 流水线代码.eax下方没有.为的就是打乱edx.避免操作edx的时候.下方指令进行等待.
.text:00401107 imul edx, [esp+40h+var_10]
.text:0040110C push eax
.text:0040110D push offset aD_0 ; "%d"
.text:00401112 mov [esp+48h+var_4], edx
.text:00401116 call _scanf
.text:0040111B mov ecx, [esp+48h+var_4]
.text:0040111F push ecx
.text:00401120 push offset aD ; "值 = %d \r\n"
.text:00401125 call _printf
.text:0040112A add esp, 40h
.text:0040112D xor eax, eax
.text:0040112F add esp, 10h
.text:00401132 retn

无流水线的汇编代码:

.text:004010FF                 mov     edx, [esp+40h+var_C]
.text:00401107 imul edx, [esp+40h+var_10]
.text:00401112 mov [esp+48h+var_4], edx 去掉流水线,代码提上来. .text:00401103 lea eax, [esp+40h+var_4]
.text:0040110C push eax
.text:0040110D push offset aD_0 ; "%d" .text:00401116 call _scanf
.text:0040111B mov ecx, [esp+48h+var_4]
.text:0040111F push ecx
.text:00401120 push offset aD ; "值 = %d \r\n"
.text:00401125 call _printf
.text:0040112A add esp, 40h 平栈优化.一起进行平栈.
.text:0040112D xor eax, eax
.text:0040112F add esp, 10h
.text:00401132 retn

去掉流水线.其实代码跟Debug下是一样的.一样进行还原.还原代码如下:

var_4 = var_c * var_10;

三丶乘法总结

乘法其实还是很简单的.只要掌握了以下几点.那么就没有一点问题了.

1.认识平栈优化.以及流水线优化. 自己会外提代码.

2.常量 * 常量 进行了常量折叠优化.也就是直接计算出来了.不会产生汇编代码.

3.变量常量 常量是2的幂的时候. 优化使用 shl等移位指令进行优化

3.变量 * 常量 常量不是2的幂 那么直接使用乘法指令了 MUL / IMUL

4.变量
变量 + 常量 等混合运算的时候.使用 lea指令进行计算了.不会使用IMUL/MUL

5.核心点就是 常量非2的幂的时候怎么进行的优化. 比如 可以通过lea指令 也可以转为2n- 自身

PC逆向之代码还原技术,第五讲汇编中乘法的代码还原的更多相关文章

  1. PC逆向之代码还原技术,第三讲汇编中加法的代码还原

    目录 PC逆向之代码还原技术,第三讲汇编中加法的代码还原 一丶汇编简介 二丶高级代码对应汇编观看. 1.代码还原解析: 总结 PC逆向之代码还原技术,第三讲汇编中加法的代码还原 一丶汇编简介 在讲解加 ...

  2. PC逆向之代码还原技术,第四讲汇编中减法的代码还原

    目录 PC逆向之代码还原技术,第四讲汇编中减法的代码还原 一丶汇编简介 二丶高级代码对应汇编观看. 1.代码还原解析: 三丶根据高级代码IDA反汇编的完整代码 四丶知识总结 PC逆向之代码还原技术,第 ...

  3. PC逆向之代码还原技术,第二讲寻找程序入口点

    PC逆向之代码还原技术,第二讲寻找程序入口点 一丶简介 程序逆向的时候.我们需要知道程序入口点.动态分析的时候以便于看是什么程序编写的. 比如VC++6.0 我们可以写一个程序测试一下 我们写一段代码 ...

  4. PC逆向之代码还原技术,第一讲基本数据类型在内存中的表现形式.浮点,指针寻址公式

    目录 代码还原技术 一丶简介代码还原 二丶代码还原中的数据类型表现形式 1.整数类型 2.无符号整数 3.有符号整数 4.浮点数数据类型 5.浮点编码 4.Double类型解析. 三丶浮点汇编 1.浮 ...

  5. PC逆向之代码还原技术,第六讲汇编中除法代码还原以及原理第二讲,被除数是正数 除数非2的幂

    目录 一丶简介 二丶代码还原讲解 1.被除数无符号 除数非2的幂 2.被除数无符号 除数为特例7 三丶代码还原总结 一丶简介 上一篇博客说的除2的幂. 如果被除数是有符号的,那么会进行调整,并使用位操 ...

  6. 大脸猫讲逆向之ARM汇编中PC寄存器详解

    i春秋作家:v4ever 近日,在研究一些开源native层hook方案的实现方式,并据此对ARM汇编层中容易出问题的一些地方做了整理,以便后来人能有从中有所收获并应用于现实问题中.当然,文中许多介绍 ...

  7. 大型.NET商业软件代码保护技术 技术与实践相结合保护辛苦创造的劳动成果

    列举工作以来遇到的各种类型的软件所采用的代码保护技术,只讲原理不涉及技术细节实现,以避免产生法律问题.有些朋友说直接把代码放在Github开源下载,开源可以促进技术交流与进步,然而值钱的代码都积压在硬 ...

  8. 浅谈android代码保护技术_加固

    可看原文: http://www.cnblogs.com/jiaoxiake/p/6536824.html 导语 我们知道Android中的反编译工作越来越让人操作熟练,我们辛苦的开发出一个apk,结 ...

  9. 浅谈android代码保护技术_ 加固

    浅谈android代码保护技术_加固 导语 我们知道Android中的反编译工作越来越让人操作熟练,我们辛苦的开发出一个apk,结果被人反编译了,那心情真心不舒服.虽然我们混淆,做到native层,但 ...

随机推荐

  1. VB生成条形码(EAN-13)

    14年给别人写的一个库存软件,用到扫码枪,所以就有了这个类. 编码规则相对简单,详见百度百科EAN-13 示例运行效果如下: 类模块:cEAN13.cls Option Explicit '★━┳━━ ...

  2. MyBatis(九) 使用association定义单个对象的封装规则

    (1)接口中编写方法 public Emp getEmpandDept(); (2)编写Mapper文件 <resultMap type="com.eu.bean.Emp" ...

  3. React事件绑定几种方法测试

    前提 es6写法的类方法默认没有绑定this,不手动绑定this值为undefined. 因此讨论以下几种绑定方式. 一.构造函数constructor中用bind绑定 class App exten ...

  4. 20175324 《Java程序设计》第七周学习总结

    教材学习内容总结 常用实用类 String类 - 程序可以直接使用String类,但不能进行扩展,即String类不可以有子类 - 常用构造方法 - String(char a[])用一个字符数组a创 ...

  5. vue组件里定时器销毁问题

    我在a页面写一个定时,让他每秒钟打印一个1,然后跳转到b页面,此时可以看到,定时器依然在执行.这样是非常消耗性能的.如下图所示: 解决方法1: 首先我在data函数里面进行定义定时器名称: data( ...

  6. 在IIS上新发布的网站,样式与js资源文件加载不到(资源文件和网页同一个域名下)

    在IIS上新发布的网站,网站能打开,但样式与js资源文件加载不到(资源文件和网页是同一个域名下,例如:网页www.xxx.com/index.aspx,图片www.xxx.com/pic.png). ...

  7. [转]Understanding OpenStack Authentication: Keystone PKI

    The latest stable release of OpenStack, codenamed Grizzly, revolutionizes the way user authenticatio ...

  8. 安装与配置Flutter开发环境

    这篇博客我们介绍了Flutter,并且对比了H5,React Native,Flutter. 由于Flutter是跨平台的开发框架,开发一次可以同时运行在Android和iOS上面,所以我们开发时最好 ...

  9. 大数据与云计算的关系是什么,Hadoop又如何参与其中?Nosql在什么位置,与BI又有什么关系?

    大数据与云计算的关系是什么,Hadoop又如何参与其中,Nosql在什么位置,与BI又有什么关系?以下这篇文字讲他们的关系讲的非常清楚.  在谈大数据的时候,首先谈到的就是大数据的4V特性,即类型复杂 ...

  10. [Swift]LeetCode90. 子集 II | Subsets II

    Given a collection of integers that might contain duplicates, nums, return all possible subsets (the ...