2015年3月8日星期日

网易云课堂Linux内核课程作业1:一个简单汇编代码的分析

关于:豆丁老豆  原创作品转载请注明出处 《Linux内核分析》MOOC课程 http://mooc.study.163.com/course/USTC-1000029000
题目要求在Linux下反汇编下面一段c代码:
int g(int x) 
{
    return x + 7; 

int f(int x) 
    return g(x); 
int main(void) 
    return f(11) + 3; 
}

通过实验楼这个网站,可以使用虚拟的linux环境,截图如下:


反汇编编译代码,执行命令:

gcc -S -o main.s main.c -m32

得到包含汇编代码的main.s文件,如下(忽略了开头以“.”开始的无关信息,第1列是行号):

1 g: 2 pushl %ebp 3 movl %esp, %ebp 4 movl 8(%ebp), %eax ------------(8) 5 addl $7, %eax ---------------(9) 6 popl %ebp 7 ret -----------------------------(10) 8 f: 9 pushl %ebp 10 movl %esp, %ebp 11 subl $4, %esp 12 movl 8(%ebp), %eax -------------(5) 13 movl %eax, (%esp) --------------(6) 14 call g -----------------------(7) 15 leave ---------------------------(11) 16 ret ---------------------------(12) 17 main: 18 pushl %ebp 19 movl %esp, %ebp ---- (1) 20 subl $4, %esp ------- (2) 21 movl $11, (%esp) -----(3) 22 call f --------------(4) 23 addl $3, %eax --------(13) 24 leave 25 ret --------------------(14)

为了了解计算机是如何工作的,我们可以模拟cpu、内存、寄存器等是如何解释执行的这段汇编代码的:

(1) 从main开始,pushl、movl的2行语句是进入一个新函数时对函数调用堆栈进行的例行操作。这里详细解释下:
(1.1) pushl负责占用新内存用来保存原始ebp,执行时又分为subl和movl 2个子操作步骤:subl指令中esp寄存器所指向的栈顶增长(占用新的内存空间),movl指令中保存调用main时在ebp寄存器中保存的调用者堆栈底地址(如果是在Shell中执行main程序,这个堆栈可能属于调用main的shell程序用的)。
(1.2) molv负责初始化ebp,将ebp修改为保存在esp寄存器中的栈顶内存地址,作为main函数的堆栈的底部,此时main函数的堆栈是空的,所以栈顶和栈底都是一个相同的地址。



(2)subl代码将esp中的地址减4个字节(共32 bits),由于堆栈在内存中是向下增长的(内存地址越小,堆栈中的位置越高),所以减小地址,就意味着栈顶增长,增加堆栈的长度,在这里增加了32个bits,看来是准备存放一个long int的数据大小了(long是32位,我们是翻译为32位计算机的汇编代码,因此内存地址都是32位的,图中的“内存地址”实际上应该乘以32,并且降序排列才对,这里简化下,默认+1代表加4个字节,升序标记)。



(3)movl代码将“立即数”11作为一个32位整数放到了 esp 寄存器所指向的内存地址里。对应C代码“return f(11)+3”中,将f函数的参数11保存起来。




(4)call语句调用f函数,因为一个call语句实际上可以分解为push和move语句,其中push如上所述包含2个子指令,负责将eip寄存器中的地址保存到函数调用堆栈中(eip值自动指向下一行第23行,本文用eip(23)代表),栈顶继续增长,然后move指令修改eip寄存器为f的地址(用行号9代表)。由于计算机CPU总是从eip寄存器中获取下一条指令的地址(这就是冯诺依曼的存储程序计算机结构的伟大之处),因此修改eip后,下面CPU将开始执行保存在f函数里的汇编指令了,CPU从准备执行第23行改为执行第9行。



