首页
社区
课程
招聘
[翻译]R4ndom破解教程全文翻译(For新手):第三章
发表于: 2015-3-31 19:02 18573

[翻译]R4ndom破解教程全文翻译(For新手):第三章

2015-3-31 19:02
18573

翻译说明:
1、本教程在  52破解论坛  及  看雪论坛  全球同步首发!
2、本教程翻译自国外的The Legend of Random的系列教程,英文原文地址:http://thelegendofrandom.com/blog/sample-page。本翻译教程只是为了给不愿看英文教程以及英文水平不好的人提供方便,同时也是自己的学习过程。该教程对英文水平要求不是很高,不过个人水平有限,有些地方翻译不准的请批评指正。
3、本翻译教程请勿用于商业用途。另,转载请注明!!!
4、感谢The Legend of Random!

发帖说明:
根据译者的时间安排,一般2-3天会上传一章。如果有段时间没更新请勿怪,因为译者的工作性质,有时是接触不到网络的。

其他章节:
全系列章节导航帖

第三章:OllyDbg的使用(上)

本章中我将会介绍OllyDbg的使用。Olly有许多的功能,唯一学好它们的方式是实践和练习。也就是说,本教程也只是给你一个简单的概述。此教程不会涉及额外的内容,后面会进行重点讨论。到最后,你应该会比较好的掌握Olly。
本章包含了一些文件。你能够下载那些文件,以及可以在这里下载到次教程的PDF版本。
它们包括一个我们将在Olly中用到的二进制文件、一个Olly备忘单、我使用的外观上有些不同的Olly以及一个新的ini文件。你可以用这个ini文件替换掉Olly默认的ini,可以给新人提供一些帮助(感谢伟大的Lena151做的这些)。你可以从 这里 直接下载或者从教程页面下载。如果你更愿意用原版的Olly,你可以从这里下载。
一、载入应用
第一步是将目的二进制文件载入Olly。你可以将二进制文件拖放到Olly的反汇编窗口,或者点击顶部工具栏中的载入图标选择目的文件。我们这里载入“FirstProgram.exe”,可以从本网站下载。Olly会进行分析(Olly的底部状态栏会显示分析进程)然后停在程序的入口点(EP):

需要注意的第一件事是EP的地址是401000,就是图片中的第一列。这是可执行文件的一个相当标准的起点(该可执行文件至少没有加过壳或混淆过)。如果你的看起来不太一样,并且Olly没有停在401000,你可以尝试点击Appearance菜单,然后选择debugging options,点击“Events”标签,并且确保“WinMain(if location is known)”被勾选上。然后重启应用。
让我们给“FirstProgram.exe”的内存空间占用情况来张快照。点击“Me”图标(如果你使用的是不同版本的Olly的话应该是“M”):