(5)这一步给eax赋值,一直到mov语句之前的代码刚都解释过了,原理和(1)是一样一样的,保存函数调用入口,栈底指向f函数的堆栈底,栈顶升高,在堆栈中准备好保存一个数值,这个数值在下面第(6)步中进行保存。现在看mov语句的赋值,这里mov语句使用“变址寻址”的方式,将ebp值加8(往堆栈的底部方向数8个字节)赋给eax寄存器,eax就定位到了刚才在第(3)步保存的参数11。至于为什么是8个字节,只要知道如果只数4个字节,就是刚才第(4)步中call代码保存的函数入口地址了,再往堆栈底部数4个字节才是那个long int类型的数值11。另外,这里用到了eax寄存器(累加寄存器)来暂存数值11的地址,这行mov代码用伪C代码可以表示为:

eax = *(int32_t *)(ebp + 8)

int32_t的指针类型数值所指向的内存地址的内容,c的指针表示,简单吧!反正听课的时候我是晕晕的 #:-p 就差点儿浆糊了。




(6)这里就是把eax中的值(数字11的内存地址)放到esp栈顶寄存器所指向的内存空间中。这样我们的f函数的堆栈里面就有“11”这个数字可以供函数内部随时使用了。

(7)调用g函数(类似前述修改eip寄存器来实现)




(8)mov和之前的代码都解释过了,保存函数调用入口,找到之前在f函数的堆栈里保存的参数“11”,并暂时放在eax寄存器里。


(9)add语句将11加7,eax = 18




(10)pop语句前面push语句保存函数调用入口的逆过程,这是cpu准备从g函数回到f函数的节奏了。pop语句会将esp寄存器所指向栈顶的值出栈,具体这条代码是将其存放到了ebp寄存器中,同时栈顶降低(加4个字节),堆栈长度缩短。


ret语句继续进行出栈操作,把之前第(7)步call语句在esp中保存的eip地址拿出来赋给eip寄存器,CPU就回到第(7)步call语句的下一句去执行了。

(11)leave语句是回到上一个函数的堆栈中,可分解为mov和pop语句,即先将当前执行函数f的栈底move放到栈顶寄存器中,然后再出栈pop,pop的原理在第(10)步已说明,这里将esp栈顶中保存的上一个函数的栈底地址放回到ebp寄存器中,这样ebp就指向了上一个函数(调用f的g函数)的堆栈栈底,然后栈顶降低,,至此,我们就离开了f函数的堆栈,它可以被OS回收了。堆栈寄存器esp、ebp们都恢复到了调用f前的g函数堆栈使用情况。

f函数使用“leave”和g函数使用“pop ebp”返回上一个调用函数的区别,leave相对多了一个把ebp的值mov到esp里的操作(即修改栈顶,指向栈底),因为g函数没有对栈顶进行修改,所以离开g的时候不需要leave,直接pop出f函数的栈底就好,而f函数对栈顶做了修改(保存参数等操作),所以要用leave先恢复栈顶指向栈底,然后才pop出main函数的栈底。

(12)同第(10),让cpu跳转到main函数。


(13)把eax加3,eax = 18 + 3 = 21


(14)同第(11)和(12),用leave和ret这一组动作就是作废main函数的堆栈,cpu跳转返回到调用main函数的程序去。其中OS会把eax中的值21,作为main函数默认返回值返回。因此如果你在shell里执行main后,再执行 echo $?,shell应该是输出21。

总结:从上面的分析可以看到,cpu是代码的执行者,代码都放在一段内存中(CS段),用eip寄存器去自动指向下一条待执行的指令,这些指令包括mov、add、sub等基本操作和pop、push、leave、ret、call等复合操作,它们对堆栈和寄存器的值进行存取和加减运算,这些操作可以实现c等相对汇编的高级语言中的语义,比如函数调用、加减运算、返回调用者等,堆栈随着函数调用而增长,随着函数使用到的参数而增长,函数完成计算后,中间结果保存到类似eax这样的寄存器中,然后出栈操作使堆栈又缩短还原,直到最后的main函数也执行完毕,堆栈归零。









2012年12月16日星期日

《韵学骊珠》密码