如果你看地址那一列,你会看到401000那行包含有大小1000、名称“FirstPro”(FirstProgram的简写形式)、区块名“.text”、包含里是“SFX,code”。随着学习进度的展开,我们就会知道exe文件中有不同的区块,包含不同的数据类型。该区块中是程序的“代码”。它有1000字节大,从内存的401000开始。
在这个的下面你会看到FirstProgram的其他区块。其中.rdata区包含着数据,其导入地址是402000,地址403000的.data区中什么都没有。最后的那个.rsrc区中存有资源(比如对话框、图片、文本等)。要注意的是这些区可以叫任何名字,这个完全依赖于程序员。
你可能会问为什么.data区是空的。好吧,它事实上就是那样。它一般包含全局变量和随机数据。Olly只是选择了不显示,因为它确实不知道那里存储了哪种数据。
区段的顶部是一个叫做“Pe Header”的区块。这是一个非常重要的区,一个我们会在将来文章中深入探讨的区。不过对于目前来说,我们只需要知道它对于Windows就像一本指令手册一样,用来按步将文件载入内存,程序运行需要多少空间,还有其他某些事情等。它在大约所有exe的头部(DLL也是一样)。
如果你继续往下看,你可以看到不只是FirstProgram程序,还有其他的文件。我们看到有comctl32, imm32,gdi32,kernel32等。这些DLL是我们程序运行所需要的。DLL是函数的集合,我们的程序能够调用那些Windows已经提供的(或者其他程序员提供的)函数。比如打开对话框、比较字符串、创建窗口以及类似的功能。统称为Windows API。程序使用这些函数的原因是,假如我们写每一个用到的函数,仅仅显示一个消息框就需要数千行的代码。然而,Windows已经提供了像CreateWindow这样的函数来为我们做这些工作。对于程序员来说这使得编程要简单的多。
你或许会问这些DLL是如何进入我们的地址空间的,windows是怎么知道哪一个是我们需要的。好吧,这些信息是存储在上列出的PE Header中的。当Windows将我们的exe载入内存时,它会检查头并找出DLL的名字,以及每个DLL中我们程序需要的函数,然后将这些函数载入我们的程序内存空间,以便于我们的程序调用它们。每个程序被载入内存时,它所需要的DLL也会被载入它的内存空间。可以想象得到,当有好几个程序当前都需要被载入内存并且都需要某个特定的DLL时,那么有些DLL就有可能被载入内存好几次。如果你需要准确的知道我们的程序调用了哪些函数,你可以右键点击Olly 的反汇编窗口,选择“Search for”——>““All Intermodular Calls”。会显示如下图:

这有点惊奇,不过这个列表非常的小。通常,对于一个商业产品来说,需要数百或数千函数。不过因为我们的程序太简单了,它需要的不是很多。你想想我们的程序干了什么,看起来好像是那么多的函数只完成了如此简单的功能!欢迎来到Windows。该窗口首先显示了DLL的名字,紧跟着的是函数的名字。比如,User32.LoadIconA是在DLL User32中,函数名字是LoadIconA。该函数通常用来载入窗口左上角的图标。
下一步,我们搜索下程序中的所有字符串。右键点击反汇编窗口,选择“SearchFor”-> “All Referenced Text Strings”:

该窗口显示了我们程序中所有能找到的字符串。因为程序非常简单,所以这里只有一点。大多数的程序如果没有加壳或混淆的话,都有多得多的字符串(有时能达到十万)。这种情况下,你有可能一个也看不到!加壳工具这样做的原因是逆向工程师(至少新人是这样)严重依赖字符串来查找重要的函数。而删除了字符串后就会难的多。想象一下,如果你搜索字符串然后看到了“Congratulations! You entered the correct serial(恭喜!你输入了正确的序列号)”会怎么样?嗯,这对于逆向来说是巨大的帮助(我们会一次又一次的看到这个)。另外,双击其中的字符串,你会来到反汇编窗口中使用该字符串的指令那。这是一个很好的特性,你能够正确的跳转到使用字符串的代码。
二、运行程序
如果你看Olly的左上角的话,会看到一个黄色背景的小区块,里面写着“暂停(Pause)”。意思是程序已经暂停了(本例中是在开始的时候),等着你进行其他操作。所以,咱们开始干一票吧!按一下F9(或者从“Debug”菜单中选择“Run”)。一会儿后,我们的程序会弹出一个对话框(它有可能显示在Olly的后面,所以最小化Olly以确保能看见窗口)。

刚才显示“Pause”的地方现在应该显示的是“Runing”。意思是程序正在运行,不过是在Olly中运行的。你可能会与我们的程序进行一些交互,看看它是如何工作的以及它干了些什么。如果你不小心关了它的话,返回到Olly并且按下Ctrl+F2(或选择Debug->Restart)以重新载入程序,然后你可以点击F9让程序再一次运行起来。
现在照着做:程序运行的时候,点击回到Olly中,然后点击暂停图标(或点击F12,也可以点击Debug->Pause菜单)。即使我们的程序正在运行,该操作会让程序暂停在内存中的任何地方。如果这时候你想看看程序,你会发现挺有意思的(程序一点也不会显示出来)。这是因为当程序暂停的时候,Windows不会更新视图。现在再一次点击F9,你会发现你又可以和程序进行交互了。如果有什么问题的话,只需要点击那个双左尖括号图标或Debug-restart (或者ctrl-F2),程序就会重新载入并暂停在入口处。如果你需要的话,你可以再一次运行它。
三、单步运行程序
运行一个程序确实挺爽,不过你却得不到有关于程序运行的太多信息。让我们试试单步运行。重新载入应用程序(重新载入按钮、Ctrl+F2或Debug->restart),然后我们会暂停在程序的开始处。按一下F8,你就会发现当前的行选择器下移了一行。Olly运行了一行指令,然后又暂停了下来。如果你够激灵的话,就会发现堆栈区下滚了一行,并且在顶部有了一个新的入口。