《韵学骊珠》终于看懂怎么用了,方法其实也简单,从头开始读几个例字就能推断了。先记下几个标点符号的意思,备忘也方便其他新手菜鸟使用。
1. 书按韵部分多个章节(每韵注明收音);
2. 每韵分“平、上、去、入”和“阴、阳”等声调分别罗列;
3. 每个声调又分“宫、商、角、徵、羽”5种发音音阻部位,同心圆符号分隔;
4. 每音阻类又按4呼(出音:“开、齐、撮、合”或“鸡鸭鱼肉”的口型)和清浊音分类,大圆圈符号分隔;
5. 每个字(大号字体)下面有小号字体写的解释和切音、本音(指入声字的北曲同音字)、其它读音等(如:“又音xy”,表示其他读音同字x和y),没有的话表示这个字的切音同上一个字。

上面不清楚的看《曲韵易通》即可。还有几个标点符号搞清楚,每个字下面的小圈是分隔符,竖线是代替本字的符号。

练习:查“數”字的4种读音和释义。(目录后面有繁体笔画检字表)
..
..
..
..
(参考答案见下)

本音 shu(包括阴上的动词和阴去的名词,姑模韵),又音 chuk(阴入声,屋读韵,形容词细密也)shuok(阴入声,约略韵,形容词多次)

2011年11月27日星期日

bitcoin thoughts - the value

On bitcoin.org, value of bitcoin is claimed to depended on the requirement of bitcoin user, the more demand the higher the value, not based on the computer power required to produce (mine) bitcoins -- a myth that proposed by Labor Value Theory (LVT).

Since Marxism concluded that only labor can produce new value, and this is a critical point of marxist political economics. I am going to make my own judgement by recovering the related financial knowledges.

Asume marxism theory is correct, then the value of bitcoin is defined by the social necessary labor time needed to "mine" bitcoins, this includes the constant capital: electricity, cost of computers, bitcoin mining softwares and Internet access; and the variable capital: mining workers's labor time.

Considering the bitcoins can be produced almost at no cost if the algorithm is not a hard computing problem and without the total bitcoin number limit, the social necessary labor time of bitcoin almost equals zero. This means bitcoin are almost the same value as paper based currency, which itself has no value, but is only accepted by some goverment to exchange any thing equals the face value. The we have the following propositions:

currency value = face value = can be exchanged with any commodity the goverment has with the same value

bitcoin value = face value = can be exchanged with any commodity the bitcoin community has with the same value

So, instead of increasing bitcoins by the bitcoin network, to maintain a steady value of bitcoins, it should backed by real value possessed by bitcoin community-- such as programming hours, hosting service, ebay items etc. It's like virtual money, but the amount is a predictable computer program result, and the exchange rate with US dollar can be determined by:
1. value possessed by bitcoin community and amount of bitcoins, which determin the real "use value" of bitcoin
2. exchange value of US dollar ( such as 50 dollar = 1 programming hour of one hacker )
3. demand and supply between US dollar and bitcoin
4. There is no 4. :)

Let's try to validate above statements by reviewing:
1. With other conditions keep unchanged, if bitcoin amount is kept up with the total real value which can be provided by the community, then the value represented by bitcoin is steady. This requires the bitcoin network ( the "Sky Net" of bitcoin ) only produce new bitcoins when it needs to buy real new value from the community ( not the old value-- old value already exchanged by bitcoins). Like the Fed bank buy US government's bonds with new USD, and the government pay USD to anyone selling commodity to it. ( The amount of USD needed by the market is always less than the total value of commodity in the market, as USD is M1 of currency, the M1 formular can be found in any textbook of finance ). In practice, it may happens like that: Hacker A has coded a unix tool for 2 hrs, suppose the market value for this 2 hrs is 100 bitcoins, then customer B loan 100 bitcoins from bank C, bank C mines 100 bitcoins with no cost, but only a 100 bitcoin credit record in B's bank statement, then B buys this tool and accurally pay A 100 bitcoins. That is, the more people loan from bitcoin bank to buy new commodities, the more new bitcoins should be mined from nowhere. In bitcoin community, anyone can be a bitcoin bank, only if other users in this community trust them -- I will only trust someone who has enough wealth and very good bank operating reputation.
2. With other conditions keep unchanged, if only US dollar inflats, then one bitcoin can buy more US dollars, the bitcoin exchange rate will go high against USD.
3. With other conditions keep unchanged, if only more people need bitcoins instead of USD, then exchange value of bitcoin ( price of bitcoin in USD) goes up.

Next, I'll wait for anyone who can provide value statements with a Demand based Value Theory. Or continue provide more practical and accurate formulas of bitcoin exchange rate.




2011年8月14日星期日

ASUS TF101 EeePad root and reflash to TW firmware

The week before last week, I bought an Android Pad, the Asus TF101 CN version in Guangzhou, I need to reflash it with some oversea firmware to use google services.
After reading XDA and some other sources, I finally flashed a TW version follow these steps (US/WW version should be the same procedure):

[edit: just run the universal script to root you TF101, following text before section *Change SKU* is obsoleted, and, you don't need change SKU to have TF101 rooted]

0. Before you begin, get a Ubuntu Linux Distro as working environment, because many tool support this platform and it do not need install drivers.
0.1 Familiar with the procedure with posts 1 and 2 from XDA forum.

1. First, we need the root privilege to overwrite system boot image from a TW firmware by using android adb command tool. But the Nvidia Tegra firmware is encrypted, so we need a special nvidia dev tool to flash the BIOS (on the Nvidia Tegra ROM) with a new cracked boot.img file, Download NVFlash tool for linux, it's riped from NVIDIA Linux Dev Tool by some hacker and including the Secure Boot key of Asus TF101. If you have flashed the BIOS already, skip to rooting procedure below.

[# procedure of cracking BIOS]

1.1 Boot into XDA mode and make a backup of current firmware:
#!/bin/bash

export PATH=$PATH:.

# all code copied from http://forum.xda-developers.com/showthread.php?t=1142567

BACKUP_DIR=~/tf101-backup-`date +%Y%m%d%H%M%S`

mkdir $BACKUP_DIR

nvflash --bct transformer.bct --setbct --configfile flash.cfg --bl bootloader.bin --odmdata 0x300d8011 --sbk 0x1682CCD8 0x8A1A43EA 0xA532EEB6 0xECFE1D98 --sync

nvflash --resume --getpartitiontable $BACKUP_DIR/partitiontable.txt

nvflash --resume --read 2 $BACKUP_DIR/02_BCT_raw.img

nvflash --resume --read 3 $BACKUP_DIR/03_PT_raw.img

nvflash --resume --read 4 $BACKUP_DIR/04_EBT_raw.img #bootloader

nvflash --resume --read 5 $BACKUP_DIR/05_SOS_raw.img #recovery

nvflash --resume --read 6 $BACKUP_DIR/06_LNX_raw.img #kernel

nvflash --resume --read 7 $BACKUP_DIR/07_BAK_raw.img

nvflash --resume --read 8 $BACKUP_DIR/08_GP1_raw.img

nvflash --resume --read 9 $BACKUP_DIR/09_APP_raw.img #system

nvflash --resume --read 10 $BACKUP_DIR/10_CAC_raw.img

nvflash --resume --read 11 $BACKUP_DIR/11_MSC_raw.img

nvflash --resume --read 12 $BACKUP_DIR/12_USP_raw.img

nvflash --resume --read 13 $BACKUP_DIR/13_PER_raw.img

nvflash --resume --read 14 $BACKUP_DIR/14_YTU_raw.img

#nvflash --resume --read 15 $BACKUP_DIR/15_UDA_raw.img #media (/!\ ~14/30GB large)

nvflash --resume --read 16 $BACKUP_DIR/16_GPT_raw.img
[#rooting procedure is omited, it's as simple as replace a number in a system file, just read the universal script mentioned above]

1.2 Now we can flash the rooted boot.img:
nvflash -r --download 6 boot.img
 
[#Change SKU] 
2. After reboot, we will have a rooted system, and we can update it with any stock firmware from ASUS of any regional SKU. The command is:

升级过程:
1.解压ROM包中的blob文件到adb目录中
2.将TF101与电脑连接
3. 进入到adb文件目录中
4.输入命令 adb push blob /data/local/
5.稍等几分钟后会提示blob文件就被传送到机身存储中
6.继续输入命令 adb shell
7.此时提示符变为#
8.输入命令 dd if=/data/local/blob of=/dev/block/mmcblk0p4
9.设备会自动重启,开机即为所刷ROM.


At last, I reflashed the rooted boot image with nvflash tool to install some system apps which require a rooted device.


2010年8月1日星期日

Dell mini10v (1011) 多系统启动 w/ hackintosh成功

A little different from Allan Kelly's recipe
1. I preserved the Dell preinstalled hardware diagnosis tools hidden partition.
2. I installed Win 7 ultimate 32bit edition
3. bootloader is using EasyBCD installed on Win 7 ( It's very powerful, and easy to use, I tried this after Chameleon failed to boot itself at the last stage, and Win 7 BCD bootloader is shown up instead )
4. I installed Ubuntu netbook remix 104 LTS instead of standard desktop version.
5. I installed Grub2 with Ubuntu to my /dev/sda, and it's working good.
6. Last but not the least, I installed OS X 10.6.3 with NetBookInstaller 0.8.4 RC1. I avoided upgrade to 10.6.4.
7. I followed this post on MyDellMini forum to fix sleep/awake problem.

more on easyBCD, it can boot Linux, Mac OS X, with my configuration, it's actually boot Grub2 for Linux, and Chameleon for Snow Leopard resp.

My partition layout:
1. Dell HW Diag Tool hidden partition (primary, FAT, 39M)
4. OS X (primary, HFS+, 58G)
2. reinstalled WinXP SP3 (primary, NTFS, 18.78G)
3. extended partition
5. Win 7 (logical, NTFS, 29G)
6. Ubuntu Netbook Remix 104 (logical, ext4, 12G)
7. Ubuntu swap partition (logical, 2G)
8. free space

One last note: I used Asus Super Slim usb HD (size: 30G), instead of usb keydrive which is slow and opt to be worn out very quickly (I bought a 8G Kingston DataTraveller USB keydrive, and have to return it back several times to replace it). And I suggest resize the partitoin on external usb HD to as small as possible to speed the formating/copying process up.

2009年7月7日星期二

recover the Windows XP in bootcamp

Yesterday, I tried to install Ubuntu on an external usb disk for my new macbook (already dual boot with Windows XP and OS X), but I forgot to check how Ubuntu installer would install the Grub, which turns out to make my Windows XP on the internal disk unbootable with a message like this: " GRUB hard disk error".
After some trial and correction, I recovered the XP with following steps:
1. Boot the laptop from XP installation CD
2. When prompted, press R to enter the recovery console
3. fixmbr
4. fixboot

2008年10月15日星期三

用firefox给医改提意见的方法

为了用电邮方式提交你的个人意见给国务院医改小组,而你用的又是firefox,那么为了解决网页上错误的javascript阻止你发表意见,你需要:
1. 安装greasemonkey 插件
2. 访问医改意见反馈网页:http://www.ndrc.gov.cn/yg
3. 添加下列用户脚本:
// ==UserScript==
// @name new
// @namespace my
// @include *
function On_Submit()
{

if(check(document.addForm))
{
window.open('','check_method','width=418,height=200');
document.addForm.submit();
}
}
// ==/UserScript==


第2个脚本:

// ==UserScript==
// @name sum
// @namespace http://2ban
// @include http://www.ndrc.gov.cn/ygyj/vcode.jsp

document.addEventListener('click', function(event) {
// event.target is the element that was clicked

// do whatever you want here
document.forms[0].submit();
// if you want to prevent the default click action
// (such as following a link), use these two commands:



event.stopPropagation();
event.preventDefault();
}, true);

// ==/UserScript==
4. 刷新页面,在出现输入校验码时,用tab键选择输入框输入,不要用鼠标点选,输入完毕后,用鼠标点提交即可。(抱歉我第一次用greasemonkey,javascript也忘了怎么用,所以大家将就用吧)

总之,我希望使用firefox的用户也有参与公共事物、发表个人意见的权力和渠道。