这是因为我们执行了一条指令,“PUSH 0”往堆栈里“压”了一个0。在堆栈中的显示是“pModule=NULL”。NULL是0的另一个名字。你有可能也注意到了那个寄存器区,ESP和EIP寄存器变红了。

当一个寄存器变红的时候,这就意味着最后执行的指令修改了该寄存器。本例中,ESP寄存器(用来存放指向栈顶的地址)增加了1,因为我们向栈中压了一个新值。EIP寄存器增加了2,其中存放了将要运行的指令的地址。因为我们已经不在地址401000了,而是在401002。因为上一个运行的指令是两个字节长。我们现在暂停在下一个指令处。这个指令是在401002,这正是当前EIP的值。
Olly现在暂停的指令是一个CALL。CALL指令意味着我们要临时暂停在我们当前所在的函数中,然后去运行另一个函数。这类似于高级语言中的方法调用,举个例子:
int main()
{
    int x = 1;
    call doSomething();
    x = x + 1;
}
这段代码中,我们首先让x等于1,然后呢我们要在逻辑上暂停这行代码,转而去调用doSomething()。当doSomething()执行完毕后,我们会返回我们原来的逻辑,然后将x加1。
当然,在汇编语言里也是一样。我们首先往栈中压了一个0,现在呢我们又想调用一个函数,例子中调用了Kernel32 dll中的GetModuleHandleA():

好,再按一次F8。当前的行指示器会下移一行,而EIP仍然会保持红色并且加了5因为刚刚运行的指令是5字节大小),堆栈也回到了它原来的地方。刚刚发生的这些是从我们按下F8开始的,F8的意思是“Step-Over(单步步过)”,CALL中的代码被调用,然后Olly暂停在了CALL的下一行。也就是CALL中的程序执行了也做了某些事,但是我们跳过去了。
好了,现在我们看看其他的选项。重启程序(Ctrl+F2),按下F8步过第一条指令,不过在CALL指令上我们这次按F7。你会注意到整个窗体都变得不一样了:

这是因为F7“Step-In(单步步入)”那个CALL,意思是Olly做了这个调用并暂停在了新函数的第一行。这种情况下,CALL跳转到了一个新的内存区域(EIP=4012d6)。理论上,如果我们按行通过这个新函数的话,我们最终还是会回到将我们带进来的那个CALL后面的语句。当然,有快捷键可以完成同样的功能,不过目前来说,咱们还是重启程序从头来吧。因为我怕教的太多容易忘。
现在我们暂停在了程序的开始,按下F8(单步步过)4次,我们会停在如下图的语句处:

你会看到在一块的四个PUSH语句。这回当你四次按下F8的时候,注意观察堆栈区,会看到栈的增长(确实是向下增长,还记不记得那个盘子的例子?)。我觉得我们开始理解什么是压栈了......
你可能会问我们为什么要将这些乱七八糟的数字往栈里压。本例中这四个数字是作为参数传递给函数的(那个函数是在地址401021处)。我们将前面的那个高级语言程序做一点点修改就会比较清楚了:
int main()
{
    int x = 1;
    int y = 0;
    call doSomething( x, y );
    x = x + 1;
}
这里我们声明了两个变量x和y,并且将它们传递给了doSomething()函数。doSomething函数将会(可能)对这些变量做些什么,然后将控制权还给调用该函数的程序。通过堆栈是将变量传递给函数的主要方法之一:每个变量被压进堆栈,然后调用函数。然后在函数中,这些变量被访问到。通常PUSH指令的逆操作是POP。
堆栈并不是做这件事的唯一方法,它只是最常用的。这些变量也可以被放到寄存器中,然后在被调用的函数内部访问寄存器。不过本例中,我们程序的编译器选择将变量放到堆栈中。在你学了汇编语言后,这些东西都会变得清晰(你正在学习汇编语言,不是吗?)。后面我们还会复习几次的。
现在,如果我们再按一次F8,你会注意到Olly的工具栏中会显示“Runing”,我们程序的对话框就会显示。这是因为我们单步步过了那个CALL,说明那个CALL中存在程序的大部分。这个调用的代码是等待用户进行一些操作的循环,所以我们永远也不会将控制权交给CALL的下一行。那么,让我们修复它......。点击回到我们的程序,点那个关闭按钮来结束应用。Olly会立即暂停在那个CALL的下一行:

你会注意到我们的程序也消失了。那是因为,在那个CALL的某个地方,对话框窗口被关闭了。如果你看下一行,你会发现我们正准备调用kernel32.dll -> ExitProcess。这是一个停止应用程序的Windows API。所以,基本上Olly在窗口被关闭了之后就暂停了,不过是在程序确实被终止之前。如果你这时按F9,程序就会终止,Olly的活动栏就会显示“Terminated(已终止)”,我们就再也不能调试任何东西了。
四、断点
我们试试别的东西,重新载入应用(Ctrl+F12),然后在地址 401011处的第二列上双击(也就是双击那个“6A 0A”操作码)。然后地址401011就会变红:

你刚才做的就是在地址401011处设置断点。当Olly到达该处时,断点就会强制Olly暂停。有好几种不同的断点会因为不同的事件而阻止程序运行。
1、软件断点(Software Breakpoints)
软件断点就是将断点所在地址处的字节用0xCC操作码替换掉,也就是int 3指令。这是一个特殊的中断,用以告知操作系统调试器希望在这里暂停,并且在执行该指令之前将控制权交给调试器。你不会看到指令被修改成0xCC,因为Olly在背后做了这个。当Olly遇到异常时它会设一个陷阱,让用户做他们希望做的事。如果你选择让程序继续运行(通过运行它或单步运行),0xCC操作码就会被原来的操作码替换回来。为了设置一个操作码,你可以双击操作码那一列,也可以先选中你想设置断点的那一行,然后右键点击它,选择Breakpoints->Troggle(或按下F2)。要删除断点你可以双击同一行,或右键点击选择Breakpoints->Remove Software Breakpoint(或再次按下F2)。
现在我们在401011处设置了一个BP(Breakpoints),让程序暂停在第一行指令处。按下F9,程序将运行并在我们设置的断点处暂停。
这里我告诉大家一些有用的东西。点击工具栏上的“Br”图标或选择菜单中的View->Breakpoints。你会看到一个断点窗口,里面显示了我们设置的断点。

通过它你可以快速的浏览所有你设置的断点。你可以双击任何一个断点,然后反汇编窗口就会跳转到那个断点处(如果你没有改变程序控制流的话,EIP仍然停在原来的地方。双击EIP寄存器会回到当前的行,并准备执行下一行)。
如果你选中一个断点,然后敲击空格键,断点就会在可用和禁用之间来回切换。你可以选中一个断点,然后敲一下“Del”键就会删除断点。
最后,重启程序,打开断点窗口,选中401011处的断点。敲一下空格键,然后“Active”列将会变成“Disable(禁用)”。现在运行程序(F9),你会发现Olly不会停在我们设置的断点处,因为它被禁用了。
2、硬件断点(Hardware Breakpoints)
硬件断点使用的是CPU的调试寄存器。CPU内建的有8个寄存器,是R0-R7。即使芯片中内建了8个,但是我们只能使用四个。它们可以被用来断在内存区的读、写和执行。硬件断点和软件断点的不同之处在于,硬件断点不会修改进程的内存,所以它更可靠,尤其是在加壳或被保护的软件中。通过右键点击相关行可以设置硬件断点,选择Breakpoints,然后选择Hardware,on Execution。

唯一查看你已经设置的内存断点(译者注:这里应该是硬件断点)是打开“Debug”菜单,选择“Hardware Breakpoints”。有个插件可以提供方便,不过我们后面再讨论。

3、内存断点(Memery Breakpoints)
有时候你想查找程序内存中的字符串或在常量,但是你又不知道程序在内存的什么地方。你可以用内存断点来告诉Olly只要任何一条指令读或写一个内存地址(或许多内存地址),然后暂停就行,在任何地方都无所谓。有三种方法设置内存断点。
对于一条指令,右键点击该行,然后选择Breakpoint->Memory, On Access or Memory, On Write。
要在内存数据区设置断点,在数据窗口中选中一个或多个字节,然后右键选择和上面一样的操作。
你也可以对整个内存区域设置断点。打开内存映射窗口(“Me”图标或View->Memery),右键相关内存区域,在弹出菜单中选择“ Set Break On Access for either Access or Write”。

五、内存数据面板的使用
你可以用数据面板检查被调试进程内存空间中的内容。如果反汇编窗口的指令、寄存器或堆栈中的任何一项包含了对内存位置的引用,你可以在该引用上右键然后选择“Follow in Dump”,随即数据面板就会向你显示该地址引用的内容。你也可以在数据面板的任何地方右键单击选择“GoTo”,然后输入要查看的地址。咱们现在试试。
确保FirstProgram已经载入并且停在了入口处。现在,按下F8八次,来到了401021地址指令处,该处指令是CALL FirstPro.40102c。如果你注意看这行的话,会注意到这个CALL会向下跳转到40102c处,在当前行下面的第三行的地方。按下F7我们单步步入那个跳转,然后我们就来到了40102c。记住这是一个CALL指令,所以我们最后还是会回到401021的(至少是该条指令后面的那条)。

现在,单步执行代码(F8)直到401062。你也可以在这行设置断点,然后按F9运行。还记得怎么设置断点吗?双击你想设置断点的那行的操作码列。你也可以选中该行,然后按F2去设置或取消断点。现在我们断在了401062:

现在,我们看看断下的那行。相关指令是MOV [LOCAL.3], FirstPro.00403009。我确定你知道(因为你已经学了汇编语言:P)这条指令是将地址00403009中的内容移动到堆栈中(这里Olly是用LOCAL.3表示的)。你可以在注释列看到Olly已经发现了该地址处的内容是字符串“MyMenu”。好,下面让我们看看。在指令上右键,选择“Follow in Dump(数据窗口跟随)”。注意这里有好几选项:

这里我们选择“Immediate constant”。这回载入指令中的任何地址。如果你选择了“Selection”,数据窗口会显示高亮行所在的地址,这里是401062(也就是我们暂停的地方)。基本上我们在数据窗口中看到的就是我们在反汇编窗口中看到的。最后,如果我们选择了“Memory address”,数据窗口会显示LOCAL.3的内存区域。这会显示我们正在使用的变量(在堆栈中)的内存。下面是选择了Immediate constant之后的数据窗口的样子:

就像你看到的,数据窗口显示的内存是从403009开始的。正是Olly按指令从中载入字符串的地址。在右边你可以看到字符串“MyMenu”。左边你可以看到每个字符的十六进制数据。你可能注意到了在“MyMenu”后面有些其他的字符串。这些字符串会在程序的其他部分被用到。
六、最后,来点有意思的!
此次教程的最后,让我们做些有意思的事情。让我们修改二进制数据来显示我们自己的信息!我们将字符串“Dialog As Main”改成我们自己的,然后看看效果。
首先,在数据窗口的ASCII列,点那个“Dialog As Main”中的“D”:

注意,左边的第一个十六进制数据也高亮了。这个数字对应字母“D”。如果你查查ASCII码表的话,就会发现字母“D”的十六进制数正是0x44。现在,选中整个“Dialog As Main”字符串:

在选中的内容上右键,选择“Binary” -> “Edit”。我们就可以修改我们程序在内存的内容:

然后就会弹出一个如下的窗口:

第一个文本框以ASCII码的形式显示字符串。第二个文本框是以UNICODE形式(我们的程序用不着,所以空着),最后那个文本框是相关字符串的原始数据。好,咱们改一下。点一下字符串的第一个字母(“D”),然后输入任何你想要将“Dialog As Main”覆盖掉的内容。要注意的是你输入的长度,别多了。否则你就会覆盖掉程序需要的其他字符串,或者更糟糕是覆盖掉了程序需要的代码!!!这里呢,我输入的是“Program R4ndom”:

完了之后呢点OK按钮,并允许程序(点Olly的运行按钮或按下F9)。切换到我们的程序,然后随便输入什么都行,然后选择菜单“Option”->"Get Text"。现在看看我们的对话框!

注意到对话框的标题有什么不同没有。

(这一章真TM长啊,翻的我累死了!!!)

附PDF格式文件下载(已排版):
教程三:OllyDbg的使用(上).pdf

附本文相关附件下载(国外链接,有时不好使):
包含Olly快捷键列表、作者使用的OllyDBG、英文视频教程、英文文档、教程中的程序、WIN32.hlp文件

[课程]Linux pwn 探索篇!

上传的附件:
收藏
免费 3
支持
分享
最新回复 (20)
雪    币: 3202
活跃值: (1917)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
好贴细看中。
2015-3-31 19:52
0
雪    币: 1556
活跃值: (848)
能力值: ( LV9,RANK:320 )
在线值:
发帖
回帖
粉丝
3
支持......希望楼主坚持下去
2015-3-31 20:47
0
雪    币: 43
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
支持.....
MARK
收藏
谢谢楼主
2015-3-31 22:12
0
雪    币: 201
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
mark
2015-3-31 22:41
0
雪    币: 7
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
正好看到这里,太巧了,谢谢楼主。。
2015-4-2 17:15
0
雪    币: 7
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
我用的是汉化版,内存映射区段显示的是中文或乱码,怎么改成英文的呢?
2015-4-2 17:16
0
雪    币: 2528
活跃值: (19)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
原网址下载英文版OD
2015-4-2 17:59
0
雪    币: 218
活跃值: (223)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
又回到了10年前。。。
2015-4-2 18:05
0
雪    币: 7
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
好吧。。谢了。。。
2015-4-2 18:35
0
雪    币: 7
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
翻译的很好。支持楼主。。不过,发现几处打错的地方。。
2015-4-2 18:36
0
雪    币: 502
活跃值: (3460)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
好人一生平安!
2015-4-2 19:07
0
雪    币: 511
活跃值: (353)
能力值: ( LV9,RANK:450 )
在线值:
发帖
回帖
粉丝
13
赶进度,还有部分是在手机上翻的,公交车上。所以会存在一些问题。还有些句子不太好。等全部翻完我会修改好,发布整个文档
2015-4-2 20:30
0
雪    币: 615
活跃值: (426)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
很不错,收藏学习了。
2015-4-3 19:35
0
雪    币: 102
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
新手学习的极好资料
2015-4-8 11:12
0
雪    币: 35
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
楼主辛苦了。 继续下一章
2015-5-1 19:10
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
打听一下,哪位知道英文档里的swf动画教程是使用什么工具录制与编辑的?
2015-5-20 13:56
0
雪    币: 2432
活跃值: (44)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
很不错,收藏学习了。
2015-5-31 22:57
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
新人,之前看教程都是模模糊糊的,这次静下心从头学起了。感谢楼主的分享。
2015-10-31 14:22
0
雪    币: 965
活跃值: (89)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
支持......希望楼主坚持下去
2016-12-27 15:21
0
雪    币: 226
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
请问大佬们有这一章的相关附件下载么~~~尤其是那个FirstProgram.exe~链接里那个网站打不开~~谢谢大佬们啦~~
2018-6-7 23:37
0
游客
登录 | 注册 方可回帖
返回
//